Clean up account switching code

This commit is contained in:
Shadowfacts 2020-01-19 11:52:06 -05:00
parent 863867c522
commit c45dd99088
Signed by untrusted user: shadowfacts
GPG Key ID: 94A5AB95422746E5
5 changed files with 66 additions and 32 deletions

View File

@ -21,6 +21,7 @@ class LocalData: ObservableObject {
if ProcessInfo.processInfo.environment.keys.contains("UI_TESTING_LOGIN") { if ProcessInfo.processInfo.environment.keys.contains("UI_TESTING_LOGIN") {
accounts = [ accounts = [
UserAccountInfo( UserAccountInfo(
id: UUID().uuidString,
instanceURL: URL(string: "http://localhost:8080")!, instanceURL: URL(string: "http://localhost:8080")!,
clientID: "client_id", clientID: "client_id",
clientSecret: "client_secret", clientSecret: "client_secret",
@ -38,15 +39,16 @@ class LocalData: ObservableObject {
get { get {
if let array = defaults.array(forKey: accountsKey) as? [[String: String]] { if let array = defaults.array(forKey: accountsKey) as? [[String: String]] {
return array.compactMap { (info) in 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 url = URL(string: instanceURL),
let id = info["clientID"], let clientId = info["clientID"],
let secret = info["clientSecret"], let secret = info["clientSecret"],
let username = info["username"], let username = info["username"],
let accessToken = info["accessToken"] else { let accessToken = info["accessToken"] else {
return nil 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 { } else {
return [] return []
@ -56,6 +58,7 @@ class LocalData: ObservableObject {
objectWillChange.send() objectWillChange.send()
let array = newValue.map { (info) in let array = newValue.map { (info) in
return [ return [
"id": info.id,
"instanceURL": info.instanceURL.absoluteString, "instanceURL": info.instanceURL.absoluteString,
"clientID": info.clientID, "clientID": info.clientID,
"clientSecret": info.clientSecret, "clientSecret": info.clientSecret,
@ -68,7 +71,7 @@ class LocalData: ObservableObject {
} }
private let mostRecentAccountKey = "mostRecentAccount" private let mostRecentAccountKey = "mostRecentAccount"
var mostRecentAccount: String? { private var mostRecentAccount: String? {
get { get {
return defaults.string(forKey: mostRecentAccountKey) return defaults.string(forKey: mostRecentAccountKey)
} }
@ -82,41 +85,60 @@ class LocalData: ObservableObject {
return !accounts.isEmpty 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 var accounts = self.accounts
if let index = accounts.firstIndex(where: { $0.instanceURL == url && $0.username == username }) { if let index = accounts.firstIndex(where: { $0.instanceURL == url && $0.username == username }) {
accounts.remove(at: index) 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) accounts.append(info)
self.accounts = accounts self.accounts = accounts
return info return info
} }
func removeAccount(_ info: UserAccountInfo) { func removeAccount(_ info: UserAccountInfo) {
accounts.removeAll(where: { $0.id == info.id })
} }
func getMostRecentAccount() -> UserAccountInfo? { func getMostRecentAccount() -> UserAccountInfo? {
if let accessToken = mostRecentAccount { guard onboardingComplete else { return nil }
return accounts.first { $0.accessToken == accessToken } let mostRecent: UserAccountInfo?
if let id = mostRecentAccount {
mostRecent = accounts.first { $0.id == id }
} else { } else {
return nil mostRecent = nil
} }
return mostRecent ?? accounts.first!
}
func setMostRecentAccount(_ account: UserAccountInfo?) {
mostRecentAccount = account?.id
} }
} }
extension LocalData { extension LocalData {
struct UserAccountInfo: Equatable, Hashable { struct UserAccountInfo: Equatable, Hashable {
let id: String
let instanceURL: URL let instanceURL: URL
let clientID: String let clientID: String
let clientSecret: String let clientSecret: String
let username: String let username: String
let accessToken: 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 { 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")
} }

View File

@ -34,7 +34,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
window!.makeKeyAndVisible() window!.makeKeyAndVisible()
NotificationCenter.default.addObserver(self, selector: #selector(onUserLoggedOut), name: .userLoggedOut, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(themePrefChanged), name: .themePreferenceChanged, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(themePrefChanged), name: .themePreferenceChanged, object: nil)
themePrefChanged() themePrefChanged()
@ -110,11 +109,20 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
} }
func activateAccount(_ account: LocalData.UserAccountInfo) { func activateAccount(_ account: LocalData.UserAccountInfo) {
LocalData.shared.mostRecentAccount = account.accessToken LocalData.shared.setMostRecentAccount(account)
window!.windowScene!.session.mastodonController = MastodonController.getForAccount(account) window!.windowScene!.session.mastodonController = MastodonController.getForAccount(account)
showAppUI() showAppUI()
} }
func logoutCurrent() {
LocalData.shared.removeAccount(LocalData.shared.getMostRecentAccount()!)
if LocalData.shared.onboardingComplete {
activateAccount(LocalData.shared.accounts.first!)
} else {
showOnboardingUI()
}
}
func showAppUI() { func showAppUI() {
let mastodonController = window!.windowScene!.session.mastodonController! let mastodonController = window!.windowScene!.session.mastodonController!
mastodonController.getOwnAccount() mastodonController.getOwnAccount()
@ -130,10 +138,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
window!.rootViewController = onboarding window!.rootViewController = onboarding
} }
@objc func onUserLoggedOut() {
showOnboardingUI()
}
@objc func themePrefChanged() { @objc func themePrefChanged() {
window?.overrideUserInterfaceStyle = Preferences.shared.theme window?.overrideUserInterfaceStyle = Preferences.shared.theme
} }

View File

@ -69,9 +69,9 @@ extension OnboardingViewController: InstanceSelectorTableViewControllerDelegate
mastodonController.authorize(authorizationCode: authCode) { (accessToken) in mastodonController.authorize(authorizationCode: authCode) { (accessToken) in
mastodonController.getOwnAccount { (account) in mastodonController.getOwnAccount { (account) in
let accountInfo = LocalData.shared.addAccount(instanceURL: instanceURL, clientID: clientID, clientSecret: clientSecret, username: account.username, accessToken: accessToken)
DispatchQueue.main.async { DispatchQueue.main.async {
let accountInfo = LocalData.shared.addAccount(instanceURL: instanceURL, clientID: clientID, clientSecret: clientSecret, username: account.username, accessToken: accessToken)
self.onboardingDelegate?.didFinishOnboarding(account: accountInfo) self.onboardingDelegate?.didFinishOnboarding(account: accountInfo)
} }
} }

View File

@ -27,6 +27,7 @@ class PreferencesNavigationController: UINavigationController {
NotificationCenter.default.addObserver(self, selector: #selector(showAddAccount), name: .addAccount, object: nil) 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(activateAccount(_:)), name: .activateAccount, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(userLoggedOut), name: .userLoggedOut, object: nil)
} }
override func viewWillDisappear(_ animated: Bool) { override func viewWillDisappear(_ animated: Bool) {
@ -48,26 +49,35 @@ class PreferencesNavigationController: UINavigationController {
} }
@objc func cancelAddAccount() { @objc func cancelAddAccount() {
dismiss(animated: true) dismiss(animated: true) // dismisses instance selector
} }
@objc func activateAccount(_ notification: Notification) { @objc func activateAccount(_ notification: Notification) {
let account = notification.userInfo!["account"] as! LocalData.UserAccountInfo let account = notification.userInfo!["account"] as! LocalData.UserAccountInfo
let sceneDelegate = self.view.window!.windowScene!.delegate as! SceneDelegate let sceneDelegate = self.view.window!.windowScene!.delegate as! SceneDelegate
dismiss(animated: true) { dismiss(animated: true) { // dismiss preferences
sceneDelegate.activateAccount(account) 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 { extension PreferencesNavigationController: OnboardingViewControllerDelegate {
func didFinishOnboarding(account: LocalData.UserAccountInfo) { 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)
}
}
}
} }
} }

View File

@ -10,7 +10,7 @@ import SwiftUI
struct PreferencesView: View { struct PreferencesView: View {
@ObservedObject var localData = LocalData.shared @ObservedObject var localData = LocalData.shared
@State private var showingLogoutConfirmation = false @State private var showingLogoutConfirmation = false
var body: some View { var body: some View {
// workaround: the navigation view is provided by MyProfileTableViewController so that it can inject the Done button // workaround: the navigation view is provided by MyProfileTableViewController so that it can inject the Done button
// NavigationView { // NavigationView {
@ -24,7 +24,7 @@ struct PreferencesView: View {
Text(account.username) Text(account.username)
.foregroundColor(.primary) .foregroundColor(.primary)
Spacer() Spacer()
if account.accessToken == self.localData.mostRecentAccount { if account == self.localData.getMostRecentAccount() {
Image(systemName: "checkmark") Image(systemName: "checkmark")
.renderingMode(.template) .renderingMode(.template)
.foregroundColor(.secondary) .foregroundColor(.secondary)
@ -37,7 +37,7 @@ struct PreferencesView: View {
}) { }) {
Text("Add Account...") Text("Add Account...")
} }
if localData.mostRecentAccount != nil { if localData.getMostRecentAccount() != nil {
Button(action: { Button(action: {
self.showingLogoutConfirmation = true self.showingLogoutConfirmation = true
}) { }) {
@ -73,8 +73,6 @@ struct PreferencesView: View {
} }
func logoutPressed() { func logoutPressed() {
// LocalData.shared.removeAccount(currentAccount)
localData.removeAccount(localData.getMostRecentAccount()!)
NotificationCenter.default.post(name: .userLoggedOut, object: nil) NotificationCenter.default.post(name: .userLoggedOut, object: nil)
} }
} }