From 4b26a798dc7e134adc116c3d9777e83bd5b7cf26 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Thu, 9 Nov 2023 15:10:50 +0200 Subject: [PATCH] Have the UserIndicatorController use a separate window, switch to using one single instance throughout the application --- .../Sources/Application/AppCoordinator.swift | 7 ++-- .../Application/Windowing/WindowManager.swift | 1 + .../AppLockFlowCoordinator.swift | 12 +++--- .../RoomFlowCoordinator.swift | 15 +++---- .../SettingsFlowCoordinator.swift | 11 ++--- .../UserSessionFlowCoordinator.swift | 24 ++++++----- .../Mocks/Generated/GeneratedMocks.swift | 3 +- .../UserIndicatorController.swift | 24 ++++------- .../UserIndicatorControllerProtocol.swift | 4 +- .../UserIndicatorPresenter.swift | 9 ++-- .../AuthenticationCoordinator.swift | 3 +- .../BugReportScreenCoordinator.swift | 2 +- .../CreateRoom/CreateRoomCoordinator.swift | 2 +- .../CreateRoom/CreateRoomViewModel.swift | 14 +++---- .../CreateRoom/View/CreateRoomScreen.swift | 4 +- .../InviteUsersScreenCoordinator.swift | 2 +- .../InviteUsersScreenViewModel.swift | 8 ++-- .../MediaPickerScreen/CameraPicker.swift | 8 ++-- .../MediaPickerScreen/DocumentPicker.swift | 8 ++-- .../MediaPickerScreenCoordinator.swift | 4 +- .../PhotoLibraryPicker.swift | 8 ++-- .../MediaUploadPreviewScreenCoordinator.swift | 2 +- .../MediaUploadPreviewScreenViewModel.swift | 10 ++--- .../ReportContentScreenCoordinator.swift | 10 ++--- .../RoomDetailsEditScreenCoordinator.swift | 7 ++-- .../RoomDetailsEditScreenViewModel.swift | 32 +++++++------- .../RoomDetailsScreenCoordinator.swift | 11 +++-- ...cureBackupKeyBackupScreenCoordinator.swift | 2 +- ...reBackupRecoveryKeyScreenCoordinator.swift | 2 +- ...cureBackupRecoveryKeyScreenViewModel.swift | 6 +-- .../View/SecureBackupRecoveryKeyScreen.swift | 2 +- .../SecureBackupScreenCoordinator.swift | 22 +++++----- .../SecureBackupScreenViewModel.swift | 8 ++-- .../View/SecureBackupScreen.swift | 2 +- .../SettingsScreenCoordinator.swift | 4 +- .../UserDetailsEditScreenCoordinator.swift | 7 ++-- .../UserDetailsEditScreenViewModel.swift | 32 +++++++------- .../StartChatScreenCoordinator.swift | 42 ++++++++----------- .../StartChatScreenViewModel.swift | 14 +++---- .../View/StartChatScreen.swift | 2 +- .../UITests/UITestsAppCoordinator.swift | 35 ++++++++++++---- .../Sources/CreateRoomViewModelTests.swift | 2 +- .../Sources/StartChatViewModelTests.swift | 2 +- .../UserIndicatorControllerTests.swift | 2 +- 44 files changed, 210 insertions(+), 221 deletions(-) diff --git a/ElementX/Sources/Application/AppCoordinator.swift b/ElementX/Sources/Application/AppCoordinator.swift index e06051ed1..2cbc672da 100644 --- a/ElementX/Sources/Application/AppCoordinator.swift +++ b/ElementX/Sources/Application/AppCoordinator.swift @@ -109,9 +109,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationCoordinatorDelegate, let appLockService = AppLockService(keychainController: keychainController, appSettings: appSettings) let appLockNavigationCoordinator = NavigationRootCoordinator() - let appLockFlowUserIndicatorController = UserIndicatorController(rootCoordinator: appLockNavigationCoordinator) appLockFlowCoordinator = AppLockFlowCoordinator(appLockService: appLockService, - userIndicatorController: appLockFlowUserIndicatorController, navigationCoordinator: appLockNavigationCoordinator) notificationManager = NotificationManager(notificationCenter: UNUserNotificationCenter.current(), @@ -167,7 +165,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationCoordinatorDelegate, func toPresentable() -> AnyView { AnyView( - ServiceLocator.shared.userIndicatorController.toPresentable() + navigationRootCoordinator.toPresentable() .environment(\.analyticsService, ServiceLocator.shared.analytics) ) } @@ -213,6 +211,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationCoordinatorDelegate, func windowManagerDidConfigureWindows(_ windowManager: WindowManager) { windowManager.alternateWindow.rootViewController = UIHostingController(rootView: appLockFlowCoordinator.toPresentable()) + ServiceLocator.shared.userIndicatorController.window = windowManager.overlayWindow } // MARK: - NotificationManagerDelegate @@ -286,7 +285,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationCoordinatorDelegate, } private static func setupServiceLocator(navigationRootCoordinator: NavigationRootCoordinator, appSettings: AppSettings) { - ServiceLocator.shared.register(userIndicatorController: UserIndicatorController(rootCoordinator: navigationRootCoordinator)) + ServiceLocator.shared.register(userIndicatorController: UserIndicatorController()) ServiceLocator.shared.register(appSettings: appSettings) ServiceLocator.shared.register(networkMonitor: NetworkMonitor()) ServiceLocator.shared.register(bugReportService: BugReportService(withBaseURL: appSettings.bugReportServiceBaseURL, diff --git a/ElementX/Sources/Application/Windowing/WindowManager.swift b/ElementX/Sources/Application/Windowing/WindowManager.swift index e9a42d1c5..db75d69ac 100644 --- a/ElementX/Sources/Application/Windowing/WindowManager.swift +++ b/ElementX/Sources/Application/Windowing/WindowManager.swift @@ -47,6 +47,7 @@ class WindowManager { // touches through to the main window. If this changes, there's another solution here: // https://www.fivestars.blog/articles/swiftui-windows/ overlayWindow.isUserInteractionEnabled = false + overlayWindow.isHidden = false alternateWindow = UIWindow(windowScene: windowScene) alternateWindow.tintColor = .compound.textActionPrimary diff --git a/ElementX/Sources/FlowCoordinators/AppLockFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/AppLockFlowCoordinator.swift index 55797cbb4..03a7c6f1b 100644 --- a/ElementX/Sources/FlowCoordinators/AppLockFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/AppLockFlowCoordinator.swift @@ -29,7 +29,6 @@ enum AppLockFlowCoordinatorAction: Equatable { /// Coordinates the display of any screens shown when the app is locked. class AppLockFlowCoordinator: CoordinatorProtocol { let appLockService: AppLockServiceProtocol - let userIndicatorController: UserIndicatorController let navigationCoordinator: NavigationRootCoordinator /// A task used to await biometric unlock before showing the PIN screen. @@ -41,18 +40,17 @@ class AppLockFlowCoordinator: CoordinatorProtocol { actionsSubject.eraseToAnyPublisher() } - init(appLockService: AppLockServiceProtocol, userIndicatorController: UserIndicatorController, navigationCoordinator: NavigationRootCoordinator) { + init(appLockService: AppLockServiceProtocol, navigationCoordinator: NavigationRootCoordinator) { self.appLockService = appLockService - self.userIndicatorController = userIndicatorController self.navigationCoordinator = navigationCoordinator // Set the initial background state. showPlaceholder() appLockService.disabledPublisher - .sink { [weak self] _ in + .sink { // When the service is disabled via a force logout, we need to remove the activity indicator. - self?.userIndicatorController.retractAllIndicators() + ServiceLocator.shared.userIndicatorController.retractAllIndicators() } .store(in: &cancellables) @@ -70,7 +68,7 @@ class AppLockFlowCoordinator: CoordinatorProtocol { } func toPresentable() -> AnyView { - AnyView(userIndicatorController.toPresentable()) + AnyView(navigationCoordinator.toPresentable()) } // MARK: - App unlock @@ -131,7 +129,7 @@ class AppLockFlowCoordinator: CoordinatorProtocol { case .appUnlocked: actionsSubject.send(.unlockApp) case .forceLogout: - userIndicatorController.submitIndicator(UserIndicator(type: .modal, title: L10n.commonSigningOut, persistent: true)) + ServiceLocator.shared.userIndicatorController.submitIndicator(UserIndicator(type: .modal, title: L10n.commonSigningOut, persistent: true)) actionsSubject.send(.forceLogout) } } diff --git a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift index f0afa967e..7d5ced788 100644 --- a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift @@ -486,8 +486,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { fatalError() } - let navigationCoordinator = NavigationStackCoordinator() - let userIndicatorController = UserIndicatorController(rootCoordinator: navigationCoordinator) + let navigationStackCoordinator = NavigationStackCoordinator() let parameters = ReportContentScreenCoordinatorParameters(eventID: eventID, senderID: senderID, roomProxy: roomProxy, @@ -509,15 +508,14 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { } .store(in: &cancellables) - navigationCoordinator.setRootCoordinator(coordinator) - navigationStackCoordinator.setSheetCoordinator(userIndicatorController) { [weak self] in + navigationStackCoordinator.setRootCoordinator(coordinator) + navigationStackCoordinator.setSheetCoordinator(navigationStackCoordinator) { [weak self] in self?.stateMachine.tryEvent(.dismissReportContent) } } private func presentMediaUploadPickerWithSource(_ source: MediaPickerScreenSource) { let stackCoordinator = NavigationStackCoordinator() - let userIndicatorController = UserIndicatorController(rootCoordinator: stackCoordinator) let mediaPickerCoordinator = MediaPickerScreenCoordinator(userIndicatorController: userIndicatorController, source: source) { [weak self] action in switch action { @@ -530,7 +528,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { stackCoordinator.setRootCoordinator(mediaPickerCoordinator) - navigationStackCoordinator.setSheetCoordinator(userIndicatorController) { [weak self] in + navigationStackCoordinator.setSheetCoordinator(stackCoordinator) { [weak self] in if case .mediaUploadPicker = self?.stateMachine.state { self?.stateMachine.tryEvent(.dismissMediaUploadPicker) } @@ -543,7 +541,6 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { } let stackCoordinator = NavigationStackCoordinator() - let userIndicatorController = UserIndicatorController(rootCoordinator: stackCoordinator) let parameters = MediaUploadPreviewScreenCoordinatorParameters(userIndicatorController: userIndicatorController, roomProxy: roomProxy, @@ -565,8 +562,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { .store(in: &cancellables) stackCoordinator.setRootCoordinator(mediaUploadPreviewScreenCoordinator) - - navigationStackCoordinator.setSheetCoordinator(userIndicatorController) { [weak self] in + + navigationStackCoordinator.setSheetCoordinator(stackCoordinator) { [weak self] in self?.stateMachine.tryEvent(.dismissMediaUploadPreview) } } diff --git a/ElementX/Sources/FlowCoordinators/SettingsFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/SettingsFlowCoordinator.swift index 87f98fcdd..6fc665007 100644 --- a/ElementX/Sources/FlowCoordinators/SettingsFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/SettingsFlowCoordinator.swift @@ -34,13 +34,13 @@ struct SettingsFlowCoordinatorParameters { let secureBackupController: SecureBackupControllerProtocol let appSettings: AppSettings let navigationSplitCoordinator: NavigationSplitCoordinator + let userIndicatorController: UserIndicatorControllerProtocol } class SettingsFlowCoordinator: FlowCoordinatorProtocol { private let parameters: SettingsFlowCoordinatorParameters private var navigationStackCoordinator: NavigationStackCoordinator! - private var userIndicatorController: UserIndicatorControllerProtocol! private var cancellables = Set() @@ -79,10 +79,8 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol { private func presentSettingsScreen(animated: Bool) { navigationStackCoordinator = NavigationStackCoordinator() - userIndicatorController = UserIndicatorController(rootCoordinator: navigationStackCoordinator) - let parameters = SettingsScreenCoordinatorParameters(navigationStackCoordinator: navigationStackCoordinator, - userIndicatorController: userIndicatorController, + userIndicatorController: parameters.userIndicatorController, userSession: parameters.userSession, appLockService: parameters.appLockService, bugReportService: parameters.bugReportService, @@ -117,11 +115,10 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol { navigationStackCoordinator.setRootCoordinator(settingsScreenCoordinator, animated: animated) - self.parameters.navigationSplitCoordinator.setSheetCoordinator(userIndicatorController) { [weak self] in + self.parameters.navigationSplitCoordinator.setSheetCoordinator(navigationStackCoordinator) { [weak self] in guard let self else { return } navigationStackCoordinator = nil - userIndicatorController = nil actionsSubject.send(.dismissedSettings) } @@ -132,7 +129,7 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol { let coordinator = SecureBackupScreenCoordinator(parameters: .init(appSettings: parameters.appSettings, secureBackupController: parameters.userSession.clientProxy.secureBackupController, navigationStackCoordinator: navigationStackCoordinator, - userIndicatorController: userIndicatorController)) + userIndicatorController: parameters.userIndicatorController)) navigationStackCoordinator.push(coordinator, animated: animated) } diff --git a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift index a7edd969d..2e182a519 100644 --- a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift @@ -86,7 +86,8 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { notificationSettings: userSession.clientProxy.notificationSettings, secureBackupController: userSession.clientProxy.secureBackupController, appSettings: appSettings, - navigationSplitCoordinator: navigationSplitCoordinator)) + navigationSplitCoordinator: navigationSplitCoordinator, + userIndicatorController: ServiceLocator.shared.userIndicatorController)) setupStateMachine() @@ -436,10 +437,13 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { private func presentStartChat(animated: Bool) { let startChatNavigationStackCoordinator = NavigationStackCoordinator() - - let userIndicatorController = UserIndicatorController(rootCoordinator: startChatNavigationStackCoordinator) + let userDiscoveryService = UserDiscoveryService(clientProxy: userSession.clientProxy) - let parameters = StartChatScreenCoordinatorParameters(userSession: userSession, userIndicatorController: userIndicatorController, navigationStackCoordinator: startChatNavigationStackCoordinator, userDiscoveryService: userDiscoveryService) + let parameters = StartChatScreenCoordinatorParameters(userSession: userSession, + userIndicatorController: ServiceLocator.shared.userIndicatorController, + navigationStackCoordinator: startChatNavigationStackCoordinator, + userDiscoveryService: userDiscoveryService) + let coordinator = StartChatScreenCoordinator(parameters: parameters) coordinator.actions.sink { [weak self] action in guard let self else { return } @@ -454,8 +458,8 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { .store(in: &cancellables) startChatNavigationStackCoordinator.setRootCoordinator(coordinator) - - navigationSplitCoordinator.setSheetCoordinator(userIndicatorController, animated: animated) { [weak self] in + + navigationSplitCoordinator.setSheetCoordinator(startChatNavigationStackCoordinator, animated: animated) { [weak self] in self?.stateMachine.processEvent(.dismissedStartChatScreen) } } @@ -465,22 +469,20 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { private func presentFeedbackScreen(animated: Bool, for image: UIImage? = nil) { let feedbackNavigationStackCoordinator = NavigationStackCoordinator() - let userIndicatorController = UserIndicatorController(rootCoordinator: feedbackNavigationStackCoordinator) - let parameters = BugReportScreenCoordinatorParameters(bugReportService: bugReportService, userID: userSession.userID, deviceID: userSession.deviceID, - userIndicatorController: userIndicatorController, + userIndicatorController: ServiceLocator.shared.userIndicatorController, screenshot: image, isModallyPresented: true) let coordinator = BugReportScreenCoordinator(parameters: parameters) coordinator.completion = { [weak self] _ in self?.navigationSplitCoordinator.setSheetCoordinator(nil) } - + feedbackNavigationStackCoordinator.setRootCoordinator(coordinator) - navigationSplitCoordinator.setSheetCoordinator(userIndicatorController, animated: animated) { [weak self] in + navigationSplitCoordinator.setSheetCoordinator(feedbackNavigationStackCoordinator, animated: animated) { [weak self] in self?.stateMachine.processEvent(.dismissedFeedbackScreen) } } diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index c7037fb31..5983abca2 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -1,4 +1,4 @@ -// Generated using Sourcery 2.1.2 — https://github.com/krzysztofzablocki/Sourcery +// Generated using Sourcery 2.1.1 — https://github.com/krzysztofzablocki/Sourcery // DO NOT EDIT // swiftlint:disable all @@ -2938,6 +2938,7 @@ class UserDiscoveryServiceMock: UserDiscoveryServiceProtocol { } } class UserIndicatorControllerMock: UserIndicatorControllerProtocol { + var window: UIWindow? var alertInfo: AlertInfo? //MARK: - submitIndicator diff --git a/ElementX/Sources/Other/UserIndicator/UserIndicatorController.swift b/ElementX/Sources/Other/UserIndicator/UserIndicatorController.swift index ea2e01e2e..36c4e950f 100644 --- a/ElementX/Sources/Other/UserIndicator/UserIndicatorController.swift +++ b/ElementX/Sources/Other/UserIndicator/UserIndicatorController.swift @@ -16,9 +16,7 @@ import SwiftUI -class UserIndicatorController: ObservableObject, UserIndicatorControllerProtocol, CustomStringConvertible { - private let rootCoordinator: CoordinatorProtocol - +class UserIndicatorController: ObservableObject, UserIndicatorControllerProtocol { private var dismissalTimer: Timer? private var displayTimes = [String: Date]() private var delayedIndicators = Set() @@ -42,14 +40,12 @@ class UserIndicatorController: ObservableObject, UserIndicatorControllerProtocol @Published var alertInfo: AlertInfo? - init(rootCoordinator: CoordinatorProtocol) { - self.rootCoordinator = rootCoordinator - } - - func toPresentable() -> AnyView { - AnyView( - UserIndicatorPresenter(userIndicatorController: self, rootView: rootCoordinator.toPresentable()) - ) + var window: UIWindow? { + didSet { + let hostingController = UIHostingController(rootView: UserIndicatorPresenter(userIndicatorController: self)) + hostingController.view.backgroundColor = .clear + window?.rootViewController = hostingController + } } func submitIndicator(_ indicator: UserIndicator, delay: Duration?) { @@ -95,12 +91,6 @@ class UserIndicatorController: ObservableObject, UserIndicatorControllerProtocol } } - // MARK: - CustomStringConvertible - - var description: String { - "UserIndicatorController(\(String(describing: rootCoordinator)))" - } - // MARK: - Private private func enqueue(indicator: UserIndicator) { diff --git a/ElementX/Sources/Other/UserIndicator/UserIndicatorControllerProtocol.swift b/ElementX/Sources/Other/UserIndicator/UserIndicatorControllerProtocol.swift index 5dfef0d7a..6dcf3a6c9 100644 --- a/ElementX/Sources/Other/UserIndicator/UserIndicatorControllerProtocol.swift +++ b/ElementX/Sources/Other/UserIndicator/UserIndicatorControllerProtocol.swift @@ -14,13 +14,15 @@ // limitations under the License. // -import Foundation +import UIKit // sourcery: AutoMockable protocol UserIndicatorControllerProtocol: CoordinatorProtocol { func submitIndicator(_ indicator: UserIndicator, delay: Duration?) func retractIndicatorWithId(_ id: String) func retractAllIndicators() + + var window: UIWindow? { get set } var alertInfo: AlertInfo? { get set } } diff --git a/ElementX/Sources/Other/UserIndicator/UserIndicatorPresenter.swift b/ElementX/Sources/Other/UserIndicator/UserIndicatorPresenter.swift index 1a754ebd6..6a513a61e 100644 --- a/ElementX/Sources/Other/UserIndicator/UserIndicatorPresenter.swift +++ b/ElementX/Sources/Other/UserIndicator/UserIndicatorPresenter.swift @@ -18,14 +18,11 @@ import SwiftUI struct UserIndicatorPresenter: View { @ObservedObject var userIndicatorController: UserIndicatorController - let rootView: AnyView var body: some View { - ZStack(alignment: .top) { - rootView - indicatorViewFor(indicator: userIndicatorController.activeIndicator) - } - .animation(.elementDefault, value: userIndicatorController.activeIndicator) + indicatorViewFor(indicator: userIndicatorController.activeIndicator) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) + .animation(.elementDefault, value: userIndicatorController.activeIndicator) } @ViewBuilder diff --git a/ElementX/Sources/Screens/Authentication/AuthenticationCoordinator.swift b/ElementX/Sources/Screens/Authentication/AuthenticationCoordinator.swift index c218354fe..0ea51dc13 100644 --- a/ElementX/Sources/Screens/Authentication/AuthenticationCoordinator.swift +++ b/ElementX/Sources/Screens/Authentication/AuthenticationCoordinator.swift @@ -103,7 +103,6 @@ class AuthenticationCoordinator: CoordinatorProtocol { private func showServerSelectionScreen(isModallyPresented: Bool) { let navigationCoordinator = NavigationStackCoordinator() - let userIndicatorController: UserIndicatorControllerProtocol! = isModallyPresented ? UserIndicatorController(rootCoordinator: navigationCoordinator) : userIndicatorController let parameters = ServerSelectionScreenCoordinatorParameters(authenticationService: authenticationService, userIndicatorController: userIndicatorController, @@ -138,7 +137,7 @@ class AuthenticationCoordinator: CoordinatorProtocol { if isModallyPresented { navigationCoordinator.setRootCoordinator(coordinator) - navigationStackCoordinator.setSheetCoordinator(userIndicatorController) + navigationStackCoordinator.setSheetCoordinator(navigationCoordinator) } else { navigationStackCoordinator.push(coordinator) } diff --git a/ElementX/Sources/Screens/BugReportScreen/BugReportScreenCoordinator.swift b/ElementX/Sources/Screens/BugReportScreen/BugReportScreenCoordinator.swift index 61041af6f..705309bcd 100644 --- a/ElementX/Sources/Screens/BugReportScreen/BugReportScreenCoordinator.swift +++ b/ElementX/Sources/Screens/BugReportScreen/BugReportScreenCoordinator.swift @@ -27,7 +27,7 @@ struct BugReportScreenCoordinatorParameters { let userID: String let deviceID: String? - weak var userIndicatorController: UserIndicatorControllerProtocol? + let userIndicatorController: UserIndicatorControllerProtocol? let screenshot: UIImage? let isModallyPresented: Bool } diff --git a/ElementX/Sources/Screens/CreateRoom/CreateRoomCoordinator.swift b/ElementX/Sources/Screens/CreateRoom/CreateRoomCoordinator.swift index 7bdca1c60..da6e15ee3 100644 --- a/ElementX/Sources/Screens/CreateRoom/CreateRoomCoordinator.swift +++ b/ElementX/Sources/Screens/CreateRoom/CreateRoomCoordinator.swift @@ -19,7 +19,7 @@ import SwiftUI struct CreateRoomCoordinatorParameters { let userSession: UserSessionProtocol - weak var userIndicatorController: UserIndicatorControllerProtocol? + let userIndicatorController: UserIndicatorControllerProtocol let createRoomParameters: CurrentValuePublisher let selectedUsers: CurrentValuePublisher<[UserProfileProxy], Never> } diff --git a/ElementX/Sources/Screens/CreateRoom/CreateRoomViewModel.swift b/ElementX/Sources/Screens/CreateRoom/CreateRoomViewModel.swift index f1030bf4f..1b20d96aa 100644 --- a/ElementX/Sources/Screens/CreateRoom/CreateRoomViewModel.swift +++ b/ElementX/Sources/Screens/CreateRoom/CreateRoomViewModel.swift @@ -23,7 +23,7 @@ class CreateRoomViewModel: CreateRoomViewModelType, CreateRoomViewModelProtocol private let userSession: UserSessionProtocol private var createRoomParameters: CreateRoomFlowParameters private let analytics: AnalyticsService - private weak var userIndicatorController: UserIndicatorControllerProtocol? + private let userIndicatorController: UserIndicatorControllerProtocol private var actionsSubject: PassthroughSubject = .init() @@ -35,7 +35,7 @@ class CreateRoomViewModel: CreateRoomViewModelType, CreateRoomViewModelProtocol createRoomParameters: CurrentValuePublisher, selectedUsers: CurrentValuePublisher<[UserProfileProxy], Never>, analytics: AnalyticsService, - userIndicatorController: UserIndicatorControllerProtocol?) { + userIndicatorController: UserIndicatorControllerProtocol) { let parameters = createRoomParameters.value self.userSession = userSession @@ -174,13 +174,13 @@ class CreateRoomViewModel: CreateRoomViewModelType, CreateRoomViewModelProtocol private static let loadingIndicatorIdentifier = "CreateRoomLoading" private func showLoadingIndicator() { - userIndicatorController?.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier, - type: .modal(progress: .indeterminate, interactiveDismissDisabled: true, allowsInteraction: false), - title: L10n.commonLoading, - persistent: true)) + userIndicatorController.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier, + type: .modal(progress: .indeterminate, interactiveDismissDisabled: true, allowsInteraction: false), + title: L10n.commonLoading, + persistent: true)) } private func hideLoadingIndicator() { - userIndicatorController?.retractIndicatorWithId(Self.loadingIndicatorIdentifier) + userIndicatorController.retractIndicatorWithId(Self.loadingIndicatorIdentifier) } } diff --git a/ElementX/Sources/Screens/CreateRoom/View/CreateRoomScreen.swift b/ElementX/Sources/Screens/CreateRoom/View/CreateRoomScreen.swift index 561609142..e7a411d2c 100644 --- a/ElementX/Sources/Screens/CreateRoom/View/CreateRoomScreen.swift +++ b/ElementX/Sources/Screens/CreateRoom/View/CreateRoomScreen.swift @@ -216,7 +216,7 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview { createRoomParameters: .init(parameters), selectedUsers: .init(selectedUsers), analytics: ServiceLocator.shared.analytics, - userIndicatorController: nil) + userIndicatorController: UserIndicatorControllerMock()) }() static let emtpyViewModel = { @@ -228,7 +228,7 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview { createRoomParameters: .init(parameters), selectedUsers: .init([]), analytics: ServiceLocator.shared.analytics, - userIndicatorController: nil) + userIndicatorController: UserIndicatorControllerMock()) }() static var previews: some View { diff --git a/ElementX/Sources/Screens/InviteUsersScreen/InviteUsersScreenCoordinator.swift b/ElementX/Sources/Screens/InviteUsersScreen/InviteUsersScreenCoordinator.swift index a70aea4c4..efe751cfa 100644 --- a/ElementX/Sources/Screens/InviteUsersScreen/InviteUsersScreenCoordinator.swift +++ b/ElementX/Sources/Screens/InviteUsersScreen/InviteUsersScreenCoordinator.swift @@ -22,7 +22,7 @@ struct InviteUsersScreenCoordinatorParameters { let roomType: InviteUsersScreenRoomType let mediaProvider: MediaProviderProtocol let userDiscoveryService: UserDiscoveryServiceProtocol - weak var userIndicatorController: UserIndicatorControllerProtocol? + let userIndicatorController: UserIndicatorControllerProtocol } enum InviteUsersScreenCoordinatorAction { diff --git a/ElementX/Sources/Screens/InviteUsersScreen/InviteUsersScreenViewModel.swift b/ElementX/Sources/Screens/InviteUsersScreen/InviteUsersScreenViewModel.swift index f386f3f93..cf6e33144 100644 --- a/ElementX/Sources/Screens/InviteUsersScreen/InviteUsersScreenViewModel.swift +++ b/ElementX/Sources/Screens/InviteUsersScreen/InviteUsersScreenViewModel.swift @@ -25,7 +25,7 @@ class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScr private let mediaProvider: MediaProviderProtocol private let userDiscoveryService: UserDiscoveryServiceProtocol private let appSettings: AppSettings - private weak var userIndicatorController: UserIndicatorControllerProtocol? + private let userIndicatorController: UserIndicatorControllerProtocol private let actionsSubject: PassthroughSubject = .init() @@ -38,7 +38,7 @@ class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScr mediaProvider: MediaProviderProtocol, userDiscoveryService: UserDiscoveryServiceProtocol, appSettings: AppSettings, - userIndicatorController: UserIndicatorControllerProtocol?) { + userIndicatorController: UserIndicatorControllerProtocol) { self.roomType = roomType self.mediaProvider = mediaProvider self.userDiscoveryService = userDiscoveryService @@ -169,11 +169,11 @@ class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScr private let userIndicatorID = UUID().uuidString private func showLoader() { - userIndicatorController?.submitIndicator(UserIndicator(id: userIndicatorID, type: .modal, title: L10n.commonLoading, persistent: true), delay: .milliseconds(200)) + userIndicatorController.submitIndicator(UserIndicator(id: userIndicatorID, type: .modal, title: L10n.commonLoading, persistent: true), delay: .milliseconds(200)) } private func hideLoader() { - userIndicatorController?.retractIndicatorWithId(userIndicatorID) + userIndicatorController.retractIndicatorWithId(userIndicatorID) } } diff --git a/ElementX/Sources/Screens/MediaPickerScreen/CameraPicker.swift b/ElementX/Sources/Screens/MediaPickerScreen/CameraPicker.swift index 8df51f6a1..cb1146c24 100644 --- a/ElementX/Sources/Screens/MediaPickerScreen/CameraPicker.swift +++ b/ElementX/Sources/Screens/MediaPickerScreen/CameraPicker.swift @@ -29,10 +29,10 @@ enum CameraPickerError: Error { } struct CameraPicker: UIViewControllerRepresentable { - private weak var userIndicatorController: UserIndicatorControllerProtocol? + private let userIndicatorController: UserIndicatorControllerProtocol private let callback: (CameraPickerAction) -> Void - init(userIndicatorController: UserIndicatorControllerProtocol?, callback: @escaping (CameraPickerAction) -> Void) { + init(userIndicatorController: UserIndicatorControllerProtocol, callback: @escaping (CameraPickerAction) -> Void) { self.userIndicatorController = userIndicatorController self.callback = callback } @@ -68,9 +68,9 @@ struct CameraPicker: UIViewControllerRepresentable { func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) { picker.delegate = nil - cameraPicker.userIndicatorController?.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier, type: .modal, title: L10n.commonLoading)) + cameraPicker.userIndicatorController.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier, type: .modal, title: L10n.commonLoading)) defer { - cameraPicker.userIndicatorController?.retractIndicatorWithId(Self.loadingIndicatorIdentifier) + cameraPicker.userIndicatorController.retractIndicatorWithId(Self.loadingIndicatorIdentifier) } if let videoURL = info[.mediaURL] as? URL { diff --git a/ElementX/Sources/Screens/MediaPickerScreen/DocumentPicker.swift b/ElementX/Sources/Screens/MediaPickerScreen/DocumentPicker.swift index ed7eb1484..a8f717a33 100644 --- a/ElementX/Sources/Screens/MediaPickerScreen/DocumentPicker.swift +++ b/ElementX/Sources/Screens/MediaPickerScreen/DocumentPicker.swift @@ -27,10 +27,10 @@ enum DocumentPickerError: Error { } struct DocumentPicker: UIViewControllerRepresentable { - private weak var userIndicatorController: UserIndicatorControllerProtocol? + private let userIndicatorController: UserIndicatorControllerProtocol private let callback: (DocumentPickerAction) -> Void - init(userIndicatorController: UserIndicatorControllerProtocol?, callback: @escaping (DocumentPickerAction) -> Void) { + init(userIndicatorController: UserIndicatorControllerProtocol, callback: @escaping (DocumentPickerAction) -> Void) { self.userIndicatorController = userIndicatorController self.callback = callback } @@ -72,9 +72,9 @@ struct DocumentPicker: UIViewControllerRepresentable { picker.delegate = nil - documentPicker.userIndicatorController?.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier, type: .modal, title: L10n.commonLoading)) + documentPicker.userIndicatorController.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier, type: .modal, title: L10n.commonLoading)) defer { - documentPicker.userIndicatorController?.retractIndicatorWithId(Self.loadingIndicatorIdentifier) + documentPicker.userIndicatorController.retractIndicatorWithId(Self.loadingIndicatorIdentifier) } do { diff --git a/ElementX/Sources/Screens/MediaPickerScreen/MediaPickerScreenCoordinator.swift b/ElementX/Sources/Screens/MediaPickerScreen/MediaPickerScreenCoordinator.swift index d870a8870..70e7db389 100644 --- a/ElementX/Sources/Screens/MediaPickerScreen/MediaPickerScreenCoordinator.swift +++ b/ElementX/Sources/Screens/MediaPickerScreen/MediaPickerScreenCoordinator.swift @@ -28,7 +28,7 @@ enum MediaPickerScreenCoordinatorAction { } class MediaPickerScreenCoordinator: CoordinatorProtocol { - private weak var userIndicatorController: UserIndicatorControllerProtocol? + private let userIndicatorController: UserIndicatorControllerProtocol private let source: MediaPickerScreenSource private let callback: ((MediaPickerScreenCoordinatorAction) -> Void)? @@ -92,6 +92,6 @@ class MediaPickerScreenCoordinator: CoordinatorProtocol { } private func showError() { - userIndicatorController?.submitIndicator(UserIndicator(title: L10n.screenMediaPickerErrorFailedSelection)) + userIndicatorController.submitIndicator(UserIndicator(title: L10n.screenMediaPickerErrorFailedSelection)) } } diff --git a/ElementX/Sources/Screens/MediaPickerScreen/PhotoLibraryPicker.swift b/ElementX/Sources/Screens/MediaPickerScreen/PhotoLibraryPicker.swift index 601c90ea7..9c8c3b0c0 100644 --- a/ElementX/Sources/Screens/MediaPickerScreen/PhotoLibraryPicker.swift +++ b/ElementX/Sources/Screens/MediaPickerScreen/PhotoLibraryPicker.swift @@ -29,10 +29,10 @@ enum PhotoLibraryPickerError: Error { } struct PhotoLibraryPicker: UIViewControllerRepresentable { - private weak var userIndicatorController: UserIndicatorControllerProtocol? + private let userIndicatorController: UserIndicatorControllerProtocol private let callback: (PhotoLibraryPickerAction) -> Void - init(userIndicatorController: UserIndicatorControllerProtocol?, callback: @escaping (PhotoLibraryPickerAction) -> Void) { + init(userIndicatorController: UserIndicatorControllerProtocol, callback: @escaping (PhotoLibraryPickerAction) -> Void) { self.userIndicatorController = userIndicatorController self.callback = callback } @@ -73,9 +73,9 @@ struct PhotoLibraryPicker: UIViewControllerRepresentable { picker.delegate = nil - photoLibraryPicker.userIndicatorController?.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier, type: .modal, title: L10n.commonLoading)) + photoLibraryPicker.userIndicatorController.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier, type: .modal, title: L10n.commonLoading)) defer { - photoLibraryPicker.userIndicatorController?.retractIndicatorWithId(Self.loadingIndicatorIdentifier) + photoLibraryPicker.userIndicatorController.retractIndicatorWithId(Self.loadingIndicatorIdentifier) } provider.loadFileRepresentation(forTypeIdentifier: contentType.identifier) { [weak self] url, error in diff --git a/ElementX/Sources/Screens/MediaUploadPreviewScreen/MediaUploadPreviewScreenCoordinator.swift b/ElementX/Sources/Screens/MediaUploadPreviewScreen/MediaUploadPreviewScreenCoordinator.swift index ec3ffd866..253ec3f21 100644 --- a/ElementX/Sources/Screens/MediaUploadPreviewScreen/MediaUploadPreviewScreenCoordinator.swift +++ b/ElementX/Sources/Screens/MediaUploadPreviewScreen/MediaUploadPreviewScreenCoordinator.swift @@ -18,7 +18,7 @@ import Combine import SwiftUI struct MediaUploadPreviewScreenCoordinatorParameters { - weak var userIndicatorController: UserIndicatorControllerProtocol? + let userIndicatorController: UserIndicatorControllerProtocol let roomProxy: RoomProxyProtocol let mediaUploadingPreprocessor: MediaUploadingPreprocessor let title: String? diff --git a/ElementX/Sources/Screens/MediaUploadPreviewScreen/MediaUploadPreviewScreenViewModel.swift b/ElementX/Sources/Screens/MediaUploadPreviewScreen/MediaUploadPreviewScreenViewModel.swift index 9bc2c308e..ec6887003 100644 --- a/ElementX/Sources/Screens/MediaUploadPreviewScreen/MediaUploadPreviewScreenViewModel.swift +++ b/ElementX/Sources/Screens/MediaUploadPreviewScreen/MediaUploadPreviewScreenViewModel.swift @@ -21,7 +21,7 @@ import SwiftUI typealias MediaUploadPreviewScreenViewModelType = StateStoreViewModel class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType, MediaUploadPreviewScreenViewModelProtocol { - private weak var userIndicatorController: UserIndicatorControllerProtocol? + private let userIndicatorController: UserIndicatorControllerProtocol private let roomProxy: RoomProxyProtocol private let mediaUploadingPreprocessor: MediaUploadingPreprocessor private let url: URL @@ -37,7 +37,7 @@ class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType, actionsSubject.eraseToAnyPublisher() } - init(userIndicatorController: UserIndicatorControllerProtocol?, + init(userIndicatorController: UserIndicatorControllerProtocol, roomProxy: RoomProxyProtocol, mediaUploadingPreprocessor: MediaUploadingPreprocessor, title: String?, @@ -105,7 +105,7 @@ class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType, private static let loadingIndicatorIdentifier = "MediaUploadPreviewLoading" private func startLoading(progressPublisher: CurrentValuePublisher) { - userIndicatorController?.submitIndicator( + userIndicatorController.submitIndicator( UserIndicator(id: Self.loadingIndicatorIdentifier, type: .modal(progress: .published(progressPublisher), interactiveDismissDisabled: false, allowsInteraction: true), title: L10n.commonSending, @@ -114,11 +114,11 @@ class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType, } private func stopLoading() { - userIndicatorController?.retractIndicatorWithId(Self.loadingIndicatorIdentifier) + userIndicatorController.retractIndicatorWithId(Self.loadingIndicatorIdentifier) requestHandle = nil } private func showError(label: String) { - userIndicatorController?.submitIndicator(UserIndicator(title: label)) + userIndicatorController.submitIndicator(UserIndicator(title: label)) } } diff --git a/ElementX/Sources/Screens/ReportContentScreen/ReportContentScreenCoordinator.swift b/ElementX/Sources/Screens/ReportContentScreen/ReportContentScreenCoordinator.swift index ad3327554..d4aefd225 100644 --- a/ElementX/Sources/Screens/ReportContentScreen/ReportContentScreenCoordinator.swift +++ b/ElementX/Sources/Screens/ReportContentScreen/ReportContentScreenCoordinator.swift @@ -21,7 +21,7 @@ struct ReportContentScreenCoordinatorParameters { let eventID: String let senderID: String let roomProxy: RoomProxyProtocol - weak var userIndicatorController: UserIndicatorControllerProtocol? + let userIndicatorController: UserIndicatorControllerProtocol } enum ReportContentScreenCoordinatorAction { @@ -81,7 +81,7 @@ final class ReportContentScreenCoordinator: CoordinatorProtocol { private static let loadingIndicatorIdentifier = "ReportContentLoading" private func startLoading() { - parameters.userIndicatorController?.submitIndicator( + parameters.userIndicatorController.submitIndicator( UserIndicator(id: Self.loadingIndicatorIdentifier, type: .modal, title: L10n.commonSending, @@ -90,10 +90,10 @@ final class ReportContentScreenCoordinator: CoordinatorProtocol { } private func stopLoading() { - parameters.userIndicatorController?.retractIndicatorWithId(Self.loadingIndicatorIdentifier) + parameters.userIndicatorController.retractIndicatorWithId(Self.loadingIndicatorIdentifier) } - + private func showError(description: String) { - parameters.userIndicatorController?.submitIndicator(UserIndicator(title: description)) + parameters.userIndicatorController.submitIndicator(UserIndicator(title: description)) } } diff --git a/ElementX/Sources/Screens/RoomDetailsEditScreen/RoomDetailsEditScreenCoordinator.swift b/ElementX/Sources/Screens/RoomDetailsEditScreen/RoomDetailsEditScreenCoordinator.swift index 6b420d087..b0b362383 100644 --- a/ElementX/Sources/Screens/RoomDetailsEditScreen/RoomDetailsEditScreenCoordinator.swift +++ b/ElementX/Sources/Screens/RoomDetailsEditScreen/RoomDetailsEditScreenCoordinator.swift @@ -22,7 +22,7 @@ struct RoomDetailsEditScreenCoordinatorParameters { let mediaProvider: MediaProviderProtocol weak var navigationStackCoordinator: NavigationStackCoordinator? let roomProxy: RoomProxyProtocol - weak var userIndicatorController: UserIndicatorControllerProtocol? + let userIndicatorController: UserIndicatorControllerProtocol } enum RoomDetailsEditScreenCoordinatorAction { @@ -71,9 +71,8 @@ final class RoomDetailsEditScreenCoordinator: CoordinatorProtocol { private func displayMediaPickerWithSource(_ source: MediaPickerScreenSource) { let stackCoordinator = NavigationStackCoordinator() - let userIndicatorController = UserIndicatorController(rootCoordinator: stackCoordinator) - let mediaPickerCoordinator = MediaPickerScreenCoordinator(userIndicatorController: userIndicatorController, source: source) { [weak self] action in + let mediaPickerCoordinator = MediaPickerScreenCoordinator(userIndicatorController: parameters.userIndicatorController, source: source) { [weak self] action in guard let self else { return } switch action { case .cancel: @@ -85,6 +84,6 @@ final class RoomDetailsEditScreenCoordinator: CoordinatorProtocol { } stackCoordinator.setRootCoordinator(mediaPickerCoordinator) - parameters.navigationStackCoordinator?.setSheetCoordinator(userIndicatorController) + parameters.navigationStackCoordinator?.setSheetCoordinator(stackCoordinator) } } diff --git a/ElementX/Sources/Screens/RoomDetailsEditScreen/RoomDetailsEditScreenViewModel.swift b/ElementX/Sources/Screens/RoomDetailsEditScreen/RoomDetailsEditScreenViewModel.swift index 2d1d8f85d..b3ecf1023 100644 --- a/ElementX/Sources/Screens/RoomDetailsEditScreen/RoomDetailsEditScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomDetailsEditScreen/RoomDetailsEditScreenViewModel.swift @@ -22,7 +22,7 @@ typealias RoomDetailsEditScreenViewModelType = StateStoreViewModel = .init() private let roomProxy: RoomProxyProtocol - private weak var userIndicatorController: UserIndicatorControllerProtocol? + private let userIndicatorController: UserIndicatorControllerProtocol private let mediaPreprocessor: MediaUploadingPreprocessor = .init() var actions: AnyPublisher { @@ -32,7 +32,7 @@ class RoomDetailsEditScreenViewModel: RoomDetailsEditScreenViewModelType, RoomDe init(accountOwner: RoomMemberProxyProtocol, mediaProvider: MediaProviderProtocol, roomProxy: RoomProxyProtocol, - userIndicatorController: UserIndicatorControllerProtocol?) { + userIndicatorController: UserIndicatorControllerProtocol) { self.roomProxy = roomProxy self.userIndicatorController = userIndicatorController @@ -75,12 +75,12 @@ class RoomDetailsEditScreenViewModel: RoomDetailsEditScreenViewModelType, RoomDe Task { let userIndicatorID = UUID().uuidString defer { - userIndicatorController?.retractIndicatorWithId(userIndicatorID) + userIndicatorController.retractIndicatorWithId(userIndicatorID) } - userIndicatorController?.submitIndicator(UserIndicator(id: userIndicatorID, - type: .modal(progress: .indeterminate, interactiveDismissDisabled: true, allowsInteraction: false), - title: L10n.commonLoading, - persistent: true)) + userIndicatorController.submitIndicator(UserIndicator(id: userIndicatorID, + type: .modal(progress: .indeterminate, interactiveDismissDisabled: true, allowsInteraction: false), + title: L10n.commonLoading, + persistent: true)) let mediaResult = await mediaPreprocessor.processMedia(at: url) @@ -88,7 +88,7 @@ class RoomDetailsEditScreenViewModel: RoomDetailsEditScreenViewModelType, RoomDe case .success(.image): state.localMedia = try? mediaResult.get() case .failure, .success: - userIndicatorController?.alertInfo = .init(id: .init()) + userIndicatorController.alertInfo = .init(id: .init()) } } } @@ -99,12 +99,12 @@ class RoomDetailsEditScreenViewModel: RoomDetailsEditScreenViewModelType, RoomDe Task { let userIndicatorID = UUID().uuidString defer { - userIndicatorController?.retractIndicatorWithId(userIndicatorID) + userIndicatorController.retractIndicatorWithId(userIndicatorID) } - userIndicatorController?.submitIndicator(UserIndicator(id: userIndicatorID, - type: .modal(progress: .indeterminate, interactiveDismissDisabled: true, allowsInteraction: false), - title: L10n.screenRoomDetailsUpdatingRoom, - persistent: true)) + userIndicatorController.submitIndicator(UserIndicator(id: userIndicatorID, + type: .modal(progress: .indeterminate, interactiveDismissDisabled: true, allowsInteraction: false), + title: L10n.screenRoomDetailsUpdatingRoom, + persistent: true)) do { try await withThrowingTaskGroup(of: Void.self) { group in @@ -135,9 +135,9 @@ class RoomDetailsEditScreenViewModel: RoomDetailsEditScreenViewModelType, RoomDe actionsSubject.send(.saveFinished) } catch { - userIndicatorController?.alertInfo = .init(id: .init(), - title: L10n.screenRoomDetailsEditionErrorTitle, - message: L10n.screenRoomDetailsEditionError) + userIndicatorController.alertInfo = .init(id: .init(), + title: L10n.screenRoomDetailsEditionErrorTitle, + message: L10n.screenRoomDetailsEditionError) } } } diff --git a/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenCoordinator.swift b/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenCoordinator.swift index 6522c4c7f..bffefa127 100644 --- a/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenCoordinator.swift +++ b/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenCoordinator.swift @@ -112,11 +112,11 @@ final class RoomDetailsScreenCoordinator: CoordinatorProtocol { private func presentInviteUsersScreen() { let inviteUsersStackCoordinator = NavigationStackCoordinator() - let userIndicatorController = UserIndicatorController(rootCoordinator: inviteUsersStackCoordinator) let inviteParameters = InviteUsersScreenCoordinatorParameters(selectedUsers: .init(selectedUsers), roomType: .room(roomProxy: parameters.roomProxy), mediaProvider: parameters.mediaProvider, - userDiscoveryService: parameters.userDiscoveryService, userIndicatorController: userIndicatorController) + userDiscoveryService: parameters.userDiscoveryService, + userIndicatorController: parameters.userIndicatorController) let coordinator = InviteUsersScreenCoordinator(parameters: inviteParameters) inviteUsersStackCoordinator.setRootCoordinator(coordinator) @@ -137,20 +137,19 @@ final class RoomDetailsScreenCoordinator: CoordinatorProtocol { } .store(in: &cancellables) - navigationStackCoordinator?.setSheetCoordinator(userIndicatorController) { [weak self] in + navigationStackCoordinator?.setSheetCoordinator(inviteUsersStackCoordinator) { [weak self] in self?.selectedUsers.value = [] } } private func presentRoomDetailsEditScreen(accountOwner: RoomMemberProxyProtocol) { let navigationStackCoordinator = NavigationStackCoordinator() - let userIndicatorController = UserIndicatorController(rootCoordinator: navigationStackCoordinator) let roomDetailsEditParameters = RoomDetailsEditScreenCoordinatorParameters(accountOwner: accountOwner, mediaProvider: parameters.mediaProvider, navigationStackCoordinator: navigationStackCoordinator, roomProxy: parameters.roomProxy, - userIndicatorController: userIndicatorController) + userIndicatorController: parameters.userIndicatorController) let roomDetailsEditCoordinator = RoomDetailsEditScreenCoordinator(parameters: roomDetailsEditParameters) roomDetailsEditCoordinator.actions.sink { [weak self] action in @@ -163,7 +162,7 @@ final class RoomDetailsScreenCoordinator: CoordinatorProtocol { navigationStackCoordinator.setRootCoordinator(roomDetailsEditCoordinator) - self.navigationStackCoordinator?.setSheetCoordinator(userIndicatorController) + self.navigationStackCoordinator?.setSheetCoordinator(navigationStackCoordinator) } private func toggleUser(_ user: UserProfileProxy) { diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupKeyBackupScreen/SecureBackupKeyBackupScreenCoordinator.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupKeyBackupScreen/SecureBackupKeyBackupScreenCoordinator.swift index 671cecda0..87fc6e6a5 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupKeyBackupScreen/SecureBackupKeyBackupScreenCoordinator.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupKeyBackupScreen/SecureBackupKeyBackupScreenCoordinator.swift @@ -19,7 +19,7 @@ import SwiftUI struct SecureBackupKeyBackupScreenCoordinatorParameters { let secureBackupController: SecureBackupControllerProtocol - weak var userIndicatorController: UserIndicatorControllerProtocol? + let userIndicatorController: UserIndicatorControllerProtocol } enum SecureBackupKeyBackupScreenCoordinatorAction { diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenCoordinator.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenCoordinator.swift index 686fffc75..a083ef719 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenCoordinator.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenCoordinator.swift @@ -19,7 +19,7 @@ import SwiftUI struct SecureBackupRecoveryKeyScreenCoordinatorParameters { let secureBackupController: SecureBackupControllerProtocol - weak var userIndicatorController: UserIndicatorControllerProtocol? + let userIndicatorController: UserIndicatorControllerProtocol } enum SecureBackupRecoveryKeyScreenCoordinatorAction { diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenViewModel.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenViewModel.swift index c2b666ed1..a90f2a8d7 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenViewModel.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/SecureBackupRecoveryKeyScreenViewModel.swift @@ -21,14 +21,14 @@ typealias SecureBackupRecoveryKeyScreenViewModelType = StateStoreViewModel = .init() var actions: AnyPublisher { actionsSubject.eraseToAnyPublisher() } - init(secureBackupController: SecureBackupControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol?) { + init(secureBackupController: SecureBackupControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol) { self.secureBackupController = secureBackupController self.userIndicatorController = userIndicatorController @@ -66,7 +66,7 @@ class SecureBackupRecoveryKeyScreenViewModel: SecureBackupRecoveryKeyScreenViewM } case .copyKey: UIPasteboard.general.string = state.recoveryKey - userIndicatorController?.submitIndicator(.init(title: "Copied recovery key")) + userIndicatorController.submitIndicator(.init(title: "Copied recovery key")) state.doneButtonEnabled = true case .keySaved: state.doneButtonEnabled = true diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/View/SecureBackupRecoveryKeyScreen.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/View/SecureBackupRecoveryKeyScreen.swift index 6e80bc996..ed933a02a 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/View/SecureBackupRecoveryKeyScreen.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupRecoveryKeyScreen/View/SecureBackupRecoveryKeyScreen.swift @@ -228,6 +228,6 @@ struct SecureBackupRecoveryKeyScreen_Previews: PreviewProvider, TestablePreview let backupController = SecureBackupControllerMock() backupController.underlyingRecoveryKeyState = CurrentValueSubject(recoveryKeyState).asCurrentValuePublisher() - return SecureBackupRecoveryKeyScreenViewModel(secureBackupController: backupController, userIndicatorController: nil) + return SecureBackupRecoveryKeyScreenViewModel(secureBackupController: backupController, userIndicatorController: UserIndicatorControllerMock()) } } diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenCoordinator.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenCoordinator.swift index fc01eef6d..8683f4436 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenCoordinator.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenCoordinator.swift @@ -21,7 +21,7 @@ struct SecureBackupScreenCoordinatorParameters { let appSettings: AppSettings let secureBackupController: SecureBackupControllerProtocol weak var navigationStackCoordinator: NavigationStackCoordinator? - weak var userIndicatorController: UserIndicatorControllerProtocol? + let userIndicatorController: UserIndicatorControllerProtocol } enum SecureBackupScreenCoordinatorAction { } @@ -51,10 +51,9 @@ final class SecureBackupScreenCoordinator: CoordinatorProtocol { switch action { case .recoveryKey: let navigationStackCoordinator = NavigationStackCoordinator() - let userIndicatorController = UserIndicatorController(rootCoordinator: navigationStackCoordinator) let recoveryKeyCoordinator = SecureBackupRecoveryKeyScreenCoordinator(parameters: .init(secureBackupController: parameters.secureBackupController, - userIndicatorController: userIndicatorController)) + userIndicatorController: parameters.userIndicatorController)) recoveryKeyCoordinator.actions.sink { [weak self] action in guard let self else { return } @@ -76,13 +75,12 @@ final class SecureBackupScreenCoordinator: CoordinatorProtocol { navigationStackCoordinator.setRootCoordinator(recoveryKeyCoordinator, animated: true) - parameters.navigationStackCoordinator?.setSheetCoordinator(userIndicatorController) + parameters.navigationStackCoordinator?.setSheetCoordinator(navigationStackCoordinator) case .keyBackup: let navigationStackCoordinator = NavigationStackCoordinator() - let userIndicatorController = UserIndicatorController(rootCoordinator: navigationStackCoordinator) let keyBackupCoordinator = SecureBackupKeyBackupScreenCoordinator(parameters: .init(secureBackupController: parameters.secureBackupController, - userIndicatorController: userIndicatorController)) + userIndicatorController: parameters.userIndicatorController)) keyBackupCoordinator.actions.sink { [weak self] action in switch action { @@ -94,7 +92,7 @@ final class SecureBackupScreenCoordinator: CoordinatorProtocol { navigationStackCoordinator.setRootCoordinator(keyBackupCoordinator, animated: true) - parameters.navigationStackCoordinator?.setSheetCoordinator(userIndicatorController) + parameters.navigationStackCoordinator?.setSheetCoordinator(navigationStackCoordinator) } } .store(in: &cancellables) @@ -107,10 +105,10 @@ final class SecureBackupScreenCoordinator: CoordinatorProtocol { // MARK: - Private private func showSuccessIndicator(title: String) { - parameters.userIndicatorController?.submitIndicator(.init(id: .init(), - type: .modal(progress: .none, interactiveDismissDisabled: false, allowsInteraction: false), - title: title, - iconName: "checkmark", - persistent: false)) + parameters.userIndicatorController.submitIndicator(.init(id: .init(), + type: .modal(progress: .none, interactiveDismissDisabled: false, allowsInteraction: false), + title: title, + iconName: "checkmark", + persistent: false)) } } diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenViewModel.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenViewModel.swift index d9b4e9827..1f6e425dc 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenViewModel.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/SecureBackupScreenViewModel.swift @@ -21,7 +21,7 @@ typealias SecureBackupScreenViewModelType = StateStoreViewModel = .init() var actions: AnyPublisher { @@ -29,7 +29,7 @@ class SecureBackupScreenViewModel: SecureBackupScreenViewModelType, SecureBackup } init(secureBackupController: SecureBackupControllerProtocol, - userIndicatorController: UserIndicatorControllerProtocol?, + userIndicatorController: UserIndicatorControllerProtocol, chatBackupDetailsURL: URL) { self.secureBackupController = secureBackupController self.userIndicatorController = userIndicatorController @@ -70,7 +70,7 @@ class SecureBackupScreenViewModel: SecureBackupScreenViewModelType, SecureBackup private func enableBackup() { Task { let loadingIndicatorIdentifier = "SecureBackupScreenLoading" - userIndicatorController?.submitIndicator(.init(id: loadingIndicatorIdentifier, type: .modal, title: L10n.commonLoading, persistent: true)) + userIndicatorController.submitIndicator(.init(id: loadingIndicatorIdentifier, type: .modal, title: L10n.commonLoading, persistent: true)) switch await secureBackupController.enable() { case .success: break @@ -79,7 +79,7 @@ class SecureBackupScreenViewModel: SecureBackupScreenViewModelType, SecureBackup state.bindings.alertInfo = .init(id: .init()) } - userIndicatorController?.retractIndicatorWithId(loadingIndicatorIdentifier) + userIndicatorController.retractIndicatorWithId(loadingIndicatorIdentifier) } } } diff --git a/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/View/SecureBackupScreen.swift b/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/View/SecureBackupScreen.swift index f7ecea6eb..2c9bb30da 100644 --- a/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/View/SecureBackupScreen.swift +++ b/ElementX/Sources/Screens/SecureBackup/SecureBackupScreen/View/SecureBackupScreen.swift @@ -160,7 +160,7 @@ struct SecureBackupScreen_Previews: PreviewProvider, TestablePreview { backupController.underlyingRecoveryKeyState = CurrentValueSubject(recoveryKeyState).asCurrentValuePublisher() return SecureBackupScreenViewModel(secureBackupController: backupController, - userIndicatorController: nil, + userIndicatorController: UserIndicatorControllerMock(), chatBackupDetailsURL: .sharedPublicDirectory) } } diff --git a/ElementX/Sources/Screens/Settings/SettingsScreen/SettingsScreenCoordinator.swift b/ElementX/Sources/Screens/Settings/SettingsScreen/SettingsScreenCoordinator.swift index b928e3663..4111a319e 100644 --- a/ElementX/Sources/Screens/Settings/SettingsScreen/SettingsScreenCoordinator.swift +++ b/ElementX/Sources/Screens/Settings/SettingsScreen/SettingsScreenCoordinator.swift @@ -19,7 +19,7 @@ import SwiftUI struct SettingsScreenCoordinatorParameters { weak var navigationStackCoordinator: NavigationStackCoordinator? - weak var userIndicatorController: UserIndicatorControllerProtocol? + let userIndicatorController: UserIndicatorControllerProtocol let userSession: UserSessionProtocol let appLockService: AppLockServiceProtocol let bugReportService: BugReportServiceProtocol @@ -258,6 +258,6 @@ final class SettingsScreenCoordinator: CoordinatorProtocol { } private func showSuccess(label: String) { - parameters.userIndicatorController?.submitIndicator(UserIndicator(title: label)) + parameters.userIndicatorController.submitIndicator(UserIndicator(title: label)) } } diff --git a/ElementX/Sources/Screens/Settings/UserDetailsEditScreen/UserDetailsEditScreenCoordinator.swift b/ElementX/Sources/Screens/Settings/UserDetailsEditScreen/UserDetailsEditScreenCoordinator.swift index 041be04d0..62d9c112b 100644 --- a/ElementX/Sources/Screens/Settings/UserDetailsEditScreen/UserDetailsEditScreenCoordinator.swift +++ b/ElementX/Sources/Screens/Settings/UserDetailsEditScreen/UserDetailsEditScreenCoordinator.swift @@ -21,7 +21,7 @@ struct UserDetailsEditScreenCoordinatorParameters { let clientProxy: ClientProxyProtocol let mediaProvider: MediaProviderProtocol weak var navigationStackCoordinator: NavigationStackCoordinator? - weak var userIndicatorController: UserIndicatorControllerProtocol? + let userIndicatorController: UserIndicatorControllerProtocol } enum UserDetailsEditScreenCoordinatorAction { } @@ -67,9 +67,8 @@ final class UserDetailsEditScreenCoordinator: CoordinatorProtocol { private func displayMediaPickerWithSource(_ source: MediaPickerScreenSource) { let stackCoordinator = NavigationStackCoordinator() - let userIndicatorController = UserIndicatorController(rootCoordinator: stackCoordinator) - let mediaPickerCoordinator = MediaPickerScreenCoordinator(userIndicatorController: userIndicatorController, source: source) { [weak self] action in + let mediaPickerCoordinator = MediaPickerScreenCoordinator(userIndicatorController: parameters.userIndicatorController, source: source) { [weak self] action in guard let self else { return } switch action { case .cancel: @@ -81,6 +80,6 @@ final class UserDetailsEditScreenCoordinator: CoordinatorProtocol { } stackCoordinator.setRootCoordinator(mediaPickerCoordinator) - parameters.navigationStackCoordinator?.setSheetCoordinator(userIndicatorController) + parameters.navigationStackCoordinator?.setSheetCoordinator(stackCoordinator) } } diff --git a/ElementX/Sources/Screens/Settings/UserDetailsEditScreen/UserDetailsEditScreenViewModel.swift b/ElementX/Sources/Screens/Settings/UserDetailsEditScreen/UserDetailsEditScreenViewModel.swift index d45a9fa75..7d0f1ded7 100644 --- a/ElementX/Sources/Screens/Settings/UserDetailsEditScreen/UserDetailsEditScreenViewModel.swift +++ b/ElementX/Sources/Screens/Settings/UserDetailsEditScreen/UserDetailsEditScreenViewModel.swift @@ -22,7 +22,7 @@ typealias UserDetailsEditScreenViewModelType = StateStoreViewModel = .init() private let clientProxy: ClientProxyProtocol - private weak var userIndicatorController: UserIndicatorControllerProtocol? + private let userIndicatorController: UserIndicatorControllerProtocol private let mediaPreprocessor: MediaUploadingPreprocessor = .init() var actions: AnyPublisher { @@ -31,7 +31,7 @@ class UserDetailsEditScreenViewModel: UserDetailsEditScreenViewModelType, UserDe init(clientProxy: ClientProxyProtocol, mediaProvider: MediaProviderProtocol, - userIndicatorController: UserIndicatorControllerProtocol?) { + userIndicatorController: UserIndicatorControllerProtocol) { self.clientProxy = clientProxy self.userIndicatorController = userIndicatorController @@ -90,12 +90,12 @@ class UserDetailsEditScreenViewModel: UserDetailsEditScreenViewModelType, UserDe Task { let userIndicatorID = UUID().uuidString defer { - userIndicatorController?.retractIndicatorWithId(userIndicatorID) + userIndicatorController.retractIndicatorWithId(userIndicatorID) } - userIndicatorController?.submitIndicator(UserIndicator(id: userIndicatorID, - type: .modal(progress: .indeterminate, interactiveDismissDisabled: true, allowsInteraction: false), - title: L10n.commonLoading, - persistent: true)) + userIndicatorController.submitIndicator(UserIndicator(id: userIndicatorID, + type: .modal(progress: .indeterminate, interactiveDismissDisabled: true, allowsInteraction: false), + title: L10n.commonLoading, + persistent: true)) let mediaResult = await mediaPreprocessor.processMedia(at: url) @@ -103,7 +103,7 @@ class UserDetailsEditScreenViewModel: UserDetailsEditScreenViewModelType, UserDe case .success(.image): state.localMedia = try? mediaResult.get() case .failure, .success: - userIndicatorController?.alertInfo = .init(id: .init(), title: L10n.commonError, message: L10n.errorUnknown) + userIndicatorController.alertInfo = .init(id: .init(), title: L10n.commonError, message: L10n.errorUnknown) } } } @@ -114,12 +114,12 @@ class UserDetailsEditScreenViewModel: UserDetailsEditScreenViewModelType, UserDe Task { let userIndicatorID = UUID().uuidString defer { - userIndicatorController?.retractIndicatorWithId(userIndicatorID) + userIndicatorController.retractIndicatorWithId(userIndicatorID) } - userIndicatorController?.submitIndicator(UserIndicator(id: userIndicatorID, - type: .modal(progress: .indeterminate, interactiveDismissDisabled: true, allowsInteraction: false), - title: L10n.screenEditProfileUpdatingDetails, - persistent: true)) + userIndicatorController.submitIndicator(UserIndicator(id: userIndicatorID, + type: .modal(progress: .indeterminate, interactiveDismissDisabled: true, allowsInteraction: false), + title: L10n.screenEditProfileUpdatingDetails, + persistent: true)) do { try await withThrowingTaskGroup(of: Void.self) { group in @@ -142,9 +142,9 @@ class UserDetailsEditScreenViewModel: UserDetailsEditScreenViewModelType, UserDe try await group.waitForAll() } } catch { - userIndicatorController?.alertInfo = .init(id: .init(), - title: L10n.screenEditProfileErrorTitle, - message: L10n.screenEditProfileError) + userIndicatorController.alertInfo = .init(id: .init(), + title: L10n.screenEditProfileErrorTitle, + message: L10n.screenEditProfileError) } } } diff --git a/ElementX/Sources/Screens/StartChatScreen/StartChatScreenCoordinator.swift b/ElementX/Sources/Screens/StartChatScreen/StartChatScreenCoordinator.swift index 0d790b992..88be1dc75 100644 --- a/ElementX/Sources/Screens/StartChatScreen/StartChatScreenCoordinator.swift +++ b/ElementX/Sources/Screens/StartChatScreen/StartChatScreenCoordinator.swift @@ -19,7 +19,7 @@ import SwiftUI struct StartChatScreenCoordinatorParameters { let userSession: UserSessionProtocol - weak var userIndicatorController: UserIndicatorControllerProtocol? + let userIndicatorController: UserIndicatorControllerProtocol weak var navigationStackCoordinator: NavigationStackCoordinator? let userDiscoveryService: UserDiscoveryServiceProtocol } @@ -44,15 +44,7 @@ final class StartChatScreenCoordinator: CoordinatorProtocol { private var selectedUsersPublisher: CurrentValuePublisher<[UserProfileProxy], Never> { selectedUsers.asCurrentValuePublisher() } - - private var navigationStackCoordinator: NavigationStackCoordinator? { - parameters.navigationStackCoordinator - } - - private var userIndicatorController: UserIndicatorControllerProtocol? { - parameters.userIndicatorController - } - + var actions: AnyPublisher { actionsSubject.eraseToAnyPublisher() } @@ -95,7 +87,8 @@ final class StartChatScreenCoordinator: CoordinatorProtocol { let inviteParameters = InviteUsersScreenCoordinatorParameters(selectedUsers: selectedUsersPublisher, roomType: .draft, mediaProvider: parameters.userSession.mediaProvider, - userDiscoveryService: parameters.userDiscoveryService) + userDiscoveryService: parameters.userDiscoveryService, + userIndicatorController: parameters.userIndicatorController) let coordinator = InviteUsersScreenCoordinator(parameters: inviteParameters) coordinator.actions.sink { [weak self] action in guard let self else { return } @@ -113,7 +106,7 @@ final class StartChatScreenCoordinator: CoordinatorProtocol { } .store(in: &cancellables) - navigationStackCoordinator?.push(coordinator) { [weak self] in + parameters.navigationStackCoordinator?.push(coordinator) { [weak self] in self?.createRoomParameters.send(.init()) self?.selectedUsers.send([]) } @@ -121,7 +114,7 @@ final class StartChatScreenCoordinator: CoordinatorProtocol { private func openCreateRoomScreen() { let createParameters = CreateRoomCoordinatorParameters(userSession: parameters.userSession, - userIndicatorController: userIndicatorController, + userIndicatorController: parameters.userIndicatorController, createRoomParameters: createRoomParametersPublisher, selectedUsers: selectedUsersPublisher) let coordinator = CreateRoomCoordinator(parameters: createParameters) @@ -144,7 +137,7 @@ final class StartChatScreenCoordinator: CoordinatorProtocol { } .store(in: &cancellables) - navigationStackCoordinator?.push(coordinator) + parameters.navigationStackCoordinator?.push(coordinator) } // MARK: - Private @@ -152,13 +145,12 @@ final class StartChatScreenCoordinator: CoordinatorProtocol { let mediaUploadingPreprocessor = MediaUploadingPreprocessor() private func displayMediaPickerWithSource(_ source: MediaPickerScreenSource) { let stackCoordinator = NavigationStackCoordinator() - let userIndicatorController = UserIndicatorController(rootCoordinator: stackCoordinator) - let mediaPickerCoordinator = MediaPickerScreenCoordinator(userIndicatorController: userIndicatorController, source: source) { [weak self] action in + let mediaPickerCoordinator = MediaPickerScreenCoordinator(userIndicatorController: parameters.userIndicatorController, source: source) { [weak self] action in guard let self else { return } switch action { case .cancel: - navigationStackCoordinator?.setSheetCoordinator(nil) + parameters.navigationStackCoordinator?.setSheetCoordinator(nil) case .selectMediaAtURL(let url): processAvatar(from: url) } @@ -166,11 +158,11 @@ final class StartChatScreenCoordinator: CoordinatorProtocol { stackCoordinator.setRootCoordinator(mediaPickerCoordinator) - navigationStackCoordinator?.setSheetCoordinator(userIndicatorController) + parameters.navigationStackCoordinator?.setSheetCoordinator(stackCoordinator) } private func processAvatar(from url: URL) { - navigationStackCoordinator?.setSheetCoordinator(nil) + parameters.navigationStackCoordinator?.setSheetCoordinator(nil) showLoadingIndicator() Task { [weak self] in guard let self else { return } @@ -180,7 +172,7 @@ final class StartChatScreenCoordinator: CoordinatorProtocol { parameters.avatarImageMedia = media createRoomParameters.send(parameters) } catch { - userIndicatorController?.alertInfo = AlertInfo(id: .init(), title: L10n.commonError, message: L10n.errorUnknown) + parameters.userIndicatorController.alertInfo = AlertInfo(id: .init(), title: L10n.commonError, message: L10n.errorUnknown) } hideLoadingIndicator() } @@ -201,13 +193,13 @@ final class StartChatScreenCoordinator: CoordinatorProtocol { private static let loadingIndicatorIdentifier = "StartChatCoordinatorLoading" private func showLoadingIndicator() { - userIndicatorController?.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier, - type: .modal, - title: L10n.commonLoading, - persistent: true)) + parameters.userIndicatorController.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier, + type: .modal, + title: L10n.commonLoading, + persistent: true)) } private func hideLoadingIndicator() { - userIndicatorController?.retractIndicatorWithId(Self.loadingIndicatorIdentifier) + parameters.userIndicatorController.retractIndicatorWithId(Self.loadingIndicatorIdentifier) } } diff --git a/ElementX/Sources/Screens/StartChatScreen/StartChatScreenViewModel.swift b/ElementX/Sources/Screens/StartChatScreen/StartChatScreenViewModel.swift index 06ece16e6..be962ef2e 100644 --- a/ElementX/Sources/Screens/StartChatScreen/StartChatScreenViewModel.swift +++ b/ElementX/Sources/Screens/StartChatScreen/StartChatScreenViewModel.swift @@ -23,7 +23,7 @@ class StartChatScreenViewModel: StartChatScreenViewModelType, StartChatScreenVie private let userSession: UserSessionProtocol private let appSettings: AppSettings private let analytics: AnalyticsService - private weak var userIndicatorController: UserIndicatorControllerProtocol? + private let userIndicatorController: UserIndicatorControllerProtocol private let userDiscoveryService: UserDiscoveryServiceProtocol private let actionsSubject: PassthroughSubject = .init() @@ -35,7 +35,7 @@ class StartChatScreenViewModel: StartChatScreenViewModelType, StartChatScreenVie init(userSession: UserSessionProtocol, appSettings: AppSettings, analytics: AnalyticsService, - userIndicatorController: UserIndicatorControllerProtocol?, + userIndicatorController: UserIndicatorControllerProtocol, userDiscoveryService: UserDiscoveryServiceProtocol) { self.userSession = userSession self.appSettings = appSettings @@ -162,13 +162,13 @@ class StartChatScreenViewModel: StartChatScreenViewModelType, StartChatScreenVie private static let loadingIndicatorIdentifier = "StartChatLoading" private func showLoadingIndicator() { - userIndicatorController?.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier, - type: .modal(progress: .indeterminate, interactiveDismissDisabled: true, allowsInteraction: false), - title: L10n.commonLoading, - persistent: true)) + userIndicatorController.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier, + type: .modal(progress: .indeterminate, interactiveDismissDisabled: true, allowsInteraction: false), + title: L10n.commonLoading, + persistent: true)) } private func hideLoadingIndicator() { - userIndicatorController?.retractIndicatorWithId(Self.loadingIndicatorIdentifier) + userIndicatorController.retractIndicatorWithId(Self.loadingIndicatorIdentifier) } } diff --git a/ElementX/Sources/Screens/StartChatScreen/View/StartChatScreen.swift b/ElementX/Sources/Screens/StartChatScreen/View/StartChatScreen.swift index e7bf0a153..e0037cdc4 100644 --- a/ElementX/Sources/Screens/StartChatScreen/View/StartChatScreen.swift +++ b/ElementX/Sources/Screens/StartChatScreen/View/StartChatScreen.swift @@ -145,7 +145,7 @@ struct StartChatScreen_Previews: PreviewProvider, TestablePreview { let viewModel = StartChatScreenViewModel(userSession: userSession, appSettings: ServiceLocator.shared.settings, analytics: ServiceLocator.shared.analytics, - userIndicatorController: nil, + userIndicatorController: UserIndicatorControllerMock(), userDiscoveryService: userDiscoveryService) return viewModel }() diff --git a/ElementX/Sources/UITests/UITestsAppCoordinator.swift b/ElementX/Sources/UITests/UITestsAppCoordinator.swift index acda9857b..28164150c 100644 --- a/ElementX/Sources/UITests/UITestsAppCoordinator.swift +++ b/ElementX/Sources/UITests/UITestsAppCoordinator.swift @@ -210,7 +210,7 @@ class MockScreen: Identifiable { let navigationStackCoordinator = NavigationStackCoordinator() let clientProxy = MockClientProxy(userID: "@mock:client.com") let coordinator = SettingsScreenCoordinator(parameters: .init(navigationStackCoordinator: navigationStackCoordinator, - userIndicatorController: nil, + userIndicatorController: UserIndicatorControllerMock(), userSession: MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider(), voiceMessageMediaManager: VoiceMessageMediaManagerMock()), @@ -674,7 +674,8 @@ class MockScreen: Identifiable { let navigationStackCoordinator = NavigationStackCoordinator() let coordinator = ReportContentScreenCoordinator(parameters: .init(eventID: "test", senderID: RoomMemberProxyMock.mockAlice.userID, - roomProxy: RoomProxyMock(with: .init(displayName: "test")))) + roomProxy: RoomProxyMock(with: .init(displayName: "test")), + userIndicatorController: UserIndicatorControllerMock())) navigationStackCoordinator.setRootCoordinator(coordinator) return navigationStackCoordinator case .startChat: @@ -684,7 +685,10 @@ class MockScreen: Identifiable { userDiscoveryMock.fetchSuggestionsReturnValue = .success([.mockAlice, .mockBob, .mockCharlie]) userDiscoveryMock.searchProfilesWithReturnValue = .success([]) let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@mock:client.com"), mediaProvider: MockMediaProvider(), voiceMessageMediaManager: VoiceMessageMediaManagerMock()) - let parameters: StartChatScreenCoordinatorParameters = .init(userSession: userSession, navigationStackCoordinator: navigationStackCoordinator, userDiscoveryService: userDiscoveryMock) + let parameters: StartChatScreenCoordinatorParameters = .init(userSession: userSession, + userIndicatorController: UserIndicatorControllerMock(), + navigationStackCoordinator: navigationStackCoordinator, + userDiscoveryService: userDiscoveryMock) let coordinator = StartChatScreenCoordinator(parameters: parameters) navigationStackCoordinator.setRootCoordinator(coordinator) return navigationStackCoordinator @@ -695,7 +699,10 @@ class MockScreen: Identifiable { userDiscoveryMock.fetchSuggestionsReturnValue = .success([]) userDiscoveryMock.searchProfilesWithReturnValue = .success([.mockBob, .mockBobby]) let userSession = MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider(), voiceMessageMediaManager: VoiceMessageMediaManagerMock()) - let coordinator = StartChatScreenCoordinator(parameters: .init(userSession: userSession, navigationStackCoordinator: navigationStackCoordinator, userDiscoveryService: userDiscoveryMock)) + let coordinator = StartChatScreenCoordinator(parameters: .init(userSession: userSession, + userIndicatorController: UserIndicatorControllerMock(), + navigationStackCoordinator: navigationStackCoordinator, + userDiscoveryService: userDiscoveryMock)) navigationStackCoordinator.setRootCoordinator(coordinator) return navigationStackCoordinator case .roomMemberDetailsAccountOwner: @@ -763,7 +770,11 @@ class MockScreen: Identifiable { let members: [RoomMemberProxyMock] = id == .inviteUsersInRoomExistingMembers ? [.mockInvitedAlice, .mockBob] : [] let roomProxy = RoomProxyMock(with: .init(displayName: "test", members: members)) let roomType: InviteUsersScreenRoomType = id == .inviteUsers ? .draft : .room(roomProxy: roomProxy) - let coordinator = InviteUsersScreenCoordinator(parameters: .init(selectedUsers: usersSubject.asCurrentValuePublisher(), roomType: roomType, mediaProvider: mediaProvider, userDiscoveryService: userDiscoveryMock)) + let coordinator = InviteUsersScreenCoordinator(parameters: .init(selectedUsers: usersSubject.asCurrentValuePublisher(), + roomType: roomType, + mediaProvider: mediaProvider, + userDiscoveryService: userDiscoveryMock, + userIndicatorController: UserIndicatorControllerMock())) coordinator.actions.sink { action in switch action { case .toggleUser(let user): @@ -787,16 +798,24 @@ class MockScreen: Identifiable { let mockUserSession = MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider(), voiceMessageMediaManager: VoiceMessageMediaManagerMock()) let createRoomParameters = CreateRoomFlowParameters() let selectedUsers: [UserProfileProxy] = [.mockAlice, .mockBob, .mockCharlie] - let parameters = CreateRoomCoordinatorParameters(userSession: mockUserSession, userIndicatorController: nil, createRoomParameters: .init(createRoomParameters), selectedUsers: .init(selectedUsers)) + let parameters = CreateRoomCoordinatorParameters(userSession: mockUserSession, + userIndicatorController: UserIndicatorControllerMock(), + createRoomParameters: .init(createRoomParameters), + selectedUsers: .init(selectedUsers)) let coordinator = CreateRoomCoordinator(parameters: parameters) navigationStackCoordinator.setRootCoordinator(coordinator) return navigationStackCoordinator case .createRoomNoUsers: let navigationStackCoordinator = NavigationStackCoordinator() let clientProxy = MockClientProxy(userID: "@mock:client.com") - let mockUserSession = MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider(), voiceMessageMediaManager: VoiceMessageMediaManagerMock()) + let mockUserSession = MockUserSession(clientProxy: clientProxy, + mediaProvider: MockMediaProvider(), + voiceMessageMediaManager: VoiceMessageMediaManagerMock()) let createRoomParameters = CreateRoomFlowParameters() - let parameters = CreateRoomCoordinatorParameters(userSession: mockUserSession, userIndicatorController: nil, createRoomParameters: .init(createRoomParameters), selectedUsers: .init([])) + let parameters = CreateRoomCoordinatorParameters(userSession: mockUserSession, + userIndicatorController: UserIndicatorControllerMock(), + createRoomParameters: .init(createRoomParameters), + selectedUsers: .init([])) let coordinator = CreateRoomCoordinator(parameters: parameters) navigationStackCoordinator.setRootCoordinator(coordinator) return navigationStackCoordinator diff --git a/UnitTests/Sources/CreateRoomViewModelTests.swift b/UnitTests/Sources/CreateRoomViewModelTests.swift index f0c6de76f..ffe442ded 100644 --- a/UnitTests/Sources/CreateRoomViewModelTests.swift +++ b/UnitTests/Sources/CreateRoomViewModelTests.swift @@ -44,7 +44,7 @@ class CreateRoomScreenViewModelTests: XCTestCase { createRoomParameters: .init(parameters), selectedUsers: usersSubject.asCurrentValuePublisher(), analytics: ServiceLocator.shared.analytics, - userIndicatorController: nil) + userIndicatorController: UserIndicatorControllerMock()) self.viewModel = viewModel viewModel.actions.sink { [weak self] action in diff --git a/UnitTests/Sources/StartChatViewModelTests.swift b/UnitTests/Sources/StartChatViewModelTests.swift index d2dff12fd..db71519f8 100644 --- a/UnitTests/Sources/StartChatViewModelTests.swift +++ b/UnitTests/Sources/StartChatViewModelTests.swift @@ -39,7 +39,7 @@ class StartChatScreenViewModelTests: XCTestCase { viewModel = StartChatScreenViewModel(userSession: userSession, appSettings: ServiceLocator.shared.settings, analytics: ServiceLocator.shared.analytics, - userIndicatorController: nil, + userIndicatorController: UserIndicatorControllerMock(), userDiscoveryService: userDiscoveryService) AppSettings.reset() diff --git a/UnitTests/Sources/UserIndicatorControllerTests.swift b/UnitTests/Sources/UserIndicatorControllerTests.swift index 7f2111eff..599a31d31 100644 --- a/UnitTests/Sources/UserIndicatorControllerTests.swift +++ b/UnitTests/Sources/UserIndicatorControllerTests.swift @@ -25,7 +25,7 @@ class UserIndicatorControllerTests: XCTestCase { private var indicatorController: UserIndicatorController! override func setUp() { - indicatorController = UserIndicatorController(rootCoordinator: PlaceholderScreenCoordinator()) + indicatorController = UserIndicatorController() } func testIndicatorQueueing() {