Initial implementation of AccountSettings. (#6275)

* Make the UserPreference macro also generate an accessor for the key name.

* Initial implementation of AccountSettings.

- Add an AccountUserDefaults storage with a userID prefix on the key.
- Migrate the 2 values reset by AppSettings.resetSessionSpecificSettings to AccountSettings.

* Rename UserSettings.volatile() to UserSettings.mock()

Given it now has a userID parameter with a default value..
This commit is contained in:
Doug
2026-10-06 17:52:24 +01:00
committed by GitHub
parent acd18f3aa9
commit 93825c01ad
96 changed files with 623 additions and 248 deletions
@@ -467,14 +467,6 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
MXLog.info("The app was upgraded from \(oldVersion) to \(newVersion)")
if oldVersion < Version(1, 6, 0) {
MXLog.info("Migrating to v1.6.0, marking identity confirmation onboarding as ran.")
if !userSessionStore.userIDs.isEmpty {
appSettings.hasRunIdentityConfirmationOnboarding = true
appSettings.hasRunNotificationPermissionsOnboarding = true
}
}
if oldVersion < Version(1, 6, 7) {
Tracing.deleteLogFiles(in: Tracing.legacyLogsDirectory)
MXLog.info("Migrating to v1.6.7, log files have been wiped")
@@ -498,6 +490,12 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
await userSession.clientProxy.optimizeStores()
MXLog.info("Finished optimizing client stores.")
if oldVersion < Version(1, 6, 0) {
MXLog.info("Migrating to v1.6.0, marking identity confirmation onboarding as ran.")
userSession.userSettings.hasRunIdentityConfirmationOnboarding = true
userSession.userSettings.hasRunNotificationPermissionsOnboarding = true
}
if oldVersion < Version(25, 6, 0) {
MXLog.info("Migrating to version 25.06.0, migrating timeline media settings to account data.")
performSettingsToAccountDataMigration(userSession: userSession)
@@ -518,6 +516,12 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
appSettings.hasSignedInBefore = true
}
if oldVersion < Version(26, 10, 1) {
MXLog.info("Migrating to version 26.10.1, moving initial 'session specific' settings.")
userSession.userSettings.migrateAppSettingsValueToAccountSettings(\.hasRunIdentityConfirmationOnboardingKey)
userSession.userSettings.migrateAppSettingsValueToAccountSettings(\.searchBreadcrumbsKey)
}
userSessionMigrationsOldVersion = nil
}
@@ -863,7 +867,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
userSessionStore.logout(userSession: userSession)
tearDownUserSession()
appSettings.resetSessionSpecificSettings()
userSession.userSettings.resetSessionSpecificSettings()
appHooks.remoteSettingsHook.reset(appSettings)
// Reset analytics
@@ -0,0 +1,42 @@
//
// 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 Combine
import Foundation
import Macros
final nonisolated class AccountSettings: @unchecked Sendable {
/// UserDefaults to be used on reads and writes.
private let store: AccountUserDefaults
private let userID: String
// MARK: Session
@UserPreference(defaultValue: false)
var hasRunIdentityConfirmationOnboarding: Bool
// MARK: Search
/// The queries the user searched for and the rooms they opened from the results, most recent first.
@UserPreference(defaultValue: [SearchBreadcrumb]())
var searchBreadcrumbs: [SearchBreadcrumb]
init(userID: String, store: UserDefaultsProtocol) {
self.store = .init(userID: userID, store: store)
self.userID = userID
}
func migrateAppSettingsValue(_ keyPath: KeyPath<AccountSettings, UserPreferenceKey>) {
let storeKey = self[keyPath: keyPath].rawValue
store.migrateAppSettingsValue(forKey: storeKey)
}
func reset() {
MXLog.warning("Resetting AccountSettings for \(userID).")
store.reset()
}
}
@@ -69,12 +69,6 @@ final nonisolated class AppSettings: @unchecked Sendable {
store.reset()
}
func resetSessionSpecificSettings() {
MXLog.warning("Resetting the user session specific AppSettings.")
resetHasRunIdentityConfirmationOnboarding()
resetSearchBreadcrumbs()
}
// MARK: - Hooks
// swiftlint:disable:next function_parameter_count
@@ -338,9 +332,6 @@ final nonisolated class AppSettings: @unchecked Sendable {
@UserPreference(defaultValue: false)
var hasRunNotificationPermissionsOnboarding: Bool
@UserPreference(defaultValue: false)
var hasRunIdentityConfirmationOnboarding: Bool
@UserPreference(defaultValue: false)
var hasRequestedLocationAlwaysLocationAuthorization: Bool
@@ -363,12 +354,6 @@ final nonisolated class AppSettings: @unchecked Sendable {
@UserPreference(defaultValue: true)
var showAllRoomListActivity: Bool
// MARK: - Search Screen
/// The queries the user searched for and the rooms they opened from the results, most recent first.
@UserPreference(defaultValue: [SearchBreadcrumb]())
var searchBreadcrumbs: [SearchBreadcrumb]
// MARK: - Room Screen
@UserPreference(defaultValue: AppBuildType.current == .debug)
@@ -482,6 +467,10 @@ final nonisolated class AppSettings: @unchecked Sendable {
self.store = store
}
func userSettings(for userID: String) -> UserSettings {
UserSettings(appSettings: self, accountSettings: .init(userID: userID, store: store))
}
static func volatile() -> AppSettings {
AppSettings(store: VolatileUserDefaults())
}
@@ -0,0 +1,66 @@
//
// 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
/// A store that scopes all of its values to a single account.
///
/// Each value keeps its own key in the parent store, prefixed with the user ID so that writes
/// remain atomic across the app and its extensions.
final nonisolated class AccountUserDefaults: UserDefaultsProtocol, @unchecked Sendable {
private let keyPrefix: String
private let store: UserDefaultsProtocol
init(userID: String, store: UserDefaultsProtocol) {
keyPrefix = "account[\(userID)]."
self.store = store
}
// MARK: Storage
func data(forKey key: String) -> Data? {
store.data(forKey: keyPrefix + key)
}
func object(forKey key: String) -> Any? {
store.object(forKey: keyPrefix + key)
}
func removeObject(forKey key: String) {
store.removeObject(forKey: keyPrefix + key)
}
func set(_ value: Any?, forKey key: String) {
store.set(value, forKey: keyPrefix + key)
}
// MARK: - Management
func migrateAppSettingsValue(forKey key: String) {
guard let value = store.object(forKey: key) else {
MXLog.info("Ignoring \(key), not set.")
return
}
store.set(value, forKey: keyPrefix + key)
store.removeObject(forKey: key)
}
func reset() {
for key in dictionaryRepresentation().keys {
removeObject(forKey: key)
}
}
func dictionaryRepresentation() -> [String: Any] {
var representation = [String: Any]()
for (key, value) in store.dictionaryRepresentation() where key.hasPrefix(keyPrefix) {
representation[String(key.dropFirst(keyPrefix.count))] = value
}
return representation
}
}
@@ -15,6 +15,7 @@ nonisolated protocol UserDefaultsProtocol: AnyObject {
func set(_ value: Any?, forKey key: String)
func reset()
func dictionaryRepresentation() -> [String: Any]
}
// MARK: - Codable handling
@@ -42,4 +42,8 @@ final nonisolated class VolatileUserDefaults: UserDefaultsProtocol, Sendable {
func reset() {
storage = [:]
}
func dictionaryRepresentation() -> [String: Any] {
storage
}
}
@@ -6,22 +6,23 @@
//
import Foundation
import Macros
/// 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 {
// TODO: @pixlwave Make this private after handling Advanced/Labs/Developer screen models.
let app: AppSettings
let account: AccountSettings
private 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())
static func mock(userID: String = "@me:matrix.org") -> UserSettings {
AppSettings.volatile().userSettings(for: userID)
}
subscript<Value>(dynamicMember keyPath: ReferenceWritableKeyPath<AppSettings, Value>) -> Value {
@@ -41,9 +42,12 @@ final nonisolated class UserSettings: Sendable {
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.
func migrateAppSettingsValueToAccountSettings(_ keyPath: KeyPath<AccountSettings, UserPreferenceKey>) {
account.migrateAppSettingsValue(keyPath)
}
func resetSessionSpecificSettings() {
account.reset()
}
}
+1 -1
View File
@@ -11,7 +11,7 @@ import Foundation
struct UserSessionMockConfiguration {
var clientProxy: ClientProxyProtocol = ClientProxyMock(.init())
var userSettings: UserSettings = .volatile()
var userSettings: UserSettings = .mock()
var contentScannerService: ContentScannerServiceProtocol?
}
@@ -56,7 +56,7 @@ private struct VisualListItemLabelStyle: LabelStyle {
// MARK: - Previews
struct VisualListItem_Previews: PreviewProvider, TestablePreview {
static let strings = AnalyticsPromptScreenStrings(termsURL: UserSettings.volatile().analyticsTermsURL)
static let strings = AnalyticsPromptScreenStrings(termsURL: UserSettings.mock().analyticsTermsURL)
static var testImage1: some View {
Image(systemName: "circle")
@@ -405,7 +405,7 @@ struct CallScreen_Previews: PreviewProvider {
voiceOnly: false,
colorScheme: .light),
allowPictureInPicture: false,
userSettings: .volatile(),
userSettings: .mock(),
analyticsService: AnalyticsServiceMock(.init()))
}
}
@@ -451,7 +451,7 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview {
isSpace: Bool = false,
selectionMode: CreateRoomScreenSpaceSelectionMode = .editableSpacesList(preSelectedSpace: nil),
isAliasAvailable: Bool = true) -> CreateRoomScreenViewModel {
let userSettings = UserSettings.volatile()
let userSettings = UserSettings.mock()
userSettings.knockingEnabled = isKnockingEnabled
let clientProxy = ClientProxyMock(.init(userIDServerName: "example.org",
@@ -65,7 +65,7 @@ struct CreateRoomSpaceSelectionSheet_Previews: PreviewProvider, TestablePreview
clientProxy.spaceService = SpaceServiceProxyMock(.init(editableSpaces: .mockJoinedSpaces2))
let userSession = UserSessionMock(.init(clientProxy: clientProxy))
let userSettings = UserSettings.volatile()
let userSettings = UserSettings.mock()
return CreateRoomScreenViewModel(isSpace: false,
spaceSelectionMode: .editableSpacesList(preSelectedSpace: nil),
@@ -103,7 +103,7 @@ struct EmojiPickerScreen: View {
@available(iOS 26.0, *)
struct EmojiPickerScreen_Previews: PreviewProvider, TestablePreview {
static let viewModel = EmojiPickerScreenViewModel(selectedEmojis: ["😀", "😄"],
emojiProvider: EmojiProvider(userSettings: .volatile()),
emojiProvider: EmojiProvider(userSettings: .mock()),
continuation: AsyncStream<String>.makeStream().continuation)
static var previews: some View {
@@ -115,7 +115,7 @@ struct EmojiPickerScreen_Previews: PreviewProvider, TestablePreview {
struct EmojiPickerScreenSheet_Previews: PreviewProvider {
static let viewModel = EmojiPickerScreenViewModel(selectedEmojis: ["😀", "😄"],
emojiProvider: EmojiProvider(userSettings: .volatile()),
emojiProvider: EmojiProvider(userSettings: .mock()),
continuation: AsyncStream<String>.makeStream().continuation)
static var previews: some View {
@@ -61,10 +61,10 @@ struct RoomListFiltersEmptyStateView_Previews: PreviewProvider, TestablePreview
VStack(spacing: 24) {
ForEach(RoomListFilter.allCases) { filter in
RoomListFiltersEmptyStateView(state: .init(activeFilters: [filter],
userSettings: .volatile()))
userSettings: .mock()))
}
RoomListFiltersEmptyStateView(state: .init(activeFilters: [.people, .favourites],
userSettings: .volatile()))
userSettings: .mock()))
}
.padding(.bottom)
.previewLayout(.sizeThatFits)
@@ -92,10 +92,10 @@ struct RoomListFiltersView: View {
struct RoomListFiltersView_Previews: PreviewProvider, TestablePreview {
static var previews: some View {
RoomListFiltersView(state: .constant(.init(userSettings: .volatile())))
RoomListFiltersView(state: .constant(.init(userSettings: .mock())))
RoomListFiltersView(state: .constant(.init(activeFilters: [.rooms, .favourites],
userSettings: .volatile())))
userSettings: .mock())))
RoomListFiltersView(state: .constant(.init(activeFilters: [.lowPriority],
userSettings: .volatile())))
userSettings: .mock())))
}
}
@@ -232,7 +232,7 @@ struct HomeScreen_Previews: PreviewProvider, TestablePreview {
return HomeScreenViewModel(userSession: userSession,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
userSettings: .volatile(),
userSettings: .mock(),
analyticsService: AnalyticsServiceMock(.init()),
bugReportService: BugReportServiceMock(.init()),
notificationManager: NotificationManagerMock(),
@@ -145,7 +145,7 @@ struct HomeScreenEmptyStateView_Previews: PreviewProvider, TestablePreview {
roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded([])))))))
return HomeScreenViewModel(userSession: userSession,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
userSettings: .volatile(),
userSettings: .mock(),
analyticsService: AnalyticsServiceMock(.init()),
bugReportService: BugReportServiceMock(.init()),
notificationManager: NotificationManagerMock(),
@@ -201,7 +201,7 @@ struct HomeScreenInviteCell_Previews: PreviewProvider, TestablePreview {
return HomeScreenViewModel(userSession: userSession,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
userSettings: .volatile(),
userSettings: .mock(),
analyticsService: AnalyticsServiceMock(.init()),
bugReportService: BugReportServiceMock(.init()),
notificationManager: NotificationManagerMock(),
@@ -133,7 +133,7 @@ struct HomeScreenKnockedCell_Previews: PreviewProvider, TestablePreview {
return HomeScreenViewModel(userSession: userSession,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
userSettings: .volatile(),
userSettings: .mock(),
analyticsService: AnalyticsServiceMock(.init()),
bugReportService: BugReportServiceMock(.init()),
notificationManager: NotificationManagerMock(),
@@ -122,7 +122,7 @@ struct HomeScreenRecoveryKeyConfirmationBanner_Previews: PreviewProvider, Testab
return HomeScreenViewModel(userSession: userSession,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
userSettings: .volatile(),
userSettings: .mock(),
analyticsService: AnalyticsServiceMock(.init()),
bugReportService: BugReportServiceMock(.init()),
notificationManager: NotificationManagerMock(),
@@ -402,7 +402,7 @@ struct JoinRoomScreenPreviewWrapper: Identifiable {
self.isSpace = isSpace
self.customPreviewName = customPreviewName
let userSettings = UserSettings.volatile()
let userSettings = UserSettings.mock()
let clientProxy = ClientProxyMock(.init(hideInviteAvatars: hideInviteAvatars))
clientProxy.canJoinRoomWithReturnValue = canJoinRoom
@@ -432,7 +432,7 @@ extension LocationSharingScreenViewModel {
let roomProxy = JoinedRoomProxyMock(.init(members: .allMembers, ownUserID: RoomMemberProxyMock.mockMe.userID))
roomProxy.makeLiveLocationServiceReturnValue = liveLocationServiceMock
let userSettings = UserSettings.volatile()
let userSettings = UserSettings.mock()
return LocationSharingScreenViewModel(interactionMode: interactionMode,
mapURLBuilder: userSettings.mapTilerConfiguration.publisher.value,
@@ -314,7 +314,7 @@ struct MediaEventsTimelineScreen_Previews: PreviewProvider, TestablePreview {
userIndicatorController: UserIndicatorControllerMock(),
appMediator: AppMediatorMock(.init()),
analyticsService: AnalyticsServiceMock(.init()),
emojiProvider: EmojiProvider(userSettings: .volatile()),
emojiProvider: EmojiProvider(userSettings: .mock()),
linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init()))
}
@@ -393,7 +393,7 @@ struct MediaUploadPreviewScreen_Previews: PreviewProvider, TestablePreview {
title: "App Icon.png",
shouldShowCaptionWarning: true,
galleryEnabled: true,
mediaUploadingPreprocessor: MediaUploadingPreprocessor(userSettings: .volatile()),
mediaUploadingPreprocessor: MediaUploadingPreprocessor(userSettings: .mock()),
timelineController: TimelineControllerMock(.init()),
clientProxy: ClientProxyMock(.init()),
userIndicatorController: UserIndicatorControllerMock())
@@ -102,6 +102,6 @@ struct AnalyticsPromptScreen_Previews: PreviewProvider, TestablePreview {
}
static func makeViewModel(showTerms: Bool = true) -> AnalyticsPromptScreenViewModel {
AnalyticsPromptScreenViewModel(termsURL: showTerms ? UserSettings.volatile().analyticsTermsURL : nil)
AnalyticsPromptScreenViewModel(termsURL: showTerms ? UserSettings.mock().analyticsTermsURL : nil)
}
}
@@ -142,7 +142,7 @@ struct IdentityConfirmationScreen_Previews: PreviewProvider, TestablePreview {
userSession.sessionSecurityStatePublisher = CurrentValuePublisher<SessionSecurityState, Never>(.init(verificationState: .unverified, recoveryState: recoveryState))
return IdentityConfirmationScreenViewModel(userSession: userSession,
userSettings: .volatile(),
userSettings: .mock(),
userIndicatorController: UserIndicatorControllerMock())
}
}
@@ -289,7 +289,7 @@ struct SessionVerification_Previews: PreviewProvider, TestablePreview {
flow: SessionVerificationScreenFlow = .deviceInitiator) -> some View {
let viewModel = SessionVerificationScreenViewModel(sessionVerificationControllerProxy: SessionVerificationControllerProxyMock.configureMock(),
flow: flow,
userSettings: .volatile(),
userSettings: .mock(),
mediaProvider: MediaProviderMock(.init()),
verificationState: state)
@@ -83,7 +83,7 @@ struct PinnedEventsTimelineScreen: View {
struct PinnedEventsTimelineScreen_Previews: PreviewProvider, TestablePreview {
static let viewModel = PinnedEventsTimelineScreenViewModel(roomProxy: JoinedRoomProxyMock(.init()),
userIndicatorController: UserIndicatorControllerMock(),
userSettings: .volatile(),
userSettings: .mock(),
analyticsService: AnalyticsServiceMock(.init()))
static let emptyTimelineViewModel = makeTimelineViewModel(timelineItems: [])
@@ -106,7 +106,7 @@ struct PinnedEventsTimelineScreen_Previews: PreviewProvider, TestablePreview {
let eventIDs = timelineItems.compactMap { ($0 as? EventBasedTimelineItemProtocol)?.id.eventID }
let timelineController = TimelineControllerMock(.init(timelineKind: .pinned, timelineItems: timelineItems))
let userSettings = UserSettings.volatile()
let userSettings = UserSettings.mock()
userSettings.messageMultiSelectEnabled = isSelecting
let userSession = UserSessionMock(.init(userSettings: userSettings))
@@ -117,7 +117,7 @@ struct PinnedEventsTimelineScreen_Previews: PreviewProvider, TestablePreview {
userIndicatorController: UserIndicatorControllerMock(),
appMediator: AppMediatorMock(.init()),
analyticsService: AnalyticsServiceMock(.init()),
emojiProvider: EmojiProvider(userSettings: .volatile()),
emojiProvider: EmojiProvider(userSettings: .mock()),
linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init()))
@@ -177,7 +177,7 @@ struct RoomDetailsEditScreen_Previews: PreviewProvider, TestablePreview {
return RoomDetailsEditScreenViewModel(roomProxy: roomProxy,
userSession: UserSessionMock(.init()),
mediaUploadingPreprocessor: MediaUploadingPreprocessor(userSettings: .volatile()),
mediaUploadingPreprocessor: MediaUploadingPreprocessor(userSettings: .mock()),
userIndicatorController: UserIndicatorControllerMock())
}
}
@@ -445,7 +445,7 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview {
return .init(roomProxy: roomProxy,
userSession: UserSessionMock(.init()),
userSettings: UserSettings.volatile(),
userSettings: UserSettings.mock(),
appHooks: AppHooks(),
analyticsService: AnalyticsServiceMock(.init()),
userIndicatorController: UserIndicatorControllerMock(),
@@ -474,7 +474,7 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview {
return .init(roomProxy: roomProxy,
userSession: UserSessionMock(.init()),
userSettings: UserSettings.volatile(),
userSettings: UserSettings.mock(),
appHooks: AppHooks(),
analyticsService: AnalyticsServiceMock(.init()),
userIndicatorController: UserIndicatorControllerMock(),
@@ -513,7 +513,7 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview {
return .init(roomProxy: roomProxy,
userSession: UserSessionMock(.init(clientProxy: clientProxyMock)),
userSettings: UserSettings.volatile(),
userSettings: UserSettings.mock(),
appHooks: AppHooks(),
analyticsService: AnalyticsServiceMock(.init()),
userIndicatorController: UserIndicatorControllerMock(),
@@ -852,7 +852,7 @@ extension ComposerToolbarViewModel {
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init(suggestions: suggestions)),
mediaProvider: MediaProviderMock(.init()),
mentionDisplayHelper: ComposerMentionDisplayHelper.mock,
userSettings: .volatile(),
userSettings: .mock(),
analyticsService: AnalyticsServiceMock(.init()),
composerDraftService: ComposerDraftServiceMock(.init()))
viewModel.state.bindings.composerFocused = focused
@@ -81,7 +81,7 @@ struct RoomAttachmentPicker_Previews: PreviewProvider, TestablePreview {
static let viewModel = makeViewModel()
static func makeViewModel() -> ComposerToolbarViewModel {
let userSettings = UserSettings.volatile()
let userSettings = UserSettings.mock()
return ComposerToolbarViewModel(roomProxy: JoinedRoomProxyMock(.init()),
wysiwygViewModel: WysiwygComposerViewModel(),
@@ -395,7 +395,7 @@ struct RoomScreen_Previews: PreviewProvider, TestablePreview {
powerLevelsConfiguration: .init(canUserSendMessage: canSendMessage)))
let roomViewModel = RoomScreenViewModel.mock(roomProxyMock: roomProxyMock)
let userSettings = UserSettings.volatile()
let userSettings = UserSettings.mock()
userSettings.messageMultiSelectEnabled = isSelecting
let userSession = UserSessionMock(.init(userSettings: userSettings))
@@ -629,31 +629,31 @@ struct SearchScreen_Previews: PreviewProvider, TestablePreview {
clientProxy: makeClientProxy(),
mediaProvider: MediaProviderMock(.init()),
userIndicatorController: UserIndicatorControllerMock(),
userSettings: UserSettings.volatile())
userSettings: UserSettings.mock())
static let noResultsViewModel = SearchScreenViewModel(roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded([]))),
clientProxy: makeClientProxy(),
mediaProvider: MediaProviderMock(.init()),
userIndicatorController: UserIndicatorControllerMock(),
userSettings: UserSettings.volatile(),
userSettings: UserSettings.mock(),
initialSearchQuery: "John Doe")
static let roomsViewModel = SearchScreenViewModel(roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))),
clientProxy: makeClientProxy(),
mediaProvider: MediaProviderMock(.init()),
userIndicatorController: UserIndicatorControllerMock(),
userSettings: UserSettings.volatile(),
userSettings: UserSettings.mock(),
initialSearchQuery: "Foundation")
static let messagesViewModel = SearchScreenViewModel(roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded([]))),
clientProxy: makeClientProxy(searchService: makeSearchService(results: .mockResults)),
mediaProvider: MediaProviderMock(.init()),
userIndicatorController: UserIndicatorControllerMock(),
userSettings: UserSettings.volatile(),
userSettings: UserSettings.mock(),
initialSearchQuery: "Foundation",
initialSearchMode: .messages)
static let loadingMessagesViewModel = SearchScreenViewModel(roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded([]))),
clientProxy: makeClientProxy(searchService: makeSearchService(paginationState: .loading)),
mediaProvider: MediaProviderMock(.init()),
userIndicatorController: UserIndicatorControllerMock(),
userSettings: UserSettings.volatile(),
userSettings: UserSettings.mock(),
initialSearchQuery: "Foundation",
initialSearchMode: .messages)
@@ -708,7 +708,7 @@ struct SearchScreen_Previews: PreviewProvider, TestablePreview {
}
private static func makeUserSettings(breadcrumbs: [SearchBreadcrumb]) -> UserSettings {
let userSettings = UserSettings.volatile()
let userSettings = UserSettings.mock()
userSettings.searchBreadcrumbs = breadcrumbs
return userSettings
}
@@ -235,7 +235,7 @@ struct SecurityAndPrivacyScreen_Previews: PreviewProvider, TestablePreview {
joinRule: .invite)),
clientProxy: ClientProxyMock(.init()),
userIndicatorController: UserIndicatorControllerMock(),
userSettings: .volatile(),
userSettings: .mock(),
appHooks: AppHooks())
static let publicViewModel = SecurityAndPrivacyScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(isEncrypted: false,
@@ -245,7 +245,7 @@ struct SecurityAndPrivacyScreen_Previews: PreviewProvider, TestablePreview {
isVisibleInPublicDirectory: true)),
clientProxy: ClientProxyMock(.init(userIDServerName: "matrix.org")),
userIndicatorController: UserIndicatorControllerMock(),
userSettings: .volatile(),
userSettings: .mock(),
appHooks: AppHooks())
static let publicNoAddressViewModel = SecurityAndPrivacyScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(isEncrypted: false,
@@ -253,11 +253,11 @@ struct SecurityAndPrivacyScreen_Previews: PreviewProvider, TestablePreview {
joinRule: .public)),
clientProxy: ClientProxyMock(.init(userIDServerName: "matrix.org")),
userIndicatorController: UserIndicatorControllerMock(),
userSettings: .volatile(),
userSettings: .mock(),
appHooks: AppHooks())
static let singleSpaceMembersViewModel = {
let userSettings = UserSettings.volatile()
let userSettings = UserSettings.mock()
let space = [SpaceServiceRoom].mockSingleRoom[0]
@@ -274,7 +274,7 @@ struct SecurityAndPrivacyScreen_Previews: PreviewProvider, TestablePreview {
}()
static let multipleSpacesMembersViewModel = {
let userSettings = UserSettings.volatile()
let userSettings = UserSettings.mock()
let spaces = [SpaceServiceRoom].mockJoinedSpaces
@@ -291,7 +291,7 @@ struct SecurityAndPrivacyScreen_Previews: PreviewProvider, TestablePreview {
}()
static let askToJoinViewModel = {
let userSettings = UserSettings.volatile()
let userSettings = UserSettings.mock()
userSettings.knockingEnabled = true
return SecurityAndPrivacyScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(isEncrypted: false,
@@ -306,7 +306,7 @@ struct SecurityAndPrivacyScreen_Previews: PreviewProvider, TestablePreview {
}()
static let singleAskToJoinSpaceMembersViewModel = {
let userSettings = UserSettings.volatile()
let userSettings = UserSettings.mock()
userSettings.knockingEnabled = true
let space = [SpaceServiceRoom].mockSingleRoom[0]
@@ -324,7 +324,7 @@ struct SecurityAndPrivacyScreen_Previews: PreviewProvider, TestablePreview {
}()
static let multipleAskToJoinSpacesMembersViewModel = {
let userSettings = UserSettings.volatile()
let userSettings = UserSettings.mock()
userSettings.knockingEnabled = true
let spaces = [SpaceServiceRoom].mockJoinedSpaces
@@ -349,7 +349,7 @@ struct SecurityAndPrivacyScreen_Previews: PreviewProvider, TestablePreview {
isVisibleInPublicDirectory: true)),
clientProxy: ClientProxyMock(.init(userIDServerName: "matrix.org")),
userIndicatorController: UserIndicatorControllerMock(),
userSettings: .volatile(),
userSettings: .mock(),
appHooks: AppHooks())
static var previews: some View {
@@ -39,7 +39,7 @@ struct AnalyticsSettingsScreen: View {
struct AnalyticsSettingsScreen_Previews: PreviewProvider, TestablePreview {
static var previews: some View {
let userSettings = UserSettings.volatile()
let userSettings = UserSettings.mock()
let viewModel = AnalyticsSettingsScreenViewModel(userSettings: userSettings,
analytics: AnalyticsServiceMock(.init()))
AnalyticsSettingsScreen(context: viewModel.context)
@@ -33,7 +33,7 @@ struct LegalInformationScreen: View {
// MARK: - Previews
struct LegalInformationScreen_Previews: PreviewProvider, TestablePreview {
static let viewModel = LegalInformationScreenViewModel(userSettings: .volatile())
static let viewModel = LegalInformationScreenViewModel(userSettings: .mock())
static var previews: some View {
LegalInformationScreen(context: viewModel.context)
}
@@ -262,7 +262,7 @@ struct NotificationSettingsScreen: View {
@available(iOS 26.0, *)
struct NotificationSettingsScreen_Previews: PreviewProvider, TestablePreview {
static let viewModel: NotificationSettingsScreenViewModel = {
let userSettings = UserSettings.volatile()
let userSettings = UserSettings.mock()
let notificationCenter = UserNotificationCenterMock()
notificationCenter.authorizationStatusReturnValue = .notDetermined
let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init())
@@ -290,7 +290,7 @@ struct NotificationSettingsScreen_Previews: PreviewProvider, TestablePreview {
}()
static let viewModelConfigurationMismatch: NotificationSettingsScreenViewModel = {
let userSettings = UserSettings.volatile()
let userSettings = UserSettings.mock()
let notificationCenter = UserNotificationCenterMock()
notificationCenter.authorizationStatusReturnValue = .notDetermined
let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init())
@@ -321,7 +321,7 @@ struct SettingsScreen_Previews: PreviewProvider, TestablePreview {
deviceID: "AAAAAAAAAAA",
displayName: "Alice Liddell",
status: .mockFocussing))))
let userSettings = UserSettings.volatile()
let userSettings = UserSettings.mock()
userSettings.multiAccountEnabled = true
return SettingsScreenViewModel(userSession: userSession,
userSettings: userSettings,
@@ -145,7 +145,7 @@ struct UserDetailsEditScreen_Previews: PreviewProvider, TestablePreview {
canChangeDisplayName: canChangeProfile))))
return UserDetailsEditScreenViewModel(userSession: userSession,
mediaUploadingPreprocessor: .init(userSettings: .volatile()),
mediaUploadingPreprocessor: .init(userSettings: .mock()),
userIndicatorController: UserIndicatorControllerMock())
}
}
@@ -132,7 +132,7 @@ struct SpaceSettingsScreen_Previews: PreviewProvider, TestablePreview {
canonicalAlias: "#space:matrix.org",
members: members)),
userSession: UserSessionMock(.init()),
userSettings: UserSettings.volatile(),
userSettings: UserSettings.mock(),
appHooks: AppHooks(),
analyticsService: AnalyticsServiceMock(.init()),
userIndicatorController: UserIndicatorControllerMock(),
@@ -154,7 +154,7 @@ struct StartChatScreen: View {
struct StartChatScreen_Previews: PreviewProvider, TestablePreview {
static let viewModel = {
let userSettings = UserSettings.volatile()
let userSettings = UserSettings.mock()
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@userid:example.com"))))
let userDiscoveryService = UserDiscoveryServiceMock()
userDiscoveryService.searchProfilesWithReturnValue = .success([.mockAlice])
@@ -1306,7 +1306,7 @@ extension TimelineViewModel {
userIndicatorController: UserIndicatorControllerMock(),
appMediator: AppMediatorMock(.init()),
analyticsService: AnalyticsServiceMock(.init()),
emojiProvider: EmojiProvider(userSettings: .volatile()),
emojiProvider: EmojiProvider(userSettings: .mock()),
linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init()))
}
@@ -340,7 +340,7 @@ struct TimelineItemMenu_Previews: PreviewProvider, TestablePreview {
isViewSourceEnabled: true,
areThreadsEnabled: true,
timelineKind: .live,
emojiProvider: EmojiProvider(userSettings: .volatile()))
emojiProvider: EmojiProvider(userSettings: .mock()))
guard let actions = provider.makeActions() else { return nil }
if var textItem = item as? TextRoomTimelineItem {
@@ -52,7 +52,7 @@ struct ReadReceiptsSummaryView_Previews: PreviewProvider, TestablePreview {
userIndicatorController: UserIndicatorControllerMock(),
appMediator: AppMediatorMock(.init()),
analyticsService: AnalyticsServiceMock(.init()),
emojiProvider: EmojiProvider(userSettings: .volatile()),
emojiProvider: EmojiProvider(userSettings: .mock()),
linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init()))
}()
@@ -426,7 +426,7 @@ private extension TimelineItemKeyForwarder {
struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview {
static let viewModel: TimelineViewModel = {
let userSettings = UserSettings.volatile()
let userSettings = UserSettings.mock()
userSettings.threadsEnabled = true
let userSession = UserSessionMock(.init(userSettings: userSettings))
@@ -445,7 +445,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
}()
static let viewModelWithPins: TimelineViewModel = {
let userSettings = UserSettings.volatile()
let userSettings = UserSettings.mock()
userSettings.threadsEnabled = true
let userSession = UserSessionMock(.init(userSettings: userSettings))
@@ -466,7 +466,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
static let unsafeViewModel = TimelineViewModel.mock(contentScannerService: ContentScannerServiceMock(.init(scanResult: false)))
static let selectingViewModel: TimelineViewModel = {
let userSettings = UserSettings.volatile()
let userSettings = UserSettings.mock()
userSettings.messageMultiSelectEnabled = true
let userSession = UserSessionMock(.init(userSettings: userSettings))
@@ -86,7 +86,7 @@ struct TimelineReadReceiptsView_Previews: PreviewProvider, TestablePreview {
userIndicatorController: UserIndicatorControllerMock(),
appMediator: AppMediatorMock(.init()),
analyticsService: AnalyticsServiceMock(.init()),
emojiProvider: EmojiProvider(userSettings: .volatile()),
emojiProvider: EmojiProvider(userSettings: .mock()),
linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init()))
@@ -97,7 +97,7 @@ struct HighlightedTimelineItemTimeline_Previews: PreviewProvider {
userIndicatorController: UserIndicatorControllerMock(),
appMediator: AppMediatorMock(.init()),
analyticsService: AnalyticsServiceMock(.init()),
emojiProvider: EmojiProvider(userSettings: .volatile()),
emojiProvider: EmojiProvider(userSettings: .mock()),
linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init()))
@@ -162,7 +162,7 @@ struct TimelineView_Previews: PreviewProvider { // Not testable as this preview
userIndicatorController: UserIndicatorControllerMock(),
appMediator: AppMediatorMock(.init()),
analyticsService: AnalyticsServiceMock(.init()),
emojiProvider: EmojiProvider(userSettings: .volatile()),
emojiProvider: EmojiProvider(userSettings: .mock()),
linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init()))
@@ -232,11 +232,10 @@ class AuthenticationService: AuthenticationServiceProtocol {
let qrCodeHandler = client.newLoginWithQrCodeHandler(oauthConfiguration: appSettings.oAuthConfiguration.rustValue)
try await qrCodeHandler.scan(qrCodeData: qrData, progressListener: listener)
// Since the QR code login flow includes verification.
appSettings.hasRunIdentityConfirmationOnboarding = true
switch await userSession(for: client) {
case .success(let userSession):
// Since the QR code login flow includes verification.
userSession.userSettings.hasRunIdentityConfirmationOnboarding = true
progressSubject.send(.signedIn(userSession))
case .failure(let error):
progressSubject.send(completion: .failure(error))
@@ -367,7 +366,7 @@ class AuthenticationService: AuthenticationServiceProtocol {
MXLog.info("Classic app account secrets imported.")
// Importing the secrets automatically verifies the session.
appSettings.hasRunIdentityConfirmationOnboarding = true
appSettings.userSettings(for: classicAppAccount.userID).hasRunIdentityConfirmationOnboarding = true
} catch {
MXLog.error("Failed to import secrets for Classic app account: \(error)")
}
@@ -59,7 +59,7 @@ class UserSessionStore: UserSessionStoreProtocol {
return .failure(.missingCredentials)
}
let userSettings = UserSettings(appSettings: appSettings, accountSettings: .init())
let userSettings = appSettings.userSettings(for: credentials.userID)
switch await restorePreviousLogin(credentials, userSettings: userSettings) {
case .success(let clientProxy):
@@ -83,7 +83,7 @@ class UserSessionStore: UserSessionStoreProtocol {
do {
let session = try client.session()
let userID = try client.userId()
let userSettings = UserSettings(appSettings: appSettings, accountSettings: .init())
let userSettings = appSettings.userSettings(for: userID)
let clientProxy = try await setupProxyForClient(client, userSettings: userSettings)
keychainController.setRestorationToken(RestorationToken(session: session,
@@ -298,7 +298,7 @@ class MockScreen: Identifiable {
roomProxy: JoinedRoomProxyMock(.init(name: "Some room name", avatarURL: nil)),
timelineController: TimelineControllerMock(.init()),
mediaPlayerProvider: MediaPlayerProviderMock(),
emojiProvider: EmojiProvider(userSettings: .volatile()),
emojiProvider: EmojiProvider(userSettings: .mock()),
linkMetadataProvider: LinkMetadataProvider(),
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()),
ongoingCallRoomIDPublisher: .init(.init(nil)),
@@ -318,7 +318,7 @@ class MockScreen: Identifiable {
roomProxy: JoinedRoomProxyMock(.init(name: "New room", avatarURL: .mockMXCAvatar)),
timelineController: timelineController,
mediaPlayerProvider: MediaPlayerProviderMock(),
emojiProvider: EmojiProvider(userSettings: .volatile()),
emojiProvider: EmojiProvider(userSettings: .mock()),
linkMetadataProvider: LinkMetadataProvider(),
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()),
ongoingCallRoomIDPublisher: .init(.init(nil)),
@@ -338,7 +338,7 @@ class MockScreen: Identifiable {
roomProxy: JoinedRoomProxyMock(.init(name: "New room", avatarURL: .mockMXCAvatar)),
timelineController: timelineController,
mediaPlayerProvider: MediaPlayerProviderMock(),
emojiProvider: EmojiProvider(userSettings: .volatile()),
emojiProvider: EmojiProvider(userSettings: .mock()),
linkMetadataProvider: LinkMetadataProvider(),
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()),
ongoingCallRoomIDPublisher: .init(.init(nil)),
@@ -357,7 +357,7 @@ class MockScreen: Identifiable {
roomProxy: JoinedRoomProxyMock(.init(name: "New room", avatarURL: .mockMXCAvatar)),
timelineController: timelineController,
mediaPlayerProvider: MediaPlayerProviderMock(),
emojiProvider: EmojiProvider(userSettings: .volatile()),
emojiProvider: EmojiProvider(userSettings: .mock()),
linkMetadataProvider: LinkMetadataProvider(),
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()),
ongoingCallRoomIDPublisher: .init(.init(nil)),
@@ -382,7 +382,7 @@ class MockScreen: Identifiable {
roomProxy: roomProxy,
timelineController: timelineController,
mediaPlayerProvider: MediaPlayerProviderMock(),
emojiProvider: EmojiProvider(userSettings: .volatile()),
emojiProvider: EmojiProvider(userSettings: .mock()),
linkMetadataProvider: LinkMetadataProvider(),
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()),
ongoingCallRoomIDPublisher: .init(.init(nil)),
@@ -407,7 +407,7 @@ class MockScreen: Identifiable {
roomProxy: roomProxy,
timelineController: timelineController,
mediaPlayerProvider: MediaPlayerProviderMock(),
emojiProvider: EmojiProvider(userSettings: .volatile()),
emojiProvider: EmojiProvider(userSettings: .mock()),
linkMetadataProvider: LinkMetadataProvider(),
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()),
ongoingCallRoomIDPublisher: .init(.init(nil)),
@@ -431,7 +431,7 @@ class MockScreen: Identifiable {
roomProxy: roomProxy,
timelineController: timelineController,
mediaPlayerProvider: MediaPlayerProviderMock(),
emojiProvider: EmojiProvider(userSettings: .volatile()),
emojiProvider: EmojiProvider(userSettings: .mock()),
linkMetadataProvider: LinkMetadataProvider(),
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()),
ongoingCallRoomIDPublisher: .init(.init(nil)),
@@ -457,7 +457,7 @@ class MockScreen: Identifiable {
roomProxy: roomProxy,
timelineController: timelineController,
mediaPlayerProvider: MediaPlayerProviderMock(),
emojiProvider: EmojiProvider(userSettings: .volatile()),
emojiProvider: EmojiProvider(userSettings: .mock()),
linkMetadataProvider: LinkMetadataProvider(),
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()),
ongoingCallRoomIDPublisher: .init(.init(nil)),
@@ -482,7 +482,7 @@ class MockScreen: Identifiable {
roomProxy: roomProxy,
timelineController: timelineController,
mediaPlayerProvider: MediaPlayerProviderMock(),
emojiProvider: EmojiProvider(userSettings: .volatile()),
emojiProvider: EmojiProvider(userSettings: .mock()),
linkMetadataProvider: LinkMetadataProvider(),
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()),
ongoingCallRoomIDPublisher: .init(.init(nil)),
@@ -503,7 +503,7 @@ class MockScreen: Identifiable {
roomProxy: JoinedRoomProxyMock(.init(name: "Timeline highlight", avatarURL: .mockMXCAvatar)),
timelineController: timelineController,
mediaPlayerProvider: MediaPlayerProviderMock(),
emojiProvider: EmojiProvider(userSettings: .volatile()),
emojiProvider: EmojiProvider(userSettings: .mock()),
linkMetadataProvider: LinkMetadataProvider(),
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()),
ongoingCallRoomIDPublisher: .init(.init(nil)),
@@ -537,7 +537,7 @@ class MockScreen: Identifiable {
roomProxy: JoinedRoomProxyMock(.init(name: "Polls timeline", avatarURL: .mockMXCAvatar)),
timelineController: timelineController,
mediaPlayerProvider: MediaPlayerProviderMock(),
emojiProvider: EmojiProvider(userSettings: .volatile()),
emojiProvider: EmojiProvider(userSettings: .mock()),
linkMetadataProvider: LinkMetadataProvider(),
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()),
ongoingCallRoomIDPublisher: .init(.init(nil)),
@@ -558,7 +558,7 @@ class MockScreen: Identifiable {
roomProxy: JoinedRoomProxyMock(.init(name: "Polls timeline", avatarURL: .mockMXCAvatar)),
timelineController: timelineController,
mediaPlayerProvider: MediaPlayerProviderMock(),
emojiProvider: EmojiProvider(userSettings: .volatile()),
emojiProvider: EmojiProvider(userSettings: .mock()),
linkMetadataProvider: LinkMetadataProvider(),
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()),
ongoingCallRoomIDPublisher: .init(.init(nil)),
@@ -579,7 +579,7 @@ class MockScreen: Identifiable {
roomProxy: JoinedRoomProxyMock(.init(name: "Polls timeline", avatarURL: .mockMXCAvatar)),
timelineController: timelineController,
mediaPlayerProvider: MediaPlayerProviderMock(),
emojiProvider: EmojiProvider(userSettings: .volatile()),
emojiProvider: EmojiProvider(userSettings: .mock()),
linkMetadataProvider: LinkMetadataProvider(),
completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()),
ongoingCallRoomIDPublisher: .init(.init(nil)),
@@ -597,14 +597,15 @@ class MockScreen: Identifiable {
requestDelay: .seconds(5))
let parameters = SessionVerificationScreenCoordinatorParameters(sessionVerificationControllerProxy: sessionVerificationControllerProxy,
flow: .deviceInitiator,
userSettings: .volatile(),
userSettings: .mock(),
mediaProvider: MediaProviderMock(.init()))
return SessionVerificationScreenCoordinator(parameters: parameters)
case .userSessionScreen, .userSessionScreenReply, .userSessionSpacesFlow:
let appSettings: AppSettings = appSettings
appSettings.hasRunIdentityConfirmationOnboarding = true
appSettings.hasRunNotificationPermissionsOnboarding = true
appSettings.analyticsConsentState = .optedOut
let userID = "@mock:client.com"
let userSettings = appSettings.userSettings(for: userID)
userSettings.hasRunIdentityConfirmationOnboarding = true
userSettings.hasRunNotificationPermissionsOnboarding = true
userSettings.analyticsConsentState = .optedOut
let roomSummaries: [RoomSummary] = if id == .userSessionSpacesFlow {
[[RoomSummary].mockSpaceInvites[0]] + .mockRooms
@@ -612,7 +613,7 @@ class MockScreen: Identifiable {
.mockRooms
}
let clientProxy = ClientProxyMock(.init(userID: "@mock:client.com",
let clientProxy = ClientProxyMock(.init(userID: userID,
deviceID: "MOCKCLIENT",
roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(roomSummaries))),
spaceServiceConfiguration: .init(topLevelSpaces: .mockSpaceList.filter(\.isSpace) + .mockSingleRoom),
@@ -634,8 +635,7 @@ class MockScreen: Identifiable {
let timelineControllerFactory = TimelineControllerFactoryMock(.init(timelineController: timelineController))
let userSession = UserSessionMock(.init(clientProxy: clientProxy,
userSettings: UserSettings(appSettings: appSettings, accountSettings: .init())))
let userSession = UserSessionMock(.init(clientProxy: clientProxy, userSettings: userSettings))
let flowCoordinator = UserSessionFlowCoordinator(isNewLogin: false,
navigationRootCoordinator: navigationRootCoordinator,
appLockService: AppLockService(keychainController: KeychainControllerMock(),
@@ -826,14 +826,15 @@ class MockScreen: Identifiable {
navigationRootCoordinator.setSheetCoordinator(navigationStackCoordinator)
return PlaceholderScreenCoordinator(hideBrandChrome: false)
case .autoUpdatingTimeline:
let userSettings = UserSettings(appSettings: appSettings, accountSettings: .init())
appSettings.hasRunIdentityConfirmationOnboarding = true
appSettings.hasRunNotificationPermissionsOnboarding = true
appSettings.analyticsConsentState = .optedOut
let userID = "@mock:client.com"
let userSettings = appSettings.userSettings(for: userID)
userSettings.hasRunIdentityConfirmationOnboarding = true
userSettings.hasRunNotificationPermissionsOnboarding = true
userSettings.analyticsConsentState = .optedOut
let navigationSplitCoordinator = NavigationSplitCoordinator(placeholderCoordinator: PlaceholderScreenCoordinator(hideBrandChrome: false))
navigationRootCoordinator.setRootCoordinator(navigationSplitCoordinator)
let clientProxy = ClientProxyMock(.init(userID: "@mock:client.com", roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms)))))
let clientProxy = ClientProxyMock(.init(userID: userID, roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms)))))
let userSession = UserSessionMock(.init(clientProxy: clientProxy, userSettings: userSettings))
let roomProxy = JoinedRoomProxyMock(.init(id: "whatever", name: "okay", shouldUseAutoUpdatingTimeline: true))
@@ -905,6 +906,6 @@ class MockScreen: Identifiable {
attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()),
stateEventStringBuilder: RoomStateEventStringBuilder(userID: "@alice:matrix.org")),
mediaProvider: MediaProviderMock(.init()),
userSettings: .volatile())
userSettings: .mock())
}
}