From f7c7cf23ed467f5344883d4d253467414cdbd590 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Thu, 20 Mar 2025 12:01:29 +0100 Subject: [PATCH] UserPreference now supports closure based default and you can also force the defaults to be always used through a subscription --- ElementX.xcodeproj/project.pbxproj | 10 +++++ ElementX/Sources/Other/UserPreference.swift | 45 ++++++++++++++++--- .../JoinRoomScreen/View/JoinRoomScreen.swift | 8 ++-- .../View/AdvancedSettingsScreen.swift | 9 ++++ ShareExtension/SupportingFiles/target.yml | 1 + 5 files changed, 63 insertions(+), 10 deletions(-) diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 62ab66473..4fddbcb18 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -1044,6 +1044,7 @@ D02DEB36D32A72A1B365E452 /* SessionVerificationScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 796CBD0C56FA0D3AEDAB255B /* SessionVerificationScreenCoordinator.swift */; }; D050D7756E92CA061ED0ABF0 /* SecureBackupLogoutConfirmationScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74E08B8A66948E9690F38B94 /* SecureBackupLogoutConfirmationScreenViewModelProtocol.swift */; }; D0A965852D6C04138FA55181 /* SecureBackupLogoutConfirmationScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCF239C619971FDE48132550 /* SecureBackupLogoutConfirmationScreenModels.swift */; }; + D104B27C5DA0626B41CE78D3 /* CurrentValuePublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 127C8472672A5BA09EF1ACF8 /* CurrentValuePublisher.swift */; }; D10BA4F041DC58580A440A32 /* RoomRolesAndPermissionsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2B1DC3B3FB40A7F4AE9B7BF /* RoomRolesAndPermissionsScreen.swift */; }; D12F440F7973F1489F61389D /* NotificationSettingsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F64447FF544298A6A3BEF85 /* NotificationSettingsScreenModels.swift */; }; D181AC8FF236B7F91C0A8C28 /* MapTiler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23AA3F4B285570805CB0CCDD /* MapTiler.swift */; }; @@ -3064,6 +3065,7 @@ isa = PBXGroup; children = ( 01C4C7DB37597D7D8379511A /* Assets.xcassets */, + D174C6E7DCA00AAFC0169925 /* ElementCall */, A0C06C0F6A8621B22BFAEB56 /* Localizations */, 8AEA6A91159FA0D3EAFCCB0D /* Sounds */, ); @@ -5500,6 +5502,13 @@ path = ShareExtension; sourceTree = ""; }; + D174C6E7DCA00AAFC0169925 /* ElementCall */ = { + isa = PBXGroup; + children = ( + ); + path = ElementCall; + sourceTree = ""; + }; D382E465AF067C1BF888BF8E /* View */ = { isa = PBXGroup; children = ( @@ -6807,6 +6816,7 @@ files = ( B8EC8A544162B0A41B9AB339 /* AppSettings.swift in Sources */, 2F2906AE9BC3D0E79A6F98F8 /* Bundle.swift in Sources */, + D104B27C5DA0626B41CE78D3 /* CurrentValuePublisher.swift in Sources */, F38D32C1B0232AAFE6A0822C /* ExtensionLogger.swift in Sources */, C022284E2774A5E1EF683B4D /* FileManager.swift in Sources */, 05FF0CD80EDAB3A7C0D4700A /* InfoPlistReader.swift in Sources */, diff --git a/ElementX/Sources/Other/UserPreference.swift b/ElementX/Sources/Other/UserPreference.swift index a15777de0..d3881c9d2 100644 --- a/ElementX/Sources/Other/UserPreference.swift +++ b/ElementX/Sources/Other/UserPreference.swift @@ -8,28 +8,59 @@ import Combine import Foundation -/// Property wrapper that allows to store data into a keyed storage. -/// It also exposes a Combine publisher for listening to value changes. -/// The publisher isn't supposed to skip consecutive duplicates if any, -/// there is no concept of Equatable at this level. +/// A property wrapper that allows storing data in a keyed storage while also exposing a Combine publisher +/// to listen for value changes. The publisher does not skip consecutive duplicates, as there is no +/// `Equatable` enforcement at this level. +/// +/// - Note: This wrapper allows enforcing a default value through the `forceDefault` closure. @propertyWrapper final class UserPreference { private let key: String private var keyedStorage: any KeyedStorage - private let defaultValue: T + private let defaultValue: () -> T private let subject: PassthroughSubject = .init() + private var cancellable: AnyCancellable? - init(key: String, defaultValue: T, keyedStorage: any KeyedStorage) { + /// A publisher that determines whether the default value is always being enforced. + let forceDefault: CurrentValuePublisher + + /// Initializes the property wrapper. + /// + /// - Parameters: + /// - key: The key used to store and retrieve the value. + /// - defaultValue: The default value to use if no stored value exists or if `forceDefault` is `true`. + /// - keyedStorage: The storage instance where the value is saved. + /// - forceDefault: A publisher that determines whether the default value should always be used. Defaults to publish`false`. Useful in the context of MDM settings. + init(key: String, + defaultValue: @autoclosure @escaping () -> T, + keyedStorage: any KeyedStorage, + forceDefault: CurrentValuePublisher = .init(.init(false))) { self.key = key self.defaultValue = defaultValue self.keyedStorage = keyedStorage + self.forceDefault = forceDefault + + cancellable = forceDefault + .sink { [weak self] value in + guard value else { + return + } + // If we are now forcing the default value, we need to update the subject with the default value. + self?.subject.send(defaultValue()) + } } var wrappedValue: T { get { - keyedStorage[key] ?? defaultValue + guard !forceDefault.value else { + return defaultValue() + } + return keyedStorage[key] ?? defaultValue() } set { + guard !forceDefault.value else { + return + } keyedStorage[key] = newValue subject.send(wrappedValue) } diff --git a/ElementX/Sources/Screens/JoinRoomScreen/View/JoinRoomScreen.swift b/ElementX/Sources/Screens/JoinRoomScreen/View/JoinRoomScreen.swift index 374402369..5db3df396 100644 --- a/ElementX/Sources/Screens/JoinRoomScreen/View/JoinRoomScreen.swift +++ b/ElementX/Sources/Screens/JoinRoomScreen/View/JoinRoomScreen.swift @@ -333,7 +333,9 @@ struct JoinRoomScreen_Previews: PreviewProvider, TestablePreview { } @ViewBuilder - static func makePreview(viewModel: JoinRoomScreenViewModel, mode: JoinRoomScreenMode) -> some View { + static func makePreview(viewModel: JoinRoomScreenViewModel, + mode: JoinRoomScreenMode, + customPreviewName: String? = nil) -> some View { if mode == .forbidden { NavigationStack { JoinRoomScreen(context: viewModel.context) @@ -344,7 +346,7 @@ struct JoinRoomScreen_Previews: PreviewProvider, TestablePreview { .onAppear { forbiddenViewModel.context.send(viewAction: .join) } - .previewDisplayName(mode.previewDisplayName) + .previewDisplayName(customPreviewName ?? mode.previewDisplayName) } else { NavigationStack { JoinRoomScreen(context: viewModel.context) @@ -352,7 +354,7 @@ struct JoinRoomScreen_Previews: PreviewProvider, TestablePreview { .snapshotPreferences(expect: viewModel.context.$viewState.map { state in state.roomDetails != nil }) - .previewDisplayName(mode.previewDisplayName) + .previewDisplayName(customPreviewName ?? mode.previewDisplayName) } } diff --git a/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/View/AdvancedSettingsScreen.swift b/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/View/AdvancedSettingsScreen.swift index 051236697..20d01377f 100644 --- a/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/View/AdvancedSettingsScreen.swift +++ b/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/View/AdvancedSettingsScreen.swift @@ -32,6 +32,15 @@ struct AdvancedSettingsScreen: View { .onChange(of: context.optimizeMediaUploads) { context.send(viewAction: .optimizeMediaUploadsChanged) } + + // TODO: Waiting for designs and copies + ListRow(label: .plain(title: "hide avatars", + description: ""), + kind: .toggle($context.hideInviteAvatars)) + + ListRow(label: .plain(title: "hide media", + description: ""), + kind: .toggle($context.hideTimelineMedia)) } } .compoundList() diff --git a/ShareExtension/SupportingFiles/target.yml b/ShareExtension/SupportingFiles/target.yml index e30f067bd..21f9e63c3 100644 --- a/ShareExtension/SupportingFiles/target.yml +++ b/ShareExtension/SupportingFiles/target.yml @@ -91,3 +91,4 @@ targets: - path: ../../ElementX/Sources/Other/Logging - path: ../../ElementX/Sources/Other/UserPreference.swift - path: ../../ElementX/Sources/UITests/UITestsScreenIdentifier.swift + - path: ../../ElementX/Sources/Other/CurrentValuePublisher.swift