Move session related responsibilities from ChatsFlowFlowCoordinator to UserSessionFlowCoordinator.

Specifically: onboarding, session verification and logout.
This commit is contained in:
Doug
2025-07-30 15:19:56 +01:00
committed by Doug
parent 1ca84c8c94
commit 6307534341
9 changed files with 342 additions and 217 deletions
@@ -17,6 +17,8 @@ import SwiftUI
let selectedIcon: KeyPath<CompoundIcons, Image>
}
// MARK: Tabs
fileprivate struct TabModule: Identifiable {
let module: NavigationModule
let title: String
@@ -58,8 +60,108 @@ import SwiftUI
}
}
// MARK: Sheets
fileprivate var sheetModule: NavigationModule? {
didSet {
if let oldValue {
logPresentationChange("Remove sheet", oldValue)
oldValue.tearDown()
}
if let sheetModule {
logPresentationChange("Set sheet", sheetModule)
sheetModule.coordinator?.start()
}
}
}
var presentationDetents: Set<PresentationDetent> = []
/// The currently presented sheet coordinator.
var sheetCoordinator: (any CoordinatorProtocol)? {
sheetModule?.coordinator
}
/// Present a sheet on top of the stack. If this NavigationStackCoordinator is embedded within a NavigationSplitCoordinator
/// then the presentation will be proxied to the split
/// - Parameters:
/// - coordinator: the coordinator to display
/// - animated: whether to animate the transition or not. Default is true
/// - dismissalCallback: called when the sheet has been dismissed, programatically or otherwise
func setSheetCoordinator(_ coordinator: (any CoordinatorProtocol)?, animated: Bool = true, dismissalCallback: (() -> Void)? = nil) {
guard let coordinator else {
sheetModule = nil
return
}
if sheetModule?.coordinator === coordinator {
fatalError("Cannot use the same coordinator more than once")
}
var transaction = Transaction()
transaction.disablesAnimations = !animated
withTransaction(transaction) {
sheetModule = NavigationModule(coordinator, dismissalCallback: dismissalCallback)
}
}
// MARK: Full Screen Cover
fileprivate var fullScreenCoverModule: NavigationModule? {
didSet {
if let oldValue {
logPresentationChange("Remove fullscreen cover", oldValue)
oldValue.tearDown()
}
if let fullScreenCoverModule {
logPresentationChange("Set fullscreen cover", fullScreenCoverModule)
fullScreenCoverModule.coordinator?.start()
}
}
}
/// The currently presented fullscreen cover coordinator
/// Fullscreen covers will be presented through the NavigationSplitCoordinator if provided
var fullScreenCoverCoordinator: (any CoordinatorProtocol)? {
fullScreenCoverModule?.coordinator
}
/// Present a fullscreen cover on top of the stack. If this NavigationStackCoordinator is embedded within a NavigationSplitCoordinator
/// then the presentation will be proxied to the split
/// - Parameters:
/// - coordinator: the coordinator to display
/// - animated: whether to animate the transition or not. Default is true
/// - dismissalCallback: called when the fullscreen cover has been dismissed, programatically or otherwise
func setFullScreenCoverCoordinator(_ coordinator: (any CoordinatorProtocol)?, animated: Bool = true, dismissalCallback: (() -> Void)? = nil) {
guard let coordinator else {
fullScreenCoverModule = nil
return
}
if fullScreenCoverModule?.coordinator === coordinator {
fatalError("Cannot use the same coordinator more than once")
}
var transaction = Transaction()
transaction.disablesAnimations = !animated
withTransaction(transaction) {
fullScreenCoverModule = NavigationModule(coordinator, dismissalCallback: dismissalCallback)
}
}
// MARK: - CoordinatorProtocol
/// No idea if this is particuarly needed for the TabView but we do this for the NavigationStackCoordinator and NavigationSplitCoordinator so it
/// doesn't seem to harm to also do it here.
func stop() {
tabModules.forEach { $0.module.tearDown() }
}
func toPresentable() -> AnyView {
AnyView(NavigationTabCoordinatorView(navigationTabCoordinator: self))
}
@@ -99,5 +201,13 @@ private struct NavigationTabCoordinatorView: View {
.id(module.id)
}
}
.sheet(item: $navigationTabCoordinator.sheetModule) { module in
module.coordinator?.toPresentable()
.id(module.id)
}
.fullScreenCover(item: $navigationTabCoordinator.fullScreenCoverModule) { module in
module.coordinator?.toPresentable()
.id(module.id)
}
}
}