jellyflood/Shared/Coordinators/Navigation/NavigationInjectionView.swift

107 lines
3.3 KiB
Swift

//
// Swiftfin is subject to the terms of the Mozilla Public
// License, v2.0. If a copy of the MPL was not distributed with this
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
//
// Copyright (c) 2025 Jellyfin & Jellyfin Contributors
//
import PreferencesView
import SwiftUI
import Transmission
// TODO: have full screen zoom presentation zoom from/to center
// - probably need to make mock view with matching ids
// TODO: have presentation dismissal be through preference keys
// - issue with all of the VC/view wrapping
extension EnvironmentValues {
@Entry
var presentationControllerShouldDismiss: Binding<Bool> = .constant(true)
}
struct NavigationInjectionView: View {
@StateObject
private var coordinator: NavigationCoordinator
@EnvironmentObject
private var rootCoordinator: RootCoordinator
@State
private var isPresentationInteractive: Bool = true
private let content: AnyView
init(
coordinator: @autoclosure @escaping () -> NavigationCoordinator,
@ViewBuilder content: @escaping () -> some View
) {
_coordinator = StateObject(wrappedValue: coordinator())
self.content = AnyView(content())
}
var body: some View {
NavigationStack(path: $coordinator.path) {
content
.navigationDestination(for: NavigationRoute.self) { route in
route.destination
}
}
.environment(
\.router,
.init(
navigationCoordinator: coordinator,
rootCoordinator: rootCoordinator
)
)
.sheet(
item: $coordinator.presentedSheet
) {
coordinator.presentedSheet = nil
} content: { route in
let newCoordinator = NavigationCoordinator()
NavigationInjectionView(coordinator: newCoordinator) {
route.destination
}
}
#if os(tvOS)
.fullScreenCover(
item: $coordinator.presentedFullScreen
) { route in
let newCoordinator = NavigationCoordinator()
NavigationInjectionView(coordinator: newCoordinator) {
route.destination
}
}
#else
.presentation(
$coordinator.presentedFullScreen,
transition: .zoomIfAvailable(
options: .init(
dimmingVisualEffect: .systemThickMaterialDark,
options: .init(
isInteractive: isPresentationInteractive
)
),
otherwise: .slide(.init(edge: .bottom), options: .init(isInteractive: isPresentationInteractive))
)
) { routeBinding, _ in
let vc = UIPreferencesHostingController {
NavigationInjectionView(coordinator: .init()) {
routeBinding.wrappedValue.destination
.environment(\.presentationControllerShouldDismiss, $isPresentationInteractive)
}
}
// TODO: presentation options for customizing background color, dimming effect, etc.
vc.view.backgroundColor = .black
return vc
}
#endif
}
}