From ff721f65772c75885daf7c01b6ab427bdb33e411 Mon Sep 17 00:00:00 2001 From: Mauro Romito <mauro.romito@element.io> Date: Wed, 19 Mar 2025 16:04:08 +0100 Subject: [PATCH] hide invite avatars when such flag is on This affects: - Invited room preview inviter avatar - Invited room preview room avatar - Invited room cell inviter avatar in the room list - Invited room cell room avatar in the room list - Push notification for an invite --- .../Sources/Application/AppSettings.swift | 5 +++++ .../Extensions/UNNotificationContent.swift | 16 ++++---------- .../SwiftUI/Views/RoomInviterLabel.swift | 3 ++- .../Screens/HomeScreen/HomeScreenModels.swift | 2 ++ .../HomeScreen/HomeScreenViewModel.swift | 4 ++++ .../View/HomeScreenInviteCell.swift | 10 +++++++-- .../JoinRoomScreen/JoinRoomScreenModels.swift | 21 ++++++++++++++++--- .../JoinRoomScreenViewModel.swift | 4 ++++ .../JoinRoomScreen/View/JoinRoomScreen.swift | 4 +++- .../AdvancedSettingsScreenModels.swift | 3 ++- .../DeveloperOptionsScreenModels.swift | 1 - .../View/DeveloperOptionsScreen.swift | 6 ------ NSE/Sources/NotificationContentBuilder.swift | 3 ++- 13 files changed, 54 insertions(+), 28 deletions(-) diff --git a/ElementX/Sources/Application/AppSettings.swift b/ElementX/Sources/Application/AppSettings.swift index 73fc31e2b..071bea0d4 100644 --- a/ElementX/Sources/Application/AppSettings.swift +++ b/ElementX/Sources/Application/AppSettings.swift @@ -12,6 +12,7 @@ import SwiftUI protocol CommonSettingsProtocol { var logLevel: LogLevel { get } var enableOnlySignedDeviceIsolationMode: Bool { get } + var hideInviteAvatars: Bool { get } var hideTimelineMedia: Bool { get } var eventCacheEnabled: Bool { get } } @@ -40,6 +41,7 @@ final class AppSettings { case appAppearance case sharePresence case hideUnreadMessagesBadge + case hideInviteAvatars case hideTimelineMedia case elementCallBaseURLOverride @@ -307,6 +309,9 @@ final class AppSettings { @UserPreference(key: UserDefaultsKeys.enableOnlySignedDeviceIsolationMode, defaultValue: false, storageType: .userDefaults(store)) var enableOnlySignedDeviceIsolationMode + @UserPreference(key: UserDefaultsKeys.hideInviteAvatars, defaultValue: false, storageType: .userDefaults(store)) + var hideInviteAvatars + @UserPreference(key: UserDefaultsKeys.hideTimelineMedia, defaultValue: false, storageType: .userDefaults(store)) var hideTimelineMedia diff --git a/ElementX/Sources/Other/Extensions/UNNotificationContent.swift b/ElementX/Sources/Other/Extensions/UNNotificationContent.swift index a06467346..3833ce03a 100644 --- a/ElementX/Sources/Other/Extensions/UNNotificationContent.swift +++ b/ElementX/Sources/Other/Extensions/UNNotificationContent.swift @@ -101,32 +101,24 @@ extension UNMutableNotificationContent { func addSenderIcon(using mediaProvider: MediaProviderProtocol?, senderID: String, senderName: String, - icon: NotificationIcon) async throws -> UNMutableNotificationContent { - // We display the placeholder only if... - var needsPlaceholder = false - + icon: NotificationIcon, + forcePlaceholder: Bool = false) async throws -> UNMutableNotificationContent { var fetchedImage: INImage? let image: INImage - if let mediaSource = icon.mediaSource { + if !forcePlaceholder, let mediaSource = icon.mediaSource { switch await mediaProvider?.loadThumbnailForSource(source: mediaSource, size: .init(width: 100, height: 100)) { case .success(let data): fetchedImage = INImage(imageData: data) case .failure(let error): MXLog.error("Couldn't add sender icon: \(error)") - // ...The provider failed to fetch - needsPlaceholder = true case .none: break } - } else { - // ...There is no media - needsPlaceholder = true } if let fetchedImage { image = fetchedImage - } else if needsPlaceholder, - let data = await getPlaceholderAvatarImageData(name: icon.groupInfo?.name ?? senderName, + } else if let data = await getPlaceholderAvatarImageData(name: icon.groupInfo?.name ?? senderName, id: icon.groupInfo?.id ?? senderID) { image = INImage(imageData: data) } else { diff --git a/ElementX/Sources/Other/SwiftUI/Views/RoomInviterLabel.swift b/ElementX/Sources/Other/SwiftUI/Views/RoomInviterLabel.swift index 49545886f..d735c8feb 100644 --- a/ElementX/Sources/Other/SwiftUI/Views/RoomInviterLabel.swift +++ b/ElementX/Sources/Other/SwiftUI/Views/RoomInviterLabel.swift @@ -38,12 +38,13 @@ struct RoomInviterDetails: Equatable { struct RoomInviterLabel: View { let inviter: RoomInviterDetails + var shouldHideAvatar = false let mediaProvider: MediaProviderProtocol? var body: some View { HStack(alignment: .firstTextBaseline, spacing: 8) { - LoadableAvatarImage(url: inviter.avatarURL, + LoadableAvatarImage(url: shouldHideAvatar ? nil : inviter.avatarURL, name: inviter.displayName, contentID: inviter.id, avatarSize: .custom(16), diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift index 501beedf0..b089efa1e 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift @@ -106,6 +106,8 @@ struct HomeScreenViewState: BindableState { return rooms } + var hideInviteAvatars = false + var bindings = HomeScreenViewStateBindings() var placeholderRooms: [HomeScreenRoom] { diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift index c036f1536..335dfa572 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift @@ -101,6 +101,10 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol } .store(in: &cancellables) + appSettings.$hideInviteAvatars + .weakAssign(to: \.state.hideInviteAvatars, on: self) + .store(in: &cancellables) + let isSearchFieldFocused = context.$viewState.map(\.bindings.isSearchFieldFocused) let searchQuery = context.$viewState.map(\.bindings.searchQuery) let activeFilters = context.$viewState.map(\.bindings.filtersState.activeFilters) diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenInviteCell.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenInviteCell.swift index 2d61d8bc2..262b8c3d9 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/HomeScreenInviteCell.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreenInviteCell.swift @@ -16,10 +16,14 @@ struct HomeScreenInviteCell: View { let room: HomeScreenRoom let context: HomeScreenViewModel.Context + private var avatar: RoomAvatar { + context.viewState.hideInviteAvatars ? .room(id: room.id, name: room.name, avatarURL: nil) : room.avatar + } + var body: some View { HStack(alignment: .top, spacing: 16) { if dynamicTypeSize < .accessibility3 { - RoomAvatarImage(avatar: room.avatar, + RoomAvatarImage(avatar: avatar, avatarSize: .custom(52), mediaProvider: context.mediaProvider) .dynamicTypeSize(dynamicTypeSize < .accessibility1 ? dynamicTypeSize : .accessibility1) @@ -71,7 +75,9 @@ struct HomeScreenInviteCell: View { private var inviterView: some View { if let inviter = room.inviter, !room.isDirect { - RoomInviterLabel(inviter: inviter, mediaProvider: context.mediaProvider) + RoomInviterLabel(inviter: inviter, + shouldHideAvatar: context.viewState.hideInviteAvatars, + mediaProvider: context.mediaProvider) .font(.compound.bodyMD) .foregroundStyle(.compound.textSecondary) } diff --git a/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenModels.swift b/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenModels.swift index 276adef7a..9c3d1777f 100644 --- a/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenModels.swift +++ b/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenModels.swift @@ -23,6 +23,15 @@ enum JoinRoomScreenMode: Equatable { case knocked case banned(sender: String?, reason: String?) case forbidden + + var isInvite: Bool { + switch self { + case .invited: + true + default: + false + } + } } struct JoinRoomScreenRoomDetails { @@ -41,9 +50,15 @@ struct JoinRoomScreenViewState: BindableState { var roomDetails: JoinRoomScreenRoomDetails? var mode: JoinRoomScreenMode = .loading + + var hideInviteAvatars = false var bindings = JoinRoomScreenViewStateBindings() + var shouldHideAvatars: Bool { + hideInviteAvatars && mode.isInvite + } + var title: String { if isDMInvite, let inviter = roomDetails?.inviter { return inviter.displayName ?? inviter.id @@ -68,9 +83,9 @@ struct JoinRoomScreenViewState: BindableState { var avatar: RoomAvatar? { if isDMInvite, let inviter = roomDetails?.inviter { - return .room(id: roomID, name: inviter.displayName, avatarURL: inviter.avatarURL) - } else if let avatar = roomDetails?.avatar { - return avatar + return .room(id: roomID, name: inviter.displayName, avatarURL: hideInviteAvatars ? nil : inviter.avatarURL) + } else if let roomDetails, let avatar = roomDetails.avatar { + return shouldHideAvatars ? .room(id: roomID, name: roomDetails.name, avatarURL: nil) : avatar } else if let name = roomDetails?.name { return .room(id: roomID, name: name, avatarURL: nil) } else { diff --git a/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenViewModel.swift b/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenViewModel.swift index b7c741315..3e958e200 100644 --- a/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenViewModel.swift +++ b/ElementX/Sources/Screens/JoinRoomScreen/JoinRoomScreenViewModel.swift @@ -41,6 +41,10 @@ class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewMo super.init(initialViewState: JoinRoomScreenViewState(roomID: roomID), mediaProvider: mediaProvider) + appSettings.$hideInviteAvatars + .weakAssign(to: \.state.hideInviteAvatars, on: self) + .store(in: &cancellables) + context.$viewState.map(\.mode) .removeDuplicates() .sink { mode in diff --git a/ElementX/Sources/Screens/JoinRoomScreen/View/JoinRoomScreen.swift b/ElementX/Sources/Screens/JoinRoomScreen/View/JoinRoomScreen.swift index 0f60d104c..374402369 100644 --- a/ElementX/Sources/Screens/JoinRoomScreen/View/JoinRoomScreen.swift +++ b/ElementX/Sources/Screens/JoinRoomScreen/View/JoinRoomScreen.swift @@ -58,7 +58,9 @@ struct JoinRoomScreen: View { private var defaultView: some View { VStack(spacing: 16) { if let inviter = context.viewState.roomDetails?.inviter { - RoomInviterLabel(inviter: inviter, mediaProvider: context.mediaProvider) + RoomInviterLabel(inviter: inviter, + shouldHideAvatar: context.viewState.hideInviteAvatars, + mediaProvider: context.mediaProvider) .multilineTextAlignment(.center) .font(.compound.bodyMD) .foregroundStyle(.compound.textSecondary) diff --git a/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/AdvancedSettingsScreenModels.swift b/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/AdvancedSettingsScreenModels.swift index bccd03252..b7c7d620c 100644 --- a/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/AdvancedSettingsScreenModels.swift +++ b/ElementX/Sources/Screens/Settings/AvancedOptionsScreen/AdvancedSettingsScreenModels.swift @@ -34,7 +34,8 @@ protocol AdvancedSettingsProtocol: AnyObject { var viewSourceEnabled: Bool { get set } var appAppearance: AppAppearance { get set } var sharePresence: Bool { get set } - + var hideTimelineMedia: Bool { get set } + var hideInviteAvatars: Bool { get set } var optimizeMediaUploads: Bool { get set } } diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift index 743f6564a..89ee2707d 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift @@ -40,7 +40,6 @@ protocol DeveloperOptionsProtocol: AnyObject { var publicSearchEnabled: Bool { get set } var hideUnreadMessagesBadge: Bool { get set } var fuzzyRoomListSearchEnabled: Bool { get set } - var hideTimelineMedia: Bool { get set } var enableOnlySignedDeviceIsolationMode: Bool { get set } var elementCallBaseURLOverride: URL? { get set } var knockingEnabled: Bool { get set } diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift index 308fa9b03..353baff70 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift @@ -46,12 +46,6 @@ struct DeveloperOptionsScreen: View { } } - Section("Room") { - Toggle(isOn: $context.hideTimelineMedia) { - Text("Hide image & video previews") - } - } - Section("Join rules") { Toggle(isOn: $context.knockingEnabled) { Text("Knocking") diff --git a/NSE/Sources/NotificationContentBuilder.swift b/NSE/Sources/NotificationContentBuilder.swift index cffc5424d..24774a3b1 100644 --- a/NSE/Sources/NotificationContentBuilder.swift +++ b/NSE/Sources/NotificationContentBuilder.swift @@ -93,7 +93,8 @@ struct NotificationContentBuilder { notification = try await notification.addSenderIcon(using: mediaProvider, senderID: notificationItem.senderID, senderName: notificationItem.senderDisplayName ?? notificationItem.roomDisplayName, - icon: icon(for: notificationItem)) + icon: icon(for: notificationItem), + forcePlaceholder: settings.hideInviteAvatars) notification.body = body return notification -- GitLab