Add a setting to hide unread messages badges. (#2412)

* Add a setting to hide grey unread messages badges.

* Move room badge logic to be built in the view model instead of in the view.

* Move setting into DeveloperOptions.
This commit is contained in:
Doug
2024-02-07 13:00:35 +00:00
committed by GitHub
parent 871471bbb0
commit 025b3e4195
12 changed files with 85 additions and 75 deletions

View File

@@ -38,6 +38,7 @@ final class AppSettings {
case richTextEditorEnabled
case appAppearance
case sendReadReceiptsEnabled
case hideUnreadMessagesBadge
case elementCallBaseURL
case elementCallEncryptionEnabled
@@ -212,6 +213,11 @@ final class AppSettings {
@UserPreference(key: UserDefaultsKeys.analyticsConsentState, defaultValue: AnalyticsConsentState.unknown, storageType: .userDefaults(store))
var analyticsConsentState
// MARK: - Home Screen
@UserPreference(key: UserDefaultsKeys.hideUnreadMessagesBadge, defaultValue: false, storageType: .userDefaults(store))
var hideUnreadMessagesBadge
// MARK: - Room Screen
@UserPreference(key: UserDefaultsKeys.timelineStyle, defaultValue: TimelineStyle.bubbles, storageType: .userDefaults(store))

View File

@@ -157,15 +157,15 @@ struct HomeScreenRoom: Identifiable, Equatable {
var name = ""
var isMarkedUnread: Bool
var badges: Badges
struct Badges: Equatable {
let isDotShown: Bool
let isMentionShown: Bool
let isMuteShown: Bool
let isCallShown: Bool
}
var hasUnreadMessages = false
var hasUnreadMentions = false
var hasUnreadNotifications = false
var hasOngoingCall = false
let isHighlighted: Bool
var timestamp: String?
@@ -173,28 +173,46 @@ struct HomeScreenRoom: Identifiable, Equatable {
var avatarURL: URL?
var notificationMode: RoomNotificationModeProxy?
var isPlaceholder = false
var hasNewContent: Bool {
hasUnreadMessages || hasUnreadMentions || hasUnreadNotifications || isMarkedUnread
}
static func placeholder() -> HomeScreenRoom {
HomeScreenRoom(id: UUID().uuidString,
roomId: nil,
name: "Placeholder room name",
isMarkedUnread: false,
hasUnreadMessages: false,
hasUnreadMentions: false,
hasUnreadNotifications: false,
badges: .init(isDotShown: false, isMentionShown: false, isMuteShown: false, isCallShown: false),
isHighlighted: false,
timestamp: "Now",
lastMessage: placeholderLastMessage,
isPlaceholder: true)
}
}
extension HomeScreenRoom {
init(details: RoomSummaryDetails, invalidated: Bool, hideUnreadMessagesBadge: Bool) {
let identifier = invalidated ? "invalidated-" + details.id : details.id
let hasUnreadMessages = hideUnreadMessagesBadge ? false : details.hasUnreadMessages
let isDotShown = hasUnreadMessages || details.hasUnreadMentions || details.hasUnreadNotifications || details.isMarkedUnread
let isMentionShown = details.hasUnreadMentions && !details.isMuted
let isMuteShown = details.isMuted
let isCallShown = details.hasOngoingCall
let isHighlighted = !details.isMuted && (details.hasUnreadNotifications || details.hasUnreadMentions || details.isMarkedUnread)
self.init(id: identifier,
roomId: details.id,
name: details.name,
badges: .init(isDotShown: isDotShown,
isMentionShown: isMentionShown,
isMuteShown: isMuteShown,
isCallShown: isCallShown),
isHighlighted: isHighlighted,
timestamp: details.lastMessageFormattedTimestamp,
lastMessage: details.lastMessage,
avatarURL: details.avatarURL)
}
}
enum RoomListFilter: Int, CaseIterable, Identifiable {
var id: Int {
rawValue

View File

@@ -90,6 +90,10 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
.weakAssign(to: \.state.markAsUnreadEnabled, on: self)
.store(in: &cancellables)
appSettings.$hideUnreadMessagesBadge
.sink { [weak self] _ in self?.updateRooms() }
.store(in: &cancellables)
let isSearchFieldFocused = context.$viewState.map(\.bindings.isSearchFieldFocused)
let searchQuery = context.$viewState.map(\.bindings.searchQuery)
isSearchFieldFocused
@@ -339,11 +343,11 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
switch summary {
case .empty:
rooms.append(HomeScreenRoom.placeholder())
case .invalidated(let details):
let room = buildRoom(with: details, invalidated: true)
rooms.append(room)
case .filled(let details):
let room = buildRoom(with: details, invalidated: false)
let room = HomeScreenRoom(details: details, invalidated: false, hideUnreadMessagesBadge: appSettings.hideUnreadMessagesBadge)
rooms.append(room)
case .invalidated(let details):
let room = HomeScreenRoom(details: details, invalidated: true, hideUnreadMessagesBadge: appSettings.hideUnreadMessagesBadge)
rooms.append(room)
}
}
@@ -353,23 +357,6 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
MXLog.verbose("Finished updating rooms")
}
private func buildRoom(with details: RoomSummaryDetails, invalidated: Bool) -> HomeScreenRoom {
let identifier = invalidated ? "invalidated-" + details.id : details.id
return HomeScreenRoom(id: identifier,
roomId: details.id,
name: details.name,
isMarkedUnread: details.isMarkedUnread,
hasUnreadMessages: details.unreadMessagesCount > 0,
hasUnreadMentions: details.unreadMentionsCount > 0,
hasUnreadNotifications: details.unreadNotificationsCount > 0,
hasOngoingCall: details.hasOngoingCall,
timestamp: details.lastMessageFormattedTimestamp,
lastMessage: details.lastMessage,
avatarURL: details.avatarURL,
notificationMode: details.notificationMode)
}
private func updateVisibleRange(_ range: Range<Int>) {
guard !range.isEmpty else {
return

View File

@@ -95,8 +95,8 @@ struct HomeScreenRoomCell: View {
if let timestamp = room.timestamp {
Text(timestamp)
.font(isHighlighted ? .compound.bodySMSemibold : .compound.bodySM)
.foregroundColor(isHighlighted ? .compound.textActionAccent : .compound.textSecondary)
.font(room.isHighlighted ? .compound.bodySMSemibold : .compound.bodySM)
.foregroundColor(room.isHighlighted ? .compound.textActionAccent : .compound.textSecondary)
}
}
}
@@ -117,38 +117,30 @@ struct HomeScreenRoomCell: View {
Spacer()
HStack(spacing: 8) {
if room.hasOngoingCall {
if room.badges.isCallShown {
CompoundIcon(\.videoCallSolid, size: .xSmall, relativeTo: .compound.bodySM)
.foregroundColor(isHighlighted ? .compound.iconAccentTertiary : .compound.iconQuaternary)
.foregroundColor(room.isHighlighted ? .compound.iconAccentTertiary : .compound.iconQuaternary)
}
if room.notificationMode == .mute {
if room.badges.isMuteShown {
CompoundIcon(\.notificationsOffSolid, size: .custom(15), relativeTo: .compound.bodyMD)
.accessibilityLabel(L10n.a11yNotificationsMuted)
.foregroundColor(.compound.iconQuaternary)
}
if room.hasUnreadMentions, room.notificationMode != .mute {
if room.badges.isMentionShown {
mentionIcon
.foregroundColor(.compound.iconAccentTertiary)
}
if room.hasNewContent {
if room.badges.isDotShown {
Circle()
.frame(width: 12, height: 12)
.foregroundColor(isHighlighted ? .compound.iconAccentTertiary : .compound.iconQuaternary)
.foregroundColor(room.isHighlighted ? .compound.iconAccentTertiary : .compound.iconQuaternary)
}
}
}
}
private var isHighlighted: Bool {
guard !room.isPlaceholder && room.notificationMode != .mute else {
return false
}
return room.hasUnreadNotifications || room.hasUnreadMentions || room.isMarkedUnread
}
private var mentionIcon: some View {
CompoundIcon(\.mention, size: .custom(15), relativeTo: .compound.bodyMD)
@@ -212,19 +204,11 @@ struct HomeScreenRoomCell_Previews: PreviewProvider, TestablePreview {
static func mockRoom(summary: RoomSummary) -> HomeScreenRoom? {
switch summary {
case .empty:
return nil
case .invalidated(let details), .filled(let details):
return HomeScreenRoom(id: UUID().uuidString,
roomId: details.id,
name: details.name,
isMarkedUnread: details.isMarkedUnread,
hasUnreadMessages: details.unreadMessagesCount > 0,
hasUnreadMentions: details.unreadMentionsCount > 0,
hasUnreadNotifications: details.unreadNotificationsCount > 0,
hasOngoingCall: details.hasOngoingCall,
timestamp: Date(timeIntervalSinceReferenceDate: 0).formattedMinimal(),
lastMessage: details.lastMessage,
notificationMode: details.notificationMode)
nil
case .invalidated(let details):
HomeScreenRoom(details: details, invalidated: true, hideUnreadMessagesBadge: false)
case .filled(let details):
HomeScreenRoom(details: details, invalidated: false, hideUnreadMessagesBadge: false)
}
}

View File

@@ -48,7 +48,7 @@ struct HomeScreenRoomList: View {
HomeScreenRoomCell(room: room, context: context, isSelected: isSelected)
.contextMenu {
if context.viewState.markAsUnreadEnabled {
if room.hasNewContent {
if room.badges.isDotShown {
Button {
context.send(viewAction: .markRoomAsRead(roomIdentifier: room.id))
} label: {

View File

@@ -51,6 +51,7 @@ protocol DeveloperOptionsProtocol: AnyObject {
var mentionsBadgeEnabled: Bool { get set }
var roomListFiltersEnabled: Bool { get set }
var markAsUnreadEnabled: Bool { get set }
var hideUnreadMessagesBadge: Bool { get set }
var elementCallBaseURL: URL { get set }
}

View File

@@ -59,6 +59,10 @@ struct DeveloperOptionsScreen: View {
Toggle(isOn: $context.markAsUnreadEnabled) {
Text("Mark as unread")
}
Toggle(isOn: $context.hideUnreadMessagesBadge) {
Text("Hide grey dots")
}
}
Section("Element Call") {

View File

@@ -39,7 +39,7 @@ class NotificationSettingsScreenViewModel: NotificationSettingsScreenViewModelTy
let bindings = NotificationSettingsScreenViewStateBindings(enableNotifications: appSettings.enableNotifications)
super.init(initialViewState: NotificationSettingsScreenViewState(bindings: bindings, isModallyPresented: isModallyPresented))
// Listen for changes to AppSettings.enableNotifications
// Listen for changes to AppSettings.
appSettings.$enableNotifications
.weakAssign(to: \.state.bindings.enableNotifications, on: self)
.store(in: &cancellables)

View File

@@ -28,15 +28,20 @@ struct NotificationSettingsScreen: View {
if context.viewState.showSystemNotificationsAlert {
userPermissionSection
}
enableNotificationSection
if context.enableNotifications {
roomsNotificationSection
if context.viewState.settings?.roomMentionsEnabled != nil {
mentionsSection
}
if context.viewState.showCallsSettings, context.viewState.settings?.callsEnabled != nil {
callsSection
}
if context.viewState.settings?.invitationsEnabled != nil {
additionalSettingsSection
}

View File

@@ -32,6 +32,11 @@ struct RoomSummaryDetails {
let canonicalAlias: String?
let inviter: RoomMemberProxyProtocol?
let hasOngoingCall: Bool
var hasUnreadMessages: Bool { unreadMessagesCount > 0 }
var hasUnreadMentions: Bool { unreadMentionsCount > 0 }
var hasUnreadNotifications: Bool { unreadNotificationsCount > 0 }
var isMuted: Bool { notificationMode == .mute }
}
extension RoomSummaryDetails: CustomStringConvertible {

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0a008adeae6b250d84187e72067a734e213b2a7557258f4bcd251b45b64c9dc3
size 241273
oid sha256:b4bd725468edb125b1f12f847a51e37da0e5d128bf4aff21d028f71c9c13201b
size 270374

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:15801a4cca3898a59aaa7607710b7193323ae25df590a1857d009d97afc45955
size 804308
oid sha256:64cea9279b782b82793e81bb6ef4de68724840fdf0e5e084c9445ea0e773d4b8
size 799721