Move navigation coordination away from the SettingsScreenCoordinator and into the SettingsFlowCoordinator

This commit is contained in:
Stefan Ceriu
2024-01-05 12:54:57 +02:00
parent c40435b419
commit 8e4e9603e4
8 changed files with 209 additions and 223 deletions
@@ -18,32 +18,31 @@ import Combine
import SwiftUI
struct SettingsScreenCoordinatorParameters {
weak var navigationStackCoordinator: NavigationStackCoordinator?
let userIndicatorController: UserIndicatorControllerProtocol
let userSession: UserSessionProtocol
let appLockService: AppLockServiceProtocol
let bugReportService: BugReportServiceProtocol
let notificationSettings: NotificationSettingsProxyProtocol
let secureBackupController: SecureBackupControllerProtocol
let appSettings: AppSettings
}
enum SettingsScreenCoordinatorAction {
case dismiss
case logout
case clearCache
case secureBackup
/// Logout without a confirmation. The user forgot their PIN.
case forceLogout
case userDetails
case accountProfile
case analytics
case appLock
case bugReport
case about
case sessionVerification
case accountSessions
case notifications
case advancedSettings
case developerOptions
}
final class SettingsScreenCoordinator: CoordinatorProtocol {
private let parameters: SettingsScreenCoordinatorParameters
private var viewModel: SettingsScreenViewModelProtocol
// periphery:ignore - retaining purpose
private var appLockSetupFlowCoordinator: AppLockSetupFlowCoordinator?
private let actionsSubject: PassthroughSubject<SettingsScreenCoordinatorAction, Never> = .init()
private var cancellables = Set<AnyCancellable>()
@@ -67,29 +66,29 @@ final class SettingsScreenCoordinator: CoordinatorProtocol {
case .close:
actionsSubject.send(.dismiss)
case .userDetails:
presentUserDetailsEditScreen()
actionsSubject.send(.userDetails)
case .accountProfile:
presentAccountProfileURL()
actionsSubject.send(.accountProfile)
case .analytics:
presentAnalyticsScreen()
actionsSubject.send(.analytics)
case .appLock:
presentAppLockSetupFlow()
actionsSubject.send(.appLock)
case .reportBug:
presentBugReportScreen()
actionsSubject.send(.bugReport)
case .about:
presentLegalInformationScreen()
actionsSubject.send(.about)
case .sessionVerification:
presentSessionVerificationScreen()
actionsSubject.send(.sessionVerification)
case .secureBackup:
actionsSubject.send(.secureBackup)
case .accountSessionsList:
presentAccountSessionsListURL()
actionsSubject.send(.accountSessions)
case .notifications:
presentNotificationSettings()
actionsSubject.send(.notifications)
case .advancedSettings:
self.presentAdvancedSettings()
actionsSubject.send(.advancedSettings)
case .developerOptions:
presentDeveloperOptions()
actionsSubject.send(.developerOptions)
case .logout:
actionsSubject.send(.logout)
}
@@ -102,163 +101,4 @@ final class SettingsScreenCoordinator: CoordinatorProtocol {
func toPresentable() -> AnyView {
AnyView(SettingsScreen(context: viewModel.context))
}
// MARK: - OIDC Account Management
private func presentAccountProfileURL() {
guard let url = viewModel.context.viewState.accountProfileURL else {
MXLog.error("Account URL is missing.")
return
}
presentAccountManagementURL(url)
}
private func presentAccountSessionsListURL() {
guard let url = viewModel.context.viewState.accountSessionsListURL else {
MXLog.error("Account URL is missing.")
return
}
presentAccountManagementURL(url)
}
private var accountSettingsPresenter: OIDCAccountSettingsPresenter?
private func presentAccountManagementURL(_ url: URL) {
guard let window = viewModel.context.viewState.window else {
MXLog.error("The window is missing.")
return
}
// Note to anyone in the future if you come back here to make this open in Safari instead of a WAS.
// As of iOS 16, there is an issue on the simulator with accessing the cookie but it works on a device. 🤷‍♂️
accountSettingsPresenter = OIDCAccountSettingsPresenter(accountURL: url, presentationAnchor: window)
accountSettingsPresenter?.start()
}
// MARK: - Private
private func presentUserDetailsEditScreen() {
let coordinator = UserDetailsEditScreenCoordinator(parameters: .init(clientProxy: parameters.userSession.clientProxy,
mediaProvider: parameters.userSession.mediaProvider,
navigationStackCoordinator: parameters.navigationStackCoordinator,
userIndicatorController: parameters.userIndicatorController))
parameters.navigationStackCoordinator?.push(coordinator)
}
private func presentAnalyticsScreen() {
let coordinator = AnalyticsSettingsScreenCoordinator(parameters: .init(appSettings: parameters.appSettings,
analytics: ServiceLocator.shared.analytics))
parameters.navigationStackCoordinator?.push(coordinator)
}
private func presentAppLockSetupFlow() {
guard let navigationStackCoordinator = parameters.navigationStackCoordinator else {
MXLog.error("The navigation stack has gone! 🌫️")
return
}
let coordinator = AppLockSetupFlowCoordinator(presentingFlow: .settings,
appLockService: parameters.appLockService,
navigationStackCoordinator: navigationStackCoordinator)
coordinator.actions.sink { [weak self] action in
guard let self else { return }
switch action {
case .complete:
// The flow coordinator tidies up the stack, no need to do anything.
appLockSetupFlowCoordinator = nil
case .forceLogout:
actionsSubject.send(.forceLogout)
}
}
.store(in: &cancellables)
appLockSetupFlowCoordinator = coordinator
coordinator.start()
}
private func presentBugReportScreen() {
let params = BugReportScreenCoordinatorParameters(bugReportService: parameters.bugReportService,
userID: parameters.userSession.userID,
deviceID: parameters.userSession.deviceID,
userIndicatorController: parameters.userIndicatorController,
screenshot: nil,
isModallyPresented: false)
let coordinator = BugReportScreenCoordinator(parameters: params)
coordinator.completion = { [weak self] result in
switch result {
case .finish:
self?.showSuccess(label: L10n.actionDone)
default:
break
}
self?.parameters.navigationStackCoordinator?.pop()
}
parameters.navigationStackCoordinator?.push(coordinator)
}
private func presentLegalInformationScreen() {
parameters.navigationStackCoordinator?.push(LegalInformationScreenCoordinator(appSettings: parameters.appSettings))
}
private func presentSessionVerificationScreen() {
guard let sessionVerificationController = parameters.userSession.sessionVerificationController else {
fatalError("The sessionVerificationController should aways be valid at this point")
}
let verificationParameters = SessionVerificationScreenCoordinatorParameters(sessionVerificationControllerProxy: sessionVerificationController)
let coordinator = SessionVerificationScreenCoordinator(parameters: verificationParameters)
coordinator.actions
.sink { [weak self] action in
guard let self else { return }
switch action {
case .done:
parameters.navigationStackCoordinator?.setSheetCoordinator(nil)
}
}
.store(in: &cancellables)
parameters.navigationStackCoordinator?.setSheetCoordinator(coordinator) { [weak self] in
self?.parameters.navigationStackCoordinator?.setSheetCoordinator(nil)
}
}
private func presentNotificationSettings() {
let notificationParameters = NotificationSettingsScreenCoordinatorParameters(navigationStackCoordinator: parameters.navigationStackCoordinator,
userSession: parameters.userSession,
userNotificationCenter: UNUserNotificationCenter.current(),
notificationSettings: parameters.notificationSettings,
isModallyPresented: false)
let coordinator = NotificationSettingsScreenCoordinator(parameters: notificationParameters)
parameters.navigationStackCoordinator?.push(coordinator)
}
private func presentAdvancedSettings() {
let coordinator = AdvancedSettingsScreenCoordinator()
parameters.navigationStackCoordinator?.push(coordinator)
}
private func presentDeveloperOptions() {
let coordinator = DeveloperOptionsScreenCoordinator()
coordinator.actions
.sink { [weak self] action in
guard let self else { return }
switch action {
case .clearCache:
actionsSubject.send(.clearCache)
}
}
.store(in: &cancellables)
parameters.navigationStackCoordinator?.push(coordinator)
}
private func showSuccess(label: String) {
parameters.userIndicatorController.submitIndicator(UserIndicator(title: label))
}
}
@@ -44,9 +44,6 @@ struct SettingsScreenViewState: BindableState {
var isSessionVerified: Bool?
var showSecureBackupBadge = false
var showDeveloperOptions: Bool
/// The presentation anchor used to display the OIDC account URL.
var window: UIWindow?
}
enum SettingsScreenViewAction {
@@ -64,7 +61,4 @@ enum SettingsScreenViewAction {
case developerOptions
case advancedSettings
case logout
/// Updates the window used for the OIDC account URL anchor.
case updateWindow(UIWindow)
}
@@ -94,12 +94,6 @@ class SettingsScreenViewModel: SettingsScreenViewModelType, SettingsScreenViewMo
actionsSubject.send(.advancedSettings)
case .developerOptions:
actionsSubject.send(.developerOptions)
case .updateWindow(let window):
Task {
guard state.window != window else { return }
state.window = window
}
}
}
}
@@ -41,9 +41,6 @@ struct SettingsScreen: View {
.navigationTitle(L10n.commonSettings)
.navigationBarTitleDisplayMode(.inline)
.toolbar { toolbar }
.introspect(.window, on: .supportedVersions) { window in
context.send(viewAction: .updateWindow(window))
}
}
private var userSection: some View {