Improve full screen support

This commit introduces new mechanisms to solve some issues with full screen support:
1. Full screen covers disappear when its caller disappears (e.g. when it
   is an event in a lazy stack, and an orientation change causes the
   view to disappear along with the full screen cover)
2. There are no mechanisms for views to determine whether they are being
   presented under a full screen cover or not, and whether the device is
   in full screen mode or not.

The commit overcomes the above limitations through the following:
1. A full screen cover on `ContentView` that can be accessed by any view
   when calling `present(fullScreenItem)`
2. A new `damus_full_screen_cover` view modifier that automatically
   tracks whether a device is in full screen mode or not, and allows
   any descendant view in its hierarchy to introspect on which view
   layer it is being presented in.

This commit lays a foundation that will later become important for
improving video coordination.

Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit is contained in:
Daniel D’Aquino
2024-11-01 18:12:49 -07:00
parent a56a59f81d
commit 1bc660c9cd
7 changed files with 253 additions and 3 deletions

View File

@@ -57,6 +57,41 @@ enum Sheets: Identifiable {
}
}
/// An item to be presented full screen in a mechanism that is more robust for timeline views.
///
/// ## Implementation notes
///
/// This is part of the `present(full_screen_item: FullScreenItem)` interface that allows views in a timeline to show something full-screen without the lazy stack issues
/// Full screen cover modifiers are not suitable in those cases because device orientation changes or programmatic scroll commands will cause the view to be unloaded along with the cover,
/// causing the user to lose the full screen view randomly.
///
/// The `ContentView` is responsible for handling these objects
///
/// New items can be added as needed.
///
enum FullScreenItem: Identifiable, Equatable {
/// A full screen media carousel for images and videos.
case full_screen_carousel(urls: [MediaUrl], selectedIndex: Binding<Int>)
var id: String {
switch self {
case .full_screen_carousel(let urls, _): return "full_screen_carousel:\(urls.map(\.url))"
}
}
static func == (lhs: FullScreenItem, rhs: FullScreenItem) -> Bool {
return lhs.id == rhs.id
}
/// The view to display the item
func view(damus_state: DamusState) -> some View {
switch self {
case .full_screen_carousel(let urls, let selectedIndex):
return FullScreenCarouselView<AnyView>(video_coordinator: damus_state.video, urls: urls, settings: damus_state.settings, selectedIndex: selectedIndex)
}
}
}
func present_sheet(_ sheet: Sheets) {
notify(.present_sheet(sheet))
}
@@ -78,6 +113,7 @@ struct ContentView: View {
@Environment(\.scenePhase) var scenePhase
@State var active_sheet: Sheets? = nil
@State var active_full_screen_item: FullScreenItem? = nil
@State var damus_state: DamusState!
@State var menu_subtitle: String? = nil
@SceneStorage("ContentView.selected_timeline") var selected_timeline: Timeline = .home {
@@ -245,6 +281,9 @@ struct ContentView: View {
}
}
.navigationViewStyle(.stack)
.damus_full_screen_cover($active_full_screen_item, damus_state: damus, content: { item in
return item.view(damus_state: damus)
})
.overlay(alignment: .bottom) {
if !hide_bar {
if !isSideBarOpened {
@@ -421,6 +460,9 @@ struct ContentView: View {
.onReceive(handle_notify(.present_sheet)) { sheet in
self.active_sheet = sheet
}
.onReceive(handle_notify(.present_full_screen_item)) { item in
self.active_full_screen_item = item
}
.onReceive(handle_notify(.zapping)) { zap_ev in
guard !zap_ev.is_custom else {
return