Fixes #2320 - Implement support for (un)marking rooms as favourite from the room list and room detail screens

This commit is contained in:
Stefan Ceriu
2024-02-19 15:13:15 +02:00
parent 3887403150
commit 6d9271ce59
60 changed files with 372 additions and 179 deletions
+2 -2
View File
@@ -6828,8 +6828,8 @@
isa = XCRemoteSwiftPackageReference; isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/matrix-org/matrix-analytics-events"; repositoryURL = "https://github.com/matrix-org/matrix-analytics-events";
requirement = { requirement = {
kind = revision; kind = upToNextMinorVersion;
revision = ad2b2d2b221bab89bf6443aafb925d52e30f4f6d; minimumVersion = 0.11.0;
}; };
}; };
C13F55E4518415CB4C278E73 /* XCRemoteSwiftPackageReference "DTCoreText" */ = { C13F55E4518415CB4C278E73 /* XCRemoteSwiftPackageReference "DTCoreText" */ = {
@@ -121,7 +121,8 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/matrix-org/matrix-analytics-events", "location" : "https://github.com/matrix-org/matrix-analytics-events",
"state" : { "state" : {
"revision" : "ad2b2d2b221bab89bf6443aafb925d52e30f4f6d" "revision" : "354562b5cabf2b9aec6cbb12e3a614490b3cc6e8",
"version" : "0.11.0"
} }
}, },
{ {
@@ -262,7 +263,7 @@
{ {
"identity" : "swiftui-introspect", "identity" : "swiftui-introspect",
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/siteline/SwiftUI-Introspect", "location" : "https://github.com/siteline/SwiftUI-Introspect.git",
"state" : { "state" : {
"revision" : "b94da693e57eaf79d16464b8b7c90d09cba4e290", "revision" : "b94da693e57eaf79d16464b8b7c90d09cba4e290",
"version" : "0.9.2" "version" : "0.9.2"
@@ -49,6 +49,7 @@ final class AppSettings {
case mentionsBadgeEnabled case mentionsBadgeEnabled
case roomListFiltersEnabled case roomListFiltersEnabled
case markAsUnreadEnabled case markAsUnreadEnabled
case markAsFavouriteEnabled
} }
private static var suiteName: String = InfoPlistReader.main.appGroupIdentifier private static var suiteName: String = InfoPlistReader.main.appGroupIdentifier
@@ -285,6 +286,9 @@ final class AppSettings {
@UserPreference(key: UserDefaultsKeys.markAsUnreadEnabled, defaultValue: false, storageType: .userDefaults(store)) @UserPreference(key: UserDefaultsKeys.markAsUnreadEnabled, defaultValue: false, storageType: .userDefaults(store))
var markAsUnreadEnabled var markAsUnreadEnabled
@UserPreference(key: UserDefaultsKeys.markAsFavouriteEnabled, defaultValue: false, storageType: .userDefaults(store))
var markAsFavouriteEnabled
#endif #endif
// MARK: - Shared // MARK: - Shared
@@ -410,7 +410,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
Task { Task {
// Flag the room as read on entering, the timeline will take care of the read receipts // Flag the room as read on entering, the timeline will take care of the read receipts
await roomProxy.flagAsRead() await roomProxy.flagAsUnread(false)
} }
let userID = userSession.clientProxy.userID let userID = userSession.clientProxy.userID
@@ -523,6 +523,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
let params = RoomDetailsScreenCoordinatorParameters(accountUserID: userSession.userID, let params = RoomDetailsScreenCoordinatorParameters(accountUserID: userSession.userID,
roomProxy: roomProxy, roomProxy: roomProxy,
mediaProvider: userSession.mediaProvider, mediaProvider: userSession.mediaProvider,
analyticsService: analytics,
userIndicatorController: userIndicatorController, userIndicatorController: userIndicatorController,
notificationSettings: userSession.clientProxy.notificationSettings, notificationSettings: userSession.clientProxy.notificationSettings,
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: appSettings.permalinkBaseURL, attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: appSettings.permalinkBaseURL,
@@ -1851,6 +1851,23 @@ class RoomProxyMock: RoomProxyProtocol {
set(value) { underlyingIsEncrypted = value } set(value) { underlyingIsEncrypted = value }
} }
var underlyingIsEncrypted: Bool! var underlyingIsEncrypted: Bool!
var isFavouriteCallsCount = 0
var isFavouriteCalled: Bool {
return isFavouriteCallsCount > 0
}
var isFavourite: Bool {
get async {
isFavouriteCallsCount += 1
if let isFavouriteClosure = isFavouriteClosure {
return await isFavouriteClosure()
} else {
return underlyingIsFavourite
}
}
}
var underlyingIsFavourite: Bool!
var isFavouriteClosure: (() async -> Bool)?
var membership: Membership { var membership: Membership {
get { return underlyingMembership } get { return underlyingMembership }
set(value) { underlyingMembership = value } set(value) { underlyingMembership = value }
@@ -2237,40 +2254,6 @@ class RoomProxyMock: RoomProxyProtocol {
return canUserTriggerRoomNotificationUserIDReturnValue return canUserTriggerRoomNotificationUserIDReturnValue
} }
} }
//MARK: - flagAsUnread
var flagAsUnreadCallsCount = 0
var flagAsUnreadCalled: Bool {
return flagAsUnreadCallsCount > 0
}
var flagAsUnreadReturnValue: Result<Void, RoomProxyError>!
var flagAsUnreadClosure: (() async -> Result<Void, RoomProxyError>)?
func flagAsUnread() async -> Result<Void, RoomProxyError> {
flagAsUnreadCallsCount += 1
if let flagAsUnreadClosure = flagAsUnreadClosure {
return await flagAsUnreadClosure()
} else {
return flagAsUnreadReturnValue
}
}
//MARK: - flagAsRead
var flagAsReadCallsCount = 0
var flagAsReadCalled: Bool {
return flagAsReadCallsCount > 0
}
var flagAsReadReturnValue: Result<Void, RoomProxyError>!
var flagAsReadClosure: (() async -> Result<Void, RoomProxyError>)?
func flagAsRead() async -> Result<Void, RoomProxyError> {
flagAsReadCallsCount += 1
if let flagAsReadClosure = flagAsReadClosure {
return await flagAsReadClosure()
} else {
return flagAsReadReturnValue
}
}
//MARK: - markAsRead //MARK: - markAsRead
var markAsReadReceiptTypeCallsCount = 0 var markAsReadReceiptTypeCallsCount = 0
@@ -2314,6 +2297,48 @@ class RoomProxyMock: RoomProxyProtocol {
return sendTypingNotificationIsTypingReturnValue return sendTypingNotificationIsTypingReturnValue
} }
} }
//MARK: - flagAsUnread
var flagAsUnreadCallsCount = 0
var flagAsUnreadCalled: Bool {
return flagAsUnreadCallsCount > 0
}
var flagAsUnreadReceivedIsUnread: Bool?
var flagAsUnreadReceivedInvocations: [Bool] = []
var flagAsUnreadReturnValue: Result<Void, RoomProxyError>!
var flagAsUnreadClosure: ((Bool) async -> Result<Void, RoomProxyError>)?
func flagAsUnread(_ isUnread: Bool) async -> Result<Void, RoomProxyError> {
flagAsUnreadCallsCount += 1
flagAsUnreadReceivedIsUnread = isUnread
flagAsUnreadReceivedInvocations.append(isUnread)
if let flagAsUnreadClosure = flagAsUnreadClosure {
return await flagAsUnreadClosure(isUnread)
} else {
return flagAsUnreadReturnValue
}
}
//MARK: - flagAsFavourite
var flagAsFavouriteCallsCount = 0
var flagAsFavouriteCalled: Bool {
return flagAsFavouriteCallsCount > 0
}
var flagAsFavouriteReceivedIsFavourite: Bool?
var flagAsFavouriteReceivedInvocations: [Bool] = []
var flagAsFavouriteReturnValue: Result<Void, RoomProxyError>!
var flagAsFavouriteClosure: ((Bool) async -> Result<Void, RoomProxyError>)?
func flagAsFavourite(_ isFavourite: Bool) async -> Result<Void, RoomProxyError> {
flagAsFavouriteCallsCount += 1
flagAsFavouriteReceivedIsFavourite = isFavourite
flagAsFavouriteReceivedInvocations.append(isFavourite)
if let flagAsFavouriteClosure = flagAsFavouriteClosure {
return await flagAsFavouriteClosure(isFavourite)
} else {
return flagAsFavouriteReturnValue
}
}
//MARK: - canUserJoinCall //MARK: - canUserJoinCall
var canUserJoinCallUserIDCallsCount = 0 var canUserJoinCallUserIDCallsCount = 0
+2 -1
View File
@@ -81,9 +81,10 @@ extension RoomProxyMock {
canUserTriggerRoomNotificationUserIDReturnValue = .success(configuration.canUserTriggerRoomNotification) canUserTriggerRoomNotificationUserIDReturnValue = .success(configuration.canUserTriggerRoomNotification)
canUserJoinCallUserIDReturnValue = .success(configuration.canUserJoinCall) canUserJoinCallUserIDReturnValue = .success(configuration.canUserJoinCall)
flagAsReadReturnValue = .success(())
flagAsUnreadReturnValue = .success(()) flagAsUnreadReturnValue = .success(())
markAsReadReceiptTypeReturnValue = .success(()) markAsReadReceiptTypeReturnValue = .success(())
underlyingIsFavourite = false
flagAsFavouriteReturnValue = .success(())
let widgetDriver = ElementCallWidgetDriverMock() let widgetDriver = ElementCallWidgetDriverMock()
widgetDriver.underlyingMessagePublisher = .init() widgetDriver.underlyingMessagePublisher = .init()
@@ -172,7 +172,8 @@ enum A11yIdentifiers {
let people = "room_details-people" let people = "room_details-people"
let invite = "room_details-invite" let invite = "room_details-invite"
let notifications = "room_details-notifications" let notifications = "room_details-notifications"
let pollsHistory = "romm_details-polls_history" let pollsHistory = "room_details-polls_history"
let favourite = "room_details-favourite"
} }
struct RoomMemberDetailsScreen { struct RoomMemberDetailsScreen {
@@ -79,8 +79,9 @@ struct ShimmerOverlay_Previews: PreviewProvider, TestablePreview {
static let viewModel = HomeScreenViewModel(userSession: MockUserSession(clientProxy: MockClientProxy(userID: ""), static let viewModel = HomeScreenViewModel(userSession: MockUserSession(clientProxy: MockClientProxy(userID: ""),
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
voiceMessageMediaManager: VoiceMessageMediaManagerMock()), voiceMessageMediaManager: VoiceMessageMediaManagerMock()),
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(), analyticsService: ServiceLocator.shared.analytics,
appSettings: ServiceLocator.shared.settings, appSettings: ServiceLocator.shared.settings,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
userIndicatorController: ServiceLocator.shared.userIndicatorController) userIndicatorController: ServiceLocator.shared.userIndicatorController)
static var previews: some View { static var previews: some View {
@@ -53,8 +53,9 @@ final class HomeScreenCoordinator: CoordinatorProtocol {
init(parameters: HomeScreenCoordinatorParameters) { init(parameters: HomeScreenCoordinatorParameters) {
viewModel = HomeScreenViewModel(userSession: parameters.userSession, viewModel = HomeScreenViewModel(userSession: parameters.userSession,
selectedRoomPublisher: parameters.selectedRoomPublisher, analyticsService: ServiceLocator.shared.analytics,
appSettings: ServiceLocator.shared.settings, appSettings: ServiceLocator.shared.settings,
selectedRoomPublisher: parameters.selectedRoomPublisher,
userIndicatorController: ServiceLocator.shared.userIndicatorController) userIndicatorController: ServiceLocator.shared.userIndicatorController)
bugReportService = parameters.bugReportService bugReportService = parameters.bugReportService
@@ -53,6 +53,7 @@ enum HomeScreenViewAction {
case globalSearch case globalSearch
case markRoomAsUnread(roomIdentifier: String) case markRoomAsUnread(roomIdentifier: String)
case markRoomAsRead(roomIdentifier: String) case markRoomAsRead(roomIdentifier: String)
case markRoomAsFavourite(roomIdentifier: String, isFavourite: Bool)
} }
enum HomeScreenRoomListMode: CustomStringConvertible { enum HomeScreenRoomListMode: CustomStringConvertible {
@@ -95,6 +96,7 @@ struct HomeScreenViewState: BindableState {
var shouldShowFilters = false var shouldShowFilters = false
var markAsUnreadEnabled = false var markAsUnreadEnabled = false
var markAsFavouriteEnabled = false
var hasPendingInvitations = false var hasPendingInvitations = false
var hasUnreadPendingInvitations = false var hasUnreadPendingInvitations = false
@@ -154,6 +156,8 @@ struct HomeScreenRoom: Identifiable, Equatable {
let isHighlighted: Bool let isHighlighted: Bool
let isFavourite: Bool
var timestamp: String? var timestamp: String?
var lastMessage: AttributedString? var lastMessage: AttributedString?
@@ -168,6 +172,7 @@ struct HomeScreenRoom: Identifiable, Equatable {
name: "Placeholder room name", name: "Placeholder room name",
badges: .init(isDotShown: false, isMentionShown: false, isMuteShown: false, isCallShown: false), badges: .init(isDotShown: false, isMentionShown: false, isMuteShown: false, isCallShown: false),
isHighlighted: false, isHighlighted: false,
isFavourite: false,
timestamp: "Now", timestamp: "Now",
lastMessage: placeholderLastMessage, lastMessage: placeholderLastMessage,
isPlaceholder: true) isPlaceholder: true)
@@ -194,6 +199,7 @@ extension HomeScreenRoom {
isMuteShown: isMuteShown, isMuteShown: isMuteShown,
isCallShown: isCallShown), isCallShown: isCallShown),
isHighlighted: isHighlighted, isHighlighted: isHighlighted,
isFavourite: details.isFavourite,
timestamp: details.lastMessageFormattedTimestamp, timestamp: details.lastMessageFormattedTimestamp,
lastMessage: details.lastMessage, lastMessage: details.lastMessage,
avatarURL: details.avatarURL) avatarURL: details.avatarURL)
@@ -21,6 +21,7 @@ typealias HomeScreenViewModelType = StateStoreViewModel<HomeScreenViewState, Hom
class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol { class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol {
private let userSession: UserSessionProtocol private let userSession: UserSessionProtocol
private let analyticsService: AnalyticsService
private let appSettings: AppSettings private let appSettings: AppSettings
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
@@ -38,10 +39,12 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
} }
init(userSession: UserSessionProtocol, init(userSession: UserSessionProtocol,
selectedRoomPublisher: CurrentValuePublisher<String?, Never>, analyticsService: AnalyticsService,
appSettings: AppSettings, appSettings: AppSettings,
selectedRoomPublisher: CurrentValuePublisher<String?, Never>,
userIndicatorController: UserIndicatorControllerProtocol) { userIndicatorController: UserIndicatorControllerProtocol) {
self.userSession = userSession self.userSession = userSession
self.analyticsService = analyticsService
self.appSettings = appSettings self.appSettings = appSettings
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
@@ -115,6 +118,10 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
.weakAssign(to: \.state.markAsUnreadEnabled, on: self) .weakAssign(to: \.state.markAsUnreadEnabled, on: self)
.store(in: &cancellables) .store(in: &cancellables)
appSettings.$markAsFavouriteEnabled
.weakAssign(to: \.state.markAsFavouriteEnabled, on: self)
.store(in: &cancellables)
appSettings.$hideUnreadMessagesBadge appSettings.$hideUnreadMessagesBadge
.sink { [weak self] _ in self?.updateRooms() } .sink { [weak self] _ in self?.updateRooms() }
.store(in: &cancellables) .store(in: &cancellables)
@@ -182,9 +189,9 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
return return
} }
switch await roomProxy.flagAsUnread() { switch await roomProxy.flagAsUnread(true) {
case .success: case .success:
ServiceLocator.shared.analytics.trackInteraction(name: .MobileRoomListRoomContextMenuUnreadToggle) analyticsService.trackInteraction(name: .MobileRoomListRoomContextMenuUnreadToggle)
case .failure(let error): case .failure(let error):
MXLog.error("Failed marking room \(roomIdentifier) as unread with error: \(error)") MXLog.error("Failed marking room \(roomIdentifier) as unread with error: \(error)")
} }
@@ -196,9 +203,9 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
return return
} }
switch await roomProxy.flagAsRead() { switch await roomProxy.flagAsUnread(false) {
case .success: case .success:
ServiceLocator.shared.analytics.trackInteraction(name: .MobileRoomListRoomContextMenuUnreadToggle) analyticsService.trackInteraction(name: .MobileRoomListRoomContextMenuUnreadToggle)
if case .failure(let error) = await roomProxy.markAsRead(receiptType: appSettings.sharePresence ? .read : .readPrivate) { if case .failure(let error) = await roomProxy.markAsRead(receiptType: appSettings.sharePresence ? .read : .readPrivate) {
MXLog.error("Failed marking room \(roomIdentifier) as read with error: \(error)") MXLog.error("Failed marking room \(roomIdentifier) as read with error: \(error)")
@@ -207,6 +214,10 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
MXLog.error("Failed flagging room \(roomIdentifier) as read with error: \(error)") MXLog.error("Failed flagging room \(roomIdentifier) as read with error: \(error)")
} }
} }
case .markRoomAsFavourite(let roomIdentifier, let isFavourite):
Task {
await markRoomAsFavourite(roomIdentifier, isFavourite: isFavourite)
}
} }
} }
@@ -241,7 +252,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
return return
} }
ServiceLocator.shared.analytics.signpost.beginFirstRooms() analyticsService.signpost.beginFirstRooms()
let hasUserBeenMigrated = appSettings.migratedAccounts[userSession.userID] == true let hasUserBeenMigrated = appSettings.migratedAccounts[userSession.userID] == true
@@ -322,7 +333,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
} }
if roomListMode == .rooms, state.roomListMode == .skeletons { if roomListMode == .rooms, state.roomListMode == .skeletons {
ServiceLocator.shared.analytics.signpost.endFirstRooms() analyticsService.signpost.endFirstRooms()
} }
state.roomListMode = roomListMode state.roomListMode = roomListMode
@@ -399,6 +410,20 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
roomSummaryProvider.updateVisibleRange(range) roomSummaryProvider.updateVisibleRange(range)
} }
private func markRoomAsFavourite(_ roomID: String, isFavourite: Bool) async {
guard let roomProxy = await userSession.clientProxy.roomForIdentifier(roomID) else {
MXLog.error("Failed retrieving room for identifier: \(roomID)")
return
}
switch await roomProxy.flagAsFavourite(isFavourite) {
case .success:
analyticsService.trackInteraction(name: .MobileRoomListRoomContextMenuFavouriteToggle)
case .failure(let error):
MXLog.error("Failed marking room \(roomID) as favourite: \(isFavourite) with error: \(error)")
}
}
private static let leaveRoomLoadingID = "LeaveRoomLoading" private static let leaveRoomLoadingID = "LeaveRoomLoading"
private func startLeaveRoomProcess(roomId: String) { private func startLeaveRoomProcess(roomId: String) {
@@ -238,8 +238,9 @@ struct HomeScreen_Previews: PreviewProvider, TestablePreview {
voiceMessageMediaManager: VoiceMessageMediaManagerMock()) voiceMessageMediaManager: VoiceMessageMediaManagerMock())
return HomeScreenViewModel(userSession: userSession, return HomeScreenViewModel(userSession: userSession,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(), analyticsService: ServiceLocator.shared.analytics,
appSettings: appSettings, appSettings: appSettings,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
userIndicatorController: ServiceLocator.shared.userIndicatorController) userIndicatorController: ServiceLocator.shared.userIndicatorController)
} }
} }
@@ -155,8 +155,9 @@ struct HomeScreenEmptyStateView_Previews: PreviewProvider, TestablePreview {
voiceMessageMediaManager: VoiceMessageMediaManagerMock()) voiceMessageMediaManager: VoiceMessageMediaManagerMock())
return HomeScreenViewModel(userSession: userSession, return HomeScreenViewModel(userSession: userSession,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(), analyticsService: ServiceLocator.shared.analytics,
appSettings: ServiceLocator.shared.settings, appSettings: ServiceLocator.shared.settings,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
userIndicatorController: ServiceLocator.shared.userIndicatorController) userIndicatorController: ServiceLocator.shared.userIndicatorController)
}() }()
} }
@@ -73,8 +73,9 @@ struct HomeScreenRecoveryKeyConfirmationBanner_Previews: PreviewProvider, Testab
voiceMessageMediaManager: VoiceMessageMediaManagerMock()) voiceMessageMediaManager: VoiceMessageMediaManagerMock())
return HomeScreenViewModel(userSession: userSession, return HomeScreenViewModel(userSession: userSession,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(), analyticsService: ServiceLocator.shared.analytics,
appSettings: ServiceLocator.shared.settings, appSettings: ServiceLocator.shared.settings,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
userIndicatorController: ServiceLocator.shared.userIndicatorController) userIndicatorController: ServiceLocator.shared.userIndicatorController)
} }
} }
@@ -181,8 +181,9 @@ struct HomeScreenRoomCell_Previews: PreviewProvider, TestablePreview {
voiceMessageMediaManager: VoiceMessageMediaManagerMock()) voiceMessageMediaManager: VoiceMessageMediaManagerMock())
return HomeScreenViewModel(userSession: userSession, return HomeScreenViewModel(userSession: userSession,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(), analyticsService: ServiceLocator.shared.analytics,
appSettings: ServiceLocator.shared.settings, appSettings: ServiceLocator.shared.settings,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
userIndicatorController: ServiceLocator.shared.userIndicatorController) userIndicatorController: ServiceLocator.shared.userIndicatorController)
}() }()
@@ -193,8 +194,9 @@ struct HomeScreenRoomCell_Previews: PreviewProvider, TestablePreview {
voiceMessageMediaManager: VoiceMessageMediaManagerMock()) voiceMessageMediaManager: VoiceMessageMediaManagerMock())
return HomeScreenViewModel(userSession: userSession, return HomeScreenViewModel(userSession: userSession,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(), analyticsService: ServiceLocator.shared.analytics,
appSettings: ServiceLocator.shared.settings, appSettings: ServiceLocator.shared.settings,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
userIndicatorController: ServiceLocator.shared.userIndicatorController) userIndicatorController: ServiceLocator.shared.userIndicatorController)
}() }()
@@ -63,6 +63,22 @@ struct HomeScreenRoomList: View {
} }
} }
if context.viewState.markAsFavouriteEnabled {
if room.isFavourite {
Button {
context.send(viewAction: .markRoomAsFavourite(roomIdentifier: room.id, isFavourite: false))
} label: {
Label(L10n.commonFavourited, icon: \.favouriteSolid)
}
} else {
Button {
context.send(viewAction: .markRoomAsFavourite(roomIdentifier: room.id, isFavourite: true))
} label: {
Label(L10n.commonFavourite, icon: \.favourite)
}
}
}
Button { Button {
context.send(viewAction: .showRoomDetails(roomIdentifier: room.id)) context.send(viewAction: .showRoomDetails(roomIdentifier: room.id))
} label: { } label: {
@@ -73,8 +73,9 @@ struct HomeScreenSessionVerificationBanner_Previews: PreviewProvider, TestablePr
voiceMessageMediaManager: VoiceMessageMediaManagerMock()) voiceMessageMediaManager: VoiceMessageMediaManagerMock())
return HomeScreenViewModel(userSession: userSession, return HomeScreenViewModel(userSession: userSession,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(), analyticsService: ServiceLocator.shared.analytics,
appSettings: ServiceLocator.shared.settings, appSettings: ServiceLocator.shared.settings,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
userIndicatorController: ServiceLocator.shared.userIndicatorController) userIndicatorController: ServiceLocator.shared.userIndicatorController)
} }
} }
@@ -186,14 +186,15 @@ private extension InvitesScreenRoomDetails {
avatarURL: nil, avatarURL: nil,
lastMessage: nil, lastMessage: nil,
lastMessageFormattedTimestamp: nil, lastMessageFormattedTimestamp: nil,
isMarkedUnread: false,
unreadMessagesCount: 0, unreadMessagesCount: 0,
unreadMentionsCount: 0, unreadMentionsCount: 0,
unreadNotificationsCount: 0, unreadNotificationsCount: 0,
notificationMode: nil, notificationMode: nil,
canonicalAlias: "#footest:somewhere.org", canonicalAlias: "#footest:somewhere.org",
inviter: inviter, inviter: inviter,
hasOngoingCall: false) hasOngoingCall: false,
isMarkedUnread: false,
isFavourite: false)
return .init(roomDetails: dmRoom, isUnread: false) return .init(roomDetails: dmRoom, isUnread: false)
} }
@@ -209,14 +210,15 @@ private extension InvitesScreenRoomDetails {
avatarURL: avatarURL, avatarURL: avatarURL,
lastMessage: nil, lastMessage: nil,
lastMessageFormattedTimestamp: nil, lastMessageFormattedTimestamp: nil,
isMarkedUnread: false,
unreadMessagesCount: 0, unreadMessagesCount: 0,
unreadMentionsCount: 0, unreadMentionsCount: 0,
unreadNotificationsCount: 0, unreadNotificationsCount: 0,
notificationMode: nil, notificationMode: nil,
canonicalAlias: alias, canonicalAlias: alias,
inviter: inviter, inviter: inviter,
hasOngoingCall: false) hasOngoingCall: false,
isMarkedUnread: false,
isFavourite: false)
return .init(roomDetails: dmRoom, isUnread: isUnread) return .init(roomDetails: dmRoom, isUnread: isUnread)
} }
} }
@@ -21,6 +21,7 @@ struct RoomDetailsScreenCoordinatorParameters {
let accountUserID: String let accountUserID: String
let roomProxy: RoomProxyProtocol let roomProxy: RoomProxyProtocol
let mediaProvider: MediaProviderProtocol let mediaProvider: MediaProviderProtocol
let analyticsService: AnalyticsService
let userIndicatorController: UserIndicatorControllerProtocol let userIndicatorController: UserIndicatorControllerProtocol
let notificationSettings: NotificationSettingsProxyProtocol let notificationSettings: NotificationSettingsProxyProtocol
let attributedStringBuilder: AttributedStringBuilderProtocol let attributedStringBuilder: AttributedStringBuilderProtocol
@@ -49,6 +50,7 @@ final class RoomDetailsScreenCoordinator: CoordinatorProtocol {
viewModel = RoomDetailsScreenViewModel(accountUserID: parameters.accountUserID, viewModel = RoomDetailsScreenViewModel(accountUserID: parameters.accountUserID,
roomProxy: parameters.roomProxy, roomProxy: parameters.roomProxy,
mediaProvider: parameters.mediaProvider, mediaProvider: parameters.mediaProvider,
analyticsService: parameters.analyticsService,
userIndicatorController: parameters.userIndicatorController, userIndicatorController: parameters.userIndicatorController,
notificationSettingsProxy: parameters.notificationSettings, notificationSettingsProxy: parameters.notificationSettings,
attributedStringBuilder: parameters.attributedStringBuilder) attributedStringBuilder: parameters.attributedStringBuilder)
@@ -129,6 +129,8 @@ struct RoomDetailsScreenViewStateBindings {
} }
} }
} }
var isFavourite = false
/// Information describing the currently displayed alert. /// Information describing the currently displayed alert.
var alertInfo: AlertInfo<RoomDetailsScreenErrorType>? var alertInfo: AlertInfo<RoomDetailsScreenErrorType>?
@@ -180,6 +182,7 @@ enum RoomDetailsScreenViewAction {
case processToogleMuteNotifications case processToogleMuteNotifications
case displayAvatar case displayAvatar
case processTapPolls case processTapPolls
case toggleFavourite(isFavourite: Bool)
} }
enum RoomDetailsScreenViewShortcut { enum RoomDetailsScreenViewShortcut {
@@ -22,6 +22,7 @@ typealias RoomDetailsScreenViewModelType = StateStoreViewModel<RoomDetailsScreen
class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScreenViewModelProtocol { class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScreenViewModelProtocol {
private let accountUserID: String private let accountUserID: String
private let roomProxy: RoomProxyProtocol private let roomProxy: RoomProxyProtocol
private let analyticsService: AnalyticsService
private let mediaProvider: MediaProviderProtocol private let mediaProvider: MediaProviderProtocol
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private let notificationSettingsProxy: NotificationSettingsProxyProtocol private let notificationSettingsProxy: NotificationSettingsProxyProtocol
@@ -42,12 +43,14 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
init(accountUserID: String, init(accountUserID: String,
roomProxy: RoomProxyProtocol, roomProxy: RoomProxyProtocol,
mediaProvider: MediaProviderProtocol, mediaProvider: MediaProviderProtocol,
analyticsService: AnalyticsService,
userIndicatorController: UserIndicatorControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol,
notificationSettingsProxy: NotificationSettingsProxyProtocol, notificationSettingsProxy: NotificationSettingsProxyProtocol,
attributedStringBuilder: AttributedStringBuilderProtocol) { attributedStringBuilder: AttributedStringBuilderProtocol) {
self.accountUserID = accountUserID self.accountUserID = accountUserID
self.roomProxy = roomProxy self.roomProxy = roomProxy
self.mediaProvider = mediaProvider self.mediaProvider = mediaProvider
self.analyticsService = analyticsService
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
self.notificationSettingsProxy = notificationSettingsProxy self.notificationSettingsProxy = notificationSettingsProxy
self.attributedStringBuilder = attributedStringBuilder self.attributedStringBuilder = attributedStringBuilder
@@ -67,6 +70,8 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
notificationSettingsState: .loading, notificationSettingsState: .loading,
bindings: .init()), bindings: .init()),
imageProvider: mediaProvider) imageProvider: mediaProvider)
updateRoomInfo()
setupRoomSubscription() setupRoomSubscription()
fetchMembers() fetchMembers()
@@ -122,6 +127,8 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
displayFullScreenAvatar() displayFullScreenAvatar()
case .processTapPolls: case .processTapPolls:
actionsSubject.send(.requestPollsHistoryPresentation) actionsSubject.send(.requestPollsHistoryPresentation)
case .toggleFavourite(let isFavourite):
Task { await toggleFavourite(isFavourite) }
} }
} }
@@ -129,21 +136,28 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
private func setupRoomSubscription() { private func setupRoomSubscription() {
roomProxy.actions roomProxy.actions
.filter { $0 == .stateUpdate } .filter { $0 == .roomInfoUpdate }
.throttle(for: .milliseconds(200), scheduler: DispatchQueue.main, latest: true) .throttle(for: .milliseconds(200), scheduler: DispatchQueue.main, latest: true)
.sink { [weak self] _ in .sink { [weak self] _ in
guard let self else { return } self?.updateRoomInfo()
self.state.title = self.roomProxy.roomTitle
let topic = attributedStringBuilder.fromPlain(self.roomProxy.topic)
self.state.topic = topic
self.state.topicSummary = topic?.unattributedStringByReplacingNewlinesWithSpaces()
self.state.avatarURL = self.roomProxy.avatarURL
self.state.joinedMembersCount = self.roomProxy.joinedMembersCount
} }
.store(in: &cancellables) .store(in: &cancellables)
} }
private func updateRoomInfo() {
state.title = roomProxy.roomTitle
let topic = attributedStringBuilder.fromPlain(roomProxy.topic)
state.topic = topic
state.topicSummary = topic?.unattributedStringByReplacingNewlinesWithSpaces()
state.avatarURL = roomProxy.avatarURL
state.joinedMembersCount = roomProxy.joinedMembersCount
Task {
state.bindings.isFavourite = await roomProxy.isFavourite
}
}
private func fetchMembers() { private func fetchMembers() {
Task { Task {
await fetchMembersIfNeeded() await fetchMembersIfNeeded()
@@ -245,6 +259,15 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
} }
state.isProcessingMuteToggleAction = false state.isProcessingMuteToggleAction = false
} }
private func toggleFavourite(_ isFavourite: Bool) async {
if case let .failure(error) = await roomProxy.flagAsFavourite(isFavourite) {
MXLog.error("Failed flagging room as favourite with error: \(error)")
state.bindings.isFavourite = !isFavourite
} else {
analyticsService.trackInteraction(name: .MobileRoomFavouriteToggle)
}
}
private static let leaveRoomLoadingID = "LeaveRoomLoading" private static let leaveRoomLoadingID = "LeaveRoomLoading"
@@ -187,6 +187,13 @@ struct RoomDetailsScreen: View {
}) })
.disabled(context.viewState.notificationSettingsState.isLoading) .disabled(context.viewState.notificationSettingsState.isLoading)
.accessibilityIdentifier(A11yIdentifiers.roomDetailsScreen.notifications) .accessibilityIdentifier(A11yIdentifiers.roomDetailsScreen.notifications)
ListRow(label: .default(title: L10n.commonFavourite, icon: \.favourite),
kind: .toggle($context.isFavourite))
.accessibilityIdentifier(A11yIdentifiers.roomDetailsScreen.favourite)
.onChange(of: context.isFavourite) { newValue in
context.send(viewAction: .toggleFavourite(isFavourite: newValue))
}
} }
.disabled(context.viewState.notificationSettingsState.isLoading) .disabled(context.viewState.notificationSettingsState.isLoading)
} }
@@ -306,6 +313,7 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview {
return RoomDetailsScreenViewModel(accountUserID: RoomMemberProxyMock.mockMe.userID, return RoomDetailsScreenViewModel(accountUserID: RoomMemberProxyMock.mockMe.userID,
roomProxy: roomProxy, roomProxy: roomProxy,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
notificationSettingsProxy: notificationSettingsProxy, notificationSettingsProxy: notificationSettingsProxy,
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: .userDirectory, attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: .userDirectory,
@@ -331,6 +339,7 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview {
return RoomDetailsScreenViewModel(accountUserID: RoomMemberProxyMock.mockMe.userID, return RoomDetailsScreenViewModel(accountUserID: RoomMemberProxyMock.mockMe.userID,
roomProxy: roomProxy, roomProxy: roomProxy,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
notificationSettingsProxy: notificationSettingsProxy, notificationSettingsProxy: notificationSettingsProxy,
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: .userDirectory, attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: .userDirectory,
@@ -355,6 +364,7 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview {
return RoomDetailsScreenViewModel(accountUserID: RoomMemberProxyMock.mockMe.userID, return RoomDetailsScreenViewModel(accountUserID: RoomMemberProxyMock.mockMe.userID,
roomProxy: roomProxy, roomProxy: roomProxy,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
notificationSettingsProxy: notificationSettingsProxy, notificationSettingsProxy: notificationSettingsProxy,
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: .userDirectory, attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: .userDirectory,
@@ -293,7 +293,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
roomProxy roomProxy
.actions .actions
.filter { $0 == .stateUpdate } .filter { $0 == .roomInfoUpdate }
.throttle(for: .seconds(1), scheduler: DispatchQueue.main, latest: true) .throttle(for: .seconds(1), scheduler: DispatchQueue.main, latest: true)
.sink { [weak self] _ in .sink { [weak self] _ in
guard let self else { return } guard let self else { return }
@@ -50,9 +50,11 @@ protocol DeveloperOptionsProtocol: AnyObject {
var userSuggestionsEnabled: Bool { get set } var userSuggestionsEnabled: Bool { get set }
var mentionsBadgeEnabled: Bool { get set } var mentionsBadgeEnabled: Bool { get set }
var roomListFiltersEnabled: Bool { get set } var roomListFiltersEnabled: Bool { get set }
var markAsUnreadEnabled: Bool { get set }
var hideUnreadMessagesBadge: Bool { get set } var hideUnreadMessagesBadge: Bool { get set }
var markAsUnreadEnabled: Bool { get set }
var markAsFavouriteEnabled: Bool { get set }
var elementCallBaseURL: URL { get set } var elementCallBaseURL: URL { get set }
} }
@@ -60,6 +60,10 @@ struct DeveloperOptionsScreen: View {
Text("Mark as unread") Text("Mark as unread")
} }
Toggle(isOn: $context.markAsFavouriteEnabled) {
Text("Mark as favourite")
}
Toggle(isOn: $context.hideUnreadMessagesBadge) { Toggle(isOn: $context.hideUnreadMessagesBadge) {
Text("Hide grey dots") Text("Hide grey dots")
} }
+43 -27
View File
@@ -94,7 +94,7 @@ class RoomProxy: RoomProxyProtocol {
await timeline.subscribeForUpdates() await timeline.subscribeForUpdates()
subscribeToRoomStateUpdates() subscribeToRoomInfoUpdates()
subscribeToTypingNotifications() subscribeToTypingNotifications()
} }
@@ -133,6 +133,12 @@ class RoomProxy: RoomProxyProtocol {
(try? room.isEncrypted()) ?? false (try? room.isEncrypted()) ?? false
} }
var isFavourite: Bool {
get async {
await (try? room.roomInfo().isFavourite) ?? false
}
}
var hasOngoingCall: Bool { var hasOngoingCall: Bool {
room.hasActiveRoomCall() room.hasActiveRoomCall()
} }
@@ -367,31 +373,7 @@ class RoomProxy: RoomProxyProtocol {
return .failure(.failedCheckingPermission) return .failure(.failedCheckingPermission)
} }
} }
func flagAsUnread() async -> Result<Void, RoomProxyError> {
MXLog.info("Flagging room \(id) as unread")
do {
try await room.setUnreadFlag(newValue: true)
return .success(())
} catch {
MXLog.error("Failed marking room \(id) as unread with error: \(error)")
return .failure(.failedFlaggingAsUnread)
}
}
func flagAsRead() async -> Result<Void, RoomProxyError> {
MXLog.info("Flagging room \(id) as read")
do {
try await room.setUnreadFlag(newValue: false)
return .success(())
} catch {
MXLog.error("Failed marking room \(id) as read with error: \(error)")
return .failure(.failedFlaggingAsRead)
}
}
func markAsRead(receiptType: ReceiptType) async -> Result<Void, RoomProxyError> { func markAsRead(receiptType: ReceiptType) async -> Result<Void, RoomProxyError> {
do { do {
try await room.markAsRead(receiptType: receiptType) try await room.markAsRead(receiptType: receiptType)
@@ -414,6 +396,40 @@ class RoomProxy: RoomProxyProtocol {
} }
} }
// MARK: - Room flags
func flagAsUnread(_ isUnread: Bool) async -> Result<Void, RoomProxyError> {
MXLog.info("Flagging room \(id) as unread: \(isUnread)")
do {
try await room.setUnreadFlag(newValue: isUnread)
return .success(())
} catch {
MXLog.error("Failed marking room \(id) as unread: \(isUnread) with error: \(error)")
return .failure(.failedFlaggingAsUnread)
}
}
func getIsFavourite() async -> Result<Bool, RoomProxyError> {
do {
let result = try await room.roomInfo().isFavourite
return .success(result)
} catch {
MXLog.error("Failed retrieving isFavourite for room: \(id) with error: \(error)")
return .failure(.failedRetrievingIsFavourite)
}
}
func flagAsFavourite(_ isFavourite: Bool) async -> Result<Void, RoomProxyError> {
do {
try await room.setIsFavourite(isFavourite: isFavourite, tagOrder: nil)
return .success(())
} catch {
MXLog.error("Failed flagging room \(id) as favourite with error: \(error)")
return .failure(.failedFlaggingAsFavourite)
}
}
// MARK: - Element Call // MARK: - Element Call
func canUserJoinCall(userID: String) async -> Result<Bool, RoomProxyError> { func canUserJoinCall(userID: String) async -> Result<Bool, RoomProxyError> {
@@ -431,10 +447,10 @@ class RoomProxy: RoomProxyProtocol {
// MARK: - Private // MARK: - Private
private func subscribeToRoomStateUpdates() { private func subscribeToRoomInfoUpdates() {
roomInfoObservationToken = room.subscribeToRoomInfoUpdates(listener: RoomInfoUpdateListener { [weak self] in roomInfoObservationToken = room.subscribeToRoomInfoUpdates(listener: RoomInfoUpdateListener { [weak self] in
MXLog.info("Received room info update") MXLog.info("Received room info update")
self?.actionsSubject.send(.stateUpdate) self?.actionsSubject.send(.roomInfoUpdate)
}) })
} }
@@ -32,13 +32,14 @@ enum RoomProxyError: Error, Equatable {
case failedUploadingAvatar case failedUploadingAvatar
case failedCheckingPermission case failedCheckingPermission
case failedFlaggingAsUnread case failedFlaggingAsUnread
case failedFlaggingAsRead
case failedMarkingAsRead case failedMarkingAsRead
case failedSendingTypingNotice case failedSendingTypingNotice
case failedRetrievingIsFavourite
case failedFlaggingAsFavourite
} }
enum RoomProxyAction { enum RoomProxyAction {
case stateUpdate case roomInfoUpdate
} }
// sourcery: AutoMockable // sourcery: AutoMockable
@@ -48,6 +49,7 @@ protocol RoomProxyProtocol {
var isPublic: Bool { get } var isPublic: Bool { get }
var isSpace: Bool { get } var isSpace: Bool { get }
var isEncrypted: Bool { get } var isEncrypted: Bool { get }
var isFavourite: Bool { get async }
var membership: Membership { get } var membership: Membership { get }
var hasOngoingCall: Bool { get } var hasOngoingCall: Bool { get }
var canonicalAlias: String? { get } var canonicalAlias: String? { get }
@@ -109,15 +111,17 @@ protocol RoomProxyProtocol {
func canUserTriggerRoomNotification(userID: String) async -> Result<Bool, RoomProxyError> func canUserTriggerRoomNotification(userID: String) async -> Result<Bool, RoomProxyError>
func flagAsUnread() async -> Result<Void, RoomProxyError>
func flagAsRead() async -> Result<Void, RoomProxyError>
func markAsRead(receiptType: ReceiptType) async -> Result<Void, RoomProxyError> func markAsRead(receiptType: ReceiptType) async -> Result<Void, RoomProxyError>
/// https://spec.matrix.org/v1.9/client-server-api/#typing-notifications /// https://spec.matrix.org/v1.9/client-server-api/#typing-notifications
@discardableResult func sendTypingNotification(isTyping: Bool) async -> Result<Void, RoomProxyError> @discardableResult func sendTypingNotification(isTyping: Bool) async -> Result<Void, RoomProxyError>
// MARK: - Room Flags
func flagAsUnread(_ isUnread: Bool) async -> Result<Void, RoomProxyError>
func flagAsFavourite(_ isFavourite: Bool) async -> Result<Void, RoomProxyError>
// MARK: - Element Call // MARK: - Element Call
func canUserJoinCall(userID: String) async -> Result<Bool, RoomProxyError> func canUserJoinCall(userID: String) async -> Result<Bool, RoomProxyError>
@@ -83,98 +83,105 @@ extension Array where Element == RoomSummary {
avatarURL: nil, avatarURL: nil,
lastMessage: AttributedString("I do not wish to take the trouble to understand mysticism"), lastMessage: AttributedString("I do not wish to take the trouble to understand mysticism"),
lastMessageFormattedTimestamp: "14:56", lastMessageFormattedTimestamp: "14:56",
isMarkedUnread: false,
unreadMessagesCount: 0, unreadMessagesCount: 0,
unreadMentionsCount: 0, unreadMentionsCount: 0,
unreadNotificationsCount: 0, unreadNotificationsCount: 0,
notificationMode: .allMessages, notificationMode: .allMessages,
canonicalAlias: nil, canonicalAlias: nil,
inviter: nil, inviter: nil,
hasOngoingCall: false)), hasOngoingCall: false,
isMarkedUnread: false,
isFavourite: false)),
.filled(details: RoomSummaryDetails(id: "2", .filled(details: RoomSummaryDetails(id: "2",
name: "Foundation and Empire", name: "Foundation and Empire",
isDirect: true, isDirect: true,
avatarURL: URL.picturesDirectory, avatarURL: URL.picturesDirectory,
lastMessage: AttributedString("How do you see the Emperor then? You think he keeps office hours?"), lastMessage: AttributedString("How do you see the Emperor then? You think he keeps office hours?"),
lastMessageFormattedTimestamp: "2:56 PM", lastMessageFormattedTimestamp: "2:56 PM",
isMarkedUnread: false,
unreadMessagesCount: 2, unreadMessagesCount: 2,
unreadMentionsCount: 0, unreadMentionsCount: 0,
unreadNotificationsCount: 2, unreadNotificationsCount: 2,
notificationMode: .mute, notificationMode: .mute,
canonicalAlias: nil, canonicalAlias: nil,
inviter: nil, inviter: nil,
hasOngoingCall: false)), hasOngoingCall: false,
isMarkedUnread: false,
isFavourite: false)),
.filled(details: RoomSummaryDetails(id: "3", .filled(details: RoomSummaryDetails(id: "3",
name: "Second Foundation", name: "Second Foundation",
isDirect: true, isDirect: true,
avatarURL: nil, avatarURL: nil,
lastMessage: try? AttributedString(markdown: "He certainly seemed no *mental genius* to me"), lastMessage: try? AttributedString(markdown: "He certainly seemed no *mental genius* to me"),
lastMessageFormattedTimestamp: "Some time ago", lastMessageFormattedTimestamp: "Some time ago",
isMarkedUnread: false,
unreadMessagesCount: 3, unreadMessagesCount: 3,
unreadMentionsCount: 0, unreadMentionsCount: 0,
unreadNotificationsCount: 0, unreadNotificationsCount: 0,
notificationMode: .mentionsAndKeywordsOnly, notificationMode: .mentionsAndKeywordsOnly,
canonicalAlias: nil, canonicalAlias: nil,
inviter: nil, inviter: nil,
hasOngoingCall: false)), hasOngoingCall: false,
isMarkedUnread: false,
isFavourite: false)),
.filled(details: RoomSummaryDetails(id: "4", .filled(details: RoomSummaryDetails(id: "4",
name: "Foundation's Edge", name: "Foundation's Edge",
isDirect: true, isDirect: true,
avatarURL: nil, avatarURL: nil,
lastMessage: AttributedString("There's an archaic measure of time that's called the month"), lastMessage: AttributedString("There's an archaic measure of time that's called the month"),
lastMessageFormattedTimestamp: "Just now", lastMessageFormattedTimestamp: "Just now",
isMarkedUnread: false,
unreadMessagesCount: 2, unreadMessagesCount: 2,
unreadMentionsCount: 2, unreadMentionsCount: 2,
unreadNotificationsCount: 2, unreadNotificationsCount: 2,
notificationMode: .allMessages, notificationMode: .allMessages,
canonicalAlias: nil, canonicalAlias: nil,
inviter: nil, inviter: nil,
hasOngoingCall: false)), hasOngoingCall: false,
isMarkedUnread: false,
isFavourite: false)),
.filled(details: RoomSummaryDetails(id: "5", .filled(details: RoomSummaryDetails(id: "5",
name: "Foundation and Earth", name: "Foundation and Earth",
isDirect: true, isDirect: true,
avatarURL: nil, avatarURL: nil,
lastMessage: AttributedString("Clearly, if Earth is powerful enough to do that, it might also be capable of adjusting minds in order to force belief in its radioactivity"), lastMessage: AttributedString("Clearly, if Earth is powerful enough to do that, it might also be capable of adjusting minds in order to force belief in its radioactivity"),
lastMessageFormattedTimestamp: "1986", lastMessageFormattedTimestamp: "1986",
isMarkedUnread: false,
unreadMessagesCount: 1, unreadMessagesCount: 1,
unreadMentionsCount: 1, unreadMentionsCount: 1,
unreadNotificationsCount: 1, unreadNotificationsCount: 1,
notificationMode: .allMessages, notificationMode: .allMessages,
canonicalAlias: nil, canonicalAlias: nil,
inviter: nil, inviter: nil,
hasOngoingCall: true)), hasOngoingCall: true,
isMarkedUnread: false,
isFavourite: false)),
.filled(details: RoomSummaryDetails(id: "6", .filled(details: RoomSummaryDetails(id: "6",
name: "Prelude to Foundation", name: "Prelude to Foundation",
isDirect: true, isDirect: true,
avatarURL: nil, avatarURL: nil,
lastMessage: AttributedString("Are you groping for the word 'paranoia'?"), lastMessage: AttributedString("Are you groping for the word 'paranoia'?"),
lastMessageFormattedTimestamp: "きょうはじゅういちがつじゅういちにちです", lastMessageFormattedTimestamp: "きょうはじゅういちがつじゅういちにちです",
isMarkedUnread: false,
unreadMessagesCount: 6, unreadMessagesCount: 6,
unreadMentionsCount: 0, unreadMentionsCount: 0,
unreadNotificationsCount: 0, unreadNotificationsCount: 0,
notificationMode: .mute, notificationMode: .mute,
canonicalAlias: nil, canonicalAlias: nil,
inviter: nil, inviter: nil,
hasOngoingCall: true)), hasOngoingCall: true,
isMarkedUnread: false,
isFavourite: false)),
.filled(details: RoomSummaryDetails(id: "0", .filled(details: RoomSummaryDetails(id: "0",
name: "Unknown", name: "Unknown",
isDirect: false, isDirect: false,
avatarURL: nil, avatarURL: nil,
lastMessage: nil, lastMessage: nil,
lastMessageFormattedTimestamp: nil, lastMessageFormattedTimestamp: nil,
isMarkedUnread: false,
unreadMessagesCount: 0, unreadMessagesCount: 0,
unreadMentionsCount: 0, unreadMentionsCount: 0,
unreadNotificationsCount: 0, unreadNotificationsCount: 0,
notificationMode: nil, notificationMode: nil,
canonicalAlias: nil, canonicalAlias: nil,
inviter: nil, inviter: nil,
hasOngoingCall: false)), hasOngoingCall: false,
isMarkedUnread: false,
isFavourite: false)),
.empty .empty
] ]
@@ -213,27 +220,29 @@ extension Array where Element == RoomSummary {
avatarURL: URL.picturesDirectory, avatarURL: URL.picturesDirectory,
lastMessage: nil, lastMessage: nil,
lastMessageFormattedTimestamp: nil, lastMessageFormattedTimestamp: nil,
isMarkedUnread: false,
unreadMessagesCount: 0, unreadMessagesCount: 0,
unreadMentionsCount: 0, unreadMentionsCount: 0,
unreadNotificationsCount: 0, unreadNotificationsCount: 0,
notificationMode: nil, notificationMode: nil,
canonicalAlias: "#footest:somewhere.org", canonicalAlias: "#footest:somewhere.org",
inviter: RoomMemberProxyMock.mockCharlie, inviter: RoomMemberProxyMock.mockCharlie,
hasOngoingCall: false)), hasOngoingCall: false,
isMarkedUnread: false,
isFavourite: false)),
.filled(details: RoomSummaryDetails(id: "someAwesomeRoomId2", .filled(details: RoomSummaryDetails(id: "someAwesomeRoomId2",
name: "Second room", name: "Second room",
isDirect: true, isDirect: true,
avatarURL: nil, avatarURL: nil,
lastMessage: nil, lastMessage: nil,
lastMessageFormattedTimestamp: nil, lastMessageFormattedTimestamp: nil,
isMarkedUnread: false,
unreadMessagesCount: 0, unreadMessagesCount: 0,
unreadMentionsCount: 0, unreadMentionsCount: 0,
unreadNotificationsCount: 0, unreadNotificationsCount: 0,
notificationMode: nil, notificationMode: nil,
canonicalAlias: nil, canonicalAlias: nil,
inviter: RoomMemberProxyMock.mockCharlie, inviter: RoomMemberProxyMock.mockCharlie,
hasOngoingCall: false)) hasOngoingCall: false,
isMarkedUnread: false,
isFavourite: false))
] ]
} }
@@ -24,7 +24,6 @@ struct RoomSummaryDetails {
let avatarURL: URL? let avatarURL: URL?
let lastMessage: AttributedString? let lastMessage: AttributedString?
let lastMessageFormattedTimestamp: String? let lastMessageFormattedTimestamp: String?
let isMarkedUnread: Bool
let unreadMessagesCount: UInt let unreadMessagesCount: UInt
let unreadMentionsCount: UInt let unreadMentionsCount: UInt
let unreadNotificationsCount: UInt let unreadNotificationsCount: UInt
@@ -33,6 +32,9 @@ struct RoomSummaryDetails {
let inviter: RoomMemberProxyProtocol? let inviter: RoomMemberProxyProtocol?
let hasOngoingCall: Bool let hasOngoingCall: Bool
let isMarkedUnread: Bool
let isFavourite: Bool
var hasUnreadMessages: Bool { unreadMessagesCount > 0 } var hasUnreadMessages: Bool { unreadMessagesCount > 0 }
var hasUnreadMentions: Bool { unreadMentionsCount > 0 } var hasUnreadMentions: Bool { unreadMentionsCount > 0 }
var hasUnreadNotifications: Bool { unreadNotificationsCount > 0 } var hasUnreadNotifications: Bool { unreadNotificationsCount > 0 }
@@ -60,7 +62,6 @@ extension RoomSummaryDetails {
avatarURL = nil avatarURL = nil
lastMessage = AttributedString(string) lastMessage = AttributedString(string)
lastMessageFormattedTimestamp = "Now" lastMessageFormattedTimestamp = "Now"
isMarkedUnread = false
unreadMessagesCount = hasUnreadMessages ? 1 : 0 unreadMessagesCount = hasUnreadMessages ? 1 : 0
unreadMentionsCount = hasUnreadMentions ? 1 : 0 unreadMentionsCount = hasUnreadMentions ? 1 : 0
unreadNotificationsCount = hasUnreadNotifications ? 1 : 0 unreadNotificationsCount = hasUnreadNotifications ? 1 : 0
@@ -68,5 +69,8 @@ extension RoomSummaryDetails {
canonicalAlias = nil canonicalAlias = nil
inviter = nil inviter = nil
hasOngoingCall = false hasOngoingCall = false
isMarkedUnread = false
isFavourite = false
} }
} }
@@ -252,14 +252,15 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol {
avatarURL: roomInfo.avatarUrl.flatMap(URL.init(string:)), avatarURL: roomInfo.avatarUrl.flatMap(URL.init(string:)),
lastMessage: attributedLastMessage, lastMessage: attributedLastMessage,
lastMessageFormattedTimestamp: lastMessageFormattedTimestamp, lastMessageFormattedTimestamp: lastMessageFormattedTimestamp,
isMarkedUnread: appSettings.markAsUnreadEnabled ? roomInfo.isMarkedUnread : false,
unreadMessagesCount: appSettings.mentionsBadgeEnabled ? UInt(roomInfo.numUnreadMessages) : 0, unreadMessagesCount: appSettings.mentionsBadgeEnabled ? UInt(roomInfo.numUnreadMessages) : 0,
unreadMentionsCount: appSettings.mentionsBadgeEnabled ? UInt(roomInfo.numUnreadMentions) : 0, unreadMentionsCount: appSettings.mentionsBadgeEnabled ? UInt(roomInfo.numUnreadMentions) : 0,
unreadNotificationsCount: appSettings.mentionsBadgeEnabled ? UInt(roomInfo.numUnreadNotifications) : UInt(roomInfo.notificationCount), unreadNotificationsCount: appSettings.mentionsBadgeEnabled ? UInt(roomInfo.numUnreadNotifications) : UInt(roomInfo.notificationCount),
notificationMode: notificationMode, notificationMode: notificationMode,
canonicalAlias: roomInfo.canonicalAlias, canonicalAlias: roomInfo.canonicalAlias,
inviter: inviterProxy, inviter: inviterProxy,
hasOngoingCall: roomInfo.hasRoomCall) hasOngoingCall: roomInfo.hasRoomCall,
isMarkedUnread: appSettings.markAsUnreadEnabled ? roomInfo.isMarkedUnread : false,
isFavourite: roomInfo.isFavourite)
return invalidated ? .invalidated(details: details) : .filled(details: details) return invalidated ? .invalidated(details: details) : .filled(details: details)
} }
@@ -590,6 +590,7 @@ class MockScreen: Identifiable {
let coordinator = RoomDetailsScreenCoordinator(parameters: .init(accountUserID: "@owner:somewhere.com", let coordinator = RoomDetailsScreenCoordinator(parameters: .init(accountUserID: "@owner:somewhere.com",
roomProxy: roomProxy, roomProxy: roomProxy,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
notificationSettings: NotificationSettingsProxyMock(with: .init()), notificationSettings: NotificationSettingsProxyMock(with: .init()),
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL,
@@ -610,6 +611,7 @@ class MockScreen: Identifiable {
let coordinator = RoomDetailsScreenCoordinator(parameters: .init(accountUserID: "@owner:somewhere.com", let coordinator = RoomDetailsScreenCoordinator(parameters: .init(accountUserID: "@owner:somewhere.com",
roomProxy: roomProxy, roomProxy: roomProxy,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
notificationSettings: NotificationSettingsProxyMock(with: .init()), notificationSettings: NotificationSettingsProxyMock(with: .init()),
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL,
@@ -632,6 +634,7 @@ class MockScreen: Identifiable {
let coordinator = RoomDetailsScreenCoordinator(parameters: .init(accountUserID: "@owner:somewhere.com", let coordinator = RoomDetailsScreenCoordinator(parameters: .init(accountUserID: "@owner:somewhere.com",
roomProxy: roomProxy, roomProxy: roomProxy,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
notificationSettings: NotificationSettingsProxyMock(with: .init()), notificationSettings: NotificationSettingsProxyMock(with: .init()),
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL,
@@ -650,6 +653,7 @@ class MockScreen: Identifiable {
let coordinator = RoomDetailsScreenCoordinator(parameters: .init(accountUserID: "@owner:somewhere.com", let coordinator = RoomDetailsScreenCoordinator(parameters: .init(accountUserID: "@owner:somewhere.com",
roomProxy: roomProxy, roomProxy: roomProxy,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
notificationSettings: NotificationSettingsProxyMock(with: .init()), notificationSettings: NotificationSettingsProxyMock(with: .init()),
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL,
@@ -669,6 +673,7 @@ class MockScreen: Identifiable {
let coordinator = RoomDetailsScreenCoordinator(parameters: .init(accountUserID: "@owner:somewhere.com", let coordinator = RoomDetailsScreenCoordinator(parameters: .init(accountUserID: "@owner:somewhere.com",
roomProxy: roomProxy, roomProxy: roomProxy,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
notificationSettings: NotificationSettingsProxyMock(with: .init()), notificationSettings: NotificationSettingsProxyMock(with: .init()),
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL,
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:e0f0d0153b192c9d39095958380d457e2cf2857fb7de3e385c48dba4b15219d2 oid sha256:39bf0f198860337c27a796c6e1431f709471d4a3f6ccbb72309b9e4eaf9780b3
size 108327 size 115152
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:ccb8342762f048022b0cb9b2100d235e120e56e485a7564c0bd572a807398bf2 oid sha256:fde6a2bcb7affaca4ac29a02eab4cc1d0c3cde194e9651b8ecfb328017889b29
size 138647 size 147446
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:1c83c28c9e8ee2196c5874fe958f1003aae29156afac721478578dc372706d48 oid sha256:82755efa8743ce3a1f215b78bd4f5300c4f04dc792e6b8b7eba11d3892296242
size 140846 size 147877
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:066a49f7d5f1e8fd46a3f35c403bd1b8257e5b30ad6c3af15ea666a783e1f399 oid sha256:7cfec1d34aae82eb3c274d61277ef0442b1d55a7882158fc85b7821f06a58f72
size 113268 size 120357
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:337b7463033f2b86866f880461402139586b960333b6f0e628b57eb3b32a0454 oid sha256:514df171e80caa49231f04d9dce0d592f35504717bf29a7c9494780a2ff16dab
size 145428 size 152121
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:46d3807de58b096fe2a08dad9b6ee6ee4b67d9d7749332d7feceee0226d01d2c oid sha256:fc5863a9f7cd340276d7db3d9f7d0d6e0e05bee9105e49a8ffa708770d57d5f4
size 134499 size 145363
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:1ff99b080b22b3c09001fc3fc835e3b41c877f5e7f2cd6cfe26ef2f7c01a54c1 oid sha256:7338da9308a75c605754f57484ba63633cfb3a72fbf15137175a1b78443830a6
size 176256 size 185001
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:9988534198d133d1f9e020e6af5628259bea9c6fdda64ac5b168230dc0a4621e oid sha256:fe0445f069162f6790004b4d580f2cdd5ecc22c5d3a39cea9cb088de37058b0a
size 176611 size 164478
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:6dd38abeb1ac142eae16fd258c9a346867f11f4075f2864e5815be30ddfcb9c6 oid sha256:5baba19d2f8690464d4a99d070d135a33be6a2f098a54e01b98191bdd2aa7817
size 141613 size 146553
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:2a99249f04ea968b762cf14ff335f048eb7d91374c01761d8262c526f858431e oid sha256:1017a6e0c07e16ad547be7c5ab8d05fbf614e3d94a05c54e266b2e544af0c624
size 194812 size 190264
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:3288dfb3ed775665e37c214c4f2d2033e3174f22f41006f5fe0d1878a60866c1 oid sha256:3804ce702c420b6086e5220d3c2e1ac53e8006d42b93b86af7eb6cb15701ca8d
size 123312 size 131020
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:47e87c18eb21430919e24ebc8307531e479e03fefffc001e45623739ea9ec62f oid sha256:3e0ebc8183bfd11643aa2cc1d02e5d31d87f4da13ba81787c209cde531b8af04
size 154961 size 163638
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:be36151a47830413040624f7d0922bd389321e11387e72c7581f691b3ea37b94 oid sha256:0a4f869249c23dac0ea7a8df6e5bf620a1b68e9d6c45395fd4c268c044d11212
size 156406 size 164896
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:40e4ca832c774e501232f9d4a5dfaceae34d72d3a8f4a0647ff26afa73f5552e oid sha256:d2c5cabe8eed9c5775f56948c2285f26985afb79a20e20232b7457ed23b29939
size 129593 size 136672
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:46290c09fa96860d002308e7fdc80dd9840c3ed12567489343ed568e1d11c55c oid sha256:604985dc95f9f9744c3905719097e02cf6ee34dbc92f64af5d5e8c20cc2de0c1
size 160957 size 167214
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:0a6eec1fb4203afc950a9d33860e5a534d15327acf91d0caf4e500ca5a26999a oid sha256:fcc306c1c49dec12330113c13bffd9798c4c7f9d81f687a8951d1ad38ddb565e
size 164960 size 174444
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:ede47654000517fbf67ef6085f4b9c9574b50a73723925690a868dfad1c4684a oid sha256:228dad18f45f4cac88e6a966d3ff8bca9464331286daa747b00c490a98a95119
size 207805 size 195223
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:0ef967694784f25e8e3614498d8ada07569910917432815523e34a9a4f79f301 oid sha256:20da96fd85689241026f81d279e34722ca3ad145833ece7616de10ecf7ad91c1
size 178881 size 176544
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:8d7232d8165976ef5cf09744c2b884c0f05a58eee69a345ccf515f27c578edd6 oid sha256:6070b52b32d7578dd532c2db11e8e26344bfa632293f73ffcb08d15e33cd3303
size 170738 size 171361
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:e75114908cf9e7049ceafeda01d5c69eb48a54bef628245a5eecd805e00e8026 oid sha256:5400964f428e7625024743f6a06278ee5ef894c69ea12fb8927f88f8baecb68a
size 201023 size 187116
+3 -2
View File
@@ -36,14 +36,15 @@ class HomeScreenRoomTests: XCTestCase {
avatarURL: nil, avatarURL: nil,
lastMessage: nil, lastMessage: nil,
lastMessageFormattedTimestamp: nil, lastMessageFormattedTimestamp: nil,
isMarkedUnread: isMarkedUnread,
unreadMessagesCount: unreadMessagesCount, unreadMessagesCount: unreadMessagesCount,
unreadMentionsCount: unreadMentionsCount, unreadMentionsCount: unreadMentionsCount,
unreadNotificationsCount: unreadNotificationsCount, unreadNotificationsCount: unreadNotificationsCount,
notificationMode: notificationMode, notificationMode: notificationMode,
canonicalAlias: nil, canonicalAlias: nil,
inviter: nil, inviter: nil,
hasOngoingCall: hasOngoingCall) hasOngoingCall: hasOngoingCall,
isMarkedUnread: isMarkedUnread,
isFavourite: false)
} }
func testNoBadge() { func testNoBadge() {
@@ -35,8 +35,9 @@ class HomeScreenViewModelTests: XCTestCase {
viewModel = HomeScreenViewModel(userSession: MockUserSession(clientProxy: clientProxy, viewModel = HomeScreenViewModel(userSession: MockUserSession(clientProxy: clientProxy,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
voiceMessageMediaManager: VoiceMessageMediaManagerMock()), voiceMessageMediaManager: VoiceMessageMediaManagerMock()),
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(), analyticsService: ServiceLocator.shared.analytics,
appSettings: ServiceLocator.shared.settings, appSettings: ServiceLocator.shared.settings,
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
userIndicatorController: ServiceLocator.shared.userIndicatorController) userIndicatorController: ServiceLocator.shared.userIndicatorController)
} }
+3 -2
View File
@@ -229,14 +229,15 @@ class LoggingTests: XCTestCase {
avatarURL: nil, avatarURL: nil,
lastMessage: AttributedString(lastMessage), lastMessage: AttributedString(lastMessage),
lastMessageFormattedTimestamp: "Now", lastMessageFormattedTimestamp: "Now",
isMarkedUnread: false,
unreadMessagesCount: 0, unreadMessagesCount: 0,
unreadMentionsCount: 0, unreadMentionsCount: 0,
unreadNotificationsCount: 0, unreadNotificationsCount: 0,
notificationMode: nil, notificationMode: nil,
canonicalAlias: nil, canonicalAlias: nil,
inviter: nil, inviter: nil,
hasOngoingCall: false) hasOngoingCall: false,
isMarkedUnread: false,
isFavourite: false)
// When logging that value // When logging that value
XCTAssert(MXLogger.logFiles.isEmpty) XCTAssert(MXLogger.logFiles.isEmpty)
@@ -36,6 +36,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com",
roomProxy: roomProxyMock, roomProxy: roomProxyMock,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
notificationSettingsProxy: notificationSettingsProxyMock, notificationSettingsProxy: notificationSettingsProxyMock,
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL,
@@ -50,6 +51,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com",
roomProxy: roomProxyMock, roomProxy: roomProxyMock,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()),
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL,
@@ -71,6 +73,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com",
roomProxy: roomProxyMock, roomProxy: roomProxyMock,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()),
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL,
@@ -93,6 +96,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com",
roomProxy: roomProxyMock, roomProxy: roomProxyMock,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()),
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL,
@@ -145,6 +149,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com",
roomProxy: roomProxyMock, roomProxy: roomProxyMock,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()),
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL,
@@ -171,6 +176,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com",
roomProxy: roomProxyMock, roomProxy: roomProxyMock,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()),
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL,
@@ -206,6 +212,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com",
roomProxy: roomProxyMock, roomProxy: roomProxyMock,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()),
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL,
@@ -242,6 +249,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com",
roomProxy: roomProxyMock, roomProxy: roomProxyMock,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()),
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL,
@@ -277,6 +285,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com",
roomProxy: roomProxyMock, roomProxy: roomProxyMock,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()),
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL,
@@ -311,6 +320,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com",
roomProxy: roomProxyMock, roomProxy: roomProxyMock,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()),
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL,
@@ -327,6 +337,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com",
roomProxy: roomProxyMock, roomProxy: roomProxyMock,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()),
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL,
@@ -360,6 +371,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com",
roomProxy: roomProxyMock, roomProxy: roomProxyMock,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()),
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL,
@@ -380,6 +392,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com",
roomProxy: roomProxyMock, roomProxy: roomProxyMock,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()),
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL,
@@ -400,6 +413,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com",
roomProxy: roomProxyMock, roomProxy: roomProxyMock,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()),
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL,
@@ -420,6 +434,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com",
roomProxy: roomProxyMock, roomProxy: roomProxyMock,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()),
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL,
@@ -439,6 +454,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com",
roomProxy: roomProxyMock, roomProxy: roomProxyMock,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()), notificationSettingsProxy: NotificationSettingsProxyMock(with: NotificationSettingsProxyMockConfiguration()),
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL,
@@ -456,6 +472,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com", viewModel = RoomDetailsScreenViewModel(accountUserID: "@owner:somewhere.com",
roomProxy: roomProxyMock, roomProxy: roomProxyMock,
mediaProvider: MockMediaProvider(), mediaProvider: MockMediaProvider(),
analyticsService: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
notificationSettingsProxy: notificationSettingsProxyMock, notificationSettingsProxy: notificationSettingsProxyMock,
attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL, attributedStringBuilder: AttributedStringBuilder(permalinkBaseURL: ServiceLocator.shared.settings.permalinkBaseURL,
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:68403794d5e16a6292920c5f0d988644a846766190832b587281f204c1720126 oid sha256:2e7e980b8146ee25a482af352b9e0c2fdcddc002e0f15dfc7a4c5171e2df2431
size 166741 size 171771
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:f80de7b2596c4a0ff78ccd1aaef6f362fb37556b10bd77e02483570d11ee6599 oid sha256:179d26714cf99868d2807d50a5ef8b0d512fd00893fcd3079e82ddd9c4d597c1
size 171882 size 157846
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:2888c0ad77204a072df2e6cb01eb3a77ed6d110516d2ba5d63407adea603d2fd oid sha256:46cc244fabd213bc6fdeebfa592002011632b8cc7b3df616607ff7d67f79b9ce
size 128095 size 139500
+1
View File
@@ -0,0 +1 @@
Implement support for (un)marking rooms as favourite from the room list and room detail screens
+1 -2
View File
@@ -55,8 +55,7 @@ packages:
# path: ../compound-ios # path: ../compound-ios
AnalyticsEvents: AnalyticsEvents:
url: https://github.com/matrix-org/matrix-analytics-events url: https://github.com/matrix-org/matrix-analytics-events
# minorVersion: 0.9.0 minorVersion: 0.11.0
revision: ad2b2d2b221bab89bf6443aafb925d52e30f4f6d
Emojibase: Emojibase:
url: https://github.com/matrix-org/emojibase-bindings url: https://github.com/matrix-org/emojibase-bindings
minorVersion: 1.0.0 minorVersion: 1.0.0