mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 16:10:20 +00:00
* Fixes #2360 - Add support for manually marking rooms as unread * Add analytics for marking rooms as (un)read * Remove unnecessary analytics abstraction levels * Fix breaking API changes following SDK bump * Add changelog * Handle PR comments * Fix unit tests
This commit is contained in:
@@ -156,7 +156,7 @@ private extension View {
|
||||
case .edit:
|
||||
self
|
||||
case .new:
|
||||
track(screen: .createPoll)
|
||||
track(screen: .CreatePollView)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ struct CreateRoomScreen: View {
|
||||
securitySection
|
||||
}
|
||||
.compoundList()
|
||||
.track(screen: .createRoom)
|
||||
.track(screen: .CreateRoom)
|
||||
.scrollDismissesKeyboard(.immediately)
|
||||
.navigationTitle(L10n.screenCreateRoomTitle)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
|
||||
@@ -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 {
|
||||
@@ -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
|
||||
@@ -153,6 +157,8 @@ struct HomeScreenRoom: Identifiable, Equatable {
|
||||
|
||||
var name = ""
|
||||
|
||||
var isMarkedUnread: Bool
|
||||
|
||||
var hasUnreadMessages = false
|
||||
|
||||
var hasUnreadMentions = false
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
@@ -143,6 +147,34 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
||||
actionsSubject.send(.presentInvitesScreen)
|
||||
case .globalSearch:
|
||||
actionsSubject.send(.presentGlobalSearch)
|
||||
case .markRoomAsUnread(let roomIdentifier):
|
||||
Task {
|
||||
guard let roomProxy = await userSession.clientProxy.roomForIdentifier(roomIdentifier) else {
|
||||
MXLog.error("Failed retrieving room for identifier: \(roomIdentifier)")
|
||||
return
|
||||
}
|
||||
|
||||
switch await roomProxy.markAsUnread() {
|
||||
case .success:
|
||||
ServiceLocator.shared.analytics.trackInteraction(name: .MobileRoomListRoomContextMenuUnreadToggle)
|
||||
case .failure(let error):
|
||||
MXLog.error("Failed marking room \(roomIdentifier) as unread with error: \(error)")
|
||||
}
|
||||
}
|
||||
case .markRoomAsRead(let roomIdentifier):
|
||||
Task {
|
||||
guard let roomProxy = await userSession.clientProxy.roomForIdentifier(roomIdentifier) else {
|
||||
MXLog.error("Failed retrieving room for identifier: \(roomIdentifier)")
|
||||
return
|
||||
}
|
||||
|
||||
switch await roomProxy.markAsRead(sendReadReceipts: true, receiptType: appSettings.sendReadReceiptsEnabled ? .read : .readPrivate) {
|
||||
case .success:
|
||||
ServiceLocator.shared.analytics.trackInteraction(name: .MobileRoomListRoomContextMenuUnreadToggle)
|
||||
case .failure(let error):
|
||||
MXLog.error("Failed marking room \(roomIdentifier) as read with error: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,6 +359,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,
|
||||
|
||||
@@ -40,7 +40,7 @@ struct HomeScreen: View {
|
||||
.navigationTitle(L10n.screenRoomlistMainSpaceTitle)
|
||||
.toolbar { toolbar }
|
||||
.background(Color.compound.bgCanvasDefault.ignoresSafeArea())
|
||||
.track(screen: .home)
|
||||
.track(screen: .Home)
|
||||
.introspect(.viewController, on: .supportedVersions) { controller in
|
||||
Task {
|
||||
if bloomView == nil {
|
||||
|
||||
@@ -133,7 +133,7 @@ struct HomeScreenRoomCell: View {
|
||||
.foregroundColor(.compound.iconAccentTertiary)
|
||||
}
|
||||
|
||||
if hasNewContent {
|
||||
if room.hasNewContent {
|
||||
Circle()
|
||||
.frame(width: 12, height: 12)
|
||||
.foregroundColor(isHighlighted ? .compound.iconAccentTertiary : .compound.iconQuaternary)
|
||||
@@ -141,21 +141,15 @@ struct HomeScreenRoomCell: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -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,
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -24,7 +24,7 @@ struct InvitesScreen: View {
|
||||
.background(Color.compound.bgCanvasDefault.ignoresSafeArea())
|
||||
.navigationTitle(L10n.actionInvitesList)
|
||||
.alert(item: $context.alertInfo)
|
||||
.track(screen: .invites)
|
||||
.track(screen: .Invites)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
@@ -186,6 +186,7 @@ private extension InvitesScreenRoomDetails {
|
||||
avatarURL: nil,
|
||||
lastMessage: nil,
|
||||
lastMessageFormattedTimestamp: nil,
|
||||
isMarkedUnread: false,
|
||||
unreadMessagesCount: 0,
|
||||
unreadMentionsCount: 0,
|
||||
unreadNotificationsCount: 0,
|
||||
@@ -208,6 +209,7 @@ private extension InvitesScreenRoomDetails {
|
||||
avatarURL: avatarURL,
|
||||
lastMessage: nil,
|
||||
lastMessageFormattedTimestamp: nil,
|
||||
isMarkedUnread: false,
|
||||
unreadMessagesCount: 0,
|
||||
unreadMentionsCount: 0,
|
||||
unreadNotificationsCount: 0,
|
||||
|
||||
@@ -34,7 +34,7 @@ struct StaticLocationScreen: View {
|
||||
}
|
||||
mapView
|
||||
}
|
||||
.track(screen: context.viewState.isLocationPickerMode ? .locationSend : .locationView)
|
||||
.track(screen: context.viewState.isLocationPickerMode ? .LocationSend : .LocationView)
|
||||
.navigationTitle(context.viewState.navigationTitle)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar { toolbar }
|
||||
|
||||
@@ -37,7 +37,7 @@ struct RoomDetailsEditScreen: View {
|
||||
.navigationTitle(L10n.screenRoomDetailsEditRoomTitle)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar { toolbar }
|
||||
.track(screen: .roomSettings)
|
||||
.track(screen: .RoomSettings)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
@@ -61,7 +61,7 @@ struct RoomDetailsScreen: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.track(screen: .roomDetails)
|
||||
.track(screen: .RoomDetails)
|
||||
.interactiveQuickLook(item: $context.mediaPreviewItem, shouldHideControls: true)
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ struct RoomMemberDetailsScreen: View {
|
||||
.compoundList()
|
||||
.alert(item: $context.ignoreUserAlert, actions: blockUserAlertActions, message: blockUserAlertMessage)
|
||||
.alert(item: $context.alertInfo)
|
||||
.track(screen: .user)
|
||||
.track(screen: .User)
|
||||
.interactiveQuickLook(item: $context.mediaPreviewItem, shouldHideControls: true)
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ struct RoomMembersListScreen: View {
|
||||
inviteButton
|
||||
}
|
||||
}
|
||||
.track(screen: .roomMembers)
|
||||
.track(screen: .RoomMembers)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ struct RoomNotificationSettingsScreen: View {
|
||||
.compoundList()
|
||||
.navigationTitle(context.viewState.navigationTitle)
|
||||
.alert(item: $context.alertInfo)
|
||||
.track(screen: .roomNotifications)
|
||||
.track(screen: .RoomNotifications)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ struct RoomNotificationSettingsUserDefinedScreen: View {
|
||||
.compoundList()
|
||||
.navigationTitle(context.viewState.navigationTitle)
|
||||
.alert(item: $context.alertInfo)
|
||||
.track(screen: .roomNotifications)
|
||||
.track(screen: .RoomNotifications)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
@@ -428,7 +428,7 @@ class RoomScreenInteractionHandler {
|
||||
analyticsService.trackComposer(inThread: false,
|
||||
isEditing: false,
|
||||
isReply: false,
|
||||
messageType: .voiceMessage,
|
||||
messageType: .VoiceMessage,
|
||||
startsThread: nil)
|
||||
|
||||
actionsSubject.send(.composer(action: .setMode(mode: .previewVoiceMessage(state: audioPlayerState, waveform: .url(recordingURL), isUploading: true))))
|
||||
|
||||
@@ -70,7 +70,7 @@ struct RoomScreen: View {
|
||||
.environmentObject(context)
|
||||
}
|
||||
.interactiveQuickLook(item: $context.mediaPreviewItem)
|
||||
.track(screen: .room)
|
||||
.track(screen: .Room)
|
||||
.onDrop(of: ["public.item", "public.file-url"], isTargeted: $dragOver) { providers -> Bool in
|
||||
guard let provider = providers.first,
|
||||
provider.isSupportedForPasteOrDrop else {
|
||||
|
||||
+1
@@ -50,6 +50,7 @@ protocol DeveloperOptionsProtocol: AnyObject {
|
||||
var userSuggestionsEnabled: Bool { get set }
|
||||
var mentionsBadgeEnabled: Bool { get set }
|
||||
var roomListFiltersEnabled: Bool { get set }
|
||||
var markAsUnreadEnabled: Bool { get set }
|
||||
|
||||
var elementCallBaseURL: URL { get set }
|
||||
}
|
||||
|
||||
+4
@@ -55,6 +55,10 @@ struct DeveloperOptionsScreen: View {
|
||||
Toggle(isOn: $context.roomListFiltersEnabled) {
|
||||
Text("Show filters")
|
||||
}
|
||||
|
||||
Toggle(isOn: $context.markAsUnreadEnabled) {
|
||||
Text("Mark as unread")
|
||||
}
|
||||
}
|
||||
|
||||
Section("Element Call") {
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ struct NotificationSettingsEditScreen: View {
|
||||
.compoundList()
|
||||
.navigationTitle(context.viewState.strings.navigationTitle)
|
||||
.alert(item: $context.alertInfo)
|
||||
.track(screen: .settingsDefaultNotifications)
|
||||
.track(screen: .SettingsDefaultNotifications)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ struct NotificationSettingsScreen: View {
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar { toolbar }
|
||||
.alert(item: $context.alertInfo)
|
||||
.track(screen: .settingsNotifications)
|
||||
.track(screen: .SettingsNotifications)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
@@ -29,7 +29,7 @@ struct StartChatScreen: View {
|
||||
}
|
||||
}
|
||||
.compoundList()
|
||||
.track(screen: .startChat)
|
||||
.track(screen: .StartChat)
|
||||
.scrollDismissesKeyboard(.immediately)
|
||||
.navigationTitle(L10n.actionStartChat)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
|
||||
Reference in New Issue
Block a user