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
@@ -454,6 +454,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationCoordinatorDelegate,
let navigationSplitCoordinator = NavigationSplitCoordinator(placeholderCoordinator: PlaceholderScreenCoordinator()) let navigationSplitCoordinator = NavigationSplitCoordinator(placeholderCoordinator: PlaceholderScreenCoordinator())
let userSessionFlowCoordinator = UserSessionFlowCoordinator(userSession: userSession, let userSessionFlowCoordinator = UserSessionFlowCoordinator(userSession: userSession,
navigationSplitCoordinator: navigationSplitCoordinator, navigationSplitCoordinator: navigationSplitCoordinator,
windowManager: windowManager,
appLockService: appLockFlowCoordinator.appLockService, appLockService: appLockFlowCoordinator.appLockService,
bugReportService: ServiceLocator.shared.bugReportService, bugReportService: ServiceLocator.shared.bugReportService,
roomTimelineControllerFactory: RoomTimelineControllerFactory(), roomTimelineControllerFactory: RoomTimelineControllerFactory(),
@@ -15,7 +15,7 @@
// //
import Combine import Combine
import Foundation import SwiftUI
enum SettingsFlowCoordinatorAction { enum SettingsFlowCoordinatorAction {
case presentedSettings case presentedSettings
@@ -28,6 +28,7 @@ enum SettingsFlowCoordinatorAction {
struct SettingsFlowCoordinatorParameters { struct SettingsFlowCoordinatorParameters {
let userSession: UserSessionProtocol let userSession: UserSessionProtocol
let windowManager: WindowManager
let appLockService: AppLockServiceProtocol let appLockService: AppLockServiceProtocol
let bugReportService: BugReportServiceProtocol let bugReportService: BugReportServiceProtocol
let notificationSettings: NotificationSettingsProxyProtocol let notificationSettings: NotificationSettingsProxyProtocol
@@ -44,6 +45,9 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
// periphery:ignore - retaining purpose
private var appLockSetupFlowCoordinator: AppLockSetupFlowCoordinator?
private let actionsSubject: PassthroughSubject<SettingsFlowCoordinatorAction, Never> = .init() private let actionsSubject: PassthroughSubject<SettingsFlowCoordinatorAction, Never> = .init()
var actions: AnyPublisher<SettingsFlowCoordinatorAction, Never> { var actions: AnyPublisher<SettingsFlowCoordinatorAction, Never> {
actionsSubject.eraseToAnyPublisher() actionsSubject.eraseToAnyPublisher()
@@ -79,14 +83,9 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
private func presentSettingsScreen(animated: Bool) { private func presentSettingsScreen(animated: Bool) {
navigationStackCoordinator = NavigationStackCoordinator() navigationStackCoordinator = NavigationStackCoordinator()
let parameters = SettingsScreenCoordinatorParameters(navigationStackCoordinator: navigationStackCoordinator, let parameters = SettingsScreenCoordinatorParameters(userSession: parameters.userSession,
userIndicatorController: parameters.userIndicatorController,
userSession: parameters.userSession,
appLockService: parameters.appLockService,
bugReportService: parameters.bugReportService,
notificationSettings: parameters.userSession.clientProxy.notificationSettings,
secureBackupController: parameters.userSession.clientProxy.secureBackupController,
appSettings: parameters.appSettings) appSettings: parameters.appSettings)
let settingsScreenCoordinator = SettingsScreenCoordinator(parameters: parameters) let settingsScreenCoordinator = SettingsScreenCoordinator(parameters: parameters)
settingsScreenCoordinator.actions settingsScreenCoordinator.actions
@@ -103,12 +102,30 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.actionsSubject.send(.runLogoutFlow) self.actionsSubject.send(.runLogoutFlow)
} }
case .clearCache:
actionsSubject.send(.clearCache)
case .secureBackup: case .secureBackup:
presentSecureBackupScreen(animated: true) presentSecureBackupScreen(animated: true)
case .forceLogout: case .userDetails:
actionsSubject.send(.forceLogout) presentUserDetailsEditScreen()
case .accountProfile:
presentAccountProfileURL()
case .analytics:
presentAnalyticsScreen()
case .appLock:
presentAppLockSetupFlow()
case .bugReport:
presentBugReportScreen()
case .about:
presentLegalInformationScreen()
case .sessionVerification:
presentSessionVerificationScreen()
case .accountSessions:
presentAccountSessionsListURL()
case .notifications:
presentNotificationSettings()
case .advancedSettings:
presentAdvancedSettings()
case .developerOptions:
presentDeveloperOptions()
} }
} }
.store(in: &cancellables) .store(in: &cancellables)
@@ -133,4 +150,151 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
navigationStackCoordinator.push(coordinator, animated: animated) navigationStackCoordinator.push(coordinator, animated: animated)
} }
private func presentUserDetailsEditScreen() {
let coordinator = UserDetailsEditScreenCoordinator(parameters: .init(clientProxy: parameters.userSession.clientProxy,
mediaProvider: parameters.userSession.mediaProvider,
navigationStackCoordinator: navigationStackCoordinator,
userIndicatorController: parameters.userIndicatorController))
navigationStackCoordinator?.push(coordinator)
}
private func presentAnalyticsScreen() {
let coordinator = AnalyticsSettingsScreenCoordinator(parameters: .init(appSettings: parameters.appSettings,
analytics: ServiceLocator.shared.analytics))
navigationStackCoordinator?.push(coordinator)
}
private func presentAppLockSetupFlow() {
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?.navigationStackCoordinator.pop()
}
navigationStackCoordinator.push(coordinator)
}
private func presentLegalInformationScreen() {
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:
navigationStackCoordinator.setSheetCoordinator(nil)
}
}
.store(in: &cancellables)
navigationStackCoordinator.setSheetCoordinator(coordinator) { [weak self] in
self?.navigationStackCoordinator.setSheetCoordinator(nil)
}
}
private func presentNotificationSettings() {
let notificationParameters = NotificationSettingsScreenCoordinatorParameters(navigationStackCoordinator: navigationStackCoordinator,
userSession: parameters.userSession,
userNotificationCenter: UNUserNotificationCenter.current(),
notificationSettings: parameters.notificationSettings,
isModallyPresented: false)
let coordinator = NotificationSettingsScreenCoordinator(parameters: notificationParameters)
navigationStackCoordinator.push(coordinator)
}
private func presentAdvancedSettings() {
let coordinator = AdvancedSettingsScreenCoordinator()
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)
navigationStackCoordinator.push(coordinator)
}
private func showSuccess(label: String) {
parameters.userIndicatorController.submitIndicator(UserIndicator(title: label))
}
// MARK: OIDC Account Management
private func presentAccountProfileURL() {
guard let url = parameters.userSession.clientProxy.accountURL(action: .profile) else {
MXLog.error("Account URL is missing.")
return
}
presentAccountManagementURL(url)
}
private func presentAccountSessionsListURL() {
guard let url = parameters.userSession.clientProxy.accountURL(action: .sessionsList) else {
MXLog.error("Account URL is missing.")
return
}
presentAccountManagementURL(url)
}
private var accountSettingsPresenter: OIDCAccountSettingsPresenter?
private func presentAccountManagementURL(_ url: URL) {
// 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: parameters.windowManager.mainWindow)
accountSettingsPresenter?.start()
}
} }
@@ -49,6 +49,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
init(userSession: UserSessionProtocol, init(userSession: UserSessionProtocol,
navigationSplitCoordinator: NavigationSplitCoordinator, navigationSplitCoordinator: NavigationSplitCoordinator,
windowManager: WindowManager,
appLockService: AppLockServiceProtocol, appLockService: AppLockServiceProtocol,
bugReportService: BugReportServiceProtocol, bugReportService: BugReportServiceProtocol,
roomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol, roomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol,
@@ -75,6 +76,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
userIndicatorController: ServiceLocator.shared.userIndicatorController) userIndicatorController: ServiceLocator.shared.userIndicatorController)
settingsFlowCoordinator = SettingsFlowCoordinator(parameters: .init(userSession: userSession, settingsFlowCoordinator = SettingsFlowCoordinator(parameters: .init(userSession: userSession,
windowManager: windowManager,
appLockService: appLockService, appLockService: appLockService,
bugReportService: bugReportService, bugReportService: bugReportService,
notificationSettings: userSession.clientProxy.notificationSettings, notificationSettings: userSession.clientProxy.notificationSettings,
@@ -18,32 +18,31 @@ import Combine
import SwiftUI import SwiftUI
struct SettingsScreenCoordinatorParameters { struct SettingsScreenCoordinatorParameters {
weak var navigationStackCoordinator: NavigationStackCoordinator?
let userIndicatorController: UserIndicatorControllerProtocol
let userSession: UserSessionProtocol let userSession: UserSessionProtocol
let appLockService: AppLockServiceProtocol
let bugReportService: BugReportServiceProtocol
let notificationSettings: NotificationSettingsProxyProtocol
let secureBackupController: SecureBackupControllerProtocol
let appSettings: AppSettings let appSettings: AppSettings
} }
enum SettingsScreenCoordinatorAction { enum SettingsScreenCoordinatorAction {
case dismiss case dismiss
case logout case logout
case clearCache
case secureBackup case secureBackup
/// Logout without a confirmation. The user forgot their PIN. case userDetails
case forceLogout case accountProfile
case analytics
case appLock
case bugReport
case about
case sessionVerification
case accountSessions
case notifications
case advancedSettings
case developerOptions
} }
final class SettingsScreenCoordinator: CoordinatorProtocol { final class SettingsScreenCoordinator: CoordinatorProtocol {
private let parameters: SettingsScreenCoordinatorParameters private let parameters: SettingsScreenCoordinatorParameters
private var viewModel: SettingsScreenViewModelProtocol private var viewModel: SettingsScreenViewModelProtocol
// periphery:ignore - retaining purpose
private var appLockSetupFlowCoordinator: AppLockSetupFlowCoordinator?
private let actionsSubject: PassthroughSubject<SettingsScreenCoordinatorAction, Never> = .init() private let actionsSubject: PassthroughSubject<SettingsScreenCoordinatorAction, Never> = .init()
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
@@ -67,29 +66,29 @@ final class SettingsScreenCoordinator: CoordinatorProtocol {
case .close: case .close:
actionsSubject.send(.dismiss) actionsSubject.send(.dismiss)
case .userDetails: case .userDetails:
presentUserDetailsEditScreen() actionsSubject.send(.userDetails)
case .accountProfile: case .accountProfile:
presentAccountProfileURL() actionsSubject.send(.accountProfile)
case .analytics: case .analytics:
presentAnalyticsScreen() actionsSubject.send(.analytics)
case .appLock: case .appLock:
presentAppLockSetupFlow() actionsSubject.send(.appLock)
case .reportBug: case .reportBug:
presentBugReportScreen() actionsSubject.send(.bugReport)
case .about: case .about:
presentLegalInformationScreen() actionsSubject.send(.about)
case .sessionVerification: case .sessionVerification:
presentSessionVerificationScreen() actionsSubject.send(.sessionVerification)
case .secureBackup: case .secureBackup:
actionsSubject.send(.secureBackup) actionsSubject.send(.secureBackup)
case .accountSessionsList: case .accountSessionsList:
presentAccountSessionsListURL() actionsSubject.send(.accountSessions)
case .notifications: case .notifications:
presentNotificationSettings() actionsSubject.send(.notifications)
case .advancedSettings: case .advancedSettings:
self.presentAdvancedSettings() actionsSubject.send(.advancedSettings)
case .developerOptions: case .developerOptions:
presentDeveloperOptions() actionsSubject.send(.developerOptions)
case .logout: case .logout:
actionsSubject.send(.logout) actionsSubject.send(.logout)
} }
@@ -102,163 +101,4 @@ final class SettingsScreenCoordinator: CoordinatorProtocol {
func toPresentable() -> AnyView { func toPresentable() -> AnyView {
AnyView(SettingsScreen(context: viewModel.context)) 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 isSessionVerified: Bool?
var showSecureBackupBadge = false var showSecureBackupBadge = false
var showDeveloperOptions: Bool var showDeveloperOptions: Bool
/// The presentation anchor used to display the OIDC account URL.
var window: UIWindow?
} }
enum SettingsScreenViewAction { enum SettingsScreenViewAction {
@@ -64,7 +61,4 @@ enum SettingsScreenViewAction {
case developerOptions case developerOptions
case advancedSettings case advancedSettings
case logout 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) actionsSubject.send(.advancedSettings)
case .developerOptions: case .developerOptions:
actionsSubject.send(.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) .navigationTitle(L10n.commonSettings)
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
.toolbar { toolbar } .toolbar { toolbar }
.introspect(.window, on: .supportedVersions) { window in
context.send(viewAction: .updateWindow(window))
}
} }
private var userSection: some View { private var userSection: some View {
@@ -52,7 +52,7 @@ class UITestsAppCoordinator: AppCoordinatorProtocol, WindowManagerDelegate {
func start() { func start() {
guard let screenID = ProcessInfo.testScreenID else { fatalError("Unable to launch with unknown screen.") } guard let screenID = ProcessInfo.testScreenID else { fatalError("Unable to launch with unknown screen.") }
let mockScreen = MockScreen(id: screenID) let mockScreen = MockScreen(id: screenID, windowManager: windowManager)
navigationRootCoordinator.setRootCoordinator(mockScreen.coordinator) navigationRootCoordinator.setRootCoordinator(mockScreen.coordinator)
self.mockScreen = mockScreen self.mockScreen = mockScreen
} }
@@ -79,12 +79,12 @@ class UITestsAppCoordinator: AppCoordinatorProtocol, WindowManagerDelegate {
@MainActor @MainActor
class MockScreen: Identifiable { class MockScreen: Identifiable {
let id: UITestsScreenIdentifier let id: UITestsScreenIdentifier
let windowManager: WindowManager? let windowManager: WindowManager
private var retainedState = [Any]() private var retainedState = [Any]()
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
init(id: UITestsScreenIdentifier, windowManager: WindowManager? = nil) { init(id: UITestsScreenIdentifier, windowManager: WindowManager) {
self.id = id self.id = id
self.windowManager = windowManager self.windowManager = windowManager
} }
@@ -209,10 +209,10 @@ class MockScreen: Identifiable {
navigationCoordinator: navigationCoordinator, navigationCoordinator: navigationCoordinator,
notificationCenter: notificationCenter) notificationCenter: notificationCenter)
guard let windowManager else { fatalError("The window manager must be supplied.") }
coordinator.actions coordinator.actions
.sink { action in .sink { [weak self] action in
guard let self else { return }
switch action { switch action {
case .lockApp: case .lockApp:
windowManager.switchToAlternate() windowManager.switchToAlternate()
@@ -268,16 +268,9 @@ class MockScreen: Identifiable {
case .settings: case .settings:
let navigationStackCoordinator = NavigationStackCoordinator() let navigationStackCoordinator = NavigationStackCoordinator()
let clientProxy = MockClientProxy(userID: "@mock:client.com") let clientProxy = MockClientProxy(userID: "@mock:client.com")
let coordinator = SettingsScreenCoordinator(parameters: .init(navigationStackCoordinator: navigationStackCoordinator, let coordinator = SettingsScreenCoordinator(parameters: .init(userSession: MockUserSession(clientProxy: clientProxy,
userIndicatorController: UserIndicatorControllerMock(),
userSession: MockUserSession(clientProxy: clientProxy,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
voiceMessageMediaManager: VoiceMessageMediaManagerMock()), voiceMessageMediaManager: VoiceMessageMediaManagerMock()),
appLockService: AppLockService(keychainController: KeychainControllerMock(),
appSettings: ServiceLocator.shared.settings),
bugReportService: BugReportServiceMock(),
notificationSettings: NotificationSettingsProxyMock(with: .init()),
secureBackupController: SecureBackupControllerMock(),
appSettings: ServiceLocator.shared.settings)) appSettings: ServiceLocator.shared.settings))
navigationStackCoordinator.setRootCoordinator(coordinator) navigationStackCoordinator.setRootCoordinator(coordinator)
return navigationStackCoordinator return navigationStackCoordinator
@@ -566,6 +559,7 @@ class MockScreen: Identifiable {
let coordinator = UserSessionFlowCoordinator(userSession: MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider(), voiceMessageMediaManager: VoiceMessageMediaManagerMock()), let coordinator = UserSessionFlowCoordinator(userSession: MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider(), voiceMessageMediaManager: VoiceMessageMediaManagerMock()),
navigationSplitCoordinator: navigationSplitCoordinator, navigationSplitCoordinator: navigationSplitCoordinator,
windowManager: windowManager,
appLockService: AppLockService(keychainController: KeychainControllerMock(), appLockService: AppLockService(keychainController: KeychainControllerMock(),
appSettings: ServiceLocator.shared.settings), appSettings: ServiceLocator.shared.settings),
bugReportService: BugReportServiceMock(), bugReportService: BugReportServiceMock(),