mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 10:33:45 +00:00
Simplify ClassicAppAccount loading (only load what we need).
This commit is contained in:
+77
-145
@@ -8,38 +8,23 @@
|
||||
import Foundation
|
||||
|
||||
class ClassicAppAccountManager {
|
||||
static let matrixKitFolder = "MatrixKit"
|
||||
static let kMXKAccountsKey = "accountsV2"
|
||||
static let kMXFileStoreFolder = "MXFileStore"
|
||||
static let kMXFileStoreUsersFolder = "users"
|
||||
static let cryptoStoreFolder = "MXCryptoStore"
|
||||
private let cacheFolder: URL
|
||||
private let aesKey: Data
|
||||
private let iv: Data
|
||||
private let cryptoStorePassphrase: Data
|
||||
|
||||
let cacheFolder: URL
|
||||
let iv: Data
|
||||
let aesKey: Data
|
||||
private(set) var accounts: [ClassicAppAccount] = []
|
||||
|
||||
private(set) var accounts: [ClassicAppMXAccount] = []
|
||||
private var users: [String: ClassicAppMXUser] = [:]
|
||||
|
||||
var activeAccounts: [ClassicAppAccount] {
|
||||
accounts
|
||||
.filter { !$0.isDisabled && !$0.isSoftLogout }
|
||||
.compactMap(activeAccount)
|
||||
}
|
||||
|
||||
init(cacheFolder: URL, iv: Data, aesKey: Data) {
|
||||
init(cacheFolder: URL, aesKey: Data, iv: Data, cryptoStorePassphrase: Data) {
|
||||
self.cacheFolder = cacheFolder
|
||||
self.iv = iv
|
||||
self.aesKey = aesKey
|
||||
}
|
||||
|
||||
/// Return the path of the file containing stored MXAccounts array
|
||||
func accountFile() -> URL {
|
||||
cacheFolder.appending(component: Self.matrixKitFolder).appending(component: Self.kMXKAccountsKey)
|
||||
self.iv = iv
|
||||
self.cryptoStorePassphrase = cryptoStorePassphrase
|
||||
}
|
||||
|
||||
func loadAccounts() {
|
||||
MXLog.info("Loading accounts from Classic app.")
|
||||
|
||||
let accountFile = accountFile()
|
||||
if FileManager.default.fileExists(atPath: accountFile.path(percentEncoded: false)) {
|
||||
let startDate = Date()
|
||||
@@ -51,103 +36,58 @@ class ClassicAppAccountManager {
|
||||
let unciphered = try ClassicAppAES.decrypt(fileContent, aesKey: aesKey, iv: iv)
|
||||
let decoder = NSKeyedUnarchiver(forReadingWith: unciphered)
|
||||
decoder.setClass(ClassicAppMXAccount.self, forClassName: "MXKAccount")
|
||||
decoder.setClass(ClassicAppMXThirdPartyIdentifier.self, forClassName: "MXThirdPartyIdentifier")
|
||||
decoder.setClass(ClassicAppMXDevice.self, forClassName: "MXDevice")
|
||||
|
||||
guard let accounts = decoder.decodeObject(forKey: "mxAccounts") as? [ClassicAppMXAccount] else {
|
||||
guard let mxAccounts = decoder.decodeObject(forKey: "mxAccounts") as? [ClassicAppMXAccount] else {
|
||||
MXLog.error("Failed to decode accounts.")
|
||||
return
|
||||
}
|
||||
|
||||
self.accounts = accounts
|
||||
MXLog.info("\(mxAccounts.count) accounts loaded in \(Date().timeIntervalSince(startDate) * 1000)ms.")
|
||||
|
||||
MXLog.info("[MXKAccountManager] loadAccounts. \(accounts.count) accounts loaded in \(Date().timeIntervalSince(startDate) * 1000)ms")
|
||||
accounts = mxAccounts
|
||||
.filter(\.isActive)
|
||||
.compactMap(makeAccount)
|
||||
|
||||
MXLog.info("\(mxAccounts.count) active accounts available.")
|
||||
} catch {
|
||||
MXLog.error("Failed to load account file: \(error)")
|
||||
}
|
||||
|
||||
for account in activeAccounts {
|
||||
if let user = loadUsers([account.userID], forAccount: account.userID).first {
|
||||
users[user.userID] = user
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func makeAccount(for mxAccount: ClassicAppMXAccount) -> ClassicAppAccount? {
|
||||
let userID = mxAccount.userID
|
||||
let user = loadUser(for: mxAccount)
|
||||
|
||||
if accounts.isEmpty {
|
||||
MXLog.info("[MXKAccountManager] loadAccounts. No accounts")
|
||||
}
|
||||
}
|
||||
|
||||
/// From `MXCryptoMachineStore`
|
||||
func cryptoStoreURL(for userID: String) -> URL {
|
||||
cacheFolder.appending(component: Self.cryptoStoreFolder).appending(component: userID)
|
||||
}
|
||||
|
||||
var fileStorePath: URL {
|
||||
cacheFolder.appending(component: Self.kMXFileStoreFolder)
|
||||
}
|
||||
|
||||
func storePath(for userID: String) -> URL {
|
||||
fileStorePath.appending(component: userID)
|
||||
}
|
||||
|
||||
/// This store contains all of the users known to the specific user ID.
|
||||
func storeUsersPath(for userID: String) -> URL {
|
||||
storePath(for: userID).appending(component: Self.kMXFileStoreUsersFolder)
|
||||
}
|
||||
|
||||
func loadUsers(_ userIDs: [String], forAccount accountUserID: String) -> [ClassicAppMXUser] {
|
||||
// Determine which groups to load based on userIds
|
||||
var groups: [String: [String]] = [:]
|
||||
for userID in userIDs {
|
||||
let groupID = String(userID.hash % 100)
|
||||
|
||||
if groups[groupID] != nil {
|
||||
groups[groupID]?.append(userID)
|
||||
} else {
|
||||
groups[groupID] = [userID]
|
||||
}
|
||||
}
|
||||
|
||||
let usersFolder = storeUsersPath(for: accountUserID)
|
||||
|
||||
var loadedUsers: [ClassicAppMXUser] = []
|
||||
for group in groups.keys {
|
||||
autoreleasepool {
|
||||
let groupFile = usersFolder.appendingPathComponent(group)
|
||||
|
||||
// Load stored users in this group
|
||||
do {
|
||||
let fileContent = try Data(contentsOf: groupFile)
|
||||
|
||||
let decoder = NSKeyedUnarchiver(forReadingWith: fileContent)
|
||||
decoder.setClass(ClassicAppMXUser.self, forClassName: "MXUser")
|
||||
decoder.setClass(ClassicAppMXUser.self, forClassName: "MXMyUser")
|
||||
|
||||
if let groupUsers = decoder.decodeObject(forKey: NSKeyedArchiveRootObjectKey) as? [String: ClassicAppMXUser] {
|
||||
let usersToLoad = Set(groups[group] ?? [])
|
||||
for user in groupUsers.values where usersToLoad.contains(user.userID) {
|
||||
loadedUsers.append(user)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
MXLog.warning("[MXFileStore] Warning: MXFileRoomStore file for users group \(group) has been corrupted")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return loadedUsers
|
||||
}
|
||||
|
||||
private func activeAccount(mxAccount: ClassicAppMXAccount) -> ClassicAppAccount? {
|
||||
guard let userID = mxAccount.credentials.userID, let serverName = serverName(for: userID) else {
|
||||
return nil
|
||||
}
|
||||
guard let serverName = serverName(for: userID) else { return nil }
|
||||
|
||||
return ClassicAppAccount(userID: userID,
|
||||
displayName: users[userID]?.displayName,
|
||||
displayName: user?.displayName,
|
||||
avatarURL: user?.avatarURL.flatMap(URL.init(string:)),
|
||||
serverName: serverName,
|
||||
cryptoStoreURL: cryptoStoreURL(for: userID))
|
||||
cryptoStoreURL: cryptoStoreURL(for: userID),
|
||||
cryptoStorePassphrase: cryptoStorePassphrase)
|
||||
}
|
||||
|
||||
private func loadUser(for mxAccount: ClassicAppMXAccount) -> ClassicAppMXUser? {
|
||||
let userID = mxAccount.userID
|
||||
let groupID = String(userID.hash % 100)
|
||||
let groupFile = storeUsersPath(for: userID).appendingPathComponent(groupID)
|
||||
|
||||
do {
|
||||
let fileContent = try Data(contentsOf: groupFile)
|
||||
let decoder = NSKeyedUnarchiver(forReadingWith: fileContent)
|
||||
decoder.setClass(ClassicAppMXUser.self, forClassName: "MXUser")
|
||||
decoder.setClass(ClassicAppMXUser.self, forClassName: "MXMyUser")
|
||||
|
||||
let groupUsers = decoder.decodeObject(forKey: NSKeyedArchiveRootObjectKey) as? [String: ClassicAppMXUser]
|
||||
return groupUsers?[userID]
|
||||
} catch {
|
||||
MXLog.warning("Users group \(groupID) file for \(mxAccount.userID) has been corrupted.")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/// The server name extracted from the user's ID.
|
||||
@@ -157,43 +97,35 @@ class ClassicAppAccountManager {
|
||||
guard components.count > 1 else { return nil }
|
||||
return components[1] // Directly use [1] as .last may be the port number.
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Probably not needed
|
||||
|
||||
private extension ClassicAppAccountManager {
|
||||
func loadUsers(forAccount accountUserID: String) {
|
||||
let startDate = Date()
|
||||
var users: [String: ClassicAppMXUser] = [:]
|
||||
|
||||
// Load all users which are distributed in several files
|
||||
let storeUsersPath = storeUsersPath(for: accountUserID)
|
||||
let groups = try? FileManager.default.contentsOfDirectory(atPath: storeUsersPath.path(percentEncoded: false))
|
||||
|
||||
if let groups {
|
||||
for group in groups {
|
||||
let groupFile = storeUsersPath.appending(path: group)
|
||||
|
||||
// Load stored users in this group
|
||||
do {
|
||||
let fileContent = try Data(contentsOf: groupFile)
|
||||
|
||||
let decoder = NSKeyedUnarchiver(forReadingWith: fileContent)
|
||||
decoder.setClass(ClassicAppMXUser.self, forClassName: "MXUser")
|
||||
decoder.setClass(ClassicAppMXUser.self, forClassName: "MXMyUser")
|
||||
|
||||
if let groupUsers = decoder.decodeObject(forKey: NSKeyedArchiveRootObjectKey) as? [String: ClassicAppMXUser] {
|
||||
// Append them
|
||||
users.merge(groupUsers) { _, new in new }
|
||||
}
|
||||
} catch {
|
||||
MXLog.error("Failed to load users from group \(group): \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MXLog.debug("[MXFileStore] Loaded \(users.count) MXUsers in \(Date().timeIntervalSince(startDate) * 1000)ms")
|
||||
|
||||
self.users = users
|
||||
|
||||
// MARK: - File URLs
|
||||
|
||||
private static let matrixKitFolder = "MatrixKit"
|
||||
private static let cryptoStoreFolder = "MXCryptoStore"
|
||||
private static let kMXKAccountsKey = "accountsV2"
|
||||
private static let kMXFileStoreFolder = "MXFileStore"
|
||||
private static let kMXFileStoreUsersFolder = "users"
|
||||
|
||||
/// The file URL that contains the app's `MXAccounts` array.
|
||||
private func accountFile() -> URL {
|
||||
cacheFolder.appending(component: Self.matrixKitFolder).appending(component: Self.kMXKAccountsKey)
|
||||
}
|
||||
|
||||
/// The database file URL as defined in `MXCryptoMachineStore`.
|
||||
private func cryptoStoreURL(for userID: String) -> URL {
|
||||
cacheFolder.appending(component: Self.cryptoStoreFolder).appending(component: userID)
|
||||
}
|
||||
|
||||
/// This store contains all of the users known to the specific user ID.
|
||||
private func storeUsersPath(for userID: String) -> URL {
|
||||
storePath(for: userID).appending(component: Self.kMXFileStoreUsersFolder)
|
||||
}
|
||||
|
||||
private func storePath(for userID: String) -> URL {
|
||||
fileStorePath.appending(component: userID)
|
||||
}
|
||||
|
||||
private var fileStorePath: URL {
|
||||
cacheFolder.appending(component: Self.kMXFileStoreFolder)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user