mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 12:07:55 +00:00
User session state machine crashes (#281)
* Fix #277 - UserSession state machine crashes on foregrounding and add the bug report screen to it * Rename home screen actions
This commit is contained in:
@@ -23,10 +23,10 @@ struct HomeScreenCoordinatorParameters {
|
||||
}
|
||||
|
||||
enum HomeScreenCoordinatorAction {
|
||||
case presentRoom(roomIdentifier: String)
|
||||
case presentSettings
|
||||
case presentBugReport
|
||||
case verifySession
|
||||
case presentRoomScreen(roomIdentifier: String)
|
||||
case presentSettingsScreen
|
||||
case presentFeedbackScreen
|
||||
case presentSessionVerificationScreen
|
||||
case signOut
|
||||
}
|
||||
|
||||
@@ -63,11 +63,11 @@ final class HomeScreenCoordinator: Coordinator, Presentable {
|
||||
|
||||
switch action {
|
||||
case .selectRoom(let roomIdentifier):
|
||||
self.callback?(.presentRoom(roomIdentifier: roomIdentifier))
|
||||
self.callback?(.presentRoomScreen(roomIdentifier: roomIdentifier))
|
||||
case .userMenu(let action):
|
||||
self.processUserMenuAction(action)
|
||||
case .verifySession:
|
||||
self.callback?(.verifySession)
|
||||
self.callback?(.presentSessionVerificationScreen)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,11 +87,11 @@ final class HomeScreenCoordinator: Coordinator, Presentable {
|
||||
private func processUserMenuAction(_ action: HomeScreenViewUserMenuAction) {
|
||||
switch action {
|
||||
case .settings:
|
||||
callback?(.presentSettings)
|
||||
callback?(.presentSettingsScreen)
|
||||
case .inviteFriends:
|
||||
presentInviteFriends()
|
||||
case .feedback:
|
||||
callback?(.presentBugReport)
|
||||
callback?(.presentFeedbackScreen)
|
||||
case .signOut:
|
||||
callback?(.signOut)
|
||||
}
|
||||
|
||||
@@ -72,6 +72,12 @@ class UserSessionFlowCoordinator: Coordinator {
|
||||
self.presentSettingsScreen()
|
||||
case (.settingsScreen, .dismissedSettingsScreen, .homeScreen):
|
||||
self.dismissSettingsScreen()
|
||||
|
||||
case (.homeScreen, .feedbackScreen, .feedbackScreen):
|
||||
self.presentFeedbackScreen()
|
||||
case (.feedbackScreen, .dismissedFeedbackScreen, .homeScreen):
|
||||
self.dismissFeedbackScreen()
|
||||
|
||||
case (_, .resignActive, .suspended):
|
||||
self.pause()
|
||||
case (_, .becomeActive, _):
|
||||
@@ -109,13 +115,13 @@ class UserSessionFlowCoordinator: Coordinator {
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .presentRoom(let roomIdentifier):
|
||||
case .presentRoomScreen(let roomIdentifier):
|
||||
self.stateMachine.processEvent(.showRoomScreen(roomId: roomIdentifier))
|
||||
case .presentSettings:
|
||||
case .presentSettingsScreen:
|
||||
self.stateMachine.processEvent(.showSettingsScreen)
|
||||
case .presentBugReport:
|
||||
self.presentBugReportScreen()
|
||||
case .verifySession:
|
||||
case .presentFeedbackScreen:
|
||||
self.stateMachine.processEvent(.feedbackScreen)
|
||||
case .presentSessionVerificationScreen:
|
||||
self.stateMachine.processEvent(.showSessionVerificationScreen)
|
||||
case .signOut:
|
||||
self.callback?(.signOut)
|
||||
@@ -254,20 +260,18 @@ class UserSessionFlowCoordinator: Coordinator {
|
||||
|
||||
alert.addAction(UIAlertAction(title: ElementL10n.no, style: .cancel))
|
||||
alert.addAction(UIAlertAction(title: ElementL10n.yes, style: .default) { [weak self] _ in
|
||||
self?.presentBugReportScreen()
|
||||
self?.stateMachine.processEvent(.feedbackScreen)
|
||||
})
|
||||
|
||||
navigationRouter.present(alert, animated: true)
|
||||
}
|
||||
|
||||
private func presentBugReportScreen(for image: UIImage? = nil) {
|
||||
private func presentFeedbackScreen(for image: UIImage? = nil) {
|
||||
let parameters = BugReportCoordinatorParameters(bugReportService: bugReportService,
|
||||
screenshot: image)
|
||||
let coordinator = BugReportCoordinator(parameters: parameters)
|
||||
coordinator.completion = { [weak self, weak coordinator] in
|
||||
guard let self, let coordinator = coordinator else { return }
|
||||
self.navigationRouter.dismissModule(animated: true)
|
||||
self.remove(childCoordinator: coordinator)
|
||||
coordinator.completion = { [weak self] in
|
||||
self?.stateMachine.processEvent(.dismissedFeedbackScreen)
|
||||
}
|
||||
|
||||
add(childCoordinator: coordinator)
|
||||
@@ -275,21 +279,23 @@ class UserSessionFlowCoordinator: Coordinator {
|
||||
let navController = ElementNavigationController(rootViewController: coordinator.toPresentable())
|
||||
navController.navigationBar.topItem?.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel,
|
||||
target: self,
|
||||
action: #selector(dismissBugReportScreen))
|
||||
action: #selector(handleFeedbackScreenCancellation))
|
||||
navController.isModalInPresentation = true
|
||||
navigationRouter.present(navController, animated: true)
|
||||
}
|
||||
|
||||
@objc
|
||||
private func dismissBugReportScreen() {
|
||||
MXLog.debug("dismissBugReportScreen")
|
||||
|
||||
guard let bugReportCoordinator = childCoordinators.first(where: { $0 is BugReportCoordinator }) else {
|
||||
private func handleFeedbackScreenCancellation() {
|
||||
stateMachine.processEvent(.dismissedFeedbackScreen)
|
||||
}
|
||||
|
||||
private func dismissFeedbackScreen() {
|
||||
guard let coordinator = childCoordinators.first(where: { $0 is BugReportCoordinator }) else {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
navigationRouter.dismissModule()
|
||||
remove(childCoordinator: bugReportCoordinator)
|
||||
remove(childCoordinator: coordinator)
|
||||
}
|
||||
|
||||
// MARK: - Application State
|
||||
|
||||
@@ -33,6 +33,9 @@ class UserSessionFlowCoordinatorStateMachine {
|
||||
/// Showing the session verification flows
|
||||
case sessionVerificationScreen
|
||||
|
||||
/// Showing the session verification flows
|
||||
case feedbackScreen
|
||||
|
||||
/// Showing the settings screen
|
||||
case settingsScreen
|
||||
|
||||
@@ -51,15 +54,20 @@ class UserSessionFlowCoordinatorStateMachine {
|
||||
/// The room screen has been dismissed
|
||||
case dismissedRoomScreen
|
||||
|
||||
/// Request the start of the session verification flow
|
||||
case showSessionVerificationScreen
|
||||
/// Session verification has finished
|
||||
case dismissedSessionVerificationScreen
|
||||
|
||||
/// Request presentation of the settings screen
|
||||
case showSettingsScreen
|
||||
/// The settings screen has been dismissed
|
||||
case dismissedSettingsScreen
|
||||
|
||||
/// Request presentation of the feedback screen
|
||||
case feedbackScreen
|
||||
/// The feedback screen has been dismissed
|
||||
case dismissedFeedbackScreen
|
||||
|
||||
/// Request the start of the session verification flow
|
||||
case showSessionVerificationScreen
|
||||
/// Session verification has finished
|
||||
case dismissedSessionVerificationScreen
|
||||
|
||||
/// Application goes into inactive state
|
||||
case resignActive
|
||||
@@ -68,13 +76,14 @@ class UserSessionFlowCoordinatorStateMachine {
|
||||
}
|
||||
|
||||
private let stateMachine: StateMachine<State, Event>
|
||||
private var stateBeforeSuspension: State = .initial
|
||||
private var stateBeforeSuspension: State?
|
||||
|
||||
init() {
|
||||
stateMachine = StateMachine(state: .initial)
|
||||
configure()
|
||||
}
|
||||
|
||||
// swiftlint:disable:next cyclomatic_complexity
|
||||
private func configure() {
|
||||
stateMachine.addRoutes(event: .start, transitions: [.initial => .homeScreen])
|
||||
|
||||
@@ -84,23 +93,46 @@ class UserSessionFlowCoordinatorStateMachine {
|
||||
return .roomScreen(roomId: roomId)
|
||||
case (.dismissedRoomScreen, .roomScreen):
|
||||
return .homeScreen
|
||||
case (.resignActive, _):
|
||||
self.stateBeforeSuspension = fromState
|
||||
return .suspended
|
||||
case (.becomeActive, .suspended):
|
||||
return self.stateBeforeSuspension
|
||||
|
||||
case (.showSettingsScreen, .homeScreen):
|
||||
return .settingsScreen
|
||||
case (.dismissedSettingsScreen, .settingsScreen):
|
||||
return .homeScreen
|
||||
|
||||
case (.feedbackScreen, .homeScreen):
|
||||
return .feedbackScreen
|
||||
case (.dismissedFeedbackScreen, .feedbackScreen):
|
||||
return .homeScreen
|
||||
|
||||
case (.showSessionVerificationScreen, .homeScreen):
|
||||
return .sessionVerificationScreen
|
||||
case (.dismissedSessionVerificationScreen, .sessionVerificationScreen):
|
||||
return .homeScreen
|
||||
|
||||
case (.resignActive, _):
|
||||
self.stateBeforeSuspension = fromState
|
||||
return .suspended
|
||||
case (.becomeActive, _):
|
||||
// Cannot become active if not previously suspended
|
||||
// Happens when the app is backgrounded before the session is setup
|
||||
guard let previousState = self.stateBeforeSuspension else {
|
||||
return self.stateMachine.state
|
||||
}
|
||||
|
||||
return previousState
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
addTransitionHandler { context in
|
||||
if let event = context.event {
|
||||
MXLog.info("Transitioning from `\(context.fromState)` to `\(context.toState)` with event `\(event)`")
|
||||
} else {
|
||||
MXLog.info("Transitioning from \(context.fromState)` to `\(context.toState)`")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to move the state machine to another state through an event
|
||||
|
||||
Reference in New Issue
Block a user