Remove the global UserIndicatorController.alertInfo, replacing it with local alertInfo usage. (#5087)
This commit is contained in:
@@ -226,12 +226,12 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
||||
guard let confirmationParameters = url.confirmationParameters else {
|
||||
return false
|
||||
}
|
||||
ServiceLocator.shared.userIndicatorController.alertInfo = .init(id: .init(),
|
||||
title: L10n.dialogConfirmLinkTitle,
|
||||
message: L10n.dialogConfirmLinkMessage(confirmationParameters.displayString,
|
||||
confirmationParameters.internalURL.absoluteString),
|
||||
primaryButton: .init(title: L10n.actionCancel, role: .cancel, action: nil),
|
||||
secondaryButton: .init(title: L10n.actionContinue) { openURLAction(confirmationParameters.internalURL) })
|
||||
navigationRootCoordinator.alertInfo = .init(id: .init(),
|
||||
title: L10n.dialogConfirmLinkTitle,
|
||||
message: L10n.dialogConfirmLinkMessage(confirmationParameters.displayString,
|
||||
confirmationParameters.internalURL.absoluteString),
|
||||
primaryButton: .init(title: L10n.actionCancel, role: .cancel, action: nil),
|
||||
secondaryButton: .init(title: L10n.actionContinue) { openURLAction(confirmationParameters.internalURL) })
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,9 @@ import SwiftUI
|
||||
overlayModule?.coordinator
|
||||
}
|
||||
|
||||
/// The lowest-level `AlertInfo`, directly available to the root of the app.
|
||||
var alertInfo: AlertInfo<UUID>?
|
||||
|
||||
/// Sets or replaces the presented coordinator
|
||||
/// - Parameter coordinator: the coordinator to display
|
||||
func setRootCoordinator(_ coordinator: (any CoordinatorProtocol)?, animated: Bool = true, dismissalCallback: (() -> Void)? = nil) {
|
||||
@@ -159,6 +162,7 @@ private struct NavigationRootCoordinatorView: View {
|
||||
ZStack {
|
||||
rootCoordinator.rootModule?.coordinator?.toPresentable()
|
||||
}
|
||||
.alert(item: $rootCoordinator.alertInfo)
|
||||
.animation(.elementDefault, value: rootCoordinator.rootModule)
|
||||
.sheet(item: $rootCoordinator.sheetModule) { module in
|
||||
module.coordinator?.toPresentable()
|
||||
|
||||
@@ -485,7 +485,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
let secureBackupController = userSession.clientProxy.secureBackupController
|
||||
|
||||
guard case let .success(isLastDevice) = await userSession.clientProxy.isOnlyDeviceLeft() else {
|
||||
flowParameters.userIndicatorController.alertInfo = .init(id: .init())
|
||||
navigationRootCoordinator.alertInfo = .init(id: .init())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -495,26 +495,26 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
}
|
||||
|
||||
guard secureBackupController.recoveryState.value == .enabled else {
|
||||
flowParameters.userIndicatorController.alertInfo = .init(id: .init(),
|
||||
title: L10n.screenSignoutRecoveryDisabledTitle,
|
||||
message: L10n.screenSignoutRecoveryDisabledSubtitle,
|
||||
primaryButton: .init(title: L10n.screenSignoutConfirmationDialogSubmit, role: .destructive) { [weak self] in
|
||||
self?.actionsSubject.send(.logout)
|
||||
}, secondaryButton: .init(title: L10n.commonSettings, role: .cancel) { [weak self] in
|
||||
self?.chatsTabFlowCoordinator.handleAppRoute(.chatBackupSettings, animated: true)
|
||||
})
|
||||
navigationRootCoordinator.alertInfo = .init(id: .init(),
|
||||
title: L10n.screenSignoutRecoveryDisabledTitle,
|
||||
message: L10n.screenSignoutRecoveryDisabledSubtitle,
|
||||
primaryButton: .init(title: L10n.screenSignoutConfirmationDialogSubmit, role: .destructive) { [weak self] in
|
||||
self?.actionsSubject.send(.logout)
|
||||
}, secondaryButton: .init(title: L10n.commonSettings, role: .cancel) { [weak self] in
|
||||
self?.chatsTabFlowCoordinator.handleAppRoute(.chatBackupSettings, animated: true)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
guard secureBackupController.keyBackupState.value == .enabled else {
|
||||
flowParameters.userIndicatorController.alertInfo = .init(id: .init(),
|
||||
title: L10n.screenSignoutKeyBackupDisabledTitle,
|
||||
message: L10n.screenSignoutKeyBackupDisabledSubtitle,
|
||||
primaryButton: .init(title: L10n.screenSignoutConfirmationDialogSubmit, role: .destructive) { [weak self] in
|
||||
self?.actionsSubject.send(.logout)
|
||||
}, secondaryButton: .init(title: L10n.commonSettings, role: .cancel) { [weak self] in
|
||||
self?.chatsTabFlowCoordinator.handleAppRoute(.chatBackupSettings, animated: true)
|
||||
})
|
||||
navigationRootCoordinator.alertInfo = .init(id: .init(),
|
||||
title: L10n.screenSignoutKeyBackupDisabledTitle,
|
||||
message: L10n.screenSignoutKeyBackupDisabledSubtitle,
|
||||
primaryButton: .init(title: L10n.screenSignoutConfirmationDialogSubmit, role: .destructive) { [weak self] in
|
||||
self?.actionsSubject.send(.logout)
|
||||
}, secondaryButton: .init(title: L10n.commonSettings, role: .cancel) { [weak self] in
|
||||
self?.chatsTabFlowCoordinator.handleAppRoute(.chatBackupSettings, animated: true)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -522,12 +522,12 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
}
|
||||
|
||||
private func logout() {
|
||||
flowParameters.userIndicatorController.alertInfo = .init(id: .init(),
|
||||
title: L10n.screenSignoutConfirmationDialogTitle,
|
||||
message: L10n.screenSignoutConfirmationDialogContent,
|
||||
primaryButton: .init(title: L10n.screenSignoutConfirmationDialogSubmit, role: .destructive) { [weak self] in
|
||||
self?.actionsSubject.send(.logout)
|
||||
})
|
||||
navigationRootCoordinator.alertInfo = .init(id: .init(),
|
||||
title: L10n.screenSignoutConfirmationDialogTitle,
|
||||
message: L10n.screenSignoutConfirmationDialogContent,
|
||||
primaryButton: .init(title: L10n.screenSignoutConfirmationDialogSubmit, role: .destructive) { [weak self] in
|
||||
self?.actionsSubject.send(.logout)
|
||||
})
|
||||
}
|
||||
|
||||
private func presentSecureBackupLogoutConfirmationScreen() {
|
||||
|
||||
@@ -19524,7 +19524,6 @@ class UserIdentityProxyMock: UserIdentityProxyProtocol, @unchecked Sendable {
|
||||
}
|
||||
class UserIndicatorControllerMock: UserIndicatorControllerProtocol, @unchecked Sendable {
|
||||
var window: UIWindow?
|
||||
var alertInfo: AlertInfo<UUID>?
|
||||
|
||||
//MARK: - submitIndicator
|
||||
|
||||
|
||||
@@ -32,8 +32,6 @@ class UserIndicatorController: ObservableObject, UserIndicatorControllerProtocol
|
||||
}
|
||||
}
|
||||
|
||||
@Published var alertInfo: AlertInfo<UUID>?
|
||||
|
||||
var window: UIWindow? {
|
||||
didSet {
|
||||
let hostingController = UIHostingController(rootView: UserIndicatorPresenter(userIndicatorController: self).statusBarHidden(ProcessInfo.isRunningUITests))
|
||||
|
||||
@@ -15,7 +15,6 @@ protocol UserIndicatorControllerProtocol: CoordinatorProtocol {
|
||||
func retractAllIndicators()
|
||||
|
||||
var window: UIWindow? { get set }
|
||||
var alertInfo: AlertInfo<UUID>? { get set }
|
||||
}
|
||||
|
||||
extension UserIndicatorControllerProtocol {
|
||||
|
||||
@@ -28,6 +28,5 @@ struct UserIndicatorPresenter: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.alert(item: $userIndicatorController.alertInfo)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ class OIDCAuthenticationPresenter: NSObject {
|
||||
let errorDescription = error.map(String.init(describing:)) ?? "Unknown error"
|
||||
MXLog.error("Missing callback URL from the web authentication session: \(errorDescription)")
|
||||
|
||||
userIndicatorController.alertInfo = AlertInfo(id: UUID())
|
||||
showFailureIndicator()
|
||||
await authenticationService.abortOIDCLogin(data: oidcData)
|
||||
return .failure(.oidcError(.unknown))
|
||||
}
|
||||
@@ -76,7 +76,7 @@ class OIDCAuthenticationPresenter: NSObject {
|
||||
return .failure(.oidcError(.userCancellation))
|
||||
case .failure(let error):
|
||||
MXLog.error("Error occurred: \(error)")
|
||||
userIndicatorController.alertInfo = AlertInfo(id: UUID())
|
||||
showFailureIndicator()
|
||||
return .failure(error)
|
||||
}
|
||||
}
|
||||
@@ -85,10 +85,16 @@ class OIDCAuthenticationPresenter: NSObject {
|
||||
activeSession?.cancel()
|
||||
}
|
||||
|
||||
private static let loadingIndicatorID = "\(OIDCAuthenticationPresenter.self)-Loading"
|
||||
private var loadingIndicatorID: String {
|
||||
"\(Self.self)-Loading"
|
||||
}
|
||||
|
||||
private var failureIndicatorID: String {
|
||||
"\(Self.self)-Failure"
|
||||
}
|
||||
|
||||
private func startLoading(delay: Duration? = nil) {
|
||||
userIndicatorController.submitIndicator(UserIndicator(id: Self.loadingIndicatorID,
|
||||
userIndicatorController.submitIndicator(UserIndicator(id: loadingIndicatorID,
|
||||
type: .modal,
|
||||
title: L10n.commonLoading,
|
||||
persistent: true),
|
||||
@@ -96,7 +102,14 @@ class OIDCAuthenticationPresenter: NSObject {
|
||||
}
|
||||
|
||||
private func stopLoading() {
|
||||
userIndicatorController.retractIndicatorWithId(Self.loadingIndicatorID)
|
||||
userIndicatorController.retractIndicatorWithId(loadingIndicatorID)
|
||||
}
|
||||
|
||||
private func showFailureIndicator() {
|
||||
userIndicatorController.submitIndicator(UserIndicator(id: failureIndicatorID,
|
||||
type: .toast,
|
||||
title: L10n.errorUnknown,
|
||||
iconName: "xmark"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import SwiftUI
|
||||
|
||||
enum CreateRoomScreenErrorType: Error {
|
||||
case failedCreatingRoom
|
||||
case failedProcessingMedia
|
||||
case failedUploadingMedia
|
||||
case fileTooLarge
|
||||
case mediaFileError
|
||||
|
||||
@@ -116,7 +116,7 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
|
||||
do {
|
||||
guard case let .success(maxUploadSize) = await userSession.clientProxy.maxMediaUploadSize else {
|
||||
MXLog.error("Failed to get max upload size")
|
||||
userIndicatorController.alertInfo = AlertInfo(id: .init())
|
||||
state.bindings.alertInfo = .init(id: .unknown)
|
||||
return
|
||||
}
|
||||
let mediaInfo = try await mediaUploadingPreprocessor.processMedia(at: fileURL, maxUploadSize: maxUploadSize).get()
|
||||
@@ -128,7 +128,7 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
|
||||
break
|
||||
}
|
||||
} catch {
|
||||
userIndicatorController.alertInfo = AlertInfo(id: .init())
|
||||
state.bindings.alertInfo = .init(id: .failedProcessingMedia)
|
||||
}
|
||||
hideLoadingIndicator()
|
||||
}
|
||||
|
||||
@@ -105,9 +105,9 @@ class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScr
|
||||
return
|
||||
}
|
||||
|
||||
userIndicatorController.alertInfo = .init(id: .init(),
|
||||
title: L10n.commonUnableToInviteTitle,
|
||||
message: L10n.commonUnableToInviteMessage)
|
||||
state.bindings.alertInfo = .init(id: .unknown,
|
||||
title: L10n.commonUnableToInviteTitle,
|
||||
message: L10n.commonUnableToInviteMessage)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,10 @@ struct RoomDetailsEditScreenViewStateBindings {
|
||||
}
|
||||
|
||||
enum RoomDetailsEditScreenAlertType {
|
||||
case failedProcessingMedia
|
||||
case unsavedChanges
|
||||
case saveError
|
||||
case unknown
|
||||
}
|
||||
|
||||
enum RoomDetailsEditScreenViewAction {
|
||||
|
||||
@@ -86,7 +86,7 @@ class RoomDetailsEditScreenViewModel: RoomDetailsEditScreenViewModelType, RoomDe
|
||||
|
||||
guard case let .success(maxUploadSize) = await clientProxy.maxMediaUploadSize else {
|
||||
MXLog.error("Failed to get max upload size")
|
||||
userIndicatorController.alertInfo = .init(id: .init())
|
||||
state.bindings.alertInfo = .init(id: .unknown)
|
||||
return
|
||||
}
|
||||
let mediaResult = await mediaUploadingPreprocessor.processMedia(at: url, maxUploadSize: maxUploadSize)
|
||||
@@ -95,7 +95,7 @@ class RoomDetailsEditScreenViewModel: RoomDetailsEditScreenViewModelType, RoomDe
|
||||
case .success(.image):
|
||||
state.localMedia = try? mediaResult.get()
|
||||
case .failure, .success:
|
||||
userIndicatorController.alertInfo = .init(id: .init())
|
||||
state.bindings.alertInfo = .init(id: .failedProcessingMedia)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -161,9 +161,9 @@ class RoomDetailsEditScreenViewModel: RoomDetailsEditScreenViewModelType, RoomDe
|
||||
|
||||
actionsSubject.send(.saveFinished)
|
||||
} catch {
|
||||
userIndicatorController.alertInfo = .init(id: .init(),
|
||||
title: L10n.screenRoomDetailsEditionErrorTitle,
|
||||
message: L10n.screenRoomDetailsEditionError)
|
||||
state.bindings.alertInfo = .init(id: .saveError,
|
||||
title: L10n.screenRoomDetailsEditionErrorTitle,
|
||||
message: L10n.screenRoomDetailsEditionError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,6 +92,11 @@ struct RoomScreenViewState: BindableState {
|
||||
struct RoomScreenViewStateBindings {
|
||||
/// The view model used to present a QuickLook media preview.
|
||||
var mediaPreviewViewModel: TimelineMediaPreviewViewModel?
|
||||
var alertInfo: AlertInfo<RoomScreenAlertType>?
|
||||
}
|
||||
|
||||
enum RoomScreenAlertType {
|
||||
case unknown
|
||||
}
|
||||
|
||||
enum RoomScreenFooterViewAction {
|
||||
|
||||
@@ -286,7 +286,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
showLoadingIndicator()
|
||||
|
||||
if case .failure = await clientProxy.pinUserIdentity(userID) {
|
||||
userIndicatorController.alertInfo = .init(id: .init(), title: L10n.commonError)
|
||||
state.bindings.alertInfo = .init(id: .unknown, title: L10n.commonError)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,7 +298,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
showLoadingIndicator()
|
||||
|
||||
if case .failure = await clientProxy.withdrawUserIdentityVerification(userID) {
|
||||
userIndicatorController.alertInfo = .init(id: .init(), title: L10n.commonError)
|
||||
state.bindings.alertInfo = .init(id: .unknown, title: L10n.commonError)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ struct RoomScreen: View {
|
||||
.toolbar { toolbar }
|
||||
.toolbarBackground(.visible, for: .navigationBar) // Fix the toolbar's background.
|
||||
.overlay { loadingIndicator }
|
||||
.alert(item: $context.alertInfo)
|
||||
.timelineMediaPreview(viewModel: $context.mediaPreviewViewModel)
|
||||
.track(screen: .Room)
|
||||
.sentryTrace("\(Self.self)")
|
||||
|
||||
@@ -52,7 +52,10 @@ struct UserDetailsEditScreenViewStateBindings {
|
||||
}
|
||||
|
||||
enum UserDetailsEditScreenAlertType {
|
||||
case failedProcessingMedia
|
||||
case unsavedChanges
|
||||
case saveError
|
||||
case unknown
|
||||
}
|
||||
|
||||
enum UserDetailsEditScreenViewAction {
|
||||
|
||||
@@ -92,7 +92,7 @@ class UserDetailsEditScreenViewModel: UserDetailsEditScreenViewModelType, UserDe
|
||||
|
||||
guard case let .success(maxUploadSize) = await clientProxy.maxMediaUploadSize else {
|
||||
MXLog.error("Failed to get max upload size")
|
||||
userIndicatorController.alertInfo = .init(id: .init())
|
||||
state.bindings.alertInfo = .init(id: .unknown)
|
||||
return
|
||||
}
|
||||
let mediaResult = await mediaUploadingPreprocessor.processMedia(at: url, maxUploadSize: maxUploadSize)
|
||||
@@ -101,7 +101,7 @@ class UserDetailsEditScreenViewModel: UserDetailsEditScreenViewModelType, UserDe
|
||||
case .success(.image):
|
||||
state.localMedia = try? mediaResult.get()
|
||||
case .failure, .success:
|
||||
userIndicatorController.alertInfo = .init(id: .init())
|
||||
state.bindings.alertInfo = .init(id: .failedProcessingMedia)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -149,9 +149,9 @@ class UserDetailsEditScreenViewModel: UserDetailsEditScreenViewModelType, UserDe
|
||||
|
||||
actionsSubject.send(.dismiss)
|
||||
} catch {
|
||||
userIndicatorController.alertInfo = .init(id: .init(),
|
||||
title: L10n.screenEditProfileErrorTitle,
|
||||
message: L10n.screenEditProfileError)
|
||||
state.bindings.alertInfo = .init(id: .saveError,
|
||||
title: L10n.screenEditProfileErrorTitle,
|
||||
message: L10n.screenEditProfileError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,6 +199,9 @@ enum TimelineAlertInfoType: Hashable {
|
||||
case sendingFailed
|
||||
case encryptionAuthenticity(String)
|
||||
case encryptionForwarder(String)
|
||||
case inviteAgain
|
||||
case unableToInvite
|
||||
case unknown
|
||||
}
|
||||
|
||||
struct RoomMemberState {
|
||||
|
||||
@@ -579,7 +579,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
||||
|
||||
shouldShowInviteAlert
|
||||
.sink { [weak self] _ in
|
||||
self?.showInviteAlert()
|
||||
self?.displayAlert(.inviteAgain)
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
@@ -865,19 +865,11 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
||||
|
||||
// MARK: - Direct chats logics
|
||||
|
||||
private func showInviteAlert() {
|
||||
userIndicatorController.alertInfo = .init(id: .init(),
|
||||
title: L10n.screenRoomInviteAgainAlertTitle,
|
||||
message: L10n.screenRoomInviteAgainAlertMessage,
|
||||
primaryButton: .init(title: L10n.actionInvite) { [weak self] in self?.inviteOtherDMUserBack() },
|
||||
secondaryButton: .init(title: L10n.actionCancel, role: .cancel, action: nil))
|
||||
}
|
||||
|
||||
private let inviteLoadingIndicatorID = UUID().uuidString
|
||||
|
||||
private func inviteOtherDMUserBack() {
|
||||
guard roomProxy.infoPublisher.value.isUserAloneInDirectRoom else {
|
||||
userIndicatorController.alertInfo = .init(id: .init(), title: L10n.commonError)
|
||||
displayAlert(.unknown)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -892,7 +884,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
||||
members.count == 2,
|
||||
let otherPerson = members.first(where: { $0.userID != roomProxy.ownUserID && $0.membership == .leave })
|
||||
else {
|
||||
userIndicatorController.alertInfo = .init(id: .init(), title: L10n.commonError)
|
||||
displayAlert(.unknown)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -900,9 +892,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
||||
case .success:
|
||||
break
|
||||
case .failure:
|
||||
userIndicatorController.alertInfo = .init(id: .init(),
|
||||
title: L10n.commonUnableToInviteTitle,
|
||||
message: L10n.commonUnableToInviteMessage)
|
||||
displayAlert(.unableToInvite)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1021,6 +1011,18 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
||||
guard let self else { return }
|
||||
appMediator.open(appSettings.historySharingDetailsURL)
|
||||
})
|
||||
case .inviteAgain:
|
||||
state.bindings.alertInfo = .init(id: .inviteAgain,
|
||||
title: L10n.screenRoomInviteAgainAlertTitle,
|
||||
message: L10n.screenRoomInviteAgainAlertMessage,
|
||||
primaryButton: .init(title: L10n.actionInvite) { [weak self] in self?.inviteOtherDMUserBack() },
|
||||
secondaryButton: .init(title: L10n.actionCancel, role: .cancel, action: nil))
|
||||
case .unableToInvite:
|
||||
state.bindings.alertInfo = .init(id: .unableToInvite,
|
||||
title: L10n.commonUnableToInviteTitle,
|
||||
message: L10n.commonUnableToInviteMessage)
|
||||
case .unknown:
|
||||
state.bindings.alertInfo = .init(id: .unknown, title: L10n.commonError)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user