// // UserAccountInfo.swift // UserAccounts // // Created by Shadowfacts on 3/5/23. // import Foundation import CryptoKit public struct UserAccountInfo: Equatable, Hashable, Identifiable { public let id: String public let instanceURL: URL public let clientID: String public let clientSecret: String public private(set) var username: String! public let accessToken: String fileprivate static let tempAccountID = "temp" static func id(instanceURL: URL, username: String?) -> String { // We hash the instance host and username to form the account ID // so that account IDs will match across devices, allowing for data syncing and handoff. var hasher = SHA256() hasher.update(data: instanceURL.host!.data(using: .utf8)!) if let username { hasher.update(data: username.data(using: .utf8)!) } return Data(hasher.finalize()).base64EncodedString() } /// Only to be used for temporary MastodonController needed to fetch own account info and create final UserAccountInfo with real username public init(tempInstanceURL instanceURL: URL, clientID: String, clientSecret: String, accessToken: String) { self.id = UserAccountInfo.tempAccountID self.instanceURL = instanceURL self.clientID = clientID self.clientSecret = clientSecret self.accessToken = accessToken } init(instanceURL: URL, clientID: String, clientSecret: String, username: String? = nil, accessToken: String) { self.id = UserAccountInfo.id(instanceURL: instanceURL, username: username) self.instanceURL = instanceURL self.clientID = clientID self.clientSecret = clientSecret self.username = username self.accessToken = accessToken } init?(userDefaultsDict dict: [String: String]) { guard let id = dict["id"], let instanceURL = dict["instanceURL"], let url = URL(string: instanceURL), let clientID = dict["clientID"], let secret = dict["clientSecret"], let accessToken = dict["accessToken"] else { return nil } self.id = id self.instanceURL = url self.clientID = clientID self.clientSecret = secret self.username = dict["username"] self.accessToken = accessToken } /// A filename-safe string for this account public var persistenceKey: String { // slashes are not allowed in the persistent store coordinator name id.replacingOccurrences(of: "/", with: "_") } public func hash(into hasher: inout Hasher) { hasher.combine(id) } public static func ==(lhs: UserAccountInfo, rhs: UserAccountInfo) -> Bool { return lhs.id == rhs.id } }