Skip to content

Commit

Permalink
Fixes #2360 - Add support for manually marking rooms as unread
Browse files Browse the repository at this point in the history
  • Loading branch information
stefanceriu committed Jan 31, 2024
1 parent 7fa9cf1 commit 2aa5384
Show file tree
Hide file tree
Showing 19 changed files with 220 additions and 22 deletions.
3 changes: 3 additions & 0 deletions ElementX/Resources/Localizations/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,7 @@
"screen_room_retry_send_menu_title" = "Your message failed to send";
"screen_room_timeline_add_reaction" = "Add emoji";
"screen_room_timeline_less_reactions" = "Show less";
"screen_room_typing_two_members" = "%1$@ and %2$@";
"screen_room_voice_message_tooltip" = "Hold to record";
"screen_roomlist_a11y_create_message" = "Create a new conversation or room";
"screen_roomlist_empty_message" = "Get started by messaging someone.";
Expand All @@ -551,6 +552,8 @@
"screen_roomlist_filter_rooms" = "Rooms";
"screen_roomlist_filter_unreads" = "Unreads";
"screen_roomlist_main_space_title" = "All Chats";
"screen_roomlist_mark_as_read" = "Mark as read";
"screen_roomlist_mark_as_unread" = "Mark as unread";
"screen_server_confirmation_change_server" = "Change account provider";
"screen_server_confirmation_message_login_element_dot_io" = "A private server for Element employees.";
"screen_server_confirmation_message_login_matrix_dot_org" = "Matrix is an open network for secure, decentralised communication.";
Expand Down
32 changes: 32 additions & 0 deletions ElementX/Resources/Localizations/en.lproj/Localizable.stringsdict
Original file line number Diff line number Diff line change
Expand Up @@ -226,5 +226,37 @@
<string>%1$d people</string>
</dict>
</dict>
<key>screen_room_typing_many_members</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@COUNT@</string>
<key>COUNT</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>%1$@, %2$@ and %3$d other</string>
<key>other</key>
<string>%1$@, %2$@ and %3$d others</string>
</dict>
</dict>
<key>screen_room_typing_notification</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@COUNT@</string>
<key>COUNT</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>%1$@ is typing</string>
<key>other</key>
<string>%1$@ are typing</string>
</dict>
</dict>
</dict>
</plist>
4 changes: 4 additions & 0 deletions ElementX/Sources/Application/AppSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ final class AppSettings {
case swiftUITimelineEnabled
case mentionsBadgeEnabled
case roomListFiltersEnabled
case markAsUnreadEnabled
}

