Use deterministic ids for accounts
This commit is contained in:
parent
dec7a6e57f
commit
7f5006c629
|
@ -7,10 +7,11 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
public struct Token: Decodable {
|
||||
public struct Token: Codable, Sendable {
|
||||
public let accessToken: String
|
||||
public let expiresIn: Int?
|
||||
public let refreshToken: String?
|
||||
public let owner: String?
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
@ -18,11 +19,21 @@ public struct Token: Decodable {
|
|||
self.accessToken = try container.decode(String.self, forKey: .accessToken)
|
||||
self.expiresIn = try container.decodeIfPresent(Int.self, forKey: .expiresIn)
|
||||
self.refreshToken = try container.decodeIfPresent(String.self, forKey: .refreshToken)
|
||||
self.owner = try container.decodeIfPresent(String.self, forKey: .owner)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(accessToken, forKey: .accessToken)
|
||||
try container.encodeIfPresent(expiresIn, forKey: .expiresIn)
|
||||
try container.encodeIfPresent(refreshToken, forKey: .refreshToken)
|
||||
try container.encodeIfPresent(owner, forKey: .owner)
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case accessToken = "access_token"
|
||||
case expiresIn = "expires_in"
|
||||
case refreshToken = "refresh_token"
|
||||
case owner
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,9 @@ class PersistentContainer: NSPersistentContainer, @unchecked Sendable {
|
|||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "PersistentContainer")
|
||||
|
||||
init(account: LocalData.Account) {
|
||||
super.init(name: "\(account.id)", managedObjectModel: PersistentContainer.managedObjectModel)
|
||||
// slashes the base64 string turn into subdirectories which we don't want
|
||||
let name = account.id.base64EncodedString().replacingOccurrences(of: "/", with: "_")
|
||||
super.init(name: name, managedObjectModel: PersistentContainer.managedObjectModel)
|
||||
|
||||
loadPersistentStores { description, error in
|
||||
if let error = error {
|
||||
|
|
|
@ -22,7 +22,7 @@ actor FervorController {
|
|||
nonisolated let account: LocalData.Account?
|
||||
private(set) var clientID: String?
|
||||
private(set) var clientSecret: String?
|
||||
private(set) var accessToken: String?
|
||||
private(set) var token: Token?
|
||||
|
||||
nonisolated let persistentContainer: PersistentContainer!
|
||||
|
||||
|
@ -32,7 +32,7 @@ actor FervorController {
|
|||
|
||||
init(instanceURL: URL, account: LocalData.Account?) async {
|
||||
self.instanceURL = instanceURL
|
||||
self.client = FervorClient(instanceURL: instanceURL, accessToken: account?.accessToken)
|
||||
self.client = FervorClient(instanceURL: instanceURL, accessToken: account?.token.accessToken)
|
||||
self.account = account
|
||||
self.clientID = account?.clientID
|
||||
self.clientSecret = account?.clientSecret
|
||||
|
@ -63,8 +63,7 @@ actor FervorController {
|
|||
}
|
||||
|
||||
func getToken(authCode: String) async throws {
|
||||
let token = try await client.token(authCode: authCode, redirectURI: FervorController.oauthRedirectURI, clientID: clientID!, clientSecret: clientSecret!)
|
||||
accessToken = token.accessToken
|
||||
token = try await client.token(authCode: authCode, redirectURI: FervorController.oauthRedirectURI, clientID: clientID!, clientSecret: clientSecret!)
|
||||
}
|
||||
|
||||
func syncAll() async throws {
|
||||
|
@ -93,6 +92,7 @@ actor FervorController {
|
|||
await ExcerptGenerator.generateAll(self)
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func syncReadToServer() async {
|
||||
var count = 0
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
//
|
||||
|
||||
import Foundation
|
||||
import Fervor
|
||||
import CryptoKit
|
||||
|
||||
struct LocalData {
|
||||
|
||||
|
@ -28,15 +30,12 @@ struct LocalData {
|
|||
}
|
||||
}
|
||||
|
||||
static var mostRecentAccountID: UUID? {
|
||||
static var mostRecentAccountID: Data? {
|
||||
get {
|
||||
guard let str = UserDefaults.standard.string(forKey: "mostRecentAccountID") else {
|
||||
return nil
|
||||
}
|
||||
return UUID(uuidString: str)
|
||||
return UserDefaults.standard.data(forKey: "mostRecentAccountID")
|
||||
}
|
||||
set {
|
||||
UserDefaults.standard.set(newValue?.uuidString, forKey: "mostRecentAccountID")
|
||||
UserDefaults.standard.set(newValue, forKey: "mostRecentAccountID")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,23 +43,32 @@ struct LocalData {
|
|||
guard let id = mostRecentAccountID else {
|
||||
return nil
|
||||
}
|
||||
return account(with: id)
|
||||
}
|
||||
|
||||
static func account(with id: Data) -> Account? {
|
||||
return accounts.first(where: { $0.id == id })
|
||||
}
|
||||
|
||||
struct Account: Codable {
|
||||
let id: UUID
|
||||
let id: Data
|
||||
let instanceURL: URL
|
||||
let clientID: String
|
||||
let clientSecret: String
|
||||
let accessToken: String
|
||||
// todo: refresh tokens
|
||||
let token: Token
|
||||
|
||||
init(instanceURL: URL, clientID: String, clientSecret: String, accessToken: String) {
|
||||
self.id = UUID()
|
||||
init(instanceURL: URL, clientID: String, clientSecret: String, token: Token) {
|
||||
// we use a hash of instance host and account id rather than random ids so that
|
||||
// user activites can uniquely identify accounts across devices
|
||||
var hasher = SHA256()
|
||||
hasher.update(data: instanceURL.host!.data(using: .utf8)!)
|
||||
hasher.update(data: token.owner!.data(using: .utf8)!)
|
||||
self.id = Data(hasher.finalize())
|
||||
|
||||
self.instanceURL = instanceURL
|
||||
self.clientID = clientID
|
||||
self.clientSecret = clientSecret
|
||||
self.accessToken = accessToken
|
||||
self.token = token
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
// Created by Shadowfacts on 10/29/21.
|
||||
//
|
||||
|
||||
@preconcurrency import Foundation
|
||||
import Foundation
|
||||
import UIKit
|
||||
import OSLog
|
||||
|
||||
|
@ -33,7 +33,9 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|||
loginVC.delegate = self
|
||||
window!.rootViewController = loginVC
|
||||
} else if activity?.activityType == NSUserActivity.activateAccountType,
|
||||
let account = LocalData.accounts.first(where: { $0.id.uuidString == activity!.userInfo?["accountID"] as? String }) {
|
||||
let idStr = activity!.userInfo?["accountID"] as? String,
|
||||
let id = Data(base64Encoded: idStr),
|
||||
let account = LocalData.account(with: id) {
|
||||
Task { @MainActor in
|
||||
fervorController = await FervorController(account: account)
|
||||
syncFromServer()
|
||||
|
@ -144,7 +146,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|||
extension SceneDelegate: LoginViewControllerDelegate {
|
||||
func didLogin(with controller: FervorController) {
|
||||
Task { @MainActor in
|
||||
let account = LocalData.Account(instanceURL: controller.instanceURL, clientID: await controller.clientID!, clientSecret: await controller.clientSecret!, accessToken: await controller.accessToken!)
|
||||
let account = LocalData.Account(instanceURL: controller.instanceURL, clientID: await controller.clientID!, clientSecret: await controller.clientSecret!, token: await controller.token!)
|
||||
LocalData.accounts.append(account)
|
||||
LocalData.mostRecentAccountID = account.id
|
||||
fervorController = await FervorController(account: account)
|
||||
|
|
Loading…
Reference in New Issue