Introduce flow parameters to simplify dependencies for child flows. (#4448)

This commit is contained in:
Doug
2025-08-27 13:56:06 +01:00
committed by GitHub
parent c140209e0c
commit dfb2425240
22 changed files with 456 additions and 586 deletions
@@ -640,19 +640,23 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
fatalError("User session not setup") fatalError("User session not setup")
} }
let userSessionFlowCoordinator = UserSessionFlowCoordinator(userSession: userSession, let flowParameters = CommonFlowParameters(userSession: userSession,
isNewLogin: isNewLogin, bugReportService: ServiceLocator.shared.bugReportService,
elementCallService: elementCallService,
timelineControllerFactory: TimelineControllerFactory(),
emojiProvider: EmojiProvider(appSettings: appSettings),
appMediator: appMediator,
appSettings: appSettings,
appHooks: appHooks,
analytics: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController,
notificationManager: notificationManager,
stateMachineFactory: StateMachineFactory())
let userSessionFlowCoordinator = UserSessionFlowCoordinator(isNewLogin: isNewLogin,
navigationRootCoordinator: navigationRootCoordinator, navigationRootCoordinator: navigationRootCoordinator,
appLockService: appLockFlowCoordinator.appLockService, appLockService: appLockFlowCoordinator.appLockService,
bugReportService: ServiceLocator.shared.bugReportService, flowParameters: flowParameters)
elementCallService: elementCallService,
timelineControllerFactory: TimelineControllerFactory(),
appMediator: appMediator,
appSettings: appSettings,
appHooks: appHooks,
analytics: ServiceLocator.shared.analytics,
notificationManager: notificationManager,
stateMachineFactory: StateMachineFactory())
userSessionFlowCoordinator.actionsPublisher userSessionFlowCoordinator.actionsPublisher
.sink { [weak self] action in .sink { [weak self] action in
@@ -14,3 +14,25 @@ protocol FlowCoordinatorProtocol {
func handleAppRoute(_ appRoute: AppRoute, animated: Bool) func handleAppRoute(_ appRoute: AppRoute, animated: Bool)
func clearRoute(animated: Bool) func clearRoute(animated: Bool)
} }
/// Core parameters that are shared across the main flows for easy dependency injection.
///
/// Please do **not** pass this type directly to screen coordinators/view models.
@MainActor
struct CommonFlowParameters {
let userSession: UserSessionProtocol
let bugReportService: BugReportServiceProtocol
let elementCallService: ElementCallServiceProtocol
let timelineControllerFactory: TimelineControllerFactoryProtocol
let emojiProvider: EmojiProviderProtocol
let appMediator: AppMediatorProtocol
let appSettings: AppSettings
let appHooks: AppHooks
let analytics: AnalyticsService
let userIndicatorController: UserIndicatorControllerProtocol
let notificationManager: NotificationManagerProtocol
let stateMachineFactory: StateMachineFactoryProtocol
var windowManager: WindowManagerProtocol { appMediator.windowManager }
var ongoingCallRoomIDPublisher: CurrentValuePublisher<String?, Never> { elementCallService.ongoingCallRoomIDPublisher }
}
@@ -19,21 +19,15 @@ enum ChatsFlowCoordinatorAction {
} }
class ChatsFlowCoordinator: FlowCoordinatorProtocol { class ChatsFlowCoordinator: FlowCoordinatorProtocol {
private let userSession: UserSessionProtocol
private let navigationSplitCoordinator: NavigationSplitCoordinator private let navigationSplitCoordinator: NavigationSplitCoordinator
private let bugReportService: BugReportServiceProtocol private let flowParameters: CommonFlowParameters
private let elementCallService: ElementCallServiceProtocol
private let appMediator: AppMediatorProtocol private var userSession: UserSessionProtocol { flowParameters.userSession }
private let appSettings: AppSettings
private let appHooks: AppHooks
private let analytics: AnalyticsService
private let notificationManager: NotificationManagerProtocol
private let stateMachine: ChatsFlowCoordinatorStateMachine private let stateMachine: ChatsFlowCoordinatorStateMachine
// periphery:ignore - retaining purpose // periphery:ignore - retaining purpose
private var roomFlowCoordinator: RoomFlowCoordinator? private var roomFlowCoordinator: RoomFlowCoordinator?
private let timelineControllerFactory: TimelineControllerFactoryProtocol
// periphery:ignore - retaining purpose // periphery:ignore - retaining purpose
private var bugReportFlowCoordinator: BugReportFlowCoordinator? private var bugReportFlowCoordinator: BugReportFlowCoordinator?
@@ -56,30 +50,12 @@ class ChatsFlowCoordinator: FlowCoordinatorProtocol {
actionsSubject.eraseToAnyPublisher() actionsSubject.eraseToAnyPublisher()
} }
init(userSession: UserSessionProtocol, init(isNewLogin: Bool,
isNewLogin: Bool,
navigationSplitCoordinator: NavigationSplitCoordinator, navigationSplitCoordinator: NavigationSplitCoordinator,
appLockService: AppLockServiceProtocol, flowParameters: CommonFlowParameters) {
bugReportService: BugReportServiceProtocol, stateMachine = flowParameters.stateMachineFactory.makeChatsFlowStateMachine()
elementCallService: ElementCallServiceProtocol,
timelineControllerFactory: TimelineControllerFactoryProtocol,
appMediator: AppMediatorProtocol,
appSettings: AppSettings,
appHooks: AppHooks,
analytics: AnalyticsService,
notificationManager: NotificationManagerProtocol,
stateMachineFactory: StateMachineFactoryProtocol) {
stateMachine = stateMachineFactory.makeChatsFlowStateMachine()
self.userSession = userSession
self.navigationSplitCoordinator = navigationSplitCoordinator self.navigationSplitCoordinator = navigationSplitCoordinator
self.bugReportService = bugReportService self.flowParameters = flowParameters
self.elementCallService = elementCallService
self.timelineControllerFactory = timelineControllerFactory
self.appMediator = appMediator
self.appSettings = appSettings
self.appHooks = appHooks
self.analytics = analytics
self.notificationManager = notificationManager
sidebarNavigationStackCoordinator = NavigationStackCoordinator(navigationSplitCoordinator: navigationSplitCoordinator) sidebarNavigationStackCoordinator = NavigationStackCoordinator(navigationSplitCoordinator: navigationSplitCoordinator)
detailNavigationStackCoordinator = NavigationStackCoordinator(navigationSplitCoordinator: navigationSplitCoordinator) detailNavigationStackCoordinator = NavigationStackCoordinator(navigationSplitCoordinator: navigationSplitCoordinator)
@@ -230,8 +206,8 @@ class ChatsFlowCoordinator: FlowCoordinatorProtocol {
case (.roomList, .feedbackScreen, .feedbackScreen): case (.roomList, .feedbackScreen, .feedbackScreen):
bugReportFlowCoordinator = BugReportFlowCoordinator(parameters: .init(presentationMode: .sheet(sidebarNavigationStackCoordinator), bugReportFlowCoordinator = BugReportFlowCoordinator(parameters: .init(presentationMode: .sheet(sidebarNavigationStackCoordinator),
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
bugReportService: bugReportService, bugReportService: flowParameters.bugReportService,
userSession: userSession)) userSession: userSession))
bugReportFlowCoordinator?.start() bugReportFlowCoordinator?.start()
case (.feedbackScreen, .dismissedFeedbackScreen, .roomList): case (.feedbackScreen, .dismissedFeedbackScreen, .roomList):
@@ -319,7 +295,7 @@ class ChatsFlowCoordinator: FlowCoordinatorProtocol {
case .receivedSyncUpdate: case .receivedSyncUpdate:
Task { Task {
let roomSummaries = self.userSession.clientProxy.staticRoomSummaryProvider.roomListPublisher.value let roomSummaries = self.userSession.clientProxy.staticRoomSummaryProvider.roomListPublisher.value
await self.notificationManager.removeDeliveredNotificationsForFullyReadRooms(roomSummaries) await self.flowParameters.notificationManager.removeDeliveredNotificationsForFullyReadRooms(roomSummaries)
} }
default: default:
break break
@@ -327,7 +303,7 @@ class ChatsFlowCoordinator: FlowCoordinatorProtocol {
} }
.store(in: &cancellables) .store(in: &cancellables)
elementCallService.actions flowParameters.elementCallService.actions
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
.sink { [weak self] action in .sink { [weak self] action in
switch action { switch action {
@@ -358,25 +334,25 @@ class ChatsFlowCoordinator: FlowCoordinatorProtocol {
case .withheldBySender: .OlmKeysNotSentError case .withheldBySender: .OlmKeysNotSentError
} }
analytics.trackError(context: nil, flowParameters.analytics.trackError(context: nil,
domain: .E2EE, domain: .E2EE,
name: errorName, name: errorName,
timeToDecryptMillis: timeToDecryptMs, timeToDecryptMillis: timeToDecryptMs,
eventLocalAgeMillis: Int(truncatingIfNeeded: info.eventLocalAgeMillis), eventLocalAgeMillis: Int(truncatingIfNeeded: info.eventLocalAgeMillis),
isFederated: info.ownHomeserver != info.senderHomeserver, isFederated: info.ownHomeserver != info.senderHomeserver,
isMatrixDotOrg: info.ownHomeserver == "matrix.org", isMatrixDotOrg: info.ownHomeserver == "matrix.org",
userTrustsOwnIdentity: info.userTrustsOwnIdentity, userTrustsOwnIdentity: info.userTrustsOwnIdentity,
wasVisibleToUser: nil) wasVisibleToUser: nil)
} }
private func presentHomeScreen() { private func presentHomeScreen() {
let parameters = HomeScreenCoordinatorParameters(userSession: userSession, let parameters = HomeScreenCoordinatorParameters(userSession: userSession,
bugReportService: bugReportService, bugReportService: flowParameters.bugReportService,
selectedRoomPublisher: selectedRoomSubject.asCurrentValuePublisher(), selectedRoomPublisher: selectedRoomSubject.asCurrentValuePublisher(),
appSettings: appSettings, appSettings: flowParameters.appSettings,
analyticsService: analytics, analyticsService: flowParameters.analytics,
notificationManager: notificationManager, notificationManager: flowParameters.notificationManager,
userIndicatorController: ServiceLocator.shared.userIndicatorController) userIndicatorController: flowParameters.userIndicatorController)
let coordinator = HomeScreenCoordinator(parameters: parameters) let coordinator = HomeScreenCoordinator(parameters: parameters)
coordinator.actions coordinator.actions
@@ -431,7 +407,7 @@ class ChatsFlowCoordinator: FlowCoordinatorProtocol {
let navigationStackCoordinator = NavigationStackCoordinator() let navigationStackCoordinator = NavigationStackCoordinator()
let coordinator = ReportRoomScreenCoordinator(parameters: .init(roomProxy: roomProxy, let coordinator = ReportRoomScreenCoordinator(parameters: .init(roomProxy: roomProxy,
userIndicatorController: ServiceLocator.shared.userIndicatorController)) userIndicatorController: flowParameters.userIndicatorController))
coordinator.actionsPublisher.sink { [weak self] action in coordinator.actionsPublisher.sink { [weak self] action in
guard let self else { return } guard let self else { return }
switch action { switch action {
@@ -456,7 +432,7 @@ class ChatsFlowCoordinator: FlowCoordinatorProtocol {
let coordinator = DeclineAndBlockScreenCoordinator(parameters: .init(userID: userID, let coordinator = DeclineAndBlockScreenCoordinator(parameters: .init(userID: userID,
roomID: roomID, roomID: roomID,
clientProxy: userSession.clientProxy, clientProxy: userSession.clientProxy,
userIndicatorController: ServiceLocator.shared.userIndicatorController)) userIndicatorController: flowParameters.userIndicatorController))
coordinator.actionsPublisher.sink { [weak self] action in coordinator.actionsPublisher.sink { [weak self] action in
guard let self else { return } guard let self else { return }
@@ -480,17 +456,9 @@ class ChatsFlowCoordinator: FlowCoordinatorProtocol {
entryPoint: RoomFlowCoordinatorEntryPoint, entryPoint: RoomFlowCoordinatorEntryPoint,
animated: Bool) { animated: Bool) {
let coordinator = RoomFlowCoordinator(roomID: roomID, let coordinator = RoomFlowCoordinator(roomID: roomID,
userSession: userSession,
isChildFlow: false, isChildFlow: false,
timelineControllerFactory: timelineControllerFactory,
navigationStackCoordinator: detailNavigationStackCoordinator, navigationStackCoordinator: detailNavigationStackCoordinator,
emojiProvider: EmojiProvider(appSettings: appSettings), flowParameters: flowParameters)
ongoingCallRoomIDPublisher: elementCallService.ongoingCallRoomIDPublisher,
appMediator: appMediator,
appSettings: appSettings,
appHooks: appHooks,
analytics: analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController)
coordinator.actions.sink { [weak self] action in coordinator.actions.sink { [weak self] action in
guard let self else { return } guard let self else { return }
@@ -528,7 +496,7 @@ class ChatsFlowCoordinator: FlowCoordinatorProtocol {
Task { Task {
let _ = await userSession.clientProxy.trackRecentlyVisitedRoom(roomID) let _ = await userSession.clientProxy.trackRecentlyVisitedRoom(roomID)
await notificationManager.removeDeliveredMessageNotifications(for: roomID) await flowParameters.notificationManager.removeDeliveredMessageNotifications(for: roomID)
} }
} }
@@ -544,13 +512,13 @@ class ChatsFlowCoordinator: FlowCoordinatorProtocol {
let startChatNavigationStackCoordinator = NavigationStackCoordinator() let startChatNavigationStackCoordinator = NavigationStackCoordinator()
let userDiscoveryService = UserDiscoveryService(clientProxy: userSession.clientProxy) let userDiscoveryService = UserDiscoveryService(clientProxy: userSession.clientProxy)
let parameters = StartChatScreenCoordinatorParameters(orientationManager: appMediator.windowManager, let parameters = StartChatScreenCoordinatorParameters(orientationManager: flowParameters.windowManager,
userSession: userSession, userSession: userSession,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
navigationStackCoordinator: startChatNavigationStackCoordinator, navigationStackCoordinator: startChatNavigationStackCoordinator,
userDiscoveryService: userDiscoveryService, userDiscoveryService: userDiscoveryService,
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: appSettings), mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: flowParameters.appSettings),
appSettings: appSettings) appSettings: flowParameters.appSettings)
let coordinator = StartChatScreenCoordinator(parameters: parameters) let coordinator = StartChatScreenCoordinator(parameters: parameters)
coordinator.actions.sink { [weak self] action in coordinator.actions.sink { [weak self] action in
@@ -590,27 +558,27 @@ class ChatsFlowCoordinator: FlowCoordinatorProtocol {
} }
private func presentCallScreen(roomProxy: JoinedRoomProxyProtocol) { private func presentCallScreen(roomProxy: JoinedRoomProxyProtocol) {
let colorScheme: ColorScheme = appMediator.windowManager.mainWindow.traitCollection.userInterfaceStyle == .light ? .light : .dark let colorScheme: ColorScheme = flowParameters.windowManager.mainWindow.traitCollection.userInterfaceStyle == .light ? .light : .dark
presentCallScreen(configuration: .init(roomProxy: roomProxy, presentCallScreen(configuration: .init(roomProxy: roomProxy,
clientProxy: userSession.clientProxy, clientProxy: userSession.clientProxy,
clientID: InfoPlistReader.main.bundleIdentifier, clientID: InfoPlistReader.main.bundleIdentifier,
elementCallBaseURL: appSettings.elementCallBaseURL, elementCallBaseURL: flowParameters.appSettings.elementCallBaseURL,
elementCallBaseURLOverride: appSettings.elementCallBaseURLOverride, elementCallBaseURLOverride: flowParameters.appSettings.elementCallBaseURLOverride,
colorScheme: colorScheme)) colorScheme: colorScheme))
} }
private var callScreenPictureInPictureController: AVPictureInPictureController? private var callScreenPictureInPictureController: AVPictureInPictureController?
private func presentCallScreen(configuration: ElementCallConfiguration) { private func presentCallScreen(configuration: ElementCallConfiguration) {
guard elementCallService.ongoingCallRoomIDPublisher.value != configuration.callRoomID else { guard flowParameters.ongoingCallRoomIDPublisher.value != configuration.callRoomID else {
MXLog.info("Returning to existing call.") MXLog.info("Returning to existing call.")
callScreenPictureInPictureController?.stopPictureInPicture() callScreenPictureInPictureController?.stopPictureInPicture()
return return
} }
let callScreenCoordinator = CallScreenCoordinator(parameters: .init(elementCallService: elementCallService, let callScreenCoordinator = CallScreenCoordinator(parameters: .init(elementCallService: flowParameters.elementCallService,
configuration: configuration, configuration: configuration,
allowPictureInPicture: true, allowPictureInPicture: true,
appHooks: appHooks)) appHooks: flowParameters.appHooks))
callScreenCoordinator.actions callScreenCoordinator.actions
.sink { [weak self] action in .sink { [weak self] action in
@@ -633,7 +601,7 @@ class ChatsFlowCoordinator: FlowCoordinatorProtocol {
navigationSplitCoordinator.setOverlayCoordinator(callScreenCoordinator, animated: true) navigationSplitCoordinator.setOverlayCoordinator(callScreenCoordinator, animated: true)
analytics.track(screen: .RoomCall) flowParameters.analytics.track(screen: .RoomCall)
} }
private func hideCallScreenOverlay() { private func hideCallScreenOverlay() {
@@ -661,7 +629,7 @@ class ChatsFlowCoordinator: FlowCoordinatorProtocol {
private func presentRecoveryKeyScreen(animated: Bool) { private func presentRecoveryKeyScreen(animated: Bool) {
let sheetNavigationStackCoordinator = NavigationStackCoordinator() let sheetNavigationStackCoordinator = NavigationStackCoordinator()
let parameters = SecureBackupRecoveryKeyScreenCoordinatorParameters(secureBackupController: userSession.clientProxy.secureBackupController, let parameters = SecureBackupRecoveryKeyScreenCoordinatorParameters(secureBackupController: userSession.clientProxy.secureBackupController,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
isModallyPresented: true) isModallyPresented: true)
let coordinator = SecureBackupRecoveryKeyScreenCoordinator(parameters: parameters) let coordinator = SecureBackupRecoveryKeyScreenCoordinator(parameters: parameters)
@@ -684,9 +652,9 @@ class ChatsFlowCoordinator: FlowCoordinatorProtocol {
private func startEncryptionResetFlow(animated: Bool) { private func startEncryptionResetFlow(animated: Bool) {
let sheetNavigationStackCoordinator = NavigationStackCoordinator() let sheetNavigationStackCoordinator = NavigationStackCoordinator()
let parameters = EncryptionResetFlowCoordinatorParameters(userSession: userSession, let parameters = EncryptionResetFlowCoordinatorParameters(userSession: userSession,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
navigationStackCoordinator: sheetNavigationStackCoordinator, navigationStackCoordinator: sheetNavigationStackCoordinator,
windowManger: appMediator.windowManager) windowManger: flowParameters.windowManager)
let coordinator = EncryptionResetFlowCoordinator(parameters: parameters) let coordinator = EncryptionResetFlowCoordinator(parameters: parameters)
coordinator.actionsPublisher.sink { [weak self] action in coordinator.actionsPublisher.sink { [weak self] action in
@@ -736,14 +704,14 @@ class ChatsFlowCoordinator: FlowCoordinatorProtocol {
let hostingController = UIHostingController(rootView: coordinator.toPresentable()) let hostingController = UIHostingController(rootView: coordinator.toPresentable())
hostingController.view.backgroundColor = .clear hostingController.view.backgroundColor = .clear
appMediator.windowManager.globalSearchWindow.rootViewController = hostingController flowParameters.windowManager.globalSearchWindow.rootViewController = hostingController
appMediator.windowManager.showGlobalSearch() flowParameters.windowManager.showGlobalSearch()
} }
private func dismissGlobalSearch() { private func dismissGlobalSearch() {
appMediator.windowManager.globalSearchWindow.rootViewController = nil flowParameters.windowManager.globalSearchWindow.rootViewController = nil
appMediator.windowManager.hideGlobalSearch() flowParameters.windowManager.hideGlobalSearch()
globalSearchScreenCoordinator = nil globalSearchScreenCoordinator = nil
} }
@@ -753,7 +721,7 @@ class ChatsFlowCoordinator: FlowCoordinatorProtocol {
private func presentRoomDirectorySearch() { private func presentRoomDirectorySearch() {
let coordinator = RoomDirectorySearchScreenCoordinator(parameters: .init(clientProxy: userSession.clientProxy, let coordinator = RoomDirectorySearchScreenCoordinator(parameters: .init(clientProxy: userSession.clientProxy,
mediaProvider: userSession.mediaProvider, mediaProvider: userSession.mediaProvider,
userIndicatorController: ServiceLocator.shared.userIndicatorController)) userIndicatorController: flowParameters.userIndicatorController))
coordinator.actionsPublisher.sink { [weak self] action in coordinator.actionsPublisher.sink { [weak self] action in
guard let self else { return } guard let self else { return }
@@ -788,8 +756,8 @@ class ChatsFlowCoordinator: FlowCoordinatorProtocol {
isPresentedModally: true, isPresentedModally: true,
clientProxy: userSession.clientProxy, clientProxy: userSession.clientProxy,
mediaProvider: userSession.mediaProvider, mediaProvider: userSession.mediaProvider,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
analytics: analytics) analytics: flowParameters.analytics)
let coordinator = UserProfileScreenCoordinator(parameters: parameters) let coordinator = UserProfileScreenCoordinator(parameters: parameters)
coordinator.actionsPublisher.sink { [weak self] action in coordinator.actionsPublisher.sink { [weak self] action in
guard let self else { return } guard let self else { return }
@@ -864,21 +832,21 @@ class ChatsFlowCoordinator: FlowCoordinatorProtocol {
private static let failureIndicatorIdentifier = "\(ChatsFlowCoordinator.self)-Failure" private static let failureIndicatorIdentifier = "\(ChatsFlowCoordinator.self)-Failure"
private func showLoadingIndicator(delay: Duration? = nil) { private func showLoadingIndicator(delay: Duration? = nil) {
ServiceLocator.shared.userIndicatorController.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier, flowParameters.userIndicatorController.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier,
type: .modal, type: .modal,
title: L10n.commonLoading, title: L10n.commonLoading,
persistent: true), persistent: true),
delay: delay) delay: delay)
} }
private func hideLoadingIndicator() { private func hideLoadingIndicator() {
ServiceLocator.shared.userIndicatorController.retractIndicatorWithId(Self.loadingIndicatorIdentifier) flowParameters.userIndicatorController.retractIndicatorWithId(Self.loadingIndicatorIdentifier)
} }
private func showFailureIndicator() { private func showFailureIndicator() {
ServiceLocator.shared.userIndicatorController.submitIndicator(UserIndicator(id: Self.failureIndicatorIdentifier, flowParameters.userIndicatorController.submitIndicator(UserIndicator(id: Self.failureIndicatorIdentifier,
type: .toast, type: .toast,
title: L10n.errorUnknown, title: L10n.errorUnknown,
iconName: "xmark")) iconName: "xmark"))
} }
} }
@@ -14,13 +14,11 @@ enum MediaEventsTimelineFlowCoordinatorAction {
} }
class MediaEventsTimelineFlowCoordinator: FlowCoordinatorProtocol { class MediaEventsTimelineFlowCoordinator: FlowCoordinatorProtocol {
private let navigationStackCoordinator: NavigationStackCoordinator
private let userSession: UserSessionProtocol
private let timelineControllerFactory: TimelineControllerFactoryProtocol
private let roomProxy: JoinedRoomProxyProtocol private let roomProxy: JoinedRoomProxyProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let navigationStackCoordinator: NavigationStackCoordinator
private let appMediator: AppMediatorProtocol private let flowParameters: CommonFlowParameters
private let emojiProvider: EmojiProviderProtocol
private var userSession: UserSessionProtocol { flowParameters.userSession }
private let actionsSubject: PassthroughSubject<MediaEventsTimelineFlowCoordinatorAction, Never> = .init() private let actionsSubject: PassthroughSubject<MediaEventsTimelineFlowCoordinatorAction, Never> = .init()
var actionsPublisher: AnyPublisher<MediaEventsTimelineFlowCoordinatorAction, Never> { var actionsPublisher: AnyPublisher<MediaEventsTimelineFlowCoordinatorAction, Never> {
@@ -29,20 +27,12 @@ class MediaEventsTimelineFlowCoordinator: FlowCoordinatorProtocol {
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
init(navigationStackCoordinator: NavigationStackCoordinator, init(roomProxy: JoinedRoomProxyProtocol,
userSession: UserSessionProtocol, navigationStackCoordinator: NavigationStackCoordinator,
timelineControllerFactory: TimelineControllerFactoryProtocol, flowParameters: CommonFlowParameters) {
roomProxy: JoinedRoomProxyProtocol,
userIndicatorController: UserIndicatorControllerProtocol,
appMediator: AppMediatorProtocol,
emojiProvider: EmojiProviderProtocol) {
self.navigationStackCoordinator = navigationStackCoordinator
self.userSession = userSession
self.timelineControllerFactory = timelineControllerFactory
self.roomProxy = roomProxy self.roomProxy = roomProxy
self.userIndicatorController = userIndicatorController self.navigationStackCoordinator = navigationStackCoordinator
self.appMediator = appMediator self.flowParameters = flowParameters
self.emojiProvider = emojiProvider
} }
func start() { func start() {
@@ -64,22 +54,22 @@ class MediaEventsTimelineFlowCoordinator: FlowCoordinatorProtocol {
attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()), attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()),
stateEventStringBuilder: RoomStateEventStringBuilder(userID: userSession.clientProxy.userID)) stateEventStringBuilder: RoomStateEventStringBuilder(userID: userSession.clientProxy.userID))
guard case let .success(mediaTimelineController) = await timelineControllerFactory.buildMessageFilteredTimelineController(focus: .live, guard case let .success(mediaTimelineController) = await flowParameters.timelineControllerFactory.buildMessageFilteredTimelineController(focus: .live,
allowedMessageTypes: [.image, .video], allowedMessageTypes: [.image, .video],
presentation: .mediaFilesScreen, presentation: .mediaFilesScreen,
roomProxy: roomProxy, roomProxy: roomProxy,
timelineItemFactory: timelineItemFactory, timelineItemFactory: timelineItemFactory,
mediaProvider: userSession.mediaProvider) else { mediaProvider: userSession.mediaProvider) else {
MXLog.error("Failed presenting media timeline") MXLog.error("Failed presenting media timeline")
return return
} }
guard case let .success(filesTimelineController) = await timelineControllerFactory.buildMessageFilteredTimelineController(focus: .live, guard case let .success(filesTimelineController) = await flowParameters.timelineControllerFactory.buildMessageFilteredTimelineController(focus: .live,
allowedMessageTypes: [.file, .audio], allowedMessageTypes: [.file, .audio],
presentation: .mediaFilesScreen, presentation: .mediaFilesScreen,
roomProxy: roomProxy, roomProxy: roomProxy,
timelineItemFactory: timelineItemFactory, timelineItemFactory: timelineItemFactory,
mediaProvider: userSession.mediaProvider) else { mediaProvider: userSession.mediaProvider) else {
MXLog.error("Failed presenting media timeline") MXLog.error("Failed presenting media timeline")
return return
} }
@@ -90,10 +80,10 @@ class MediaEventsTimelineFlowCoordinator: FlowCoordinatorProtocol {
mediaProvider: userSession.mediaProvider, mediaProvider: userSession.mediaProvider,
mediaPlayerProvider: MediaPlayerProvider(), mediaPlayerProvider: MediaPlayerProvider(),
voiceMessageMediaManager: userSession.voiceMessageMediaManager, voiceMessageMediaManager: userSession.voiceMessageMediaManager,
appMediator: appMediator, appMediator: flowParameters.appMediator,
emojiProvider: emojiProvider, emojiProvider: flowParameters.emojiProvider,
userIndicatorController: userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
timelineControllerFactory: timelineControllerFactory, timelineControllerFactory: flowParameters.timelineControllerFactory,
clientProxy: userSession.clientProxy) clientProxy: userSession.clientProxy)
let coordinator = MediaEventsTimelineScreenCoordinator(parameters: parameters) let coordinator = MediaEventsTimelineScreenCoordinator(parameters: parameters)
@@ -57,23 +57,18 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
private var verificationStateCancellable: AnyCancellable? private var verificationStateCancellable: AnyCancellable?
init(userSession: UserSessionProtocol, init(isNewLogin: Bool,
appLockService: AppLockServiceProtocol, appLockService: AppLockServiceProtocol,
analyticsService: AnalyticsService,
appSettings: AppSettings,
notificationManager: NotificationManagerProtocol,
navigationStackCoordinator: NavigationStackCoordinator, navigationStackCoordinator: NavigationStackCoordinator,
userIndicatorController: UserIndicatorControllerProtocol, flowParameters: CommonFlowParameters) {
windowManager: WindowManagerProtocol,
isNewLogin: Bool) {
self.userSession = userSession
self.appLockService = appLockService
self.analyticsService = analyticsService
self.appSettings = appSettings
self.notificationManager = notificationManager
self.userIndicatorController = userIndicatorController
self.windowManager = windowManager
self.isNewLogin = isNewLogin self.isNewLogin = isNewLogin
userSession = flowParameters.userSession
self.appLockService = appLockService
analyticsService = flowParameters.analytics
appSettings = flowParameters.appSettings
notificationManager = flowParameters.notificationManager
userIndicatorController = flowParameters.userIndicatorController
windowManager = flowParameters.windowManager
self.navigationStackCoordinator = navigationStackCoordinator self.navigationStackCoordinator = navigationStackCoordinator
@@ -298,7 +293,7 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
private func presentRecoveryKeyScreen() { private func presentRecoveryKeyScreen() {
let parameters = SecureBackupRecoveryKeyScreenCoordinatorParameters(secureBackupController: userSession.clientProxy.secureBackupController, let parameters = SecureBackupRecoveryKeyScreenCoordinatorParameters(secureBackupController: userSession.clientProxy.secureBackupController,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: userIndicatorController,
isModallyPresented: false) isModallyPresented: false)
let coordinator = SecureBackupRecoveryKeyScreenCoordinator(parameters: parameters) let coordinator = SecureBackupRecoveryKeyScreenCoordinator(parameters: parameters)
@@ -16,14 +16,11 @@ enum PinnedEventsTimelineFlowCoordinatorAction {
} }
class PinnedEventsTimelineFlowCoordinator: FlowCoordinatorProtocol { class PinnedEventsTimelineFlowCoordinator: FlowCoordinatorProtocol {
private let navigationStackCoordinator: NavigationStackCoordinator
private let userSession: UserSessionProtocol
private let timelineControllerFactory: TimelineControllerFactoryProtocol
private let roomProxy: JoinedRoomProxyProtocol private let roomProxy: JoinedRoomProxyProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let navigationStackCoordinator: NavigationStackCoordinator
private let appSettings: AppSettings private let flowParameters: CommonFlowParameters
private let appMediator: AppMediatorProtocol
private let emojiProvider: EmojiProviderProtocol private var userSession: UserSessionProtocol { flowParameters.userSession }
private let actionsSubject: PassthroughSubject<PinnedEventsTimelineFlowCoordinatorAction, Never> = .init() private let actionsSubject: PassthroughSubject<PinnedEventsTimelineFlowCoordinatorAction, Never> = .init()
var actionsPublisher: AnyPublisher<PinnedEventsTimelineFlowCoordinatorAction, Never> { var actionsPublisher: AnyPublisher<PinnedEventsTimelineFlowCoordinatorAction, Never> {
@@ -32,22 +29,12 @@ class PinnedEventsTimelineFlowCoordinator: FlowCoordinatorProtocol {
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
init(navigationStackCoordinator: NavigationStackCoordinator, init(roomProxy: JoinedRoomProxyProtocol,
userSession: UserSessionProtocol, navigationStackCoordinator: NavigationStackCoordinator,
timelineControllerFactory: TimelineControllerFactoryProtocol, flowParameters: CommonFlowParameters) {
roomProxy: JoinedRoomProxyProtocol,
userIndicatorController: UserIndicatorControllerProtocol,
appSettings: AppSettings,
appMediator: AppMediatorProtocol,
emojiProvider: EmojiProviderProtocol) {
self.navigationStackCoordinator = navigationStackCoordinator
self.userSession = userSession
self.timelineControllerFactory = timelineControllerFactory
self.roomProxy = roomProxy self.roomProxy = roomProxy
self.userIndicatorController = userIndicatorController self.navigationStackCoordinator = navigationStackCoordinator
self.appSettings = appSettings self.flowParameters = flowParameters
self.appMediator = appMediator
self.emojiProvider = emojiProvider
} }
func start() { func start() {
@@ -68,9 +55,9 @@ class PinnedEventsTimelineFlowCoordinator: FlowCoordinatorProtocol {
attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()), attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()),
stateEventStringBuilder: RoomStateEventStringBuilder(userID: userID)) stateEventStringBuilder: RoomStateEventStringBuilder(userID: userID))
guard case let .success(timelineController) = await timelineControllerFactory.buildPinnedEventsTimelineController(roomProxy: roomProxy, guard case let .success(timelineController) = await flowParameters.timelineControllerFactory.buildPinnedEventsTimelineController(roomProxy: roomProxy,
timelineItemFactory: timelineItemFactory, timelineItemFactory: timelineItemFactory,
mediaProvider: userSession.mediaProvider) else { mediaProvider: userSession.mediaProvider) else {
fatalError("This can never fail because we allow this view to be presented only when the timeline is fully loaded and not nil") fatalError("This can never fail because we allow this view to be presented only when the timeline is fully loaded and not nil")
} }
@@ -79,9 +66,9 @@ class PinnedEventsTimelineFlowCoordinator: FlowCoordinatorProtocol {
mediaProvider: userSession.mediaProvider, mediaProvider: userSession.mediaProvider,
mediaPlayerProvider: MediaPlayerProvider(), mediaPlayerProvider: MediaPlayerProvider(),
voiceMessageMediaManager: userSession.voiceMessageMediaManager, voiceMessageMediaManager: userSession.voiceMessageMediaManager,
appMediator: appMediator, appMediator: flowParameters.appMediator,
emojiProvider: emojiProvider, emojiProvider: flowParameters.emojiProvider,
timelineControllerFactory: timelineControllerFactory, timelineControllerFactory: flowParameters.timelineControllerFactory,
clientProxy: userSession.clientProxy)) clientProxy: userSession.clientProxy))
coordinator.actions coordinator.actions
@@ -110,8 +97,8 @@ class PinnedEventsTimelineFlowCoordinator: FlowCoordinatorProtocol {
let stackCoordinator = NavigationStackCoordinator() let stackCoordinator = NavigationStackCoordinator()
let params = StaticLocationScreenCoordinatorParameters(interactionMode: .viewOnly(geoURI: geoURI, description: description), let params = StaticLocationScreenCoordinatorParameters(interactionMode: .viewOnly(geoURI: geoURI, description: description),
mapURLBuilder: appSettings.mapTilerConfiguration, mapURLBuilder: flowParameters.appSettings.mapTilerConfiguration,
appMediator: appMediator) appMediator: flowParameters.appMediator)
let coordinator = StaticLocationScreenCoordinator(parameters: params) let coordinator = StaticLocationScreenCoordinator(parameters: params)
coordinator.actions.sink { [weak self] action in coordinator.actions.sink { [weak self] action in
@@ -140,7 +127,7 @@ class PinnedEventsTimelineFlowCoordinator: FlowCoordinatorProtocol {
clientProxy: userSession.clientProxy, clientProxy: userSession.clientProxy,
roomSummaryProvider: roomSummaryProvider, roomSummaryProvider: roomSummaryProvider,
mediaProvider: userSession.mediaProvider, mediaProvider: userSession.mediaProvider,
userIndicatorController: userIndicatorController) userIndicatorController: flowParameters.userIndicatorController)
let coordinator = MessageForwardingScreenCoordinator(parameters: parameters) let coordinator = MessageForwardingScreenCoordinator(parameters: parameters)
coordinator.actions.sink { [weak self] action in coordinator.actions.sink { [weak self] action in
@@ -56,17 +56,11 @@ struct FocusEvent: Hashable {
// swiftlint:disable:next type_body_length // swiftlint:disable:next type_body_length
class RoomFlowCoordinator: FlowCoordinatorProtocol { class RoomFlowCoordinator: FlowCoordinatorProtocol {
private let roomID: String private let roomID: String
private let userSession: UserSessionProtocol
private let isChildFlow: Bool private let isChildFlow: Bool
private let timelineControllerFactory: TimelineControllerFactoryProtocol
private let navigationStackCoordinator: NavigationStackCoordinator private let navigationStackCoordinator: NavigationStackCoordinator
private let emojiProvider: EmojiProviderProtocol private let flowParameters: CommonFlowParameters
private let ongoingCallRoomIDPublisher: CurrentValuePublisher<String?, Never>
private let appMediator: AppMediatorProtocol private var userSession: UserSessionProtocol { flowParameters.userSession }
private let appSettings: AppSettings
private let appHooks: AppHooks
private let analytics: AnalyticsService
private let userIndicatorController: UserIndicatorControllerProtocol
private var roomProxy: JoinedRoomProxyProtocol! private var roomProxy: JoinedRoomProxyProtocol!
@@ -94,33 +88,17 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
private var timelineController: TimelineControllerProtocol? private var timelineController: TimelineControllerProtocol?
init(roomID: String, init(roomID: String,
userSession: UserSessionProtocol,
isChildFlow: Bool, isChildFlow: Bool,
timelineControllerFactory: TimelineControllerFactoryProtocol,
navigationStackCoordinator: NavigationStackCoordinator, navigationStackCoordinator: NavigationStackCoordinator,
emojiProvider: EmojiProviderProtocol, flowParameters: CommonFlowParameters) {
ongoingCallRoomIDPublisher: CurrentValuePublisher<String?, Never>,
appMediator: AppMediatorProtocol,
appSettings: AppSettings,
appHooks: AppHooks,
analytics: AnalyticsService,
userIndicatorController: UserIndicatorControllerProtocol) {
self.roomID = roomID self.roomID = roomID
self.userSession = userSession
self.isChildFlow = isChildFlow self.isChildFlow = isChildFlow
self.timelineControllerFactory = timelineControllerFactory
self.navigationStackCoordinator = navigationStackCoordinator self.navigationStackCoordinator = navigationStackCoordinator
self.emojiProvider = emojiProvider self.flowParameters = flowParameters
self.ongoingCallRoomIDPublisher = ongoingCallRoomIDPublisher
self.appMediator = appMediator
self.appSettings = appSettings
self.appHooks = appHooks
self.analytics = analytics
self.userIndicatorController = userIndicatorController
setupStateMachine() setupStateMachine()
analytics.signpost.beginRoomFlow(roomID) flowParameters.analytics.signpost.beginRoomFlow(roomID)
} }
// MARK: - FlowCoordinatorProtocol // MARK: - FlowCoordinatorProtocol
@@ -220,8 +198,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
let parameters = RoomChangeRolesScreenCoordinatorParameters(mode: .owner, let parameters = RoomChangeRolesScreenCoordinatorParameters(mode: .owner,
roomProxy: roomProxy, roomProxy: roomProxy,
mediaProvider: userSession.mediaProvider, mediaProvider: userSession.mediaProvider,
userIndicatorController: userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
analytics: analytics) analytics: flowParameters.analytics)
let stackCoordinator = NavigationStackCoordinator() let stackCoordinator = NavigationStackCoordinator()
let coordinator = RoomChangeRolesScreenCoordinator(parameters: parameters) let coordinator = RoomChangeRolesScreenCoordinator(parameters: parameters)
coordinator.actionsPublisher.sink { [weak self] action in coordinator.actionsPublisher.sink { [weak self] action in
@@ -519,7 +497,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
// Flag the room as read on entering, the timeline will take care of the read receipts // Flag the room as read on entering, the timeline will take care of the read receipts
Task { await roomProxy.flagAsUnread(false) } Task { await roomProxy.flagAsUnread(false) }
analytics.trackViewRoom(isDM: roomProxy.infoPublisher.value.isDirect, isSpace: roomProxy.infoPublisher.value.isSpace) flowParameters.analytics.trackViewRoom(isDM: roomProxy.infoPublisher.value.isDirect, isSpace: roomProxy.infoPublisher.value.isSpace)
let coordinator = makeRoomScreenCoordinator(presentationAction: presentationAction, animated: animated) let coordinator = makeRoomScreenCoordinator(presentationAction: presentationAction, animated: animated)
roomScreenCoordinator = coordinator roomScreenCoordinator = coordinator
@@ -554,10 +532,10 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
let timelineItemFactory = RoomTimelineItemFactory(userID: userID, let timelineItemFactory = RoomTimelineItemFactory(userID: userID,
attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()), attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()),
stateEventStringBuilder: RoomStateEventStringBuilder(userID: userID)) stateEventStringBuilder: RoomStateEventStringBuilder(userID: userID))
let timelineController = timelineControllerFactory.buildTimelineController(roomProxy: roomProxy, let timelineController = flowParameters.timelineControllerFactory.buildTimelineController(roomProxy: roomProxy,
initialFocussedEventID: presentationAction?.focusedEvent?.eventID, initialFocussedEventID: presentationAction?.focusedEvent?.eventID,
timelineItemFactory: timelineItemFactory, timelineItemFactory: timelineItemFactory,
mediaProvider: userSession.mediaProvider) mediaProvider: userSession.mediaProvider)
self.timelineController = timelineController self.timelineController = timelineController
let completionSuggestionService = CompletionSuggestionService(roomProxy: roomProxy, let completionSuggestionService = CompletionSuggestionService(roomProxy: roomProxy,
@@ -574,14 +552,14 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
mediaProvider: userSession.mediaProvider, mediaProvider: userSession.mediaProvider,
mediaPlayerProvider: MediaPlayerProvider(), mediaPlayerProvider: MediaPlayerProvider(),
voiceMessageMediaManager: userSession.voiceMessageMediaManager, voiceMessageMediaManager: userSession.voiceMessageMediaManager,
emojiProvider: emojiProvider, emojiProvider: flowParameters.emojiProvider,
completionSuggestionService: completionSuggestionService, completionSuggestionService: completionSuggestionService,
ongoingCallRoomIDPublisher: ongoingCallRoomIDPublisher, ongoingCallRoomIDPublisher: flowParameters.ongoingCallRoomIDPublisher,
appMediator: appMediator, appMediator: flowParameters.appMediator,
appSettings: appSettings, appSettings: flowParameters.appSettings,
appHooks: appHooks, appHooks: flowParameters.appHooks,
composerDraftService: composerDraftService, composerDraftService: composerDraftService,
timelineControllerFactory: timelineControllerFactory) timelineControllerFactory: flowParameters.timelineControllerFactory)
let coordinator = RoomScreenCoordinator(parameters: parameters) let coordinator = RoomScreenCoordinator(parameters: parameters)
coordinator.actions coordinator.actions
@@ -650,10 +628,10 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
fatalError("Invalid thread event ID") fatalError("Invalid thread event ID")
} }
guard case let .success(timelineController) = await timelineControllerFactory.buildThreadTimelineController(eventID: threadRootEventID, guard case let .success(timelineController) = await flowParameters.timelineControllerFactory.buildThreadTimelineController(eventID: threadRootEventID,
roomProxy: roomProxy, roomProxy: roomProxy,
timelineItemFactory: timelineItemFactory, timelineItemFactory: timelineItemFactory,
mediaProvider: userSession.mediaProvider) else { mediaProvider: userSession.mediaProvider) else {
MXLog.error("Failed presenting media timeline") MXLog.error("Failed presenting media timeline")
return return
} }
@@ -670,12 +648,12 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
mediaProvider: userSession.mediaProvider, mediaProvider: userSession.mediaProvider,
mediaPlayerProvider: MediaPlayerProvider(), mediaPlayerProvider: MediaPlayerProvider(),
voiceMessageMediaManager: userSession.voiceMessageMediaManager, voiceMessageMediaManager: userSession.voiceMessageMediaManager,
emojiProvider: emojiProvider, emojiProvider: flowParameters.emojiProvider,
completionSuggestionService: completionSuggestionService, completionSuggestionService: completionSuggestionService,
appMediator: appMediator, appMediator: flowParameters.appMediator,
appSettings: appSettings, appSettings: flowParameters.appSettings,
composerDraftService: composerDraftService, composerDraftService: composerDraftService,
timelineControllerFactory: timelineControllerFactory)) timelineControllerFactory: flowParameters.timelineControllerFactory))
coordinator.actions.sink { [weak self] action in coordinator.actions.sink { [weak self] action in
guard let self else { return } guard let self else { return }
@@ -723,8 +701,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
via: via, via: via,
clientProxy: userSession.clientProxy, clientProxy: userSession.clientProxy,
mediaProvider: userSession.mediaProvider, mediaProvider: userSession.mediaProvider,
userIndicatorController: userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
appSettings: appSettings)) appSettings: flowParameters.appSettings))
joinRoomScreenCoordinator = coordinator joinRoomScreenCoordinator = coordinator
@@ -742,9 +720,9 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
await storeAndSubscribeToRoomProxy(roomProxy) await storeAndSubscribeToRoomProxy(roomProxy)
stateMachine.tryEvent(.presentRoom(presentationAction: nil), userInfo: EventUserInfo(animated: animated)) stateMachine.tryEvent(.presentRoom(presentationAction: nil), userInfo: EventUserInfo(animated: animated))
analytics.trackJoinedRoom(isDM: roomProxy.infoPublisher.value.isDirect, flowParameters.analytics.trackJoinedRoom(isDM: roomProxy.infoPublisher.value.isDirect,
isSpace: roomProxy.infoPublisher.value.isSpace, isSpace: roomProxy.infoPublisher.value.isSpace,
activeMemberCount: UInt(roomProxy.infoPublisher.value.activeMembersCount)) activeMemberCount: UInt(roomProxy.infoPublisher.value.activeMembersCount))
} else { } else {
stateMachine.tryEvent(.dismissFlow, userInfo: EventUserInfo(animated: animated)) stateMachine.tryEvent(.dismissFlow, userInfo: EventUserInfo(animated: animated))
} }
@@ -790,18 +768,18 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
timelineController = nil timelineController = nil
actionsSubject.send(.finished) actionsSubject.send(.finished)
analytics.signpost.endRoomFlow() flowParameters.analytics.signpost.endRoomFlow()
} }
private func presentRoomDetails(isRoot: Bool, animated: Bool) async { private func presentRoomDetails(isRoot: Bool, animated: Bool) async {
let params = RoomDetailsScreenCoordinatorParameters(roomProxy: roomProxy, let params = RoomDetailsScreenCoordinatorParameters(roomProxy: roomProxy,
clientProxy: userSession.clientProxy, clientProxy: userSession.clientProxy,
mediaProvider: userSession.mediaProvider, mediaProvider: userSession.mediaProvider,
analyticsService: analytics, analyticsService: flowParameters.analytics,
userIndicatorController: userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
notificationSettings: userSession.clientProxy.notificationSettings, notificationSettings: userSession.clientProxy.notificationSettings,
attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()), attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()),
appMediator: appMediator) appMediator: flowParameters.appMediator)
let coordinator = RoomDetailsScreenCoordinator(parameters: params) let coordinator = RoomDetailsScreenCoordinator(parameters: params)
coordinator.actions.sink { [weak self] action in coordinator.actions.sink { [weak self] action in
guard let self else { return } guard let self else { return }
@@ -862,8 +840,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
let parameters = RoomMembersListScreenCoordinatorParameters(clientProxy: userSession.clientProxy, let parameters = RoomMembersListScreenCoordinatorParameters(clientProxy: userSession.clientProxy,
roomProxy: roomProxy, roomProxy: roomProxy,
mediaProvider: userSession.mediaProvider, mediaProvider: userSession.mediaProvider,
userIndicatorController: userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
analytics: analytics) analytics: flowParameters.analytics)
let coordinator = RoomMembersListScreenCoordinator(parameters: parameters) let coordinator = RoomMembersListScreenCoordinator(parameters: parameters)
coordinator.actions coordinator.actions
@@ -887,7 +865,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
private func presentKnockRequestsList() { private func presentKnockRequestsList() {
let parameters = KnockRequestsListScreenCoordinatorParameters(roomProxy: roomProxy, let parameters = KnockRequestsListScreenCoordinatorParameters(roomProxy: roomProxy,
mediaProvider: userSession.mediaProvider, mediaProvider: userSession.mediaProvider,
userIndicatorController: userIndicatorController) userIndicatorController: flowParameters.userIndicatorController)
let coordinator = KnockRequestsListScreenCoordinator(parameters: parameters) let coordinator = KnockRequestsListScreenCoordinator(parameters: parameters)
navigationStackCoordinator.push(coordinator) { [weak self] in navigationStackCoordinator.push(coordinator) { [weak self] in
@@ -901,11 +879,11 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
let roomDetailsEditParameters = RoomDetailsEditScreenCoordinatorParameters(roomProxy: roomProxy, let roomDetailsEditParameters = RoomDetailsEditScreenCoordinatorParameters(roomProxy: roomProxy,
clientProxy: userSession.clientProxy, clientProxy: userSession.clientProxy,
mediaProvider: userSession.mediaProvider, mediaProvider: userSession.mediaProvider,
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: appSettings), mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: flowParameters.appSettings),
navigationStackCoordinator: stackCoordinator, navigationStackCoordinator: stackCoordinator,
userIndicatorController: userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
orientationManager: appMediator.windowManager, orientationManager: flowParameters.appMediator.windowManager,
appSettings: appSettings) appSettings: flowParameters.appSettings)
let roomDetailsEditCoordinator = RoomDetailsEditScreenCoordinator(parameters: roomDetailsEditParameters) let roomDetailsEditCoordinator = RoomDetailsEditScreenCoordinator(parameters: roomDetailsEditParameters)
roomDetailsEditCoordinator.actions.sink { [weak self] action in roomDetailsEditCoordinator.actions.sink { [weak self] action in
@@ -933,7 +911,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
senderID: senderID, senderID: senderID,
roomProxy: roomProxy, roomProxy: roomProxy,
clientProxy: userSession.clientProxy, clientProxy: userSession.clientProxy,
userIndicatorController: userIndicatorController) userIndicatorController: flowParameters.userIndicatorController)
let coordinator = ReportContentScreenCoordinator(parameters: parameters) let coordinator = ReportContentScreenCoordinator(parameters: parameters)
coordinator.actions coordinator.actions
@@ -946,7 +924,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
case .cancel: case .cancel:
break break
case .finish: case .finish:
userIndicatorController.submitIndicator(UserIndicator(title: L10n.commonReportSubmitted, iconName: "checkmark")) flowParameters.userIndicatorController.submitIndicator(UserIndicator(title: L10n.commonReportSubmitted, iconName: "checkmark"))
} }
} }
.store(in: &cancellables) .store(in: &cancellables)
@@ -963,8 +941,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
let stackCoordinator = NavigationStackCoordinator() let stackCoordinator = NavigationStackCoordinator()
let mediaPickerCoordinator = MediaPickerScreenCoordinator(mode: mode, let mediaPickerCoordinator = MediaPickerScreenCoordinator(mode: mode,
userIndicatorController: userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
orientationManager: appMediator.windowManager) { [weak self] action in orientationManager: flowParameters.appMediator.windowManager) { [weak self] action in
guard let self else { guard let self else {
return return
} }
@@ -1000,11 +978,11 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
let parameters = MediaUploadPreviewScreenCoordinatorParameters(mediaURLs: mediaURLs, let parameters = MediaUploadPreviewScreenCoordinatorParameters(mediaURLs: mediaURLs,
title: title, title: title,
isRoomEncrypted: roomProxy.infoPublisher.value.isEncrypted, isRoomEncrypted: roomProxy.infoPublisher.value.isEncrypted,
shouldShowCaptionWarning: appSettings.shouldShowMediaCaptionWarning, shouldShowCaptionWarning: flowParameters.appSettings.shouldShowMediaCaptionWarning,
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: appSettings), mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: flowParameters.appSettings),
timelineController: timelineController, timelineController: timelineController,
clientProxy: userSession.clientProxy, clientProxy: userSession.clientProxy,
userIndicatorController: userIndicatorController) userIndicatorController: flowParameters.userIndicatorController)
let mediaUploadPreviewScreenCoordinator = MediaUploadPreviewScreenCoordinator(parameters: parameters) let mediaUploadPreviewScreenCoordinator = MediaUploadPreviewScreenCoordinator(parameters: parameters)
@@ -1030,7 +1008,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
selectedEmoji: Set<String>, selectedEmoji: Set<String>,
timelineController: TimelineControllerProtocol, timelineController: TimelineControllerProtocol,
animated: Bool) { animated: Bool) {
let params = EmojiPickerScreenCoordinatorParameters(emojiProvider: emojiProvider, let params = EmojiPickerScreenCoordinatorParameters(emojiProvider: flowParameters.emojiProvider,
itemID: itemID, selectedEmojis: selectedEmoji) itemID: itemID, selectedEmojis: selectedEmoji)
let coordinator = EmojiPickerScreenCoordinator(parameters: params) let coordinator = EmojiPickerScreenCoordinator(parameters: params)
@@ -1065,8 +1043,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
let stackCoordinator = NavigationStackCoordinator() let stackCoordinator = NavigationStackCoordinator()
let params = StaticLocationScreenCoordinatorParameters(interactionMode: interactionMode, let params = StaticLocationScreenCoordinatorParameters(interactionMode: interactionMode,
mapURLBuilder: appSettings.mapTilerConfiguration, mapURLBuilder: flowParameters.appSettings.mapTilerConfiguration,
appMediator: appMediator) appMediator: flowParameters.appMediator)
let coordinator = StaticLocationScreenCoordinator(parameters: params) let coordinator = StaticLocationScreenCoordinator(parameters: params)
coordinator.actions.sink { [weak self] action in coordinator.actions.sink { [weak self] action in
@@ -1082,11 +1060,11 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
self.navigationStackCoordinator.setSheetCoordinator(nil) self.navigationStackCoordinator.setSheetCoordinator(nil)
} }
self.analytics.trackComposer(inThread: false, self.flowParameters.analytics.trackComposer(inThread: false,
isEditing: false, isEditing: false,
isReply: false, isReply: false,
messageType: isUserLocation ? .LocationUser : .LocationPin, messageType: isUserLocation ? .LocationUser : .LocationPin,
startsThread: nil) startsThread: nil)
case .close: case .close:
self.navigationStackCoordinator.setSheetCoordinator(nil) self.navigationStackCoordinator.setSheetCoordinator(nil)
} }
@@ -1145,19 +1123,19 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
Task { Task {
let result = await timelineController.createPoll(question: question, answers: options, pollKind: pollKind) let result = await timelineController.createPoll(question: question, answers: options, pollKind: pollKind)
self.analytics.trackComposer(inThread: false, self.flowParameters.analytics.trackComposer(inThread: false,
isEditing: false, isEditing: false,
isReply: false, isReply: false,
messageType: .Poll, messageType: .Poll,
startsThread: nil) startsThread: nil)
self.analytics.trackPollCreated(isUndisclosed: pollKind == .undisclosed, numberOfAnswers: options.count) self.flowParameters.analytics.trackPollCreated(isUndisclosed: pollKind == .undisclosed, numberOfAnswers: options.count)
switch result { switch result {
case .success: case .success:
break break
case .failure: case .failure:
self.userIndicatorController.submitIndicator(UserIndicator(title: L10n.errorUnknown)) self.flowParameters.userIndicatorController.submitIndicator(UserIndicator(title: L10n.errorUnknown))
} }
} }
} }
@@ -1170,7 +1148,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
case .success: case .success:
break break
case .failure: case .failure:
self.userIndicatorController.submitIndicator(UserIndicator(title: L10n.errorUnknown)) self.flowParameters.userIndicatorController.submitIndicator(UserIndicator(title: L10n.errorUnknown))
} }
} }
} }
@@ -1178,7 +1156,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
private func deletePoll(mode: PollFormMode) { private func deletePoll(mode: PollFormMode) {
Task { Task {
guard case .edit(let pollStartID, _) = mode else { guard case .edit(let pollStartID, _) = mode else {
self.userIndicatorController.submitIndicator(UserIndicator(title: L10n.errorUnknown)) self.flowParameters.userIndicatorController.submitIndicator(UserIndicator(title: L10n.errorUnknown))
return return
} }
@@ -1188,7 +1166,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
case .success: case .success:
break break
case .failure: case .failure:
self.userIndicatorController.submitIndicator(UserIndicator(title: L10n.errorUnknown)) self.flowParameters.userIndicatorController.submitIndicator(UserIndicator(title: L10n.errorUnknown))
} }
} }
} }
@@ -1200,12 +1178,12 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()), attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()),
stateEventStringBuilder: RoomStateEventStringBuilder(userID: userID)) stateEventStringBuilder: RoomStateEventStringBuilder(userID: userID))
let timelineController = timelineControllerFactory.buildTimelineController(roomProxy: roomProxy, let timelineController = flowParameters.timelineControllerFactory.buildTimelineController(roomProxy: roomProxy,
initialFocussedEventID: nil, initialFocussedEventID: nil,
timelineItemFactory: timelineItemFactory, timelineItemFactory: timelineItemFactory,
mediaProvider: userSession.mediaProvider) mediaProvider: userSession.mediaProvider)
let parameters = RoomPollsHistoryScreenCoordinatorParameters(pollInteractionHandler: PollInteractionHandler(analyticsService: analytics, let parameters = RoomPollsHistoryScreenCoordinatorParameters(pollInteractionHandler: PollInteractionHandler(analyticsService: flowParameters.analytics,
timelineController: timelineController), timelineController: timelineController),
timelineController: timelineController) timelineController: timelineController)
let coordinator = RoomPollsHistoryScreenCoordinator(parameters: parameters) let coordinator = RoomPollsHistoryScreenCoordinator(parameters: parameters)
@@ -1231,8 +1209,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
roomProxy: roomProxy, roomProxy: roomProxy,
clientProxy: userSession.clientProxy, clientProxy: userSession.clientProxy,
mediaProvider: userSession.mediaProvider, mediaProvider: userSession.mediaProvider,
userIndicatorController: userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
analytics: analytics) analytics: flowParameters.analytics)
let coordinator = RoomMemberDetailsScreenCoordinator(parameters: params) let coordinator = RoomMemberDetailsScreenCoordinator(parameters: params)
coordinator.actions.sink { [weak self] action in coordinator.actions.sink { [weak self] action in
@@ -1260,8 +1238,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
isPresentedModally: false, isPresentedModally: false,
clientProxy: userSession.clientProxy, clientProxy: userSession.clientProxy,
mediaProvider: userSession.mediaProvider, mediaProvider: userSession.mediaProvider,
userIndicatorController: userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
analytics: analytics) analytics: flowParameters.analytics)
let coordinator = UserProfileScreenCoordinator(parameters: parameters) let coordinator = UserProfileScreenCoordinator(parameters: parameters)
coordinator.actionsPublisher.sink { [weak self] action in coordinator.actionsPublisher.sink { [weak self] action in
guard let self else { return } guard let self else { return }
@@ -1296,7 +1274,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
clientProxy: userSession.clientProxy, clientProxy: userSession.clientProxy,
roomSummaryProvider: roomSummaryProvider, roomSummaryProvider: roomSummaryProvider,
mediaProvider: userSession.mediaProvider, mediaProvider: userSession.mediaProvider,
userIndicatorController: userIndicatorController) userIndicatorController: flowParameters.userIndicatorController)
let coordinator = MessageForwardingScreenCoordinator(parameters: parameters) let coordinator = MessageForwardingScreenCoordinator(parameters: parameters)
coordinator.actions.sink { [weak self] action in coordinator.actions.sink { [weak self] action in
@@ -1344,7 +1322,6 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
let parameters = NotificationSettingsScreenCoordinatorParameters(navigationStackCoordinator: stackCoordinator, let parameters = NotificationSettingsScreenCoordinatorParameters(navigationStackCoordinator: stackCoordinator,
userSession: userSession, userSession: userSession,
userNotificationCenter: UNUserNotificationCenter.current(), userNotificationCenter: UNUserNotificationCenter.current(),
notificationSettings: userSession.clientProxy.notificationSettings,
isModallyPresented: true) isModallyPresented: true)
let coordinator = NotificationSettingsScreenCoordinator(parameters: parameters) let coordinator = NotificationSettingsScreenCoordinator(parameters: parameters)
coordinator.actions.sink { [weak self] action in coordinator.actions.sink { [weak self] action in
@@ -1370,7 +1347,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
roomType: .room(roomProxy: roomProxy), roomType: .room(roomProxy: roomProxy),
mediaProvider: userSession.mediaProvider, mediaProvider: userSession.mediaProvider,
userDiscoveryService: UserDiscoveryService(clientProxy: userSession.clientProxy), userDiscoveryService: UserDiscoveryService(clientProxy: userSession.clientProxy),
userIndicatorController: userIndicatorController) userIndicatorController: flowParameters.userIndicatorController)
let coordinator = InviteUsersScreenCoordinator(parameters: inviteParameters) let coordinator = InviteUsersScreenCoordinator(parameters: inviteParameters)
stackCoordinator.setRootCoordinator(coordinator) stackCoordinator.setRootCoordinator(coordinator)
@@ -1424,9 +1401,9 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
return return
} }
userIndicatorController.alertInfo = .init(id: .init(), flowParameters.userIndicatorController.alertInfo = .init(id: .init(),
title: L10n.commonUnableToInviteTitle, title: L10n.commonUnableToInviteTitle,
message: L10n.commonUnableToInviteMessage) message: L10n.commonUnableToInviteMessage)
} }
} }
@@ -1434,8 +1411,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
let parameters = RoomRolesAndPermissionsFlowCoordinatorParameters(roomProxy: roomProxy, let parameters = RoomRolesAndPermissionsFlowCoordinatorParameters(roomProxy: roomProxy,
mediaProvider: userSession.mediaProvider, mediaProvider: userSession.mediaProvider,
navigationStackCoordinator: navigationStackCoordinator, navigationStackCoordinator: navigationStackCoordinator,
userIndicatorController: userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
analytics: analytics) analytics: flowParameters.analytics)
let coordinator = RoomRolesAndPermissionsFlowCoordinator(parameters: parameters) let coordinator = RoomRolesAndPermissionsFlowCoordinator(parameters: parameters)
coordinator.actionsPublisher.sink { [weak self] action in coordinator.actionsPublisher.sink { [weak self] action in
switch action { switch action {
@@ -1453,7 +1430,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
let coordinator = ResolveVerifiedUserSendFailureScreenCoordinator(parameters: .init(failure: failure, let coordinator = ResolveVerifiedUserSendFailureScreenCoordinator(parameters: .init(failure: failure,
sendHandle: sendHandle, sendHandle: sendHandle,
roomProxy: roomProxy, roomProxy: roomProxy,
userIndicatorController: userIndicatorController)) userIndicatorController: flowParameters.userIndicatorController))
coordinator.actionsPublisher.sink { [weak self] action in coordinator.actionsPublisher.sink { [weak self] action in
guard let self else { return } guard let self else { return }
@@ -1472,7 +1449,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
private func presentSecurityAndPrivacyScreen() { private func presentSecurityAndPrivacyScreen() {
let coordinator = SecurityAndPrivacyScreenCoordinator(parameters: .init(roomProxy: roomProxy, let coordinator = SecurityAndPrivacyScreenCoordinator(parameters: .init(roomProxy: roomProxy,
clientProxy: userSession.clientProxy, clientProxy: userSession.clientProxy,
userIndicatorController: userIndicatorController)) userIndicatorController: flowParameters.userIndicatorController))
coordinator.actionsPublisher.sink { [weak self] action in coordinator.actionsPublisher.sink { [weak self] action in
guard let self else { return } guard let self else { return }
@@ -1493,7 +1470,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
let stackCoordinator = NavigationStackCoordinator() let stackCoordinator = NavigationStackCoordinator()
let coordinator = EditRoomAddressScreenCoordinator(parameters: .init(roomProxy: roomProxy, let coordinator = EditRoomAddressScreenCoordinator(parameters: .init(roomProxy: roomProxy,
clientProxy: userSession.clientProxy, clientProxy: userSession.clientProxy,
userIndicatorController: userIndicatorController)) userIndicatorController: flowParameters.userIndicatorController))
coordinator.actionsPublisher.sink { [weak self] action in coordinator.actionsPublisher.sink { [weak self] action in
switch action { switch action {
@@ -1510,7 +1487,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
private func presentReportRoom() { private func presentReportRoom() {
let stackCoordinator = NavigationStackCoordinator() let stackCoordinator = NavigationStackCoordinator()
let coordinator = ReportRoomScreenCoordinator(parameters: .init(roomProxy: roomProxy, let coordinator = ReportRoomScreenCoordinator(parameters: .init(roomProxy: roomProxy,
userIndicatorController: userIndicatorController)) userIndicatorController: flowParameters.userIndicatorController))
coordinator.actionsPublisher.sink { [weak self] action in coordinator.actionsPublisher.sink { [weak self] action in
guard let self else { return } guard let self else { return }
@@ -1535,7 +1512,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
let coordinator = DeclineAndBlockScreenCoordinator(parameters: .init(userID: userID, let coordinator = DeclineAndBlockScreenCoordinator(parameters: .init(userID: userID,
roomID: roomID, roomID: roomID,
clientProxy: userSession.clientProxy, clientProxy: userSession.clientProxy,
userIndicatorController: userIndicatorController)) userIndicatorController: flowParameters.userIndicatorController))
coordinator.actionsPublisher.sink { [weak self] action in coordinator.actionsPublisher.sink { [weak self] action in
guard let self else { return } guard let self else { return }
switch action { switch action {
@@ -1558,17 +1535,9 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
private func startChildFlow(for roomID: String, via: [String], entryPoint: RoomFlowCoordinatorEntryPoint) { private func startChildFlow(for roomID: String, via: [String], entryPoint: RoomFlowCoordinatorEntryPoint) {
let coordinator = RoomFlowCoordinator(roomID: roomID, let coordinator = RoomFlowCoordinator(roomID: roomID,
userSession: userSession,
isChildFlow: true, isChildFlow: true,
timelineControllerFactory: timelineControllerFactory,
navigationStackCoordinator: navigationStackCoordinator, navigationStackCoordinator: navigationStackCoordinator,
emojiProvider: emojiProvider, flowParameters: flowParameters)
ongoingCallRoomIDPublisher: ongoingCallRoomIDPublisher,
appMediator: appMediator,
appSettings: appSettings,
appHooks: appHooks,
analytics: analytics,
userIndicatorController: userIndicatorController)
coordinator.actions.sink { [weak self] action in coordinator.actions.sink { [weak self] action in
guard let self else { return } guard let self else { return }
@@ -1601,14 +1570,9 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
private func startPinnedEventsTimelineFlow() { private func startPinnedEventsTimelineFlow() {
let stackCoordinator = NavigationStackCoordinator() let stackCoordinator = NavigationStackCoordinator()
let flowCoordinator = PinnedEventsTimelineFlowCoordinator(navigationStackCoordinator: stackCoordinator, let flowCoordinator = PinnedEventsTimelineFlowCoordinator(roomProxy: roomProxy,
userSession: userSession, navigationStackCoordinator: stackCoordinator,
timelineControllerFactory: timelineControllerFactory, flowParameters: flowParameters)
roomProxy: roomProxy,
userIndicatorController: userIndicatorController,
appSettings: appSettings,
appMediator: appMediator,
emojiProvider: emojiProvider)
flowCoordinator.actionsPublisher.sink { [weak self] action in flowCoordinator.actionsPublisher.sink { [weak self] action in
guard let self else { guard let self else {
@@ -1641,13 +1605,9 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
} }
private func startMediaEventsTimelineFlow() async { private func startMediaEventsTimelineFlow() async {
let flowCoordinator = MediaEventsTimelineFlowCoordinator(navigationStackCoordinator: navigationStackCoordinator, let flowCoordinator = MediaEventsTimelineFlowCoordinator(roomProxy: roomProxy,
userSession: userSession, navigationStackCoordinator: navigationStackCoordinator,
timelineControllerFactory: timelineControllerFactory, flowParameters: flowParameters)
roomProxy: roomProxy,
userIndicatorController: userIndicatorController,
appMediator: appMediator,
emojiProvider: emojiProvider)
flowCoordinator.actionsPublisher.sink { [weak self] action in flowCoordinator.actionsPublisher.sink { [weak self] action in
guard let self else { return } guard let self else { return }
@@ -1674,15 +1634,15 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
private static let loadingIndicatorID = "\(RoomFlowCoordinator.self)-Loading" private static let loadingIndicatorID = "\(RoomFlowCoordinator.self)-Loading"
private func showLoadingIndicator(delay: Duration? = nil) { private func showLoadingIndicator(delay: Duration? = nil) {
userIndicatorController.submitIndicator(.init(id: Self.loadingIndicatorID, flowParameters.userIndicatorController.submitIndicator(.init(id: Self.loadingIndicatorID,
type: .modal(progress: .indeterminate, type: .modal(progress: .indeterminate,
interactiveDismissDisabled: false, interactiveDismissDisabled: false,
allowsInteraction: false), allowsInteraction: false),
title: L10n.commonLoading, persistent: true), title: L10n.commonLoading, persistent: true),
delay: delay) delay: delay)
} }
private func hideLoadingIndicator() { private func hideLoadingIndicator() {
userIndicatorController.retractIndicatorWithId(Self.loadingIndicatorID) flowParameters.userIndicatorController.retractIndicatorWithId(Self.loadingIndicatorID)
} }
} }
@@ -16,22 +16,10 @@ enum SettingsFlowCoordinatorAction {
case forceLogout case forceLogout
} }
struct SettingsFlowCoordinatorParameters {
let userSession: UserSessionProtocol
let windowManager: WindowManagerProtocol
let appLockService: AppLockServiceProtocol
let bugReportService: BugReportServiceProtocol
let notificationSettings: NotificationSettingsProxyProtocol
let secureBackupController: SecureBackupControllerProtocol
let appSettings: AppSettings
let navigationStackCoordinator: NavigationStackCoordinator
let userIndicatorController: UserIndicatorControllerProtocol
let analytics: AnalyticsService
}
class SettingsFlowCoordinator: FlowCoordinatorProtocol { class SettingsFlowCoordinator: FlowCoordinatorProtocol {
private let parameters: SettingsFlowCoordinatorParameters private let appLockService: AppLockServiceProtocol
private var navigationStackCoordinator: NavigationStackCoordinator { parameters.navigationStackCoordinator } private let navigationStackCoordinator: NavigationStackCoordinator
private let flowParameters: CommonFlowParameters
// periphery:ignore - retaining purpose // periphery:ignore - retaining purpose
private var appLockSetupFlowCoordinator: AppLockSetupFlowCoordinator? private var appLockSetupFlowCoordinator: AppLockSetupFlowCoordinator?
@@ -47,8 +35,12 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
actionsSubject.eraseToAnyPublisher() actionsSubject.eraseToAnyPublisher()
} }
init(parameters: SettingsFlowCoordinatorParameters) { init(appLockService: AppLockServiceProtocol,
self.parameters = parameters navigationStackCoordinator: NavigationStackCoordinator,
flowParameters: CommonFlowParameters) {
self.appLockService = appLockService
self.navigationStackCoordinator = navigationStackCoordinator
self.flowParameters = flowParameters
} }
func start() { func start() {
@@ -73,9 +65,9 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
// MARK: - Private // MARK: - Private
private func presentSettingsScreen(animated: Bool) { private func presentSettingsScreen(animated: Bool) {
let settingsScreenCoordinator = SettingsScreenCoordinator(parameters: .init(userSession: parameters.userSession, let settingsScreenCoordinator = SettingsScreenCoordinator(parameters: .init(userSession: flowParameters.userSession,
appSettings: parameters.appSettings, appSettings: flowParameters.appSettings,
isBugReportServiceEnabled: parameters.bugReportService.isEnabled)) isBugReportServiceEnabled: flowParameters.bugReportService.isEnabled))
settingsScreenCoordinator.actions settingsScreenCoordinator.actions
.sink { [weak self] action in .sink { [weak self] action in
@@ -98,9 +90,9 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
presentAppLockSetupFlow() presentAppLockSetupFlow()
case .bugReport: case .bugReport:
bugReportFlowCoordinator = BugReportFlowCoordinator(parameters: .init(presentationMode: .push(navigationStackCoordinator), bugReportFlowCoordinator = BugReportFlowCoordinator(parameters: .init(presentationMode: .push(navigationStackCoordinator),
userIndicatorController: parameters.userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
bugReportService: parameters.bugReportService, bugReportService: flowParameters.bugReportService,
userSession: parameters.userSession)) userSession: flowParameters.userSession))
bugReportFlowCoordinator?.start() bugReportFlowCoordinator?.start()
case .about: case .about:
presentLegalInformationScreen() presentLegalInformationScreen()
@@ -122,9 +114,9 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
} }
private func startEncryptionSettingsFlow(animated: Bool) { private func startEncryptionSettingsFlow(animated: Bool) {
let coordinator = EncryptionSettingsFlowCoordinator(parameters: .init(userSession: parameters.userSession, let coordinator = EncryptionSettingsFlowCoordinator(parameters: .init(userSession: flowParameters.userSession,
appSettings: parameters.appSettings, appSettings: flowParameters.appSettings,
userIndicatorController: parameters.userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
navigationStackCoordinator: navigationStackCoordinator)) navigationStackCoordinator: navigationStackCoordinator))
coordinator.actionsPublisher.sink { [weak self] action in coordinator.actionsPublisher.sink { [weak self] action in
switch action { switch action {
@@ -140,26 +132,26 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
} }
private func presentUserDetailsEditScreen() { private func presentUserDetailsEditScreen() {
let coordinator = UserDetailsEditScreenCoordinator(parameters: .init(orientationManager: parameters.windowManager, let coordinator = UserDetailsEditScreenCoordinator(parameters: .init(orientationManager: flowParameters.windowManager,
clientProxy: parameters.userSession.clientProxy, clientProxy: flowParameters.userSession.clientProxy,
mediaProvider: parameters.userSession.mediaProvider, mediaProvider: flowParameters.userSession.mediaProvider,
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: parameters.appSettings), mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: flowParameters.appSettings),
navigationStackCoordinator: navigationStackCoordinator, navigationStackCoordinator: navigationStackCoordinator,
userIndicatorController: parameters.userIndicatorController, userIndicatorController: flowParameters.userIndicatorController,
appSettings: parameters.appSettings)) appSettings: flowParameters.appSettings))
navigationStackCoordinator.push(coordinator) navigationStackCoordinator.push(coordinator)
} }
private func presentAnalyticsScreen() { private func presentAnalyticsScreen() {
let coordinator = AnalyticsSettingsScreenCoordinator(parameters: .init(appSettings: parameters.appSettings, let coordinator = AnalyticsSettingsScreenCoordinator(parameters: .init(appSettings: flowParameters.appSettings,
analytics: parameters.analytics)) analytics: flowParameters.analytics))
navigationStackCoordinator.push(coordinator) navigationStackCoordinator.push(coordinator)
} }
private func presentAppLockSetupFlow() { private func presentAppLockSetupFlow() {
let coordinator = AppLockSetupFlowCoordinator(presentingFlow: .settings, let coordinator = AppLockSetupFlowCoordinator(presentingFlow: .settings,
appLockService: parameters.appLockService, appLockService: appLockService,
navigationStackCoordinator: navigationStackCoordinator) navigationStackCoordinator: navigationStackCoordinator)
coordinator.actions.sink { [weak self] action in coordinator.actions.sink { [weak self] action in
guard let self else { return } guard let self else { return }
@@ -178,32 +170,31 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
} }
private func presentLegalInformationScreen() { private func presentLegalInformationScreen() {
navigationStackCoordinator.push(LegalInformationScreenCoordinator(appSettings: parameters.appSettings)) navigationStackCoordinator.push(LegalInformationScreenCoordinator(appSettings: flowParameters.appSettings))
} }
private func presentBlockedUsersScreen() { private func presentBlockedUsersScreen() {
let coordinator = BlockedUsersScreenCoordinator(parameters: .init(hideProfiles: parameters.appSettings.hideIgnoredUserProfiles, let coordinator = BlockedUsersScreenCoordinator(parameters: .init(hideProfiles: flowParameters.appSettings.hideIgnoredUserProfiles,
clientProxy: parameters.userSession.clientProxy, clientProxy: flowParameters.userSession.clientProxy,
mediaProvider: parameters.userSession.mediaProvider, mediaProvider: flowParameters.userSession.mediaProvider,
userIndicatorController: parameters.userIndicatorController)) userIndicatorController: flowParameters.userIndicatorController))
navigationStackCoordinator.push(coordinator) navigationStackCoordinator.push(coordinator)
} }
private func presentNotificationSettings() { private func presentNotificationSettings() {
let notificationParameters = NotificationSettingsScreenCoordinatorParameters(navigationStackCoordinator: navigationStackCoordinator, let notificationParameters = NotificationSettingsScreenCoordinatorParameters(navigationStackCoordinator: navigationStackCoordinator,
userSession: parameters.userSession, userSession: flowParameters.userSession,
userNotificationCenter: UNUserNotificationCenter.current(), userNotificationCenter: UNUserNotificationCenter.current(),
notificationSettings: parameters.notificationSettings,
isModallyPresented: false) isModallyPresented: false)
let coordinator = NotificationSettingsScreenCoordinator(parameters: notificationParameters) let coordinator = NotificationSettingsScreenCoordinator(parameters: notificationParameters)
navigationStackCoordinator.push(coordinator) navigationStackCoordinator.push(coordinator)
} }
private func presentAdvancedSettings() { private func presentAdvancedSettings() {
let coordinator = AdvancedSettingsScreenCoordinator(parameters: .init(appSettings: parameters.appSettings, let coordinator = AdvancedSettingsScreenCoordinator(parameters: .init(appSettings: flowParameters.appSettings,
analytics: parameters.analytics, analytics: flowParameters.analytics,
clientProxy: parameters.userSession.clientProxy, clientProxy: flowParameters.userSession.clientProxy,
userIndicatorController: parameters.userIndicatorController)) userIndicatorController: flowParameters.userIndicatorController))
navigationStackCoordinator.push(coordinator) navigationStackCoordinator.push(coordinator)
} }
@@ -225,8 +216,8 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
} }
private func presentDeactivateAccount() { private func presentDeactivateAccount() {
let parameters = DeactivateAccountScreenCoordinatorParameters(clientProxy: parameters.userSession.clientProxy, let parameters = DeactivateAccountScreenCoordinatorParameters(clientProxy: flowParameters.userSession.clientProxy,
userIndicatorController: parameters.userIndicatorController) userIndicatorController: flowParameters.userIndicatorController)
let coordinator = DeactivateAccountScreenCoordinator(parameters: parameters) let coordinator = DeactivateAccountScreenCoordinator(parameters: parameters)
coordinator.actionsPublisher coordinator.actionsPublisher
@@ -249,7 +240,7 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
private func presentAccountManagementURL(_ url: URL) { 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. // 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. 🤷‍♂️ // 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 = OIDCAccountSettingsPresenter(accountURL: url, presentationAnchor: flowParameters.windowManager.mainWindow)
accountSettingsPresenter?.start() accountSettingsPresenter?.start()
} }
} }
@@ -16,12 +16,11 @@ enum SpaceExplorerFlowCoordinatorAction: Equatable {
class SpaceExplorerFlowCoordinator: FlowCoordinatorProtocol { class SpaceExplorerFlowCoordinator: FlowCoordinatorProtocol {
private let userSession: UserSessionProtocol private let userSession: UserSessionProtocol
private var flowParameters: CommonFlowParameters
private let navigationSplitCoordinator: NavigationSplitCoordinator private let navigationSplitCoordinator: NavigationSplitCoordinator
private let sidebarNavigationStackCoordinator: NavigationStackCoordinator private let sidebarNavigationStackCoordinator: NavigationStackCoordinator
private let detailNavigationStackCoordinator: NavigationStackCoordinator private let detailNavigationStackCoordinator: NavigationStackCoordinator
private let userIndicatorController: UserIndicatorControllerProtocol
private var spaceFlowCoordinator: SpaceFlowCoordinator? private var spaceFlowCoordinator: SpaceFlowCoordinator?
enum State: StateType { enum State: StateType {
@@ -52,12 +51,10 @@ class SpaceExplorerFlowCoordinator: FlowCoordinatorProtocol {
actionsSubject.eraseToAnyPublisher() actionsSubject.eraseToAnyPublisher()
} }
init(userSession: UserSessionProtocol, init(navigationSplitCoordinator: NavigationSplitCoordinator, flowParameters: CommonFlowParameters) {
navigationSplitCoordinator: NavigationSplitCoordinator, userSession = flowParameters.userSession
userIndicatorController: UserIndicatorControllerProtocol) {
self.userSession = userSession
self.navigationSplitCoordinator = navigationSplitCoordinator self.navigationSplitCoordinator = navigationSplitCoordinator
self.userIndicatorController = userIndicatorController self.flowParameters = flowParameters
sidebarNavigationStackCoordinator = NavigationStackCoordinator(navigationSplitCoordinator: navigationSplitCoordinator) sidebarNavigationStackCoordinator = NavigationStackCoordinator(navigationSplitCoordinator: navigationSplitCoordinator)
detailNavigationStackCoordinator = NavigationStackCoordinator(navigationSplitCoordinator: navigationSplitCoordinator) detailNavigationStackCoordinator = NavigationStackCoordinator(navigationSplitCoordinator: navigationSplitCoordinator)
@@ -117,7 +114,7 @@ class SpaceExplorerFlowCoordinator: FlowCoordinatorProtocol {
private func presentSpaceList() { private func presentSpaceList() {
let parameters = SpaceListScreenCoordinatorParameters(userSession: userSession, let parameters = SpaceListScreenCoordinatorParameters(userSession: userSession,
selectedSpaceSubject: selectedSpaceSubject.asCurrentValuePublisher(), selectedSpaceSubject: selectedSpaceSubject.asCurrentValuePublisher(),
userIndicatorController: userIndicatorController) userIndicatorController: flowParameters.userIndicatorController)
let coordinator = SpaceListScreenCoordinator(parameters: parameters) let coordinator = SpaceListScreenCoordinator(parameters: parameters)
coordinator.actionsPublisher coordinator.actionsPublisher
.sink { [weak self] action in .sink { [weak self] action in
@@ -138,9 +135,8 @@ class SpaceExplorerFlowCoordinator: FlowCoordinatorProtocol {
let coordinator = SpaceFlowCoordinator(spaceRoomListProxy: spaceRoomListProxy, let coordinator = SpaceFlowCoordinator(spaceRoomListProxy: spaceRoomListProxy,
spaceServiceProxy: userSession.clientProxy.spaceService, spaceServiceProxy: userSession.clientProxy.spaceService,
isChildFlow: false, isChildFlow: false,
mediaProvider: userSession.mediaProvider,
navigationStackCoordinator: detailNavigationStackCoordinator, navigationStackCoordinator: detailNavigationStackCoordinator,
userIndicatorController: userIndicatorController) flowParameters: flowParameters)
coordinator.actionsPublisher coordinator.actionsPublisher
.sink { [weak self] action in .sink { [weak self] action in
@@ -18,13 +18,12 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
private let spaceServiceProxy: SpaceServiceProxyProtocol private let spaceServiceProxy: SpaceServiceProxyProtocol
private let isChildFlow: Bool private let isChildFlow: Bool
private let mediaProvider: MediaProviderProtocol
private let navigationStackCoordinator: NavigationStackCoordinator private let navigationStackCoordinator: NavigationStackCoordinator
private let userIndicatorController: UserIndicatorControllerProtocol
private var childSpaceFlowCoordinator: SpaceFlowCoordinator? private var childSpaceFlowCoordinator: SpaceFlowCoordinator?
private let flowParameters: CommonFlowParameters
indirect enum State: StateType { indirect enum State: StateType {
/// The state machine hasn't started. /// The state machine hasn't started.
case initial case initial
@@ -57,17 +56,14 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
init(spaceRoomListProxy: SpaceRoomListProxyProtocol, init(spaceRoomListProxy: SpaceRoomListProxyProtocol,
spaceServiceProxy: SpaceServiceProxyProtocol, spaceServiceProxy: SpaceServiceProxyProtocol,
isChildFlow: Bool, isChildFlow: Bool,
mediaProvider: MediaProviderProtocol,
navigationStackCoordinator: NavigationStackCoordinator, navigationStackCoordinator: NavigationStackCoordinator,
userIndicatorController: UserIndicatorControllerProtocol) { flowParameters: CommonFlowParameters) {
self.spaceRoomListProxy = spaceRoomListProxy self.spaceRoomListProxy = spaceRoomListProxy
self.spaceServiceProxy = spaceServiceProxy self.spaceServiceProxy = spaceServiceProxy
self.isChildFlow = isChildFlow self.isChildFlow = isChildFlow
self.flowParameters = flowParameters
self.mediaProvider = mediaProvider
self.navigationStackCoordinator = navigationStackCoordinator self.navigationStackCoordinator = navigationStackCoordinator
self.userIndicatorController = userIndicatorController
stateMachine = .init(state: .initial) stateMachine = .init(state: .initial)
configureStateMachine() configureStateMachine()
@@ -130,8 +126,8 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
private func presentSpace() { private func presentSpace() {
let parameters = SpaceScreenCoordinatorParameters(spaceRoomListProxy: spaceRoomListProxy, let parameters = SpaceScreenCoordinatorParameters(spaceRoomListProxy: spaceRoomListProxy,
spaceServiceProxy: spaceServiceProxy, spaceServiceProxy: spaceServiceProxy,
mediaProvider: mediaProvider, mediaProvider: flowParameters.userSession.mediaProvider,
userIndicatorController: userIndicatorController) userIndicatorController: flowParameters.userIndicatorController)
let coordinator = SpaceScreenCoordinator(parameters: parameters) let coordinator = SpaceScreenCoordinator(parameters: parameters)
coordinator.actionsPublisher coordinator.actionsPublisher
.sink { [weak self] action in .sink { [weak self] action in
@@ -160,9 +156,8 @@ class SpaceFlowCoordinator: FlowCoordinatorProtocol {
let coordinator = SpaceFlowCoordinator(spaceRoomListProxy: spaceRoomListProxy, let coordinator = SpaceFlowCoordinator(spaceRoomListProxy: spaceRoomListProxy,
spaceServiceProxy: spaceServiceProxy, spaceServiceProxy: spaceServiceProxy,
isChildFlow: true, isChildFlow: true,
mediaProvider: mediaProvider,
navigationStackCoordinator: navigationStackCoordinator, navigationStackCoordinator: navigationStackCoordinator,
userIndicatorController: userIndicatorController) flowParameters: flowParameters)
coordinator.actionsPublisher coordinator.actionsPublisher
.sink { [weak self] action in .sink { [weak self] action in
@@ -21,14 +21,12 @@ enum UserSessionFlowCoordinatorAction {
class UserSessionFlowCoordinator: FlowCoordinatorProtocol { class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
enum HomeTab: Hashable { case chats, spaces } enum HomeTab: Hashable { case chats, spaces }
private let userSession: UserSessionProtocol
private let navigationRootCoordinator: NavigationRootCoordinator private let navigationRootCoordinator: NavigationRootCoordinator
private let navigationTabCoordinator: NavigationTabCoordinator<HomeTab> private let navigationTabCoordinator: NavigationTabCoordinator<HomeTab>
private let appLockService: AppLockServiceProtocol private let appLockService: AppLockServiceProtocol
private let bugReportService: BugReportServiceProtocol private let flowParameters: CommonFlowParameters
private let appMediator: AppMediatorProtocol
private let appSettings: AppSettings private var userSession: UserSessionProtocol { flowParameters.userSession }
private let analytics: AnalyticsService
private let onboardingFlowCoordinator: OnboardingFlowCoordinator private let onboardingFlowCoordinator: OnboardingFlowCoordinator
private let onboardingStackCoordinator: NavigationStackCoordinator private let onboardingStackCoordinator: NavigationStackCoordinator
@@ -67,75 +65,46 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
actionsSubject.eraseToAnyPublisher() actionsSubject.eraseToAnyPublisher()
} }
init(userSession: UserSessionProtocol, init(isNewLogin: Bool,
isNewLogin: Bool,
navigationRootCoordinator: NavigationRootCoordinator, navigationRootCoordinator: NavigationRootCoordinator,
appLockService: AppLockServiceProtocol, appLockService: AppLockServiceProtocol,
bugReportService: BugReportServiceProtocol, flowParameters: CommonFlowParameters) {
elementCallService: ElementCallServiceProtocol,
timelineControllerFactory: TimelineControllerFactoryProtocol,
appMediator: AppMediatorProtocol,
appSettings: AppSettings,
appHooks: AppHooks,
analytics: AnalyticsService,
notificationManager: NotificationManagerProtocol,
stateMachineFactory: StateMachineFactoryProtocol) {
self.userSession = userSession
self.navigationRootCoordinator = navigationRootCoordinator self.navigationRootCoordinator = navigationRootCoordinator
self.appLockService = appLockService self.appLockService = appLockService
self.bugReportService = bugReportService self.flowParameters = flowParameters
self.appMediator = appMediator
self.appSettings = appSettings
self.analytics = analytics
navigationTabCoordinator = NavigationTabCoordinator() navigationTabCoordinator = NavigationTabCoordinator()
navigationRootCoordinator.setRootCoordinator(navigationTabCoordinator) navigationRootCoordinator.setRootCoordinator(navigationTabCoordinator)
let chatsSplitCoordinator = NavigationSplitCoordinator(placeholderCoordinator: PlaceholderScreenCoordinator()) let chatsSplitCoordinator = NavigationSplitCoordinator(placeholderCoordinator: PlaceholderScreenCoordinator())
chatsFlowCoordinator = ChatsFlowCoordinator(userSession: userSession, chatsFlowCoordinator = ChatsFlowCoordinator(isNewLogin: isNewLogin,
isNewLogin: isNewLogin,
navigationSplitCoordinator: chatsSplitCoordinator, navigationSplitCoordinator: chatsSplitCoordinator,
appLockService: appLockService, flowParameters: flowParameters)
bugReportService: bugReportService,
elementCallService: elementCallService,
timelineControllerFactory: timelineControllerFactory,
appMediator: appMediator,
appSettings: appSettings,
appHooks: appHooks,
analytics: analytics,
notificationManager: notificationManager,
stateMachineFactory: stateMachineFactory)
chatsTabDetails = .init(tag: HomeTab.chats, title: L10n.screenHomeTabChats, icon: \.chat, selectedIcon: \.chatSolid) chatsTabDetails = .init(tag: HomeTab.chats, title: L10n.screenHomeTabChats, icon: \.chat, selectedIcon: \.chatSolid)
chatsTabDetails.navigationSplitCoordinator = chatsSplitCoordinator chatsTabDetails.navigationSplitCoordinator = chatsSplitCoordinator
if !appSettings.spacesEnabled { if !flowParameters.appSettings.spacesEnabled {
chatsTabDetails.barVisibilityOverride = .hidden chatsTabDetails.barVisibilityOverride = .hidden
} }
let spacesSplitCoordinator = NavigationSplitCoordinator(placeholderCoordinator: PlaceholderScreenCoordinator()) let spacesSplitCoordinator = NavigationSplitCoordinator(placeholderCoordinator: PlaceholderScreenCoordinator())
spaceExplorerFlowCoordinator = SpaceExplorerFlowCoordinator(userSession: userSession, spaceExplorerFlowCoordinator = SpaceExplorerFlowCoordinator(navigationSplitCoordinator: spacesSplitCoordinator,
navigationSplitCoordinator: spacesSplitCoordinator, flowParameters: flowParameters)
userIndicatorController: ServiceLocator.shared.userIndicatorController)
spacesTabDetails = .init(tag: HomeTab.spaces, title: L10n.screenHomeTabSpaces, icon: \.space, selectedIcon: \.spaceSolid) spacesTabDetails = .init(tag: HomeTab.spaces, title: L10n.screenHomeTabSpaces, icon: \.space, selectedIcon: \.spaceSolid)
spacesTabDetails.navigationSplitCoordinator = spacesSplitCoordinator spacesTabDetails.navigationSplitCoordinator = spacesSplitCoordinator
onboardingStackCoordinator = NavigationStackCoordinator() onboardingStackCoordinator = NavigationStackCoordinator()
onboardingFlowCoordinator = OnboardingFlowCoordinator(userSession: userSession, onboardingFlowCoordinator = OnboardingFlowCoordinator(isNewLogin: isNewLogin,
appLockService: appLockService, appLockService: appLockService,
analyticsService: analytics,
appSettings: appSettings,
notificationManager: notificationManager,
navigationStackCoordinator: onboardingStackCoordinator, navigationStackCoordinator: onboardingStackCoordinator,
userIndicatorController: ServiceLocator.shared.userIndicatorController, flowParameters: flowParameters)
windowManager: appMediator.windowManager,
isNewLogin: isNewLogin)
navigationTabCoordinator.setTabs([ navigationTabCoordinator.setTabs([
.init(coordinator: chatsSplitCoordinator, details: chatsTabDetails), .init(coordinator: chatsSplitCoordinator, details: chatsTabDetails),
.init(coordinator: spacesSplitCoordinator, details: spacesTabDetails) .init(coordinator: spacesSplitCoordinator, details: spacesTabDetails)
]) ])
stateMachine = stateMachineFactory.makeUserSessionFlowStateMachine(state: .initial) stateMachine = flowParameters.stateMachineFactory.makeUserSessionFlowStateMachine(state: .initial)
configureStateMachine() configureStateMachine()
setupObservers() setupObservers()
@@ -272,7 +241,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
} }
.store(in: &cancellables) .store(in: &cancellables)
appSettings.$spacesEnabled flowParameters.appSettings.$spacesEnabled
.combineLatest(userSession.clientProxy.spaceService.joinedSpacesPublisher) .combineLatest(userSession.clientProxy.spaceService.joinedSpacesPublisher)
.map { $0 && !$1.isEmpty ? nil : .hidden } .map { $0 && !$1.isEmpty ? nil : .hidden }
.weakAssign(to: \.chatsTabDetails.barVisibilityOverride, on: self) .weakAssign(to: \.chatsTabDetails.barVisibilityOverride, on: self)
@@ -294,16 +263,9 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
private func startSettingsFlow() { private func startSettingsFlow() {
let navigationStackCoordinator = NavigationStackCoordinator() let navigationStackCoordinator = NavigationStackCoordinator()
let coordinator = SettingsFlowCoordinator(parameters: .init(userSession: userSession, let coordinator = SettingsFlowCoordinator(appLockService: appLockService,
windowManager: appMediator.windowManager, navigationStackCoordinator: navigationStackCoordinator,
appLockService: appLockService, flowParameters: flowParameters)
bugReportService: bugReportService,
notificationSettings: userSession.clientProxy.notificationSettings,
secureBackupController: userSession.clientProxy.secureBackupController,
appSettings: appSettings,
navigationStackCoordinator: navigationStackCoordinator,
userIndicatorController: ServiceLocator.shared.userIndicatorController,
analytics: analytics))
coordinator.actions.sink { [weak self] action in coordinator.actions.sink { [weak self] action in
guard let self else { return } guard let self else { return }
@@ -365,7 +327,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
let parameters = SessionVerificationScreenCoordinatorParameters(sessionVerificationControllerProxy: sessionVerificationController, let parameters = SessionVerificationScreenCoordinatorParameters(sessionVerificationControllerProxy: sessionVerificationController,
flow: flow, flow: flow,
appSettings: appSettings, appSettings: flowParameters.appSettings,
mediaProvider: userSession.mediaProvider) mediaProvider: userSession.mediaProvider)
let coordinator = SessionVerificationScreenCoordinator(parameters: parameters) let coordinator = SessionVerificationScreenCoordinator(parameters: parameters)
@@ -390,7 +352,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
let secureBackupController = userSession.clientProxy.secureBackupController let secureBackupController = userSession.clientProxy.secureBackupController
guard case let .success(isLastDevice) = await userSession.clientProxy.isOnlyDeviceLeft() else { guard case let .success(isLastDevice) = await userSession.clientProxy.isOnlyDeviceLeft() else {
ServiceLocator.shared.userIndicatorController.alertInfo = .init(id: .init()) flowParameters.userIndicatorController.alertInfo = .init(id: .init())
return return
} }
@@ -400,26 +362,26 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
} }
guard secureBackupController.recoveryState.value == .enabled else { guard secureBackupController.recoveryState.value == .enabled else {
ServiceLocator.shared.userIndicatorController.alertInfo = .init(id: .init(), flowParameters.userIndicatorController.alertInfo = .init(id: .init(),
title: L10n.screenSignoutRecoveryDisabledTitle, title: L10n.screenSignoutRecoveryDisabledTitle,
message: L10n.screenSignoutRecoveryDisabledSubtitle, message: L10n.screenSignoutRecoveryDisabledSubtitle,
primaryButton: .init(title: L10n.screenSignoutConfirmationDialogSubmit, role: .destructive) { [weak self] in primaryButton: .init(title: L10n.screenSignoutConfirmationDialogSubmit, role: .destructive) { [weak self] in
self?.actionsSubject.send(.logout) self?.actionsSubject.send(.logout)
}, secondaryButton: .init(title: L10n.commonSettings, role: .cancel) { [weak self] in }, secondaryButton: .init(title: L10n.commonSettings, role: .cancel) { [weak self] in
self?.chatsFlowCoordinator.handleAppRoute(.chatBackupSettings, animated: true) self?.chatsFlowCoordinator.handleAppRoute(.chatBackupSettings, animated: true)
}) })
return return
} }
guard secureBackupController.keyBackupState.value == .enabled else { guard secureBackupController.keyBackupState.value == .enabled else {
ServiceLocator.shared.userIndicatorController.alertInfo = .init(id: .init(), flowParameters.userIndicatorController.alertInfo = .init(id: .init(),
title: L10n.screenSignoutKeyBackupDisabledTitle, title: L10n.screenSignoutKeyBackupDisabledTitle,
message: L10n.screenSignoutKeyBackupDisabledSubtitle, message: L10n.screenSignoutKeyBackupDisabledSubtitle,
primaryButton: .init(title: L10n.screenSignoutConfirmationDialogSubmit, role: .destructive) { [weak self] in primaryButton: .init(title: L10n.screenSignoutConfirmationDialogSubmit, role: .destructive) { [weak self] in
self?.actionsSubject.send(.logout) self?.actionsSubject.send(.logout)
}, secondaryButton: .init(title: L10n.commonSettings, role: .cancel) { [weak self] in }, secondaryButton: .init(title: L10n.commonSettings, role: .cancel) { [weak self] in
self?.chatsFlowCoordinator.handleAppRoute(.chatBackupSettings, animated: true) self?.chatsFlowCoordinator.handleAppRoute(.chatBackupSettings, animated: true)
}) })
return return
} }
@@ -427,17 +389,17 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
} }
private func logout() { private func logout() {
ServiceLocator.shared.userIndicatorController.alertInfo = .init(id: .init(), flowParameters.userIndicatorController.alertInfo = .init(id: .init(),
title: L10n.screenSignoutConfirmationDialogTitle, title: L10n.screenSignoutConfirmationDialogTitle,
message: L10n.screenSignoutConfirmationDialogContent, message: L10n.screenSignoutConfirmationDialogContent,
primaryButton: .init(title: L10n.screenSignoutConfirmationDialogSubmit, role: .destructive) { [weak self] in primaryButton: .init(title: L10n.screenSignoutConfirmationDialogSubmit, role: .destructive) { [weak self] in
self?.actionsSubject.send(.logout) self?.actionsSubject.send(.logout)
}) })
} }
private func presentSecureBackupLogoutConfirmationScreen() { private func presentSecureBackupLogoutConfirmationScreen() {
let coordinator = SecureBackupLogoutConfirmationScreenCoordinator(parameters: .init(secureBackupController: userSession.clientProxy.secureBackupController, let coordinator = SecureBackupLogoutConfirmationScreenCoordinator(parameters: .init(secureBackupController: userSession.clientProxy.secureBackupController,
appMediator: appMediator)) appMediator: flowParameters.appMediator))
coordinator.actions coordinator.actions
.sink { [weak self] action in .sink { [weak self] action in
+3 -1
View File
@@ -18,6 +18,8 @@ struct ClientProxyMockConfiguration {
var recoveryState: SecureBackupRecoveryState = .enabled var recoveryState: SecureBackupRecoveryState = .enabled
var notificationSettings = NotificationSettingsProxyMock(with: .init())
var timelineMediaVisibility = TimelineMediaVisibility.always var timelineMediaVisibility = TimelineMediaVisibility.always
var hideInviteAvatars = false var hideInviteAvatars = false
@@ -54,7 +56,7 @@ extension ClientProxyMock {
ignoredUsersPublisher = CurrentValueSubject<[String]?, Never>([RoomMemberProxyMock].allMembers.map(\.userID)).asCurrentValuePublisher() ignoredUsersPublisher = CurrentValueSubject<[String]?, Never>([RoomMemberProxyMock].allMembers.map(\.userID)).asCurrentValuePublisher()
notificationSettings = NotificationSettingsProxyMock(with: .init()) notificationSettings = configuration.notificationSettings
isOnlyDeviceLeftReturnValue = .success(false) isOnlyDeviceLeftReturnValue = .success(false)
accountURLActionReturnValue = "https://matrix.org/account" accountURLActionReturnValue = "https://matrix.org/account"
@@ -12,7 +12,6 @@ struct NotificationSettingsEditScreenCoordinatorParameters {
weak var navigationStackCoordinator: NavigationStackCoordinator? weak var navigationStackCoordinator: NavigationStackCoordinator?
let chatType: NotificationSettingsChatType let chatType: NotificationSettingsChatType
let userSession: UserSessionProtocol let userSession: UserSessionProtocol
let notificationSettings: NotificationSettingsProxyProtocol
} }
final class NotificationSettingsEditScreenCoordinator: CoordinatorProtocol { final class NotificationSettingsEditScreenCoordinator: CoordinatorProtocol {
@@ -24,8 +23,7 @@ final class NotificationSettingsEditScreenCoordinator: CoordinatorProtocol {
self.parameters = parameters self.parameters = parameters
viewModel = NotificationSettingsEditScreenViewModel(chatType: parameters.chatType, viewModel = NotificationSettingsEditScreenViewModel(chatType: parameters.chatType,
userSession: parameters.userSession, userSession: parameters.userSession)
notificationSettingsProxy: parameters.notificationSettings)
} }
func start() { func start() {
@@ -51,7 +49,7 @@ final class NotificationSettingsEditScreenCoordinator: CoordinatorProtocol {
guard case let .joined(roomProxy) = await parameters.userSession.clientProxy.roomForIdentifier(roomID) else { return } guard case let .joined(roomProxy) = await parameters.userSession.clientProxy.roomForIdentifier(roomID) else { return }
let roomNotificationSettingsParameters = RoomNotificationSettingsScreenCoordinatorParameters(navigationStackCoordinator: parameters.navigationStackCoordinator, let roomNotificationSettingsParameters = RoomNotificationSettingsScreenCoordinatorParameters(navigationStackCoordinator: parameters.navigationStackCoordinator,
notificationSettingsProxy: parameters.notificationSettings, notificationSettingsProxy: parameters.userSession.clientProxy.notificationSettings,
roomProxy: roomProxy, roomProxy: roomProxy,
displayAsUserDefinedRoomSettings: true) displayAsUserDefinedRoomSettings: true)
let roomNotificationSettingsCoordinator = RoomNotificationSettingsScreenCoordinator(parameters: roomNotificationSettingsParameters) let roomNotificationSettingsCoordinator = RoomNotificationSettingsScreenCoordinator(parameters: roomNotificationSettingsParameters)
@@ -26,11 +26,11 @@ class NotificationSettingsEditScreenViewModel: NotificationSettingsEditScreenVie
actionsSubject.eraseToAnyPublisher() actionsSubject.eraseToAnyPublisher()
} }
init(chatType: NotificationSettingsChatType, userSession: UserSessionProtocol, notificationSettingsProxy: NotificationSettingsProxyProtocol) { init(chatType: NotificationSettingsChatType, userSession: UserSessionProtocol) {
let bindings = NotificationSettingsEditScreenViewStateBindings() let bindings = NotificationSettingsEditScreenViewStateBindings()
self.chatType = chatType self.chatType = chatType
self.userSession = userSession self.userSession = userSession
self.notificationSettingsProxy = notificationSettingsProxy notificationSettingsProxy = userSession.clientProxy.notificationSettings
roomSummaryProvider = userSession.clientProxy.roomSummaryProvider roomSummaryProvider = userSession.clientProxy.roomSummaryProvider
super.init(initialViewState: NotificationSettingsEditScreenViewState(bindings: bindings, super.init(initialViewState: NotificationSettingsEditScreenViewState(bindings: bindings,
@@ -65,10 +65,10 @@ struct NotificationSettingsEditScreen_Previews: PreviewProvider, TestablePreview
notificationSettingsProxy.getRoomsWithUserDefinedRulesReturnValue = [RoomSummary].mockRooms.map(\.id) notificationSettingsProxy.getRoomsWithUserDefinedRulesReturnValue = [RoomSummary].mockRooms.map(\.id)
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@alice:example.com", let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@alice:example.com",
roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))))))) roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))),
notificationSettings: notificationSettingsProxy))))
var viewModel = NotificationSettingsEditScreenViewModel(chatType: .groupChat, var viewModel = NotificationSettingsEditScreenViewModel(chatType: .groupChat,
userSession: userSession, userSession: userSession)
notificationSettingsProxy: notificationSettingsProxy)
viewModel.fetchInitialContent() viewModel.fetchInitialContent()
return viewModel return viewModel
}() }()
@@ -78,10 +78,10 @@ struct NotificationSettingsEditScreen_Previews: PreviewProvider, TestablePreview
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneReturnValue = .mentionsAndKeywordsOnly notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneReturnValue = .mentionsAndKeywordsOnly
notificationSettingsProxy.getRoomsWithUserDefinedRulesReturnValue = [] notificationSettingsProxy.getRoomsWithUserDefinedRulesReturnValue = []
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@alice:example.com", let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@alice:example.com",
roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))))))) roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))),
notificationSettings: notificationSettingsProxy))))
var viewModel = NotificationSettingsEditScreenViewModel(chatType: .oneToOneChat, var viewModel = NotificationSettingsEditScreenViewModel(chatType: .oneToOneChat,
userSession: userSession, userSession: userSession)
notificationSettingsProxy: notificationSettingsProxy)
viewModel.fetchInitialContent() viewModel.fetchInitialContent()
return viewModel return viewModel
}() }()
@@ -90,11 +90,11 @@ struct NotificationSettingsEditScreen_Previews: PreviewProvider, TestablePreview
let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init()) let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init())
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneReturnValue = .mentionsAndKeywordsOnly notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneReturnValue = .mentionsAndKeywordsOnly
notificationSettingsProxy.getRoomsWithUserDefinedRulesReturnValue = [] notificationSettingsProxy.getRoomsWithUserDefinedRulesReturnValue = []
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "John Doe")))) let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "John Doe",
notificationSettings: notificationSettingsProxy))))
var viewModel = NotificationSettingsEditScreenViewModel(chatType: .oneToOneChat, var viewModel = NotificationSettingsEditScreenViewModel(chatType: .oneToOneChat,
userSession: userSession, userSession: userSession)
notificationSettingsProxy: notificationSettingsProxy)
viewModel.state.pendingMode = .mentionsAndKeywordsOnly viewModel.state.pendingMode = .mentionsAndKeywordsOnly
viewModel.fetchInitialContent() viewModel.fetchInitialContent()
return viewModel return viewModel
@@ -106,10 +106,10 @@ struct NotificationSettingsEditScreen_Previews: PreviewProvider, TestablePreview
notificationSettingsProxy.getRoomsWithUserDefinedRulesReturnValue = [RoomSummary].mockRooms.map(\.id) notificationSettingsProxy.getRoomsWithUserDefinedRulesReturnValue = [RoomSummary].mockRooms.map(\.id)
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@alice:example.com", let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@alice:example.com",
roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))))))) roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))),
notificationSettings: notificationSettingsProxy))))
var viewModel = NotificationSettingsEditScreenViewModel(chatType: .groupChat, var viewModel = NotificationSettingsEditScreenViewModel(chatType: .groupChat,
userSession: userSession, userSession: userSession)
notificationSettingsProxy: notificationSettingsProxy)
viewModel.fetchInitialContent() viewModel.fetchInitialContent()
return viewModel return viewModel
}() }()
@@ -50,13 +50,14 @@ struct NotificationSettingsEditScreenRoomCell_Previews: PreviewProvider, Testabl
static var previews: some View { static var previews: some View {
let summaryProvider = RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))) let summaryProvider = RoomSummaryProviderMock(.init(state: .loaded(.mockRooms)))
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "John Doe", roomSummaryProvider: summaryProvider))))
let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init()) let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init())
notificationSettingsProxy.getRoomsWithUserDefinedRulesReturnValue = [] notificationSettingsProxy.getRoomsWithUserDefinedRulesReturnValue = []
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "John Doe",
roomSummaryProvider: summaryProvider,
notificationSettings: notificationSettingsProxy))))
let viewModel = NotificationSettingsEditScreenViewModel(chatType: .groupChat, let viewModel = NotificationSettingsEditScreenViewModel(chatType: .groupChat,
userSession: userSession, userSession: userSession)
notificationSettingsProxy: notificationSettingsProxy)
let rooms: [NotificationSettingsEditScreenRoom] = summaryProvider.roomListPublisher.value.compactMap { summary -> NotificationSettingsEditScreenRoom? in let rooms: [NotificationSettingsEditScreenRoom] = summaryProvider.roomListPublisher.value.compactMap { summary -> NotificationSettingsEditScreenRoom? in
NotificationSettingsEditScreenRoom(id: UUID().uuidString, NotificationSettingsEditScreenRoom(id: UUID().uuidString,
@@ -12,7 +12,6 @@ struct NotificationSettingsScreenCoordinatorParameters {
weak var navigationStackCoordinator: NavigationStackCoordinator? weak var navigationStackCoordinator: NavigationStackCoordinator?
let userSession: UserSessionProtocol let userSession: UserSessionProtocol
let userNotificationCenter: UserNotificationCenterProtocol let userNotificationCenter: UserNotificationCenterProtocol
let notificationSettings: NotificationSettingsProxyProtocol
let isModallyPresented: Bool let isModallyPresented: Bool
} }
@@ -39,7 +38,7 @@ final class NotificationSettingsScreenCoordinator: CoordinatorProtocol {
viewModel = NotificationSettingsScreenViewModel(appSettings: ServiceLocator.shared.settings, viewModel = NotificationSettingsScreenViewModel(appSettings: ServiceLocator.shared.settings,
userNotificationCenter: parameters.userNotificationCenter, userNotificationCenter: parameters.userNotificationCenter,
notificationSettingsProxy: parameters.notificationSettings, notificationSettingsProxy: parameters.userSession.clientProxy.notificationSettings,
isModallyPresented: parameters.isModallyPresented) isModallyPresented: parameters.isModallyPresented)
} }
@@ -67,8 +66,7 @@ final class NotificationSettingsScreenCoordinator: CoordinatorProtocol {
private func presentEditScreen(chatType: NotificationSettingsChatType) { private func presentEditScreen(chatType: NotificationSettingsChatType) {
let editSettingsParameters = NotificationSettingsEditScreenCoordinatorParameters(navigationStackCoordinator: parameters.navigationStackCoordinator, let editSettingsParameters = NotificationSettingsEditScreenCoordinatorParameters(navigationStackCoordinator: parameters.navigationStackCoordinator,
chatType: chatType, chatType: chatType,
userSession: parameters.userSession, userSession: parameters.userSession)
notificationSettings: parameters.notificationSettings)
let editSettingsCoordinator = NotificationSettingsEditScreenCoordinator(parameters: editSettingsParameters) let editSettingsCoordinator = NotificationSettingsEditScreenCoordinator(parameters: editSettingsParameters)
navigationStackCoordinator?.push(editSettingsCoordinator) navigationStackCoordinator?.push(editSettingsCoordinator)
} }
@@ -593,20 +593,22 @@ class MockScreen: Identifiable {
let appMediator = AppMediatorMock.default let appMediator = AppMediatorMock.default
appMediator.underlyingWindowManager = windowManager appMediator.underlyingWindowManager = windowManager
let flowCoordinator = UserSessionFlowCoordinator(userSession: UserSessionMock(.init(clientProxy: clientProxy)), let flowCoordinator = UserSessionFlowCoordinator(isNewLogin: false,
isNewLogin: false,
navigationRootCoordinator: navigationRootCoordinator, navigationRootCoordinator: navigationRootCoordinator,
appLockService: AppLockService(keychainController: KeychainControllerMock(), appLockService: AppLockService(keychainController: KeychainControllerMock(),
appSettings: ServiceLocator.shared.settings), appSettings: ServiceLocator.shared.settings),
bugReportService: BugReportServiceMock(.init()), flowParameters: CommonFlowParameters(userSession: UserSessionMock(.init(clientProxy: clientProxy)),
elementCallService: ElementCallServiceMock(.init()), bugReportService: BugReportServiceMock(.init()),
timelineControllerFactory: TimelineControllerFactoryMock(.init()), elementCallService: ElementCallServiceMock(.init()),
appMediator: appMediator, timelineControllerFactory: TimelineControllerFactoryMock(.init()),
appSettings: appSettings, emojiProvider: EmojiProvider(appSettings: appSettings),
appHooks: AppHooks(), appMediator: appMediator,
analytics: ServiceLocator.shared.analytics, appSettings: appSettings,
notificationManager: NotificationManagerMock(), appHooks: AppHooks(),
stateMachineFactory: StateMachineFactory()) analytics: ServiceLocator.shared.analytics,
userIndicatorController: UserIndicatorControllerMock(),
notificationManager: NotificationManagerMock(),
stateMachineFactory: StateMachineFactory()))
flowCoordinator.start() flowCoordinator.start()
@@ -751,20 +753,20 @@ class MockScreen: Identifiable {
mediaProvider: MediaProviderMock(configuration: .init()), mediaProvider: MediaProviderMock(configuration: .init()),
appSettings: ServiceLocator.shared.settings) appSettings: ServiceLocator.shared.settings)
let flowCoordinator = ChatsFlowCoordinator(userSession: UserSessionMock(.init(clientProxy: clientProxy)), let flowCoordinator = ChatsFlowCoordinator(isNewLogin: false,
isNewLogin: false,
navigationSplitCoordinator: navigationSplitCoordinator, navigationSplitCoordinator: navigationSplitCoordinator,
appLockService: AppLockService(keychainController: KeychainControllerMock(), flowParameters: CommonFlowParameters(userSession: UserSessionMock(.init(clientProxy: clientProxy)),
appSettings: ServiceLocator.shared.settings), bugReportService: BugReportServiceMock(.init()),
bugReportService: BugReportServiceMock(.init()), elementCallService: ElementCallServiceMock(.init()),
elementCallService: ElementCallServiceMock(.init()), timelineControllerFactory: TimelineControllerFactoryMock(.init(timelineController: timelineController)),
timelineControllerFactory: TimelineControllerFactoryMock(.init(timelineController: timelineController)), emojiProvider: EmojiProvider(appSettings: appSettings),
appMediator: AppMediatorMock.default, appMediator: AppMediatorMock.default,
appSettings: appSettings, appSettings: appSettings,
appHooks: AppHooks(), appHooks: AppHooks(),
analytics: ServiceLocator.shared.analytics, analytics: ServiceLocator.shared.analytics,
notificationManager: NotificationManagerMock(), userIndicatorController: UserIndicatorControllerMock(),
stateMachineFactory: StateMachineFactory()) notificationManager: NotificationManagerMock(),
stateMachineFactory: StateMachineFactory()))
flowCoordinator.start() flowCoordinator.start()
@@ -33,19 +33,21 @@ class ChatsFlowCoordinatorTests: XCTestCase {
notificationManager = NotificationManagerMock() notificationManager = NotificationManagerMock()
chatsFlowCoordinator = ChatsFlowCoordinator(userSession: UserSessionMock(.init(clientProxy: clientProxy)), let flowParameters = CommonFlowParameters(userSession: UserSessionMock(.init(clientProxy: clientProxy)),
isNewLogin: false, bugReportService: BugReportServiceMock(.init()),
elementCallService: ElementCallServiceMock(.init()),
timelineControllerFactory: timelineControllerFactory,
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
appMediator: AppMediatorMock.default,
appSettings: ServiceLocator.shared.settings,
appHooks: AppHooks(),
analytics: ServiceLocator.shared.analytics,
userIndicatorController: UserIndicatorControllerMock(),
notificationManager: notificationManager,
stateMachineFactory: stateMachineFactory)
chatsFlowCoordinator = ChatsFlowCoordinator(isNewLogin: false,
navigationSplitCoordinator: splitCoordinator, navigationSplitCoordinator: splitCoordinator,
appLockService: AppLockServiceMock(), flowParameters: flowParameters)
bugReportService: BugReportServiceMock(.init()),
elementCallService: ElementCallServiceMock(.init()),
timelineControllerFactory: timelineControllerFactory,
appMediator: AppMediatorMock.default,
appSettings: ServiceLocator.shared.settings,
appHooks: AppHooks(),
analytics: ServiceLocator.shared.analytics,
notificationManager: notificationManager,
stateMachineFactory: stateMachineFactory)
let deferred = deferFulfillment(stateMachineFactory.chatsFlowStatePublisher) { $0 == .roomList(roomListSelectedRoomID: nil) } let deferred = deferFulfillment(stateMachineFactory.chatsFlowStatePublisher) { $0 == .roomList(roomListSelectedRoomID: nil) }
chatsFlowCoordinator.start() chatsFlowCoordinator.start()
@@ -14,17 +14,19 @@ import XCTest
class NotificationSettingsEditScreenViewModelTests: XCTestCase { class NotificationSettingsEditScreenViewModelTests: XCTestCase {
private var viewModel: NotificationSettingsEditScreenViewModelProtocol! private var viewModel: NotificationSettingsEditScreenViewModelProtocol!
private var notificationSettingsProxy: NotificationSettingsProxyMock! private var notificationSettingsProxy: NotificationSettingsProxyMock!
private var userSession: UserSessionProtocol! private var userSession: UserSessionMock!
private var clientProxy: ClientProxyMock!
private var context: NotificationSettingsEditScreenViewModelType.Context { private var context: NotificationSettingsEditScreenViewModelType.Context {
viewModel.context viewModel.context
} }
@MainActor override func setUpWithError() throws { @MainActor override func setUpWithError() throws {
let clientProxy = ClientProxyMock(.init(userID: "@a:b.com"))
userSession = UserSessionMock(.init(clientProxy: clientProxy))
notificationSettingsProxy = NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()) notificationSettingsProxy = NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration())
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneReturnValue = .allMessages notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneReturnValue = .allMessages
clientProxy = ClientProxyMock(.init(userID: "@a:b.com", notificationSettings: notificationSettingsProxy))
userSession = UserSessionMock(.init(clientProxy: clientProxy))
} }
func testFetchSettings() async throws { func testFetchSettings() async throws {
@@ -36,9 +38,7 @@ class NotificationSettingsEditScreenViewModelTests: XCTestCase {
return .mentionsAndKeywordsOnly return .mentionsAndKeywordsOnly
} }
} }
viewModel = NotificationSettingsEditScreenViewModel(chatType: .groupChat, viewModel = NotificationSettingsEditScreenViewModel(chatType: .groupChat, userSession: userSession)
userSession: userSession,
notificationSettingsProxy: notificationSettingsProxy)
let deferred = deferFulfillment(viewModel.context.observe(\.viewState.defaultMode)) { $0 != nil } let deferred = deferFulfillment(viewModel.context.observe(\.viewState.defaultMode)) { $0 != nil }
@@ -75,9 +75,7 @@ class NotificationSettingsEditScreenViewModelTests: XCTestCase {
notificationSettingsProxy.canPushEncryptedEventsToDeviceClosure = { notificationSettingsProxy.canPushEncryptedEventsToDeviceClosure = {
true true
} }
viewModel = NotificationSettingsEditScreenViewModel(chatType: .groupChat, viewModel = NotificationSettingsEditScreenViewModel(chatType: .groupChat, userSession: userSession)
userSession: userSession,
notificationSettingsProxy: notificationSettingsProxy)
let deferred = deferFulfillment(viewModel.context.observe(\.viewState.defaultMode)) { $0 != nil } let deferred = deferFulfillment(viewModel.context.observe(\.viewState.defaultMode)) { $0 != nil }
@@ -104,9 +102,7 @@ class NotificationSettingsEditScreenViewModelTests: XCTestCase {
func testSetModeAllMessages() async throws { func testSetModeAllMessages() async throws {
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneReturnValue = .mentionsAndKeywordsOnly notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneReturnValue = .mentionsAndKeywordsOnly
viewModel = NotificationSettingsEditScreenViewModel(chatType: .groupChat, viewModel = NotificationSettingsEditScreenViewModel(chatType: .groupChat, userSession: userSession)
userSession: userSession,
notificationSettingsProxy: notificationSettingsProxy)
let deferred = deferFulfillment(viewModel.context.observe(\.viewState.defaultMode)) { $0 != nil } let deferred = deferFulfillment(viewModel.context.observe(\.viewState.defaultMode)) { $0 != nil }
viewModel.fetchInitialContent() viewModel.fetchInitialContent()
@@ -142,9 +138,7 @@ class NotificationSettingsEditScreenViewModelTests: XCTestCase {
} }
func testSetModeMentions() async throws { func testSetModeMentions() async throws {
viewModel = NotificationSettingsEditScreenViewModel(chatType: .groupChat, viewModel = NotificationSettingsEditScreenViewModel(chatType: .groupChat, userSession: userSession)
userSession: userSession,
notificationSettingsProxy: notificationSettingsProxy)
let deferred = deferFulfillment(viewModel.context.observe(\.viewState.defaultMode)) { $0 != nil } let deferred = deferFulfillment(viewModel.context.observe(\.viewState.defaultMode)) { $0 != nil }
@@ -183,9 +177,7 @@ class NotificationSettingsEditScreenViewModelTests: XCTestCase {
func testSetModeDirectChats() async throws { func testSetModeDirectChats() async throws {
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneReturnValue = .mentionsAndKeywordsOnly notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneReturnValue = .mentionsAndKeywordsOnly
// Initialize for direct chats // Initialize for direct chats
viewModel = NotificationSettingsEditScreenViewModel(chatType: .oneToOneChat, viewModel = NotificationSettingsEditScreenViewModel(chatType: .oneToOneChat, userSession: userSession)
userSession: userSession,
notificationSettingsProxy: notificationSettingsProxy)
let deferred = deferFulfillment(viewModel.context.observe(\.viewState.defaultMode)) { $0 != nil } let deferred = deferFulfillment(viewModel.context.observe(\.viewState.defaultMode)) { $0 != nil }
@@ -216,9 +208,7 @@ class NotificationSettingsEditScreenViewModelTests: XCTestCase {
func testSetModeFailure() async throws { func testSetModeFailure() async throws {
notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneReturnValue = .mentionsAndKeywordsOnly notificationSettingsProxy.getDefaultRoomNotificationModeIsEncryptedIsOneToOneReturnValue = .mentionsAndKeywordsOnly
notificationSettingsProxy.setDefaultRoomNotificationModeIsEncryptedIsOneToOneModeThrowableError = NotificationSettingsError.Generic(msg: "error") notificationSettingsProxy.setDefaultRoomNotificationModeIsEncryptedIsOneToOneModeThrowableError = NotificationSettingsError.Generic(msg: "error")
viewModel = NotificationSettingsEditScreenViewModel(chatType: .oneToOneChat, viewModel = NotificationSettingsEditScreenViewModel(chatType: .oneToOneChat, userSession: userSession)
userSession: userSession,
notificationSettingsProxy: notificationSettingsProxy)
let deferred = deferFulfillment(viewModel.context.observe(\.viewState.defaultMode)) { $0 != nil } let deferred = deferFulfillment(viewModel.context.observe(\.viewState.defaultMode)) { $0 != nil }
@@ -238,9 +228,7 @@ class NotificationSettingsEditScreenViewModelTests: XCTestCase {
func testSelectRoom() async throws { func testSelectRoom() async throws {
let roomID = "!roomidentifier:matrix.org" let roomID = "!roomidentifier:matrix.org"
viewModel = NotificationSettingsEditScreenViewModel(chatType: .oneToOneChat, viewModel = NotificationSettingsEditScreenViewModel(chatType: .oneToOneChat, userSession: userSession)
userSession: userSession,
notificationSettingsProxy: notificationSettingsProxy)
let deferredActions = deferFulfillment(viewModel.actions) { action in let deferredActions = deferFulfillment(viewModel.actions) { action in
switch action { switch action {
@@ -358,18 +358,23 @@ class RoomFlowCoordinatorTests: XCTestCase {
"1" "1"
} }
roomFlowCoordinator = RoomFlowCoordinator(roomID: roomID, let flowParameters = CommonFlowParameters(userSession: UserSessionMock(.init(clientProxy: clientProxy)),
userSession: UserSessionMock(.init(clientProxy: clientProxy)), bugReportService: BugReportServiceMock(.init()),
isChildFlow: asChildFlow, elementCallService: ElementCallServiceMock(.init()),
timelineControllerFactory: timelineControllerFactory, timelineControllerFactory: timelineControllerFactory,
navigationStackCoordinator: navigationStackCoordinator,
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings), emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
ongoingCallRoomIDPublisher: .init(.init(nil)),
appMediator: AppMediatorMock.default, appMediator: AppMediatorMock.default,
appSettings: ServiceLocator.shared.settings, appSettings: ServiceLocator.shared.settings,
appHooks: AppHooks(), appHooks: AppHooks(),
analytics: ServiceLocator.shared.analytics, analytics: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController) userIndicatorController: ServiceLocator.shared.userIndicatorController,
notificationManager: NotificationManagerMock(),
stateMachineFactory: StateMachineFactory())
roomFlowCoordinator = RoomFlowCoordinator(roomID: roomID,
isChildFlow: asChildFlow,
navigationStackCoordinator: navigationStackCoordinator,
flowParameters: flowParameters)
} }
} }
@@ -35,19 +35,23 @@ class UserSessionFlowCoordinatorTests: XCTestCase {
notificationManager = NotificationManagerMock() notificationManager = NotificationManagerMock()
userSessionFlowCoordinator = UserSessionFlowCoordinator(userSession: UserSessionMock(.init(clientProxy: clientProxy)), let flowParameters = CommonFlowParameters(userSession: UserSessionMock(.init(clientProxy: clientProxy)),
isNewLogin: false, bugReportService: BugReportServiceMock(.init()),
elementCallService: ElementCallServiceMock(.init()),
timelineControllerFactory: timelineControllerFactory,
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
appMediator: AppMediatorMock.default,
appSettings: ServiceLocator.shared.settings,
appHooks: AppHooks(),
analytics: ServiceLocator.shared.analytics,
userIndicatorController: UserIndicatorControllerMock(),
notificationManager: notificationManager,
stateMachineFactory: stateMachineFactory)
userSessionFlowCoordinator = UserSessionFlowCoordinator(isNewLogin: false,
navigationRootCoordinator: rootCoordinator, navigationRootCoordinator: rootCoordinator,
appLockService: AppLockServiceMock(), appLockService: AppLockServiceMock(),
bugReportService: BugReportServiceMock(.init()), flowParameters: flowParameters)
elementCallService: ElementCallServiceMock(.init()),
timelineControllerFactory: timelineControllerFactory,
appMediator: AppMediatorMock.default,
appSettings: ServiceLocator.shared.settings,
appHooks: AppHooks(),
analytics: ServiceLocator.shared.analytics,
notificationManager: notificationManager,
stateMachineFactory: stateMachineFactory)
userSessionFlowCoordinator.start() userSessionFlowCoordinator.start()
} }