diff --git a/ElementX/Sources/Application/AppCoordinator.swift b/ElementX/Sources/Application/AppCoordinator.swift index f9ae849df..b9da6e44c 100644 --- a/ElementX/Sources/Application/AppCoordinator.swift +++ b/ElementX/Sources/Application/AppCoordinator.swift @@ -85,6 +85,11 @@ class AppCoordinator: AppCoordinatorProtocol { } func start() { + guard stateMachine.state == .initial else { + MXLog.error("Received a start request when already started") + return + } + stateMachine.processEvent(userSessionStore.hasSessions ? .startWithExistingSession : .startWithAuthentication) } @@ -255,21 +260,27 @@ class AppCoordinator: AppCoordinatorProtocol { deobserveUserSessionChanges() - if !isSoftLogout { - Task { - // first log out from the server - _ = await userSession.clientProxy.logout() - - // regardless of the result, clear user data - userSessionStore.logout(userSession: userSession) - userSession = nil - notificationManager?.delegate = nil - notificationManager = nil - } + guard !isSoftLogout else { + stateMachine.processEvent(.completedSigningOut) + return } - // complete logging out - stateMachine.processEvent(.completedSigningOut) + Task { + showLoadingIndicator() + + // first log out from the server + _ = await userSession.clientProxy.logout() + + // regardless of the result, clear user data + userSessionStore.logout(userSession: userSession) + userSession = nil + notificationManager?.delegate = nil + notificationManager = nil + + stateMachine.processEvent(.completedSigningOut) + + hideLoadingIndicator() + } } private func presentSplashScreen(isSoftLogout: Bool = false) { diff --git a/ElementX/Sources/Application/AppCoordinatorStateMachine.swift b/ElementX/Sources/Application/AppCoordinatorStateMachine.swift index dc8afb0e2..1f17eba03 100644 --- a/ElementX/Sources/Application/AppCoordinatorStateMachine.swift +++ b/ElementX/Sources/Application/AppCoordinatorStateMachine.swift @@ -61,6 +61,10 @@ class AppCoordinatorStateMachine { private let stateMachine: StateMachine + var state: AppCoordinatorStateMachine.State { + stateMachine.state + } + init() { stateMachine = StateMachine(state: .initial) configure() diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenCoordinator.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenCoordinator.swift index d36336f7a..dd9d39ad7 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenCoordinator.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenCoordinator.swift @@ -25,7 +25,7 @@ struct HomeScreenCoordinatorParameters { } enum HomeScreenCoordinatorAction { - case presentRoomScreen(roomIdentifier: String) + case presentRoom(roomIdentifier: String) case presentSettingsScreen case presentFeedbackScreen case presentSessionVerificationScreen @@ -50,12 +50,18 @@ final class HomeScreenCoordinator: CoordinatorProtocol { guard let self else { return } switch action { - case .selectRoom(let roomIdentifier): - self.callback?(.presentRoomScreen(roomIdentifier: roomIdentifier)) - case .userMenu(let action): - self.processUserMenuAction(action) - case .verifySession: + case .presentRoom(let roomIdentifier): + self.callback?(.presentRoom(roomIdentifier: roomIdentifier)) + case .presentFeedbackScreen: + self.callback?(.presentFeedbackScreen) + case .presentSettingsScreen: + self.callback?(.presentSettingsScreen) + case .presentInviteFriendsScreen: + self.presentInviteFriends() + case .presentSessionVerificationScreen: self.callback?(.presentSessionVerificationScreen) + case .signOut: + self.callback?(.signOut) } } } @@ -63,15 +69,11 @@ final class HomeScreenCoordinator: CoordinatorProtocol { // MARK: - Public func start() { + #if !DEBUG if parameters.bugReportService.crashedLastRun { - viewModel.presentAlert( - AlertInfo(id: UUID(), - title: ElementL10n.sendBugReportAppCrashed, - primaryButton: .init(title: ElementL10n.no, action: nil), - secondaryButton: .init(title: ElementL10n.yes) { [weak self] in - self?.callback?(.presentFeedbackScreen) - })) + viewModel.presentCrashedLastRunAlert() } + #endif } func toPresentable() -> AnyView { @@ -79,19 +81,6 @@ final class HomeScreenCoordinator: CoordinatorProtocol { } // MARK: - Private - - private func processUserMenuAction(_ action: HomeScreenViewUserMenuAction) { - switch action { - case .settings: - callback?(.presentSettingsScreen) - case .inviteFriends: - presentInviteFriends() - case .feedback: - callback?(.presentFeedbackScreen) - case .signOut: - callback?(.signOut) - } - } private func presentInviteFriends() { parameters.navigationStackCoordinator.setSheetCoordinator(InviteFriendsCoordinator(userId: parameters.userSession.userID)) diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift index 774a366b2..fbc771af3 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift @@ -18,9 +18,12 @@ import Foundation import UIKit enum HomeScreenViewModelAction { - case selectRoom(roomIdentifier: String) - case userMenu(action: HomeScreenViewUserMenuAction) - case verifySession + case presentRoom(roomIdentifier: String) + case presentSessionVerificationScreen + case presentSettingsScreen + case presentInviteFriendsScreen + case presentFeedbackScreen + case signOut } enum HomeScreenViewUserMenuAction { diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift index ae197dd7c..3955d5204 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift @@ -112,6 +112,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol // MARK: - Public + // swiftlint:disable:next cyclomatic_complexity override func process(viewAction: HomeScreenViewAction) async { switch viewAction { case .loadRoomData(let roomIdentifier): @@ -119,11 +120,20 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol loadDataForRoomIdentifier(roomIdentifier) } case .selectRoom(let roomIdentifier): - callback?(.selectRoom(roomIdentifier: roomIdentifier)) + callback?(.presentRoom(roomIdentifier: roomIdentifier)) case .userMenu(let action): - callback?(.userMenu(action: action)) + switch action { + case .feedback: + callback?(.presentFeedbackScreen) + case .settings: + callback?(.presentSettingsScreen) + case .inviteFriends: + callback?(.presentInviteFriendsScreen) + case .signOut: + callback?(.signOut) + } case .verifySession: - callback?(.verifySession) + callback?(.presentSessionVerificationScreen) case .skipSessionVerification: state.showSessionVerificationBanner = false case .updatedVisibleItemIdentifiers(let identifiers): @@ -131,8 +141,13 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol } } - func presentAlert(_ alertInfo: AlertInfo) { - state.bindings.alertInfo = alertInfo + func presentCrashedLastRunAlert() { + state.bindings.alertInfo = AlertInfo(id: UUID(), + title: ElementL10n.sendBugReportAppCrashed, + primaryButton: .init(title: ElementL10n.no, action: nil), + secondaryButton: .init(title: ElementL10n.yes) { [weak self] in + self?.callback?(.presentFeedbackScreen) + }) } // MARK: - Private diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModelProtocol.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModelProtocol.swift index 54d15d17b..dfa6b035d 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModelProtocol.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModelProtocol.swift @@ -23,5 +23,5 @@ protocol HomeScreenViewModelProtocol { var context: HomeScreenViewModelType.Context { get } - func presentAlert(_ alert: AlertInfo) + func presentCrashedLastRunAlert() } diff --git a/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryDetails.swift b/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryDetails.swift index 35fc248bc..49d03cc53 100644 --- a/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryDetails.swift +++ b/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryDetails.swift @@ -25,3 +25,9 @@ struct RoomSummaryDetails { let lastMessageTimestamp: Date? let unreadNotificationCount: UInt } + +extension RoomSummaryDetails: CustomStringConvertible { + var description: String { + "id: \"\(id)\", isDirect: \"\(isDirect)\", unreadNotificationCount: \"\(unreadNotificationCount)\"" + } +} diff --git a/ElementX/Sources/Services/UserSession/UserSessionFlowCoordinator.swift b/ElementX/Sources/Services/UserSession/UserSessionFlowCoordinator.swift index 92a3c1466..d4b1d211f 100644 --- a/ElementX/Sources/Services/UserSession/UserSessionFlowCoordinator.swift +++ b/ElementX/Sources/Services/UserSession/UserSessionFlowCoordinator.swift @@ -125,7 +125,7 @@ class UserSessionFlowCoordinator: CoordinatorProtocol { guard let self else { return } switch action { - case .presentRoomScreen(let roomIdentifier): + case .presentRoom(let roomIdentifier): self.stateMachine.processEvent(.selectRoom(roomId: roomIdentifier)) case .presentSettingsScreen: self.stateMachine.processEvent(.showSettingsScreen) diff --git a/UnitTests/Sources/HomeScreenViewModelTests.swift b/UnitTests/Sources/HomeScreenViewModelTests.swift index b6220566b..3304aeeae 100644 --- a/UnitTests/Sources/HomeScreenViewModelTests.swift +++ b/UnitTests/Sources/HomeScreenViewModelTests.swift @@ -35,7 +35,7 @@ class HomeScreenViewModelTests: XCTestCase { var selectedRoomId = "" viewModel.callback = { result in switch result { - case .selectRoom(let roomId): + case .presentRoom(let roomId): correctResult = true selectedRoomId = roomId default: @@ -53,8 +53,8 @@ class HomeScreenViewModelTests: XCTestCase { var correctResult = false viewModel.callback = { result in switch result { - case .userMenu(let action): - correctResult = action == .settings + case .presentSettingsScreen: + correctResult = true default: break } diff --git a/changelog.d/340.bugfix b/changelog.d/340.bugfix new file mode 100644 index 000000000..6c03a21da --- /dev/null +++ b/changelog.d/340.bugfix @@ -0,0 +1 @@ +Wait for logout confirmation before changing the app state \ No newline at end of file diff --git a/changelog.d/pr-437.bugfix b/changelog.d/pr-437.bugfix new file mode 100644 index 000000000..8a5fe1a28 --- /dev/null +++ b/changelog.d/pr-437.bugfix @@ -0,0 +1 @@ +Prevent crash popups when force quitting the application \ No newline at end of file