func stateActions()

in megazords/ios-rust/Sources/MozillaRustComponentsWrapper/FxAClient/FxAccountManager.swift [357:554]


    func stateActions(forState: AccountState, via: Event) -> Event? {
        switch forState {
        case .start: do {
                switch via {
                case .initialize: do {
                        if let acct = tryRestoreAccount() {
                            account = acct
                            return .accountRestored
                        } else {
                            return .accountNotFound
                        }
                    }
                default: return nil
                }
            }
        case .notAuthenticated: do {
                switch via {
                case .logout: do {
                        // Clean up internal account state and destroy the current FxA device record.
                        requireAccount().disconnect()
                        FxALog.info("Disconnected FxA account")
                        profile = nil
                        constellation = nil
                        accountStorage.clear()
                        // If we cannot instantiate FxA something is *really* wrong, crashing is a valid option.
                        account = createAccount()
                        DispatchQueue.main.async {
                            NotificationCenter.default.post(
                                name: .accountLoggedOut,
                                object: nil
                            )
                        }
                    }
                case .accountNotFound: do {
                        account = createAccount()
                    }
                default: break // Do nothing
                }
            }
        case .authenticatedNoProfile: do {
                switch via {
                case let .authenticated(authData): do {
                        FxALog.info("Registering persistence callback")
                        requireAccount().registerPersistCallback(statePersistenceCallback)

                        FxALog.debug("Completing oauth flow")
                        do {
                            try requireAccount().completeOAuthFlow(code: authData.code, state: authData.state)
                        } catch {
                            // Reasons this can fail:
                            // - network errors
                            // - unknown auth state
                            // - authenticating via web-content; we didn't beginOAuthFlowAsync
                            FxALog.error("Error completing OAuth flow: \(error)")
                        }

                        FxALog.info("Initializing device")
                        requireConstellation().initDevice(
                            name: deviceConfig.name,
                            type: deviceConfig.deviceType,
                            capabilities: deviceConfig.capabilities
                        )

                        postAuthenticated(authType: authData.authType)

                        return Event.fetchProfile(ignoreCache: false)
                    }
                case .accountRestored: do {
                        FxALog.info("Registering persistence callback")
                        requireAccount().registerPersistCallback(statePersistenceCallback)

                        FxALog.info("Ensuring device capabilities...")
                        requireConstellation().ensureCapabilities(capabilities: deviceConfig.capabilities)

                        postAuthenticated(authType: .existingAccount)

                        return Event.fetchProfile(ignoreCache: false)
                    }
                case .recoveredFromAuthenticationProblem: do {
                        FxALog.info("Registering persistence callback")
                        requireAccount().registerPersistCallback(statePersistenceCallback)

                        FxALog.info("Initializing device")
                        requireConstellation().initDevice(
                            name: deviceConfig.name,
                            type: deviceConfig.deviceType,
                            capabilities: deviceConfig.capabilities
                        )

                        postAuthenticated(authType: .recovered)

                        return Event.fetchProfile(ignoreCache: false)
                    }
                case let .changedPassword(newSessionToken): do {
                        do {
                            try requireAccount().handleSessionTokenChange(sessionToken: newSessionToken)

                            FxALog.info("Initializing device")
                            requireConstellation().initDevice(
                                name: deviceConfig.name,
                                type: deviceConfig.deviceType,
                                capabilities: deviceConfig.capabilities
                            )

                            postAuthenticated(authType: .existingAccount)

                            return Event.fetchProfile(ignoreCache: false)
                        } catch {
                            FxALog.error("Error handling the session token change: \(error)")
                        }
                    }
                case let .fetchProfile(ignoreCache): do {
                        // Profile fetching and account authentication issues:
                        // https://github.com/mozilla/application-services/issues/483
                        FxALog.info("Fetching profile...")

                        do {
                            profile = try requireAccount().getProfile(ignoreCache: ignoreCache)
                        } catch {
                            return Event.failedToFetchProfile
                        }
                        return Event.fetchedProfile
                    }
                default: break // Do nothing
                }
            }
        case .authenticatedWithProfile: do {
                switch via {
                case .fetchedProfile: do {
                        DispatchQueue.main.async {
                            NotificationCenter.default.post(
                                name: .accountProfileUpdate,
                                object: nil,
                                userInfo: ["profile": self.profile!]
                            )
                        }
                    }
                case let .fetchProfile(refresh): do {
                        FxALog.info("Refreshing profile...")
                        do {
                            profile = try requireAccount().getProfile(ignoreCache: refresh)
                        } catch {
                            return Event.failedToFetchProfile
                        }
                        return Event.fetchedProfile
                    }
                default: break // Do nothing
                }
            }
        case .authenticationProblem:
            switch via {
            case .authenticationError: do {
                    // Somewhere in the system, we've just hit an authentication problem.
                    // There are two main causes:
                    // 1) an access token we've obtain from fxalib via 'getAccessToken' expired
                    // 2) password was changed, or device was revoked
                    // We can recover from (1) and test if we're in (2) by asking the fxalib.
                    // If it succeeds, then we can go back to whatever
                    // state we were in before. Future operations that involve access tokens should
                    // succeed.

                    func onError() {
                        // We are either certainly in the scenario (2), or were unable to determine
                        // our connectivity state. Let's assume we need to re-authenticate.
                        // This uncertainty about real state means that, hopefully rarely,
                        // we will disconnect users that hit transient network errors during
                        // an authorization check.
                        // See https://github.com/mozilla-mobile/android-components/issues/3347
                        FxALog.error("Unable to recover from an auth problem.")
                        DispatchQueue.main.async {
                            NotificationCenter.default.post(
                                name: .accountAuthProblems,
                                object: nil
                            )
                        }
                    }

                    do {
                        let account = requireAccount()
                        let info = try account.checkAuthorizationStatus()
                        if !info.active {
                            onError()
                            return nil
                        }
                        account.clearAccessTokenCache()
                        // Make sure we're back on track by re-requesting the profile access token.
                        _ = try account.getAccessToken(scope: OAuthScope.profile)
                        return .recoveredFromAuthenticationProblem
                    } catch {
                        onError()
                    }
                    return nil
                }
            default: break // Do nothing
            }
        }
        return nil
    }