mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 16:10:20 +00:00
Add a new AccountSettings type and compose it with AppSettings into UserSettings.
The new type doesn't contain any settings yet.
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// Copyright 2026 Element Creations Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// The settings for a signed in account, composed of the app wide settings and the settings
|
||||
/// specific to that account.
|
||||
@dynamicMemberLookup
|
||||
final nonisolated class UserSettings: Sendable {
|
||||
let app: AppSettings
|
||||
let account: AccountSettings
|
||||
|
||||
init(appSettings: AppSettings, accountSettings: AccountSettings) {
|
||||
self.app = appSettings
|
||||
self.account = accountSettings
|
||||
}
|
||||
|
||||
static func volatile() -> UserSettings {
|
||||
let appSettings = AppSettings.volatile()
|
||||
return UserSettings(appSettings: appSettings, accountSettings: .init())
|
||||
}
|
||||
|
||||
subscript<Value>(dynamicMember keyPath: ReferenceWritableKeyPath<AppSettings, Value>) -> Value {
|
||||
get { app[keyPath: keyPath] }
|
||||
set { app[keyPath: keyPath] = newValue }
|
||||
}
|
||||
|
||||
subscript<Value>(dynamicMember keyPath: KeyPath<AppSettings, Value>) -> Value {
|
||||
app[keyPath: keyPath]
|
||||
}
|
||||
|
||||
subscript<Value>(dynamicMember keyPath: ReferenceWritableKeyPath<AccountSettings, Value>) -> Value {
|
||||
get { account[keyPath: keyPath] }
|
||||
set { account[keyPath: keyPath] = newValue }
|
||||
}
|
||||
|
||||
subscript<Value>(dynamicMember keyPath: KeyPath<AccountSettings, Value>) -> Value {
|
||||
account[keyPath: keyPath]
|
||||
}
|
||||
}
|
||||
|
||||
/// Settings specific to an account.
|
||||
struct AccountSettings {
|
||||
// Placeholder for now, implementation to follow with a dedicated user defaults store scoped to a specific user ID.
|
||||
}
|
||||
@@ -15,11 +15,11 @@ class UserSession: UserSessionProtocol {
|
||||
private var authErrorCancellable: AnyCancellable?
|
||||
|
||||
let clientProxy: ClientProxyProtocol
|
||||
let mediaProvider: MediaProviderProtocol
|
||||
let userSettings: UserSettings
|
||||
|
||||
let mediaProvider: MediaProviderProtocol
|
||||
let voiceMessageMediaManager: VoiceMessageMediaManagerProtocol
|
||||
let liveLocationManager: LiveLocationManagerProtocol
|
||||
|
||||
/// Scans media content, `nil` when no content scanner is configured for the server.
|
||||
let contentScannerService: ContentScannerServiceProtocol?
|
||||
|
||||
@@ -30,8 +30,14 @@ class UserSession: UserSessionProtocol {
|
||||
sessionSecurityStateSubject.asCurrentValuePublisher()
|
||||
}
|
||||
|
||||
init(clientProxy: ClientProxyProtocol, mediaProvider: MediaProviderProtocol, voiceMessageMediaManager: VoiceMessageMediaManagerProtocol, liveLocationManager: LiveLocationManagerProtocol) {
|
||||
init(clientProxy: ClientProxyProtocol,
|
||||
userSettings: UserSettings,
|
||||
mediaProvider: MediaProviderProtocol,
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerProtocol,
|
||||
liveLocationManager: LiveLocationManagerProtocol) {
|
||||
self.clientProxy = clientProxy
|
||||
self.userSettings = userSettings
|
||||
|
||||
self.mediaProvider = mediaProvider
|
||||
self.voiceMessageMediaManager = voiceMessageMediaManager
|
||||
self.liveLocationManager = liveLocationManager
|
||||
|
||||
@@ -21,10 +21,11 @@ struct SessionSecurityState: Equatable {
|
||||
// sourcery: AutoMockable
|
||||
protocol UserSessionProtocol: Sendable {
|
||||
var clientProxy: ClientProxyProtocol { get }
|
||||
var userSettings: UserSettings { get }
|
||||
|
||||
var mediaProvider: MediaProviderProtocol { get }
|
||||
var voiceMessageMediaManager: VoiceMessageMediaManagerProtocol { get }
|
||||
var liveLocationManager: LiveLocationManagerProtocol { get }
|
||||
|
||||
/// Scans media content, `nil` when no content scanner is configured for the server.
|
||||
var contentScannerService: ContentScannerServiceProtocol? { get }
|
||||
|
||||
|
||||
@@ -59,9 +59,11 @@ class UserSessionStore: UserSessionStoreProtocol {
|
||||
return .failure(.missingCredentials)
|
||||
}
|
||||
|
||||
switch await restorePreviousLogin(credentials) {
|
||||
let userSettings = UserSettings(appSettings: appSettings, accountSettings: .init())
|
||||
|
||||
switch await restorePreviousLogin(credentials, userSettings: userSettings) {
|
||||
case .success(let clientProxy):
|
||||
return await .success(buildUserSessionWithClient(clientProxy))
|
||||
return await .success(buildUserSessionWithClient(clientProxy, userSettings: userSettings))
|
||||
case .failure(let error):
|
||||
MXLog.error("Failed restoring login with error: \(error)")
|
||||
|
||||
@@ -77,7 +79,8 @@ class UserSessionStore: UserSessionStoreProtocol {
|
||||
do {
|
||||
let session = try client.session()
|
||||
let userID = try client.userId()
|
||||
let clientProxy = try await setupProxyForClient(client)
|
||||
let userSettings = UserSettings(appSettings: appSettings, accountSettings: .init())
|
||||
let clientProxy = try await setupProxyForClient(client, userSettings: userSettings)
|
||||
|
||||
keychainController.setRestorationToken(RestorationToken(session: session,
|
||||
sessionDirectories: sessionDirectories,
|
||||
@@ -87,7 +90,7 @@ class UserSessionStore: UserSessionStoreProtocol {
|
||||
|
||||
MXLog.info("Set up session for user \(userID) at: \(sessionDirectories)")
|
||||
|
||||
return await .success(buildUserSessionWithClient(clientProxy))
|
||||
return await .success(buildUserSessionWithClient(clientProxy, userSettings: userSettings))
|
||||
} catch {
|
||||
MXLog.error("Failed creating user session with error: \(error)")
|
||||
return .failure(.failedSettingUpSession)
|
||||
@@ -106,7 +109,7 @@ class UserSessionStore: UserSessionStoreProtocol {
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func buildUserSessionWithClient(_ clientProxy: ClientProxyProtocol) async -> UserSessionProtocol {
|
||||
private func buildUserSessionWithClient(_ clientProxy: ClientProxyProtocol, userSettings: UserSettings) async -> UserSessionProtocol {
|
||||
let mediaProvider = MediaProvider(mediaLoader: clientProxy.mediaLoader,
|
||||
imageCache: .onlyInMemory,
|
||||
homeserverReachabilityPublisher: clientProxy.homeserverReachabilityPublisher)
|
||||
@@ -115,16 +118,17 @@ class UserSessionStore: UserSessionStoreProtocol {
|
||||
|
||||
let liveLocationManager = await MainActor.run {
|
||||
LiveLocationManager(clientProxy: clientProxy,
|
||||
appSettings: appSettings)
|
||||
userSettings: userSettings)
|
||||
}
|
||||
|
||||
return UserSession(clientProxy: clientProxy,
|
||||
userSettings: userSettings,
|
||||
mediaProvider: mediaProvider,
|
||||
voiceMessageMediaManager: voiceMessageMediaManager,
|
||||
liveLocationManager: liveLocationManager)
|
||||
}
|
||||
|
||||
private func restorePreviousLogin(_ credentials: KeychainCredentials) async -> Result<ClientProxyProtocol, UserSessionStoreError> {
|
||||
private func restorePreviousLogin(_ credentials: KeychainCredentials, userSettings: UserSettings) async -> Result<ClientProxyProtocol, UserSessionStoreError> {
|
||||
guard credentials.restorationToken.sessionDirectories.isNonTransientUserDataValid() else {
|
||||
MXLog.error("Failed restoring login, missing non-transient user data")
|
||||
return .failure(.failedRestoringLogin)
|
||||
@@ -142,7 +146,7 @@ class UserSessionStore: UserSessionStoreProtocol {
|
||||
|
||||
Task(priority: .low) { await appHooks.remoteSettingsHook.updateCache(using: client) }
|
||||
|
||||
return try await .success(setupProxyForClient(client))
|
||||
return try await .success(setupProxyForClient(client, userSettings: userSettings))
|
||||
} catch UserSessionStoreError.failedSettingUpClientProxy(let error) {
|
||||
// If this has failed, there is likely something wrong with the creation of the sync service
|
||||
// There is nothing we can do, but at the same time we don't want the user to the get logged out
|
||||
@@ -154,11 +158,11 @@ class UserSessionStore: UserSessionStoreProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
private func setupProxyForClient(_ client: ClientProtocol) async throws -> ClientProxyProtocol {
|
||||
private func setupProxyForClient(_ client: ClientProtocol, userSettings: UserSettings) async throws -> ClientProxyProtocol {
|
||||
do {
|
||||
return try await ClientProxy(client: client,
|
||||
networkMonitor: networkMonitor,
|
||||
appSettings: appSettings,
|
||||
userSettings: userSettings,
|
||||
analyticsService: analyticsService)
|
||||
} catch {
|
||||
throw UserSessionStoreError.failedSettingUpClientProxy(error)
|
||||
|
||||
Reference in New Issue
Block a user