diff --git a/ElementX/Sources/Application/AppSettings.swift b/ElementX/Sources/Application/AppSettings.swift index 5a2c073fc..d9877439e 100644 --- a/ElementX/Sources/Application/AppSettings.swift +++ b/ElementX/Sources/Application/AppSettings.swift @@ -18,7 +18,7 @@ protocol CommonSettingsProtocol { var traceLogPacks: Set { get } var enableOnlySignedDeviceIsolationMode: Bool { get } var hideInviteAvatars: Bool { get } - var hideTimelineMedia: Bool { get } + var timelineMediaVisibility: TimelineMediaVisibility { get } } /// Store Element specific app settings. @@ -47,7 +47,7 @@ final class AppSettings { case sharePresence case hideUnreadMessagesBadge case hideInviteAvatars - case hideTimelineMedia + case timelineMediaVisibility case elementCallBaseURLOverride @@ -353,8 +353,14 @@ final class AppSettings { @UserPreference(key: UserDefaultsKeys.hideInviteAvatars, defaultValue: false, storageType: .userDefaults(store)) var hideInviteAvatars - @UserPreference(key: UserDefaultsKeys.hideTimelineMedia, defaultValue: false, storageType: .userDefaults(store)) - var hideTimelineMedia + @UserPreference(key: UserDefaultsKeys.timelineMediaVisibility, defaultValue: TimelineMediaVisibility.always, storageType: .userDefaults(store)) + var timelineMediaVisibility } extension AppSettings: CommonSettingsProtocol { } + +enum TimelineMediaVisibility: Codable { + case always + case privateOnly + case never +} diff --git a/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/AdvancedSettingsScreenModels.swift b/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/AdvancedSettingsScreenModels.swift index b7c7d620c..eb06d5d47 100644 --- a/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/AdvancedSettingsScreenModels.swift +++ b/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/AdvancedSettingsScreenModels.swift @@ -34,7 +34,7 @@ protocol AdvancedSettingsProtocol: AnyObject { var viewSourceEnabled: Bool { get set } var appAppearance: AppAppearance { get set } var sharePresence: Bool { get set } - var hideTimelineMedia: Bool { get set } + var timelineMediaVisibility: TimelineMediaVisibility { get set } var hideInviteAvatars: Bool { get set } var optimizeMediaUploads: Bool { get set } } diff --git a/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/View/AdvancedSettingsScreen.swift b/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/View/AdvancedSettingsScreen.swift index e6897f0c9..e2e2df9df 100644 --- a/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/View/AdvancedSettingsScreen.swift +++ b/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/View/AdvancedSettingsScreen.swift @@ -35,6 +35,7 @@ struct AdvancedSettingsScreen: View { } moderationAndSafetySection + timelineMediaSection } .compoundList() .navigationTitle(L10n.commonAdvancedSettings) @@ -43,8 +44,6 @@ struct AdvancedSettingsScreen: View { private var moderationAndSafetySection: some View { Section { - ListRow(label: .plain(title: L10n.screenAdvancedSettingsHideTimelineMediaToggleTitle), - kind: .toggle($context.hideTimelineMedia)) ListRow(label: .plain(title: L10n.screenAdvancedSettingsHideInviteAvatarsToggleTitle), kind: .toggle($context.hideInviteAvatars)) } header: { @@ -52,6 +51,23 @@ struct AdvancedSettingsScreen: View { .compoundListSectionHeader() } } + + private var timelineMediaSection: some View { + Section { + ListRow(label: .plain(title: L10n.screenAdvancedSettingsShowMediaTimelineAlwaysShow), + kind: .selection(isSelected: context.timelineMediaVisibility == .always) { context.timelineMediaVisibility = .always }) + ListRow(label: .plain(title: L10n.screenAdvancedSettingsShowMediaTimelinePrivateRooms), + kind: .selection(isSelected: context.timelineMediaVisibility == .privateOnly) { context.timelineMediaVisibility = .privateOnly }) + ListRow(label: .plain(title: L10n.screenAdvancedSettingsShowMediaTimelineAlwaysHide), + kind: .selection(isSelected: context.timelineMediaVisibility == .never) { context.timelineMediaVisibility = .never }) + } header: { + Text(L10n.screenAdvancedSettingsShowMediaTimelineTitle) + .compoundListSectionHeader() + } footer: { + Text(L10n.screenAdvancedSettingsShowMediaTimelineSubtitle) + .compoundListSectionFooter() + } + } } private extension AppAppearance { diff --git a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift index d18223410..c6106e665 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift @@ -88,13 +88,21 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { timelineControllerFactory: timelineControllerFactory, clientProxy: clientProxy) + let hideTimelineMedia = switch appSettings.timelineMediaVisibility { + case .always: + false + case .privateOnly: + roomProxy.infoPublisher.value.isPublic + case .never: + true + } super.init(initialViewState: TimelineViewState(timelineKind: timelineController.timelineKind, roomID: roomProxy.id, isDirectOneToOneRoom: roomProxy.isDirectOneToOneRoom, timelineState: TimelineState(focussedEvent: focussedEventID.map { .init(eventID: $0, appearance: .immediate) }), ownUserID: roomProxy.ownUserID, isViewSourceEnabled: appSettings.viewSourceEnabled, - hideTimelineMedia: appSettings.hideTimelineMedia, + hideTimelineMedia: hideTimelineMedia, pinnedEventIDs: roomProxy.infoPublisher.value.pinnedEventIDs, emojiProvider: emojiProvider, mapTilerConfiguration: appSettings.mapTilerConfiguration, @@ -519,7 +527,21 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { .weakAssign(to: \.state.isViewSourceEnabled, on: self) .store(in: &cancellables) - appSettings.$hideTimelineMedia + appSettings.$timelineMediaVisibility + .combineLatest(roomProxy.infoPublisher + .map(\.isPublic) + .removeDuplicates() + .receive(on: DispatchQueue.main)) + .map { timelineMediaVisibility, isPublic in + switch timelineMediaVisibility { + case .always: + false + case .privateOnly: + isPublic + case .never: + true + } + } .weakAssign(to: \.state.hideTimelineMedia, on: self) .store(in: &cancellables) } diff --git a/NSE/Sources/NotificationContentBuilder.swift b/NSE/Sources/NotificationContentBuilder.swift index 24774a3b1..e06bf1bd8 100644 --- a/NSE/Sources/NotificationContentBuilder.swift +++ b/NSE/Sources/NotificationContentBuilder.swift @@ -106,7 +106,9 @@ struct NotificationContentBuilder { let displayName = notificationItem.senderDisplayName ?? notificationItem.roomDisplayName notification.body = String(messageEventStringBuilder.buildAttributedString(for: messageType, senderDisplayName: displayName).characters) - guard !settings.hideTimelineMedia else { return notification } + // For now we are fine hiding all images if the visibility is not `always` while we wait for an `isPublic` flag + // to be implemented in the SDK. + guard settings.timelineMediaVisibility == .always else { return notification } switch messageType { case .image(content: let content):