mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 21:39:12 +00:00
Soft logout removal (#672)
* Remove isSoftLogout from everywhere except the client delegate - bump the RustSDK to v1.0.43 - cleanup the app coordinator state machine and login/logout flows * Merge `succeededSigningIn` and `succeededRestoringSession` into `createdUserSession` in order to simplify the logic
This commit is contained in:
parent
1fe2e7e882
commit
e4117e3205
@ -95,8 +95,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/matrix-org/matrix-rust-components-swift",
|
||||
"state" : {
|
||||
"revision" : "852bb356fbd2b5053f17b3c6c27c8c098cf5f055",
|
||||
"version" : "1.0.42-alpha"
|
||||
"revision" : "32645162ea39badc586d610d2ada6bbc52f398ba",
|
||||
"version" : "1.0.43-alpha"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -154,7 +154,6 @@ class AppCoordinator: AppCoordinatorProtocol {
|
||||
userSessionStore.reset()
|
||||
}
|
||||
|
||||
// swiftlint:disable:next cyclomatic_complexity
|
||||
private func setupStateMachine() {
|
||||
stateMachine.addTransitionHandler { [weak self] context in
|
||||
guard let self else { return }
|
||||
@ -162,25 +161,19 @@ class AppCoordinator: AppCoordinatorProtocol {
|
||||
switch (context.fromState, context.event, context.toState) {
|
||||
case (.initial, .startWithAuthentication, .signedOut):
|
||||
self.startAuthentication()
|
||||
case (.signedOut, .succeededSigningIn, .signedIn):
|
||||
case (.signedOut, .createdUserSession, .signedIn):
|
||||
self.setupUserSession()
|
||||
case (.initial, .startWithExistingSession, .restoringSession):
|
||||
self.restoreUserSession()
|
||||
case (.restoringSession, .failedRestoringSession, .signedOut):
|
||||
self.showLoginErrorToast()
|
||||
self.presentSplashScreen()
|
||||
case (.restoringSession, .succeededRestoringSession, .signedIn):
|
||||
case (.restoringSession, .createdUserSession, .signedIn):
|
||||
self.setupUserSession()
|
||||
case (_, .signOut, .signingOut):
|
||||
self.logout(isSoftLogout: false)
|
||||
case (.signingOut, .completedSigningOut, .signedOut):
|
||||
self.tearDownUserSession()
|
||||
self.presentSplashScreen()
|
||||
case (_, .remoteSignOut(let isSoftLogout), .remoteSigningOut):
|
||||
self.logout(isSoftLogout: isSoftLogout)
|
||||
case (.remoteSigningOut(let isSoftLogout), .completedSigningOut, .signedOut):
|
||||
self.tearDownUserSession()
|
||||
self.presentSplashScreen(isSoftLogout: isSoftLogout)
|
||||
case (_, .signOut(let isSoft), .signingOut):
|
||||
self.logout(isSoft: isSoft)
|
||||
case (.signingOut, .completedSigningOut(let isSoft), .signedOut):
|
||||
self.presentSplashScreen(isSoftLogout: isSoft)
|
||||
default:
|
||||
fatalError("Unknown transition: \(context)")
|
||||
}
|
||||
@ -196,11 +189,7 @@ class AppCoordinator: AppCoordinatorProtocol {
|
||||
switch await userSessionStore.restoreUserSession() {
|
||||
case .success(let userSession):
|
||||
self.userSession = userSession
|
||||
if userSession.isSoftLogout {
|
||||
stateMachine.processEvent(.remoteSignOut(isSoft: true))
|
||||
} else {
|
||||
stateMachine.processEvent(.succeededRestoringSession)
|
||||
}
|
||||
stateMachine.processEvent(.createdUserSession)
|
||||
case .failure:
|
||||
MXLog.error("Failed to restore an existing session.")
|
||||
stateMachine.processEvent(.failedRestoringSession)
|
||||
@ -243,12 +232,9 @@ class AppCoordinator: AppCoordinatorProtocol {
|
||||
switch result {
|
||||
case .signedIn(let session):
|
||||
self.userSession = session
|
||||
self.stateMachine.processEvent(.succeededSigningIn)
|
||||
self.stateMachine.processEvent(.createdUserSession)
|
||||
case .clearAllData:
|
||||
// clear user data
|
||||
self.userSessionStore.logout(userSession: self.userSession)
|
||||
self.userSession = nil
|
||||
self.startAuthentication()
|
||||
self.stateMachine.processEvent(.signOut(isSoft: false))
|
||||
}
|
||||
}
|
||||
|
||||
@ -266,7 +252,7 @@ class AppCoordinator: AppCoordinatorProtocol {
|
||||
userSessionFlowCoordinator.callback = { [weak self] action in
|
||||
switch action {
|
||||
case .signOut:
|
||||
self?.stateMachine.processEvent(.signOut)
|
||||
self?.stateMachine.processEvent(.signOut(isSoft: false))
|
||||
}
|
||||
}
|
||||
|
||||
@ -277,7 +263,7 @@ class AppCoordinator: AppCoordinatorProtocol {
|
||||
navigationRootCoordinator.setRootCoordinator(navigationSplitCoordinator)
|
||||
}
|
||||
|
||||
private func logout(isSoftLogout: Bool) {
|
||||
private func logout(isSoft: Bool) {
|
||||
showLoadingIndicator()
|
||||
|
||||
defer {
|
||||
@ -287,8 +273,8 @@ class AppCoordinator: AppCoordinatorProtocol {
|
||||
userSession.clientProxy.stopSync()
|
||||
userSessionFlowCoordinator?.stop()
|
||||
|
||||
guard !isSoftLogout else {
|
||||
stateMachine.processEvent(.completedSigningOut)
|
||||
guard !isSoft else {
|
||||
stateMachine.processEvent(.completedSigningOut(isSoft: isSoft))
|
||||
return
|
||||
}
|
||||
|
||||
@ -298,8 +284,9 @@ class AppCoordinator: AppCoordinatorProtocol {
|
||||
|
||||
// regardless of the result, clear user data
|
||||
userSessionStore.logout(userSession: userSession)
|
||||
tearDownUserSession()
|
||||
|
||||
stateMachine.processEvent(.completedSigningOut)
|
||||
stateMachine.processEvent(.completedSigningOut(isSoft: isSoft))
|
||||
}
|
||||
}
|
||||
|
||||
@ -361,11 +348,7 @@ class AppCoordinator: AppCoordinatorProtocol {
|
||||
guard let self else { return }
|
||||
switch callback {
|
||||
case .didReceiveAuthError(let isSoftLogout):
|
||||
self.stateMachine.processEvent(.remoteSignOut(isSoft: isSoftLogout))
|
||||
case .updateRestoreTokenNeeded:
|
||||
if let userSession = self.userSession {
|
||||
_ = self.userSessionStore.refreshRestorationToken(for: userSession)
|
||||
}
|
||||
self.stateMachine.processEvent(.signOut(isSoft: isSoftLogout))
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -463,7 +446,7 @@ class AppCoordinator: AppCoordinatorProtocol {
|
||||
extension AppCoordinator: AuthenticationCoordinatorDelegate {
|
||||
func authenticationCoordinator(_ authenticationCoordinator: AuthenticationCoordinator, didLoginWithSession userSession: UserSessionProtocol) {
|
||||
self.userSession = userSession
|
||||
stateMachine.processEvent(.succeededSigningIn)
|
||||
stateMachine.processEvent(.createdUserSession)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,32 +31,27 @@ class AppCoordinatorStateMachine {
|
||||
case signedIn
|
||||
|
||||
/// Processing a sign out request
|
||||
case signingOut
|
||||
|
||||
/// Processing a remote sign out
|
||||
case remoteSigningOut(isSoft: Bool)
|
||||
case signingOut(isSoft: Bool)
|
||||
}
|
||||
|
||||
/// Events that can be triggered on the AppCoordinator state machine
|
||||
enum Event: EventType {
|
||||
/// Start the `AppCoordinator` by showing authentication.
|
||||
case startWithAuthentication
|
||||
/// Signing in succeeded
|
||||
case succeededSigningIn
|
||||
|
||||
/// Start the `AppCoordinator` by restoring an existing account.
|
||||
case startWithExistingSession
|
||||
/// Restoring session succeeded.
|
||||
case succeededRestoringSession
|
||||
|
||||
/// Restoring session failed.
|
||||
case failedRestoringSession
|
||||
|
||||
/// A session has been created
|
||||
case createdUserSession
|
||||
|
||||
/// Request sign out
|
||||
case signOut
|
||||
/// Remote sign out.
|
||||
case remoteSignOut(isSoft: Bool)
|
||||
case signOut(isSoft: Bool)
|
||||
/// Signing out completed
|
||||
case completedSigningOut
|
||||
case completedSigningOut(isSoft: Bool)
|
||||
}
|
||||
|
||||
private let stateMachine: StateMachine<State, Event>
|
||||
@ -72,21 +67,18 @@ class AppCoordinatorStateMachine {
|
||||
|
||||
private func configure() {
|
||||
stateMachine.addRoutes(event: .startWithAuthentication, transitions: [.initial => .signedOut])
|
||||
stateMachine.addRoutes(event: .succeededSigningIn, transitions: [.signedOut => .signedIn])
|
||||
stateMachine.addRoutes(event: .createdUserSession, transitions: [.signedOut => .signedIn])
|
||||
|
||||
stateMachine.addRoutes(event: .startWithExistingSession, transitions: [.initial => .restoringSession])
|
||||
stateMachine.addRoutes(event: .succeededRestoringSession, transitions: [.restoringSession => .signedIn])
|
||||
stateMachine.addRoutes(event: .createdUserSession, transitions: [.restoringSession => .signedIn])
|
||||
stateMachine.addRoutes(event: .failedRestoringSession, transitions: [.restoringSession => .signedOut])
|
||||
|
||||
stateMachine.addRoutes(event: .signOut, transitions: [.any => .signingOut])
|
||||
stateMachine.addRoutes(event: .completedSigningOut, transitions: [.signingOut => .signedOut])
|
||||
|
||||
// Transitions with associated values need to be handled through `addRouteMapping`
|
||||
stateMachine.addRouteMapping { event, fromState, _ in
|
||||
switch (event, fromState) {
|
||||
case (.remoteSignOut(let isSoft), _):
|
||||
return .remoteSigningOut(isSoft: isSoft)
|
||||
case (.completedSigningOut, .remoteSigningOut):
|
||||
case (.signOut(let isSoft), _):
|
||||
return .signingOut(isSoft: isSoft)
|
||||
case (.completedSigningOut, .signingOut):
|
||||
return .signedOut
|
||||
default:
|
||||
return nil
|
||||
|
@ -35,11 +35,6 @@ private class WeakClientProxyWrapper: ClientDelegate, SlidingSyncObserver {
|
||||
clientProxy?.didReceiveAuthError(isSoftLogout: isSoftLogout)
|
||||
}
|
||||
|
||||
func didUpdateRestoreToken() {
|
||||
MXLog.info("Did update restoration token")
|
||||
clientProxy?.didUpdateRestoreToken()
|
||||
}
|
||||
|
||||
// MARK: - SlidingSyncDelegate
|
||||
|
||||
func didReceiveSyncUpdate(summary: UpdateSummary) {
|
||||
@ -98,10 +93,6 @@ class ClientProxy: ClientProxyProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
var isSoftLogout: Bool {
|
||||
client.isSoftLogout()
|
||||
}
|
||||
|
||||
var deviceId: String? {
|
||||
do {
|
||||
return try client.deviceId()
|
||||
@ -126,7 +117,7 @@ class ClientProxy: ClientProxyProtocol {
|
||||
|
||||
func startSync() {
|
||||
MXLog.info("Starting sync")
|
||||
guard !client.isSoftLogout(), slidingSyncObserverToken == nil else {
|
||||
guard slidingSyncObserverToken == nil else {
|
||||
return
|
||||
}
|
||||
|
||||
@ -383,10 +374,6 @@ class ClientProxy: ClientProxyProtocol {
|
||||
callbacks.send(.receivedAuthError(isSoftLogout: isSoftLogout))
|
||||
}
|
||||
|
||||
fileprivate func didUpdateRestoreToken() {
|
||||
callbacks.send(.updatedRestoreToken)
|
||||
}
|
||||
|
||||
fileprivate func didReceiveSlidingSyncUpdate(summary: UpdateSummary) {
|
||||
callbacks.send(.receivedSyncUpdate)
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ import MatrixRustSDK
|
||||
enum ClientProxyCallback {
|
||||
case receivedSyncUpdate
|
||||
case receivedAuthError(isSoftLogout: Bool)
|
||||
case updatedRestoreToken
|
||||
}
|
||||
|
||||
enum ClientProxyError: Error {
|
||||
@ -69,8 +68,6 @@ protocol ClientProxyProtocol: AnyObject, MediaLoaderProtocol {
|
||||
|
||||
var userID: String { get }
|
||||
|
||||
var isSoftLogout: Bool { get }
|
||||
|
||||
var deviceId: String? { get }
|
||||
|
||||
var homeserver: String { get }
|
||||
|
@ -22,7 +22,6 @@ class MockClientProxy: ClientProxyProtocol {
|
||||
let callbacks = PassthroughSubject<ClientProxyCallback, Never>()
|
||||
|
||||
let userID: String
|
||||
let isSoftLogout = false
|
||||
let deviceId: String? = nil
|
||||
let homeserver = ""
|
||||
let restorationToken: RestorationToken? = nil
|
||||
|
@ -20,7 +20,6 @@ struct MockUserSession: UserSessionProtocol {
|
||||
let callbacks = PassthroughSubject<UserSessionCallback, Never>()
|
||||
let sessionVerificationController: SessionVerificationControllerProxyProtocol? = nil
|
||||
var userID: String { clientProxy.userID }
|
||||
var isSoftLogout: Bool { clientProxy.isSoftLogout }
|
||||
var deviceID: String? { clientProxy.deviceId }
|
||||
var homeserver: String { clientProxy.homeserver }
|
||||
let clientProxy: ClientProxyProtocol
|
||||
|
@ -21,10 +21,8 @@ class UserSession: UserSessionProtocol {
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
private var checkForSessionVerificationControllerCancellable: AnyCancellable?
|
||||
private var authErrorCancellable: AnyCancellable?
|
||||
private var restoreTokenUpdateCancellable: AnyCancellable?
|
||||
|
||||
var userID: String { clientProxy.userID }
|
||||
var isSoftLogout: Bool { clientProxy.isSoftLogout }
|
||||
var deviceID: String? { clientProxy.deviceId }
|
||||
var homeserver: String { clientProxy.homeserver }
|
||||
|
||||
@ -39,7 +37,6 @@ class UserSession: UserSessionProtocol {
|
||||
|
||||
setupSessionVerificationWatchdog()
|
||||
setupAuthErrorWatchdog()
|
||||
setupRestoreTokenUpdateWatchdog()
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
@ -102,21 +99,4 @@ class UserSession: UserSessionProtocol {
|
||||
private func tearDownAuthErrorWatchdog() {
|
||||
authErrorCancellable = nil
|
||||
}
|
||||
|
||||
// MARK: Restore Token Update Watchdog
|
||||
|
||||
private func setupRestoreTokenUpdateWatchdog() {
|
||||
restoreTokenUpdateCancellable = clientProxy.callbacks
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] callback in
|
||||
if case .updatedRestoreToken = callback {
|
||||
self?.callbacks.send(.updateRestoreTokenNeeded)
|
||||
self?.tearDownRestoreTokenUpdateWatchdog()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func tearDownRestoreTokenUpdateWatchdog() {
|
||||
restoreTokenUpdateCancellable = nil
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ enum UserSessionCallback {
|
||||
case sessionVerificationNeeded
|
||||
case didVerifySession
|
||||
case didReceiveAuthError(isSoftLogout: Bool)
|
||||
case updateRestoreTokenNeeded
|
||||
}
|
||||
|
||||
protocol UserSessionProtocol {
|
||||
@ -29,8 +28,6 @@ protocol UserSessionProtocol {
|
||||
var userID: String { get }
|
||||
var deviceID: String? { get }
|
||||
|
||||
var isSoftLogout: Bool { get }
|
||||
|
||||
var clientProxy: ClientProxyProtocol { get }
|
||||
var mediaProvider: MediaProviderProtocol { get }
|
||||
|
||||
|
@ -29,7 +29,6 @@ extension MatrixRustSDK.Session: Codable {
|
||||
userId: try container.decode(String.self, forKey: .userId),
|
||||
deviceId: try container.decode(String.self, forKey: .deviceId),
|
||||
homeserverUrl: try container.decode(String.self, forKey: .homeserverUrl),
|
||||
isSoftLogout: try container.decode(Bool.self, forKey: .isSoftLogout),
|
||||
slidingSyncProxy: try container.decode(String.self, forKey: .slidingSyncProxy))
|
||||
}
|
||||
|
||||
@ -40,11 +39,10 @@ extension MatrixRustSDK.Session: Codable {
|
||||
try container.encode(userId, forKey: .userId)
|
||||
try container.encode(deviceId, forKey: .deviceId)
|
||||
try container.encode(homeserverUrl, forKey: .homeserverUrl)
|
||||
try container.encode(isSoftLogout, forKey: .isSoftLogout)
|
||||
try container.encode(slidingSyncProxy, forKey: .slidingSyncProxy)
|
||||
}
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case accessToken, refreshToken, userId, deviceId, homeserverUrl, isSoftLogout, slidingSyncProxy
|
||||
case accessToken, refreshToken, userId, deviceId, homeserverUrl, slidingSyncProxy
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,6 @@ class KeychainControllerTests: XCTestCase {
|
||||
userId: "userId",
|
||||
deviceId: "deviceId",
|
||||
homeserverUrl: "homeserverUrl",
|
||||
isSoftLogout: false,
|
||||
slidingSyncProxy: "https://my.sync.proxy"))
|
||||
keychain.setRestorationToken(restorationToken, forUsername: username)
|
||||
|
||||
@ -53,7 +52,6 @@ class KeychainControllerTests: XCTestCase {
|
||||
userId: "userId",
|
||||
deviceId: "deviceId",
|
||||
homeserverUrl: "homeserverUrl",
|
||||
isSoftLogout: false,
|
||||
slidingSyncProxy: "https://my.sync.proxy"))
|
||||
keychain.setRestorationToken(restorationToken, forUsername: username)
|
||||
XCTAssertEqual(keychain.restorationTokens().count, 1, "The keychain should have 1 restoration token.")
|
||||
@ -75,7 +73,6 @@ class KeychainControllerTests: XCTestCase {
|
||||
userId: "userId",
|
||||
deviceId: "deviceId",
|
||||
homeserverUrl: "homeserverUrl",
|
||||
isSoftLogout: false,
|
||||
slidingSyncProxy: "https://my.sync.proxy"))
|
||||
keychain.setRestorationToken(restorationToken, forUsername: "@test\(index):example.com")
|
||||
}
|
||||
@ -96,7 +93,6 @@ class KeychainControllerTests: XCTestCase {
|
||||
userId: "userId",
|
||||
deviceId: "deviceId",
|
||||
homeserverUrl: "homeserverUrl",
|
||||
isSoftLogout: false,
|
||||
slidingSyncProxy: "https://my.sync.proxy"))
|
||||
keychain.setRestorationToken(restorationToken, forUsername: "@test\(index):example.com")
|
||||
}
|
||||
|
@ -69,20 +69,4 @@ final class UserSessionTests: XCTestCase {
|
||||
controller.callbacks.send(.finished)
|
||||
waitForExpectations(timeout: 1.0)
|
||||
}
|
||||
|
||||
func test_whenUserSessionReceivesUpdatedRestoreToken_updateRestoreTokenNeededEventReceived() throws {
|
||||
let expectation = expectation(description: "UpdatedRestoreToken expectation")
|
||||
userSession.callbacks.sink { callback in
|
||||
switch callback {
|
||||
case .updateRestoreTokenNeeded:
|
||||
expectation.fulfill()
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
clientProxy.callbacks.send(.updatedRestoreToken)
|
||||
waitForExpectations(timeout: 1.0)
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ include:
|
||||
packages:
|
||||
MatrixRustSDK:
|
||||
url: https://github.com/matrix-org/matrix-rust-components-swift
|
||||
exactVersion: 1.0.42-alpha
|
||||
exactVersion: 1.0.43-alpha
|
||||
# path: ../matrix-rust-sdk
|
||||
DesignKit:
|
||||
path: DesignKit
|
||||
|
Loading…
x
Reference in New Issue
Block a user