diff --git a/ElementX/Sources/Application/AppSettings.swift b/ElementX/Sources/Application/AppSettings.swift index 73fc31e2bb37defc076558385f242b77577204b9..071bea0d4059697bf98719ac92e266cdc03a1c5e 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 a064673463f4be86fe0a8976c3556d1115ecc1a6..3833ce03a4927f464d21db19ef11036f7d0ac934 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 49545886f8958e43220d2e55984f1c2e880e5417..d735c8feb9dea2755a340a4a3759c685c15bbb20 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 501beedf05feced2e9732da4f8507f3c5478819a..b089efa1e9d7e94f7600975b89a07ad9a98f2c75 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 c036f15368d6d14d60ca404a12ad84ddf5505632..335dfa572298bbde031348fbb985f25c59781836 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 2d61d8bc2e8eaa0e8c374b65b6dd7028910b38f6..262b8c3d94154e34249e7bf9e2e7a587f244c614 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 276adef7ab9d6c110d38b57b47eaf30f86d5ca1c..9c3d1777f0079ed3e449bdfd34f90f9e148d6c98 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 b7c74131559b8740f9deb92441b3b716be4d90bd..3e958e2005459e6ba2b3adec1548db1b78df1ee4 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 0f60d104c241b6a46887ac3c89d70a7631dbd330..3744023697fc2577d366ea21379a0cacc0fc2455 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 bccd03252251ab3d799e28bfcaebd1111ff22e6b..b7c7d620cbf48fc426aad518a8edbaecfe8e2ead 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 743f6564a15d6f23fe33e20c120d5c39556039e9..89ee2707da22e703387970dc710bc7998d8d4bb7 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 308fa9b03e310b24854ea57dd76bafce418f4b85..353baff704721bb444e53202aa25a673083f67af 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 cffc5424d9ee8ee4dbc38c15f14f7c0dcb3c67c4..24774a3b143f628226c92c903cd1230cca9fd9cb 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