Have the UserIndicatorController use a separate window, switch to using one single instance throughout the application

This commit is contained in:
Stefan Ceriu 2023-11-09 15:10:50 +02:00 committed by Stefan Ceriu
parent 1102beb3dd
commit 4b26a798dc
44 changed files with 210 additions and 221 deletions

View File

@ -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,

View File

@ -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

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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<AnyCancellable>()
@ -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)
}

View File

@ -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)
}
}

View File

@ -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<UUID>?
//MARK: - submitIndicator

View File

@ -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<String>()
@ -42,14 +40,12 @@ class UserIndicatorController: ObservableObject, UserIndicatorControllerProtocol
@Published var alertInfo: AlertInfo<UUID>?
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) {

View File

@ -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<UUID>? { get set }
}

View File

@ -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

View File

@ -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)
}

View File

@ -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
}

View File

@ -19,7 +19,7 @@ import SwiftUI
struct CreateRoomCoordinatorParameters {
let userSession: UserSessionProtocol
weak var userIndicatorController: UserIndicatorControllerProtocol?
let userIndicatorController: UserIndicatorControllerProtocol
let createRoomParameters: CurrentValuePublisher<CreateRoomFlowParameters, Never>
let selectedUsers: CurrentValuePublisher<[UserProfileProxy], Never>
}

View File

@ -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<CreateRoomViewModelAction, Never> = .init()
@ -35,7 +35,7 @@ class CreateRoomViewModel: CreateRoomViewModelType, CreateRoomViewModelProtocol
createRoomParameters: CurrentValuePublisher<CreateRoomFlowParameters, Never>,
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)
}
}

View File

@ -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 {

View File

@ -22,7 +22,7 @@ struct InviteUsersScreenCoordinatorParameters {
let roomType: InviteUsersScreenRoomType
let mediaProvider: MediaProviderProtocol
let userDiscoveryService: UserDiscoveryServiceProtocol
weak var userIndicatorController: UserIndicatorControllerProtocol?
let userIndicatorController: UserIndicatorControllerProtocol
}
enum InviteUsersScreenCoordinatorAction {

View File

@ -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<InviteUsersScreenViewModelAction, Never> = .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)
}
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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))
}
}

View File

@ -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

View File

@ -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?

View File

@ -21,7 +21,7 @@ import SwiftUI
typealias MediaUploadPreviewScreenViewModelType = StateStoreViewModel<MediaUploadPreviewScreenViewState, MediaUploadPreviewScreenViewAction>
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<Double, Never>) {
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))
}
}

View File

@ -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))
}
}

View File

@ -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)
}
}

View File

@ -22,7 +22,7 @@ typealias RoomDetailsEditScreenViewModelType = StateStoreViewModel<RoomDetailsEd
class RoomDetailsEditScreenViewModel: RoomDetailsEditScreenViewModelType, RoomDetailsEditScreenViewModelProtocol {
private let actionsSubject: PassthroughSubject<RoomDetailsEditScreenViewModelAction, Never> = .init()
private let roomProxy: RoomProxyProtocol
private weak var userIndicatorController: UserIndicatorControllerProtocol?
private let userIndicatorController: UserIndicatorControllerProtocol
private let mediaPreprocessor: MediaUploadingPreprocessor = .init()
var actions: AnyPublisher<RoomDetailsEditScreenViewModelAction, Never> {
@ -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)
}
}
}

View File

