mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-06 04:18:31 +00:00
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
This commit is contained in:
@@ -12,6 +12,7 @@ import SwiftUI
|
|||||||
protocol CommonSettingsProtocol {
|
protocol CommonSettingsProtocol {
|
||||||
var logLevel: LogLevel { get }
|
var logLevel: LogLevel { get }
|
||||||
var enableOnlySignedDeviceIsolationMode: Bool { get }
|
var enableOnlySignedDeviceIsolationMode: Bool { get }
|
||||||
|
var hideInviteAvatars: Bool { get }
|
||||||
var hideTimelineMedia: Bool { get }
|
var hideTimelineMedia: Bool { get }
|
||||||
var eventCacheEnabled: Bool { get }
|
var eventCacheEnabled: Bool { get }
|
||||||
}
|
}
|
||||||
@@ -40,6 +41,7 @@ final class AppSettings {
|
|||||||
case appAppearance
|
case appAppearance
|
||||||
case sharePresence
|
case sharePresence
|
||||||
case hideUnreadMessagesBadge
|
case hideUnreadMessagesBadge
|
||||||
|
case hideInviteAvatars
|
||||||
case hideTimelineMedia
|
case hideTimelineMedia
|
||||||
|
|
||||||
case elementCallBaseURLOverride
|
case elementCallBaseURLOverride
|
||||||
@@ -307,6 +309,9 @@ final class AppSettings {
|
|||||||
@UserPreference(key: UserDefaultsKeys.enableOnlySignedDeviceIsolationMode, defaultValue: false, storageType: .userDefaults(store))
|
@UserPreference(key: UserDefaultsKeys.enableOnlySignedDeviceIsolationMode, defaultValue: false, storageType: .userDefaults(store))
|
||||||
var enableOnlySignedDeviceIsolationMode
|
var enableOnlySignedDeviceIsolationMode
|
||||||
|
|
||||||
|
@UserPreference(key: UserDefaultsKeys.hideInviteAvatars, defaultValue: false, storageType: .userDefaults(store))
|
||||||
|
var hideInviteAvatars
|
||||||
|
|
||||||
@UserPreference(key: UserDefaultsKeys.hideTimelineMedia, defaultValue: false, storageType: .userDefaults(store))
|
@UserPreference(key: UserDefaultsKeys.hideTimelineMedia, defaultValue: false, storageType: .userDefaults(store))
|
||||||
var hideTimelineMedia
|
var hideTimelineMedia
|
||||||
|
|
||||||
|
|||||||
@@ -101,32 +101,24 @@ extension UNMutableNotificationContent {
|
|||||||
func addSenderIcon(using mediaProvider: MediaProviderProtocol?,
|
func addSenderIcon(using mediaProvider: MediaProviderProtocol?,
|
||||||
senderID: String,
|
senderID: String,
|
||||||
senderName: String,
|
senderName: String,
|
||||||
icon: NotificationIcon) async throws -> UNMutableNotificationContent {
|
icon: NotificationIcon,
|
||||||
// We display the placeholder only if...
|
forcePlaceholder: Bool = false) async throws -> UNMutableNotificationContent {
|
||||||
var needsPlaceholder = false
|
|
||||||
|
|
||||||
var fetchedImage: INImage?
|
var fetchedImage: INImage?
|
||||||
let image: 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)) {
|
switch await mediaProvider?.loadThumbnailForSource(source: mediaSource, size: .init(width: 100, height: 100)) {
|
||||||
case .success(let data):
|
case .success(let data):
|
||||||
fetchedImage = INImage(imageData: data)
|
fetchedImage = INImage(imageData: data)
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
MXLog.error("Couldn't add sender icon: \(error)")
|
MXLog.error("Couldn't add sender icon: \(error)")
|
||||||
// ...The provider failed to fetch
|
|
||||||
needsPlaceholder = true
|
|
||||||
case .none:
|
case .none:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// ...There is no media
|
|
||||||
needsPlaceholder = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let fetchedImage {
|
if let fetchedImage {
|
||||||
image = fetchedImage
|
image = fetchedImage
|
||||||
} else if needsPlaceholder,
|
} else if let data = await getPlaceholderAvatarImageData(name: icon.groupInfo?.name ?? senderName,
|
||||||
let data = await getPlaceholderAvatarImageData(name: icon.groupInfo?.name ?? senderName,
|
|
||||||
id: icon.groupInfo?.id ?? senderID) {
|
id: icon.groupInfo?.id ?? senderID) {
|
||||||
image = INImage(imageData: data)
|
image = INImage(imageData: data)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -38,12 +38,13 @@ struct RoomInviterDetails: Equatable {
|
|||||||
|
|
||||||
struct RoomInviterLabel: View {
|
struct RoomInviterLabel: View {
|
||||||
let inviter: RoomInviterDetails
|
let inviter: RoomInviterDetails
|
||||||
|
var shouldHideAvatar = false
|
||||||
|
|
||||||
let mediaProvider: MediaProviderProtocol?
|
let mediaProvider: MediaProviderProtocol?
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(alignment: .firstTextBaseline, spacing: 8) {
|
HStack(alignment: .firstTextBaseline, spacing: 8) {
|
||||||
LoadableAvatarImage(url: inviter.avatarURL,
|
LoadableAvatarImage(url: shouldHideAvatar ? nil : inviter.avatarURL,
|
||||||
name: inviter.displayName,
|
name: inviter.displayName,
|
||||||
contentID: inviter.id,
|
contentID: inviter.id,
|
||||||
avatarSize: .custom(16),
|
avatarSize: .custom(16),
|
||||||
|
|||||||
@@ -106,6 +106,8 @@ struct HomeScreenViewState: BindableState {
|
|||||||
return rooms
|
return rooms
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var hideInviteAvatars = false
|
||||||
|
|
||||||
var bindings = HomeScreenViewStateBindings()
|
var bindings = HomeScreenViewStateBindings()
|
||||||
|
|
||||||
var placeholderRooms: [HomeScreenRoom] {
|
var placeholderRooms: [HomeScreenRoom] {
|
||||||
|
|||||||
@@ -101,6 +101,10 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
|||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
appSettings.$hideInviteAvatars
|
||||||
|
.weakAssign(to: \.state.hideInviteAvatars, on: self)
|
||||||
|
.store(in: &cancellables)
|
||||||
|
|
||||||
let isSearchFieldFocused = context.$viewState.map(\.bindings.isSearchFieldFocused)
|
let isSearchFieldFocused = context.$viewState.map(\.bindings.isSearchFieldFocused)
|
||||||
let searchQuery = context.$viewState.map(\.bindings.searchQuery)
|
let searchQuery = context.$viewState.map(\.bindings.searchQuery)
|
||||||
let activeFilters = context.$viewState.map(\.bindings.filtersState.activeFilters)
|
let activeFilters = context.$viewState.map(\.bindings.filtersState.activeFilters)
|
||||||
|
|||||||
@@ -16,10 +16,14 @@ struct HomeScreenInviteCell: View {
|
|||||||
let room: HomeScreenRoom
|
let room: HomeScreenRoom
|
||||||
let context: HomeScreenViewModel.Context
|
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 {
|
var body: some View {
|
||||||
HStack(alignment: .top, spacing: 16) {
|
HStack(alignment: .top, spacing: 16) {
|
||||||
if dynamicTypeSize < .accessibility3 {
|
if dynamicTypeSize < .accessibility3 {
|
||||||
RoomAvatarImage(avatar: room.avatar,
|
RoomAvatarImage(avatar: avatar,
|
||||||
avatarSize: .custom(52),
|
avatarSize: .custom(52),
|
||||||
mediaProvider: context.mediaProvider)
|
mediaProvider: context.mediaProvider)
|
||||||
.dynamicTypeSize(dynamicTypeSize < .accessibility1 ? dynamicTypeSize : .accessibility1)
|
.dynamicTypeSize(dynamicTypeSize < .accessibility1 ? dynamicTypeSize : .accessibility1)
|
||||||
@@ -71,7 +75,9 @@ struct HomeScreenInviteCell: View {
|
|||||||
private var inviterView: some View {
|
private var inviterView: some View {
|
||||||
if let inviter = room.inviter,
|
if let inviter = room.inviter,
|
||||||
!room.isDirect {
|
!room.isDirect {
|
||||||
RoomInviterLabel(inviter: inviter, mediaProvider: context.mediaProvider)
|
RoomInviterLabel(inviter: inviter,
|
||||||
|
shouldHideAvatar: context.viewState.hideInviteAvatars,
|
||||||
|
mediaProvider: context.mediaProvider)
|
||||||
.font(.compound.bodyMD)
|
.font(.compound.bodyMD)
|
||||||
.foregroundStyle(.compound.textSecondary)
|
.foregroundStyle(.compound.textSecondary)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,15 @@ enum JoinRoomScreenMode: Equatable {
|
|||||||
case knocked
|
case knocked
|
||||||
case banned(sender: String?, reason: String?)
|
case banned(sender: String?, reason: String?)
|
||||||
case forbidden
|
case forbidden
|
||||||
|
|
||||||
|
var isInvite: Bool {
|
||||||
|
switch self {
|
||||||
|
case .invited:
|
||||||
|
true
|
||||||
|
default:
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct JoinRoomScreenRoomDetails {
|
struct JoinRoomScreenRoomDetails {
|
||||||
@@ -42,8 +51,14 @@ struct JoinRoomScreenViewState: BindableState {
|
|||||||
|
|
||||||
var mode: JoinRoomScreenMode = .loading
|
var mode: JoinRoomScreenMode = .loading
|
||||||
|
|
||||||
|
var hideInviteAvatars = false
|
||||||
|
|
||||||
var bindings = JoinRoomScreenViewStateBindings()
|
var bindings = JoinRoomScreenViewStateBindings()
|
||||||
|
|
||||||
|
var shouldHideAvatars: Bool {
|
||||||
|
hideInviteAvatars && mode.isInvite
|
||||||
|
}
|
||||||
|
|
||||||
var title: String {
|
var title: String {
|
||||||
if isDMInvite, let inviter = roomDetails?.inviter {
|
if isDMInvite, let inviter = roomDetails?.inviter {
|
||||||
return inviter.displayName ?? inviter.id
|
return inviter.displayName ?? inviter.id
|
||||||
@@ -68,9 +83,9 @@ struct JoinRoomScreenViewState: BindableState {
|
|||||||
|
|
||||||
var avatar: RoomAvatar? {
|
var avatar: RoomAvatar? {
|
||||||
if isDMInvite, let inviter = roomDetails?.inviter {
|
if isDMInvite, let inviter = roomDetails?.inviter {
|
||||||
return .room(id: roomID, name: inviter.displayName, avatarURL: inviter.avatarURL)
|
return .room(id: roomID, name: inviter.displayName, avatarURL: hideInviteAvatars ? nil : inviter.avatarURL)
|
||||||
} else if let avatar = roomDetails?.avatar {
|
} else if let roomDetails, let avatar = roomDetails.avatar {
|
||||||
return avatar
|
return shouldHideAvatars ? .room(id: roomID, name: roomDetails.name, avatarURL: nil) : avatar
|
||||||
} else if let name = roomDetails?.name {
|
} else if let name = roomDetails?.name {
|
||||||
return .room(id: roomID, name: name, avatarURL: nil)
|
return .room(id: roomID, name: name, avatarURL: nil)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewMo
|
|||||||
|
|
||||||
super.init(initialViewState: JoinRoomScreenViewState(roomID: roomID), mediaProvider: mediaProvider)
|
super.init(initialViewState: JoinRoomScreenViewState(roomID: roomID), mediaProvider: mediaProvider)
|
||||||
|
|
||||||
|
appSettings.$hideInviteAvatars
|
||||||
|
.weakAssign(to: \.state.hideInviteAvatars, on: self)
|
||||||
|
.store(in: &cancellables)
|
||||||
|
|
||||||
context.$viewState.map(\.mode)
|
context.$viewState.map(\.mode)
|
||||||
.removeDuplicates()
|
.removeDuplicates()
|
||||||
.sink { mode in
|
.sink { mode in
|
||||||
|
|||||||
@@ -58,7 +58,9 @@ struct JoinRoomScreen: View {
|
|||||||
private var defaultView: some View {
|
private var defaultView: some View {
|
||||||
VStack(spacing: 16) {
|
VStack(spacing: 16) {
|
||||||
if let inviter = context.viewState.roomDetails?.inviter {
|
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)
|
.multilineTextAlignment(.center)
|
||||||
.font(.compound.bodyMD)
|
.font(.compound.bodyMD)
|
||||||
.foregroundStyle(.compound.textSecondary)
|
.foregroundStyle(.compound.textSecondary)
|
||||||
|
|||||||
+2
-1
@@ -34,7 +34,8 @@ protocol AdvancedSettingsProtocol: AnyObject {
|
|||||||
var viewSourceEnabled: Bool { get set }
|
var viewSourceEnabled: Bool { get set }
|
||||||
var appAppearance: AppAppearance { get set }
|
var appAppearance: AppAppearance { get set }
|
||||||
var sharePresence: Bool { get set }
|
var sharePresence: Bool { get set }
|
||||||
|
var hideTimelineMedia: Bool { get set }
|
||||||
|
var hideInviteAvatars: Bool { get set }
|
||||||
var optimizeMediaUploads: Bool { get set }
|
var optimizeMediaUploads: Bool { get set }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
-1
@@ -40,7 +40,6 @@ protocol DeveloperOptionsProtocol: AnyObject {
|
|||||||
var publicSearchEnabled: Bool { get set }
|
var publicSearchEnabled: Bool { get set }
|
||||||
var hideUnreadMessagesBadge: Bool { get set }
|
var hideUnreadMessagesBadge: Bool { get set }
|
||||||
var fuzzyRoomListSearchEnabled: Bool { get set }
|
var fuzzyRoomListSearchEnabled: Bool { get set }
|
||||||
var hideTimelineMedia: Bool { get set }
|
|
||||||
var enableOnlySignedDeviceIsolationMode: Bool { get set }
|
var enableOnlySignedDeviceIsolationMode: Bool { get set }
|
||||||
var elementCallBaseURLOverride: URL? { get set }
|
var elementCallBaseURLOverride: URL? { get set }
|
||||||
var knockingEnabled: Bool { get set }
|
var knockingEnabled: Bool { get set }
|
||||||
|
|||||||
-6
@@ -46,12 +46,6 @@ struct DeveloperOptionsScreen: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Section("Room") {
|
|
||||||
Toggle(isOn: $context.hideTimelineMedia) {
|
|
||||||
Text("Hide image & video previews")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Section("Join rules") {
|
Section("Join rules") {
|
||||||
Toggle(isOn: $context.knockingEnabled) {
|
Toggle(isOn: $context.knockingEnabled) {
|
||||||
Text("Knocking")
|
Text("Knocking")
|
||||||
|
|||||||
@@ -93,7 +93,8 @@ struct NotificationContentBuilder {
|
|||||||
notification = try await notification.addSenderIcon(using: mediaProvider,
|
notification = try await notification.addSenderIcon(using: mediaProvider,
|
||||||
senderID: notificationItem.senderID,
|
senderID: notificationItem.senderID,
|
||||||
senderName: notificationItem.senderDisplayName ?? notificationItem.roomDisplayName,
|
senderName: notificationItem.senderDisplayName ?? notificationItem.roomDisplayName,
|
||||||
icon: icon(for: notificationItem))
|
icon: icon(for: notificationItem),
|
||||||
|
forcePlaceholder: settings.hideInviteAvatars)
|
||||||
notification.body = body
|
notification.body = body
|
||||||
|
|
||||||
return notification
|
return notification
|
||||||
|
|||||||
Reference in New Issue
Block a user