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:
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0a008adeae6b250d84187e72067a734e213b2a7557258f4bcd251b45b64c9dc3
|
||||
size 241273
|
||||
oid sha256:b4bd725468edb125b1f12f847a51e37da0e5d128bf4aff21d028f71c9c13201b
|
||||
size 270374
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:15801a4cca3898a59aaa7607710b7193323ae25df590a1857d009d97afc45955
|
||||
size 804308
|
||||
oid sha256:64cea9279b782b82793e81bb6ef4de68724840fdf0e5e084c9445ea0e773d4b8
|
||||
size 799721
|
||||
|
||||
Reference in New Issue
Block a user