@ -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) {

View File

@ -19,7 +19,7 @@ import SwiftUI
struct SecureBackupKeyBackupScreenCoordinatorParameters {
let secureBackupController: SecureBackupControllerProtocol
weak var userIndicatorController: UserIndicatorControllerProtocol?
let userIndicatorController: UserIndicatorControllerProtocol
}
enum SecureBackupKeyBackupScreenCoordinatorAction {

View File

@ -19,7 +19,7 @@ import SwiftUI
struct SecureBackupRecoveryKeyScreenCoordinatorParameters {
let secureBackupController: SecureBackupControllerProtocol
weak var userIndicatorController: UserIndicatorControllerProtocol?
let userIndicatorController: UserIndicatorControllerProtocol
}
enum SecureBackupRecoveryKeyScreenCoordinatorAction {

View File

@ -21,14 +21,14 @@ typealias SecureBackupRecoveryKeyScreenViewModelType = StateStoreViewModel<Secur
class SecureBackupRecoveryKeyScreenViewModel: SecureBackupRecoveryKeyScreenViewModelType, SecureBackupRecoveryKeyScreenViewModelProtocol {
private let secureBackupController: SecureBackupControllerProtocol
private weak var userIndicatorController: UserIndicatorControllerProtocol?
private let userIndicatorController: UserIndicatorControllerProtocol
private var actionsSubject: PassthroughSubject<SecureBackupRecoveryKeyScreenViewModelAction, Never> = .init()
var actions: AnyPublisher<SecureBackupRecoveryKeyScreenViewModelAction, Never> {
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

View File

@ -228,6 +228,6 @@ struct SecureBackupRecoveryKeyScreen_Previews: PreviewProvider, TestablePreview
let backupController = SecureBackupControllerMock()
backupController.underlyingRecoveryKeyState = CurrentValueSubject<SecureBackupRecoveryKeyState, Never>(recoveryKeyState).asCurrentValuePublisher()
return SecureBackupRecoveryKeyScreenViewModel(secureBackupController: backupController, userIndicatorController: nil)
return SecureBackupRecoveryKeyScreenViewModel(secureBackupController: backupController, userIndicatorController: UserIndicatorControllerMock())
}
}

View File

@ -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))
}
}

View File

@ -21,7 +21,7 @@ typealias SecureBackupScreenViewModelType = StateStoreViewModel<SecureBackupScre
class SecureBackupScreenViewModel: SecureBackupScreenViewModelType, SecureBackupScreenViewModelProtocol {
private let secureBackupController: SecureBackupControllerProtocol
private weak var userIndicatorController: UserIndicatorControllerProtocol?
private let userIndicatorController: UserIndicatorControllerProtocol
private var actionsSubject: PassthroughSubject<SecureBackupScreenViewModelAction, Never> = .init()
var actions: AnyPublisher<SecureBackupScreenViewModelAction, Never> {
@ -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)
}
}
}

View File

@ -160,7 +160,7 @@ struct SecureBackupScreen_Previews: PreviewProvider, TestablePreview {
backupController.underlyingRecoveryKeyState = CurrentValueSubject<SecureBackupRecoveryKeyState, Never>(recoveryKeyState).asCurrentValuePublisher()
return SecureBackupScreenViewModel(secureBackupController: backupController,
userIndicatorController: nil,
userIndicatorController: UserIndicatorControllerMock(),
chatBackupDetailsURL: .sharedPublicDirectory)
}
}

View File

@ -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))
}
}

View File

@ -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)
}
}

View File

@ -22,7 +22,7 @@ typealias UserDetailsEditScreenViewModelType = StateStoreViewModel<UserDetailsEd
class UserDetailsEditScreenViewModel: UserDetailsEditScreenViewModelType, UserDetailsEditScreenViewModelProtocol {
private let actionsSubject: PassthroughSubject<UserDetailsEditScreenViewModelAction, Never> = .init()
private let clientProxy: ClientProxyProtocol
private weak var userIndicatorController: UserIndicatorControllerProtocol?
private let userIndicatorController: UserIndicatorControllerProtocol
private let mediaPreprocessor: MediaUploadingPreprocessor = .init()
var actions: AnyPublisher<UserDetailsEditScreenViewModelAction, Never> {
@ -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)
}
}
}

View File

@ -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<StartChatScreenCoordinatorAction, Never> {
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)
}
}

View File

@ -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<StartChatScreenViewModelAction, Never> = .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)
}
}

View File

@ -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
}()

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -25,7 +25,7 @@ class UserIndicatorControllerTests: XCTestCase {
private var indicatorController: UserIndicatorController!
override func setUp() {
indicatorController = UserIndicatorController(rootCoordinator: PlaceholderScreenCoordinator())
indicatorController = UserIndicatorController()
}
func testIndicatorQueueing() {