private static var suiteName: String = InfoPlistReader.main.appGroupIdentifier
Expand Down Expand Up @@ -282,6 +283,9 @@ final class AppSettings {
@UserPreference(key: UserDefaultsKeys.roomListFiltersEnabled, defaultValue: false, storageType: .userDefaults(store))
var roomListFiltersEnabled

@UserPreference(key: UserDefaultsKeys.markAsUnreadEnabled, defaultValue: false, storageType: .userDefaults(store))
var markAsUnreadEnabled

#endif

// MARK: - Shared
Expand Down
6 changes: 6 additions & 0 deletions ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,12 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {

self.roomProxy = roomProxy

Task {
// Mark the room as read on entering but don't send read receipts
// as those will be handled by the timeline
await roomProxy.markAsRead(sendReadReceipts: false, receiptType: appSettings.sendReadReceiptsEnabled ? .read : .readPrivate)
}

let userID = userSession.clientProxy.userID

let timelineItemFactory = RoomTimelineItemFactory(userID: userID,
Expand Down
16 changes: 16 additions & 0 deletions ElementX/Sources/Generated/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1300,6 +1300,18 @@ public enum L10n {
public static var screenRoomTimelineAddReaction: String { return L10n.tr("Localizable", "screen_room_timeline_add_reaction") }
/// Show less
public static var screenRoomTimelineLessReactions: String { return L10n.tr("Localizable", "screen_room_timeline_less_reactions") }
/// Plural format key: "%#@COUNT@"
public static func screenRoomTypingManyMembers(_ p1: Int) -> String {
return L10n.tr("Localizable", "screen_room_typing_many_members", p1)
}
/// Plural format key: "%#@COUNT@"
public static func screenRoomTypingNotification(_ p1: Int) -> String {
return L10n.tr("Localizable", "screen_room_typing_notification", p1)
}
/// %1$@ and %2$@
public static func screenRoomTypingTwoMembers(_ p1: Any, _ p2: Any) -> String {
return L10n.tr("Localizable", "screen_room_typing_two_members", String(describing: p1), String(describing: p2))
}
/// Hold to record
public static var screenRoomVoiceMessageTooltip: String { return L10n.tr("Localizable", "screen_room_voice_message_tooltip") }
/// Create a new conversation or room
Expand All @@ -1320,6 +1332,10 @@ public enum L10n {
public static var screenRoomlistFilterUnreads: String { return L10n.tr("Localizable", "screen_roomlist_filter_unreads") }
/// All Chats
public static var screenRoomlistMainSpaceTitle: String { return L10n.tr("Localizable", "screen_roomlist_main_space_title") }
/// Mark as read
public static var screenRoomlistMarkAsRead: String { return L10n.tr("Localizable", "screen_roomlist_mark_as_read") }
/// Mark as unread
public static var screenRoomlistMarkAsUnread: String { return L10n.tr("Localizable", "screen_roomlist_mark_as_unread") }
/// Change account provider
public static var screenServerConfirmationChangeServer: String { return L10n.tr("Localizable", "screen_server_confirmation_change_server") }
/// A private server for Element employees.
Expand Down
38 changes: 38 additions & 0 deletions ElementX/Sources/Mocks/Generated/GeneratedMocks.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2222,6 +2222,44 @@ class RoomProxyMock: RoomProxyProtocol {
return canUserTriggerRoomNotificationUserIDReturnValue
}
}
//MARK: - markAsUnread

var markAsUnreadCallsCount = 0
var markAsUnreadCalled: Bool {
return markAsUnreadCallsCount > 0
}
var markAsUnreadReturnValue: Result<Void, RoomProxyError>!
var markAsUnreadClosure: (() async -> Result<Void, RoomProxyError>)?

func markAsUnread() async -> Result<Void, RoomProxyError> {
markAsUnreadCallsCount += 1
if let markAsUnreadClosure = markAsUnreadClosure {
return await markAsUnreadClosure()
} else {
return markAsUnreadReturnValue
}
}
//MARK: - markAsRead

var markAsReadSendReadReceiptsReceiptTypeCallsCount = 0
var markAsReadSendReadReceiptsReceiptTypeCalled: Bool {
return markAsReadSendReadReceiptsReceiptTypeCallsCount > 0
}
var markAsReadSendReadReceiptsReceiptTypeReceivedArguments: (sendReadReceipts: Bool, receiptType: ReceiptType)?
var markAsReadSendReadReceiptsReceiptTypeReceivedInvocations: [(sendReadReceipts: Bool, receiptType: ReceiptType)] = []
var markAsReadSendReadReceiptsReceiptTypeReturnValue: Result<Void, RoomProxyError>!
var markAsReadSendReadReceiptsReceiptTypeClosure: ((Bool, ReceiptType) async -> Result<Void, RoomProxyError>)?

func markAsRead(sendReadReceipts: Bool, receiptType: ReceiptType) async -> Result<Void, RoomProxyError> {
markAsReadSendReadReceiptsReceiptTypeCallsCount += 1
markAsReadSendReadReceiptsReceiptTypeReceivedArguments = (sendReadReceipts: sendReadReceipts, receiptType: receiptType)
markAsReadSendReadReceiptsReceiptTypeReceivedInvocations.append((sendReadReceipts: sendReadReceipts, receiptType: receiptType))
if let markAsReadSendReadReceiptsReceiptTypeClosure = markAsReadSendReadReceiptsReceiptTypeClosure {
return await markAsReadSendReadReceiptsReceiptTypeClosure(sendReadReceipts, receiptType)
} else {
return markAsReadSendReadReceiptsReceiptTypeReturnValue
}
}
//MARK: - canUserJoinCall

var canUserJoinCallUserIDCallsCount = 0
Expand Down
2 changes: 1 addition & 1 deletion ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Generated using Sourcery 2.1.2 — https://github.com/krzysztofzablocki/Sourcery
// Generated using Sourcery 2.1.3 — https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT

// swiftlint:disable all
Expand Down
11 changes: 11 additions & 0 deletions ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ enum HomeScreenViewAction {
case updateVisibleItemRange(range: Range<Int>, isScrolling: Bool)
case selectInvites
case globalSearch
case markRoomAsUnread(roomIdentifier: String)
case markRoomAsRead(roomIdentifier: String)
}

enum HomeScreenRoomListMode: CustomStringConvertible {
Expand Down Expand Up @@ -102,7 +104,9 @@ struct HomeScreenViewState: BindableState {

var rooms: [HomeScreenRoom] = []
var roomListMode: HomeScreenRoomListMode = .skeletons

var shouldShowFilters = false
var markAsUnreadEnabled = false

var hasPendingInvitations = false
var hasUnreadPendingInvitations = false
Expand Down Expand Up @@ -153,6 +157,8 @@ struct HomeScreenRoom: Identifiable, Equatable {

var name = ""

var isMarkedUnread: Bool

var hasUnreadMessages = false

var hasUnreadMentions = false
Expand All @@ -171,10 +177,15 @@ struct HomeScreenRoom: Identifiable, Equatable {

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,
Expand Down
21 changes: 21 additions & 0 deletions ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
.weakAssign(to: \.state.shouldShowFilters, on: self)
.store(in: &cancellables)

appSettings.$markAsUnreadEnabled
.weakAssign(to: \.state.markAsUnreadEnabled, on: self)
.store(in: &cancellables)

let isSearchFieldFocused = context.$viewState.map(\.bindings.isSearchFieldFocused)
let searchQuery = context.$viewState.map(\.bindings.searchQuery)
isSearchFieldFocused
Expand Down Expand Up @@ -143,6 +147,22 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
actionsSubject.send(.presentInvitesScreen)
case .globalSearch:
actionsSubject.send(.presentGlobalSearch)
case .markRoomAsUnread(let roomIdentifier):
Task {
if let roomProxy = await userSession.clientProxy.roomForIdentifier(roomIdentifier) {
if case .failure(let error) = await roomProxy.markAsUnread() {
MXLog.error("Failed marking room \(roomIdentifier) as unread with error: \(error)")
}
}
}
case .markRoomAsRead(let roomIdentifier):
Task {
if let roomProxy = await userSession.clientProxy.roomForIdentifier(roomIdentifier) {
if case .failure(let error) = await roomProxy.markAsRead(sendReadReceipts: true, receiptType: appSettings.sendReadReceiptsEnabled ? .read : .readPrivate) {
MXLog.error("Failed marking room \(roomIdentifier) as read with error: \(error)")
}
}
}
}
}

Expand Down Expand Up @@ -327,6 +347,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
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,
Expand Down
19 changes: 7 additions & 12 deletions ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -133,29 +133,23 @@ struct HomeScreenRoomCell: View {
.foregroundColor(.compound.iconAccentTertiary)
}

if hasNewContent {
if room.hasNewContent {
Circle()
.frame(width: 12, height: 12)
.foregroundColor(isHighlighted ? .compound.iconAccentTertiary : .compound.iconQuaternary)
}
}
}
}

private var hasNewContent: Bool {
room.hasUnreadMessages ||
room.hasUnreadMentions ||
room.hasUnreadNotifications
}


private var isHighlighted: Bool {
guard !room.isPlaceholder &&
room.notificationMode != .mute else {
guard !room.isPlaceholder && room.notificationMode != .mute else {
return false
}
return room.hasUnreadNotifications || room.hasUnreadMentions
}

return room.hasUnreadNotifications || room.hasUnreadMentions || room.isMarkedUnread
}

private var mentionIcon: some View {
CompoundIcon(\.mention, size: .custom(15), relativeTo: .compound.bodyMD)
.accessibilityLabel(L10n.a11yNotificationsMentionsOnly)
Expand Down Expand Up @@ -223,6 +217,7 @@ struct HomeScreenRoomCell_Previews: PreviewProvider, TestablePreview {
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,
Expand Down
17 changes: 17 additions & 0 deletions ElementX/Sources/Screens/HomeScreen/View/HomeScreenRoomList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,25 @@ struct HomeScreenRoomList: View {
.redacted(reason: .placeholder)
} else {
let isSelected = context.viewState.selectedRoomID == room.id

HomeScreenRoomCell(room: room, context: context, isSelected: isSelected)
.contextMenu {
if context.viewState.markAsUnreadEnabled {
if room.hasNewContent {
Button {
context.send(viewAction: .markRoomAsRead(roomIdentifier: room.id))
} label: {
Text(L10n.screenRoomlistMarkAsRead)
}
} else {
Button {
context.send(viewAction: .markRoomAsUnread(roomIdentifier: room.id))
} label: {
Text(L10n.screenRoomlistMarkAsUnread)
}
}
}

Button {
context.send(viewAction: .showRoomDetails(roomIdentifier: room.id))
} label: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ private extension InvitesScreenRoomDetails {
avatarURL: nil,
lastMessage: nil,
lastMessageFormattedTimestamp: nil,
isMarkedUnread: false,
unreadMessagesCount: 0,
unreadMentionsCount: 0,
unreadNotificationsCount: 0,
Expand All @@ -208,6 +209,7 @@ private extension InvitesScreenRoomDetails {
avatarURL: avatarURL,
lastMessage: nil,
lastMessageFormattedTimestamp: nil,
isMarkedUnread: false,
unreadMessagesCount: 0,
unreadMentionsCount: 0,
unreadNotificationsCount: 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ protocol DeveloperOptionsProtocol: AnyObject {
var swiftUITimelineEnabled: Bool { get set }
var mentionsBadgeEnabled: Bool { get set }
var roomListFiltersEnabled: Bool { get set }
var markAsUnreadEnabled: Bool { get set }

var elementCallBaseURL: URL { get set }
var elementCallUseEncryption: Bool { get set }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ struct DeveloperOptionsScreen: View {
Toggle(isOn: $context.roomListFiltersEnabled) {
Text("Show filters")
}

Toggle(isOn: $context.markAsUnreadEnabled) {
Text("Mark as unread")
}
}

Section("Element Call") {
Expand Down
Loading

0 comments on commit 2aa5384

Please sign in to comment.