From c45dd9908853a74262e86c43638be287a85bba14 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Sun, 19 Jan 2020 11:52:06 -0500 Subject: [PATCH] Clean up account switching code --- Tusker/LocalData.swift | 44 ++++++++++++++----- Tusker/SceneDelegate.swift | 16 ++++--- .../Onboarding/OnboardingViewController.swift | 4 +- .../PreferencesNavigationController.swift | 26 +++++++---- .../Screens/Preferences/PreferencesView.swift | 8 ++-- 5 files changed, 66 insertions(+), 32 deletions(-) diff --git a/Tusker/LocalData.swift b/Tusker/LocalData.swift index f3954fdd..7aafa461 100644 --- a/Tusker/LocalData.swift +++ b/Tusker/LocalData.swift @@ -21,6 +21,7 @@ class LocalData: ObservableObject { if ProcessInfo.processInfo.environment.keys.contains("UI_TESTING_LOGIN") { accounts = [ UserAccountInfo( + id: UUID().uuidString, instanceURL: URL(string: "http://localhost:8080")!, clientID: "client_id", clientSecret: "client_secret", @@ -38,15 +39,16 @@ class LocalData: ObservableObject { get { if let array = defaults.array(forKey: accountsKey) as? [[String: String]] { return array.compactMap { (info) in - guard let instanceURL = info["instanceURL"], + guard let id = info["id"], + let instanceURL = info["instanceURL"], let url = URL(string: instanceURL), - let id = info["clientID"], + let clientId = info["clientID"], let secret = info["clientSecret"], let username = info["username"], let accessToken = info["accessToken"] else { return nil } - return UserAccountInfo(instanceURL: url, clientID: id, clientSecret: secret, username: username, accessToken: accessToken) + return UserAccountInfo(id: id, instanceURL: url, clientID: clientId, clientSecret: secret, username: username, accessToken: accessToken) } } else { return [] @@ -56,6 +58,7 @@ class LocalData: ObservableObject { objectWillChange.send() let array = newValue.map { (info) in return [ + "id": info.id, "instanceURL": info.instanceURL.absoluteString, "clientID": info.clientID, "clientSecret": info.clientSecret, @@ -68,7 +71,7 @@ class LocalData: ObservableObject { } private let mostRecentAccountKey = "mostRecentAccount" - var mostRecentAccount: String? { + private var mostRecentAccount: String? { get { return defaults.string(forKey: mostRecentAccountKey) } @@ -82,41 +85,60 @@ class LocalData: ObservableObject { return !accounts.isEmpty } - func addAccount(instanceURL url: URL, clientID id: String, clientSecret secret: String, username: String, accessToken: String) -> UserAccountInfo { + func addAccount(instanceURL url: URL, clientID: String, clientSecret secret: String, username: String, accessToken: String) -> UserAccountInfo { var accounts = self.accounts if let index = accounts.firstIndex(where: { $0.instanceURL == url && $0.username == username }) { accounts.remove(at: index) } - let info = UserAccountInfo(instanceURL: url, clientID: id, clientSecret: secret, username: username, accessToken: accessToken) + let id = UUID().uuidString + let info = UserAccountInfo(id: id, instanceURL: url, clientID: clientID, clientSecret: secret, username: username, accessToken: accessToken) accounts.append(info) self.accounts = accounts return info } func removeAccount(_ info: UserAccountInfo) { - + accounts.removeAll(where: { $0.id == info.id }) } func getMostRecentAccount() -> UserAccountInfo? { - if let accessToken = mostRecentAccount { - return accounts.first { $0.accessToken == accessToken } + guard onboardingComplete else { return nil } + let mostRecent: UserAccountInfo? + if let id = mostRecentAccount { + mostRecent = accounts.first { $0.id == id } } else { - return nil + mostRecent = nil } + return mostRecent ?? accounts.first! + } + + func setMostRecentAccount(_ account: UserAccountInfo?) { + mostRecentAccount = account?.id } } extension LocalData { struct UserAccountInfo: Equatable, Hashable { + let id: String let instanceURL: URL let clientID: String let clientSecret: String let username: String let accessToken: String + + func hash(into hasher: inout Hasher) { + hasher.combine(id) + } + + static func ==(lhs: UserAccountInfo, rhs: UserAccountInfo) -> Bool { + return lhs.id == rhs.id + } } } extension Notification.Name { - static let userLoggedOut = Notification.Name("userLoggedOut") + static let userLoggedOut = Notification.Name("Tusker.userLoggedOut") + static let addAccount = Notification.Name("Tusker.addAccount") + static let activateAccount = Notification.Name("Tusker.activateAccount") } diff --git a/Tusker/SceneDelegate.swift b/Tusker/SceneDelegate.swift index d4b939fb..b2a172a2 100644 --- a/Tusker/SceneDelegate.swift +++ b/Tusker/SceneDelegate.swift @@ -34,7 +34,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { window!.makeKeyAndVisible() - NotificationCenter.default.addObserver(self, selector: #selector(onUserLoggedOut), name: .userLoggedOut, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(themePrefChanged), name: .themePreferenceChanged, object: nil) themePrefChanged() @@ -110,11 +109,20 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } func activateAccount(_ account: LocalData.UserAccountInfo) { - LocalData.shared.mostRecentAccount = account.accessToken + LocalData.shared.setMostRecentAccount(account) window!.windowScene!.session.mastodonController = MastodonController.getForAccount(account) showAppUI() } + func logoutCurrent() { + LocalData.shared.removeAccount(LocalData.shared.getMostRecentAccount()!) + if LocalData.shared.onboardingComplete { + activateAccount(LocalData.shared.accounts.first!) + } else { + showOnboardingUI() + } + } + func showAppUI() { let mastodonController = window!.windowScene!.session.mastodonController! mastodonController.getOwnAccount() @@ -130,10 +138,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { window!.rootViewController = onboarding } - @objc func onUserLoggedOut() { - showOnboardingUI() - } - @objc func themePrefChanged() { window?.overrideUserInterfaceStyle = Preferences.shared.theme } diff --git a/Tusker/Screens/Onboarding/OnboardingViewController.swift b/Tusker/Screens/Onboarding/OnboardingViewController.swift index e799e2a6..9bb2af60 100644 --- a/Tusker/Screens/Onboarding/OnboardingViewController.swift +++ b/Tusker/Screens/Onboarding/OnboardingViewController.swift @@ -69,9 +69,9 @@ extension OnboardingViewController: InstanceSelectorTableViewControllerDelegate mastodonController.authorize(authorizationCode: authCode) { (accessToken) in mastodonController.getOwnAccount { (account) in - let accountInfo = LocalData.shared.addAccount(instanceURL: instanceURL, clientID: clientID, clientSecret: clientSecret, username: account.username, accessToken: accessToken) - DispatchQueue.main.async { + let accountInfo = LocalData.shared.addAccount(instanceURL: instanceURL, clientID: clientID, clientSecret: clientSecret, username: account.username, accessToken: accessToken) + self.onboardingDelegate?.didFinishOnboarding(account: accountInfo) } } diff --git a/Tusker/Screens/Preferences/PreferencesNavigationController.swift b/Tusker/Screens/Preferences/PreferencesNavigationController.swift index c4a6c12e..5b93c231 100644 --- a/Tusker/Screens/Preferences/PreferencesNavigationController.swift +++ b/Tusker/Screens/Preferences/PreferencesNavigationController.swift @@ -27,6 +27,7 @@ class PreferencesNavigationController: UINavigationController { NotificationCenter.default.addObserver(self, selector: #selector(showAddAccount), name: .addAccount, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(activateAccount(_:)), name: .activateAccount, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(userLoggedOut), name: .userLoggedOut, object: nil) } override func viewWillDisappear(_ animated: Bool) { @@ -48,26 +49,35 @@ class PreferencesNavigationController: UINavigationController { } @objc func cancelAddAccount() { - dismiss(animated: true) + dismiss(animated: true) // dismisses instance selector } @objc func activateAccount(_ notification: Notification) { let account = notification.userInfo!["account"] as! LocalData.UserAccountInfo let sceneDelegate = self.view.window!.windowScene!.delegate as! SceneDelegate - dismiss(animated: true) { + dismiss(animated: true) { // dismiss preferences sceneDelegate.activateAccount(account) } } + + @objc func userLoggedOut() { + let sceneDelegate = self.view.window!.windowScene!.delegate as! SceneDelegate + dismiss(animated: true) { // dismiss preferences + sceneDelegate.logoutCurrent() + } + } } -extension Notification.Name { - static let addAccount = Notification.Name("Tusker.addAccount") - static let activateAccount = Notification.Name("Tusker.activateAccount") -} - extension PreferencesNavigationController: OnboardingViewControllerDelegate { func didFinishOnboarding(account: LocalData.UserAccountInfo) { - LocalData.shared.mostRecentAccount = account.accessToken + DispatchQueue.main.async { + let sceneDelegate = self.view.window!.windowScene!.delegate as! SceneDelegate + self.dismiss(animated: true) { // dismiss instance selector + self.dismiss(animated: true) { // dismiss preferences + sceneDelegate.activateAccount(account) + } + } + } } } diff --git a/Tusker/Screens/Preferences/PreferencesView.swift b/Tusker/Screens/Preferences/PreferencesView.swift index 38cbf993..b30daa52 100644 --- a/Tusker/Screens/Preferences/PreferencesView.swift +++ b/Tusker/Screens/Preferences/PreferencesView.swift @@ -10,7 +10,7 @@ import SwiftUI struct PreferencesView: View { @ObservedObject var localData = LocalData.shared @State private var showingLogoutConfirmation = false - + var body: some View { // workaround: the navigation view is provided by MyProfileTableViewController so that it can inject the Done button // NavigationView { @@ -24,7 +24,7 @@ struct PreferencesView: View { Text(account.username) .foregroundColor(.primary) Spacer() - if account.accessToken == self.localData.mostRecentAccount { + if account == self.localData.getMostRecentAccount() { Image(systemName: "checkmark") .renderingMode(.template) .foregroundColor(.secondary) @@ -37,7 +37,7 @@ struct PreferencesView: View { }) { Text("Add Account...") } - if localData.mostRecentAccount != nil { + if localData.getMostRecentAccount() != nil { Button(action: { self.showingLogoutConfirmation = true }) { @@ -73,8 +73,6 @@ struct PreferencesView: View { } func logoutPressed() { -// LocalData.shared.removeAccount(currentAccount) - localData.removeAccount(localData.getMostRecentAccount()!) NotificationCenter.default.post(name: .userLoggedOut, object: nil) } }