From 5b2f6cfbf481b89a36b302ef2243501cf3610db6 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Wed, 26 Feb 2025 13:55:59 +0200 Subject: [PATCH] Show DM recipient verification badges on the room details screen profile button (#3824) --- .../RoomDetailsScreenModels.swift | 15 +- .../RoomDetailsScreenViewModel.swift | 57 +++-- .../View/RoomDetailsScreen.swift | 223 +++++++++++------- ...d-en-GB.DM-Room-Verification-Violation.png | 3 + ...ailsScreen-iPad-en-GB.DM-Room-Verified.png | 3 + ...-pseudo.DM-Room-Verification-Violation.png | 3 + ...ilsScreen-iPad-pseudo.DM-Room-Verified.png | 3 + ...6-en-GB.DM-Room-Verification-Violation.png | 3 + ...creen-iPhone-16-en-GB.DM-Room-Verified.png | 3 + ...-pseudo.DM-Room-Verification-Violation.png | 3 + ...reen-iPhone-16-pseudo.DM-Room-Verified.png | 3 + .../Sources/RoomDetailsViewModelTests.swift | 32 +-- 12 files changed, 219 insertions(+), 132 deletions(-) create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-en-GB.DM-Room-Verification-Violation.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-en-GB.DM-Room-Verified.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-pseudo.DM-Room-Verification-Violation.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-pseudo.DM-Room-Verified.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-en-GB.DM-Room-Verification-Violation.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-en-GB.DM-Room-Verified.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-pseudo.DM-Room-Verification-Violation.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-pseudo.DM-Room-Verified.png diff --git a/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenModels.swift b/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenModels.swift index f7bdc54dc1..942a07a208 100644 --- a/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenModels.swift +++ b/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenModels.swift @@ -30,6 +30,11 @@ enum RoomDetailsScreenViewModelAction: Equatable { // MARK: View +struct DMRecipientInfo { + var member: RoomMemberDetails + var verificationState: UserIdentityVerificationState? +} + struct RoomDetailsScreenViewState: BindableState { var details: RoomDetails @@ -60,11 +65,11 @@ struct RoomDetailsScreenViewState: BindableState { var knockRequestsCount = 0 var canSeeKnockingRequests: Bool { - knockingEnabled && dmRecipient == nil && isKnockableRoom && (canInviteUsers || canKickUsers || canBanUsers) + knockingEnabled && dmRecipientInfo == nil && isKnockableRoom && (canInviteUsers || canKickUsers || canBanUsers) } var canSeeSecurityAndPrivacy: Bool { - knockingEnabled && dmRecipient == nil && canEditRolesOrPermissions + knockingEnabled && dmRecipientInfo == nil && canEditRolesOrPermissions } var canEdit: Bool { @@ -77,7 +82,7 @@ struct RoomDetailsScreenViewState: BindableState { var bindings: RoomDetailsScreenViewStateBindings - var dmRecipient: RoomMemberDetails? + var dmRecipientInfo: DMRecipientInfo? var accountOwner: RoomMemberDetails? var shortcuts: [RoomDetailsScreenViewShortcut] { @@ -85,10 +90,10 @@ struct RoomDetailsScreenViewState: BindableState { if !ProcessInfo.processInfo.isiOSAppOnMac, canJoinCall { shortcuts.append(.call) } - if dmRecipient == nil, canInviteUsers { + if dmRecipientInfo == nil, canInviteUsers { shortcuts.append(.invite) } - if let permalink = dmRecipient?.permalink { + if let permalink = dmRecipientInfo?.member.permalink { shortcuts.append(.share(link: permalink)) } else if let permalink { shortcuts.append(.share(link: permalink)) diff --git a/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift b/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift index 33ac1c35c9..5c373fc073 100644 --- a/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift @@ -20,7 +20,6 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr private let attributedStringBuilder: AttributedStringBuilderProtocol private let appSettings: AppSettings - private var dmRecipient: RoomMemberProxyProtocol? private var pinnedEventsTimelineProvider: TimelineProviderProtocol? { didSet { guard let pinnedEventsTimelineProvider else { @@ -171,7 +170,7 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr case .processTapSecurityAndPrivacy: actionsSubject.send(.displaySecurityAndPrivacy) case .processTapRecipientProfile: - guard let userID = dmRecipient?.userID else { + guard let userID = state.dmRecipientInfo?.member.id else { return } actionsSubject.send(.requestRecipientDetailsPresentation(userID: userID)) @@ -234,11 +233,16 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr .receive(on: DispatchQueue.main) .sink { [weak self, ownUserID = roomProxy.ownUserID] members in guard let self else { return } - let accountOwner = members.first { $0.userID == ownUserID } - let dmRecipient = members.first { $0.userID != ownUserID } - self.dmRecipient = dmRecipient - self.state.dmRecipient = dmRecipient.map(RoomMemberDetails.init(withProxy:)) - self.state.accountOwner = accountOwner.map(RoomMemberDetails.init(withProxy:)) + + if let accountOwner = members.first(where: { $0.userID == ownUserID }) { + self.state.accountOwner = .init(withProxy: accountOwner) + } + + if let dmRecipient = members.first(where: { $0.userID != ownUserID }) { + self.state.dmRecipientInfo = .init(member: .init(withProxy: dmRecipient)) + + Task { await self.updateMemberIdentityVerificationStates() } + } } .store(in: &cancellables) @@ -251,16 +255,25 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr return } - for member in roomProxy.membersPublisher.value { - if case let .success(identity) = await clientProxy.userIdentity(for: member.userID) { - if identity?.verificationState == .verificationViolation { - state.hasMemberIdentityVerificationStateViolations = true - return + if roomProxy.isDirectOneToOneRoom { + if var dmRecipientInfo = state.dmRecipientInfo { + if case let .success(userIdentity) = await clientProxy.userIdentity(for: dmRecipientInfo.member.id) { + dmRecipientInfo.verificationState = userIdentity?.verificationState + state.dmRecipientInfo = dmRecipientInfo } } + } else { + for member in roomProxy.membersPublisher.value { + if case let .success(userIdentity) = await clientProxy.userIdentity(for: member.userID) { + if userIdentity?.verificationState == .verificationViolation { + state.hasMemberIdentityVerificationStateViolations = true + return + } + } + } + + state.hasMemberIdentityVerificationStateViolations = false } - - state.hasMemberIdentityVerificationStateViolations = false } private func updatePowerLevelPermissions() async { @@ -357,7 +370,7 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr } private func ignore() async { - guard let dmUserID = dmRecipient?.userID else { + guard let dmUserID = state.dmRecipientInfo?.member.id else { MXLog.error("Attempting to ignore a nil DM Recipient") state.bindings.alertInfo = .init(id: .unknown) return @@ -369,16 +382,16 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr switch result { case .success: // Mutating the optional in place when built for Release crashes 🤷‍♂️ - var dmRecipient = state.dmRecipient - dmRecipient?.isIgnored = true - state.dmRecipient = dmRecipient + var dmRecipientInfo = state.dmRecipientInfo + dmRecipientInfo?.member.isIgnored = true + state.dmRecipientInfo = dmRecipientInfo case .failure: state.bindings.alertInfo = .init(id: .unknown) } } private func unignore() async { - guard let dmUserID = dmRecipient?.userID else { + guard let dmUserID = state.dmRecipientInfo?.member.id else { MXLog.error("Attempting to unignore a nil DM Recipient") state.bindings.alertInfo = .init(id: .unknown) return @@ -390,9 +403,9 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr switch result { case .success: // Mutating the optional in place when built for Release crashes 🤷‍♂️ - var dmRecipient = state.dmRecipient - dmRecipient?.isIgnored = false - state.dmRecipient = dmRecipient + var dmRecipientInfo = state.dmRecipientInfo + dmRecipientInfo?.member.isIgnored = false + state.dmRecipientInfo = dmRecipientInfo case .failure: state.bindings.alertInfo = .init(id: .unknown) } diff --git a/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift b/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift index f4e6848606..f0a79b01aa 100644 --- a/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift +++ b/ElementX/Sources/Screens/RoomDetailsScreen/View/RoomDetailsScreen.swift @@ -21,7 +21,7 @@ struct RoomDetailsScreen: View { configurationSection - if context.viewState.dmRecipient == nil { + if context.viewState.dmRecipientInfo == nil { peopleSection } @@ -29,7 +29,7 @@ struct RoomDetailsScreen: View { securitySection - if let recipient = context.viewState.dmRecipient { + if let recipient = context.viewState.dmRecipientInfo?.member { ignoreUserSection(user: recipient) } @@ -136,16 +136,14 @@ struct RoomDetailsScreen: View { private var aboutSection: some View { Section { - ListRow(label: .default(title: L10n.screenRoomDetailsPinnedEventsRowTitle, - icon: \.pin), + ListRow(label: .default(title: L10n.screenRoomDetailsPinnedEventsRowTitle, icon: \.pin), details: context.viewState.pinnedEventsActionState.isLoading ? .isWaiting(true) : .title(context.viewState.pinnedEventsActionState.count), kind: context.viewState.pinnedEventsActionState.isLoading ? .label : .navigationLink { context.send(viewAction: .processTapPinnedEvents) }) .disabled(context.viewState.pinnedEventsActionState.isLoading) - ListRow(label: .default(title: L10n.screenPollsHistoryTitle, - icon: \.polls), + ListRow(label: .default(title: L10n.screenPollsHistoryTitle, icon: \.polls), kind: .navigationLink { context.send(viewAction: .processTapPolls) }) @@ -160,8 +158,7 @@ struct RoomDetailsScreen: View { private var configurationSection: some View { Section { - ListRow(label: .default(title: L10n.screenRoomDetailsNotificationTitle, - icon: \.notifications), + ListRow(label: .default(title: L10n.screenRoomDetailsNotificationTitle, icon: \.notifications), details: context.viewState.notificationSettingsState.isLoading ? .isWaiting(true) : context.viewState.notificationSettingsState.isError ? .systemIcon(.exclamationmarkCircle) : .title(context.viewState.notificationSettingsState.label), @@ -179,19 +176,32 @@ struct RoomDetailsScreen: View { } if context.viewState.canSeeSecurityAndPrivacy { - ListRow(label: .default(title: L10n.screenRoomDetailsSecurityAndPrivacyTitle, - icon: \.lock), + ListRow(label: .default(title: L10n.screenRoomDetailsSecurityAndPrivacyTitle, icon: \.lock), kind: .navigationLink { context.send(viewAction: .processTapSecurityAndPrivacy) }) } - if context.viewState.dmRecipient != nil { - ListRow(label: .default(title: L10n.screenRoomDetailsProfileRowTitle, - icon: \.userProfile), - kind: .navigationLink { - context.send(viewAction: .processTapRecipientProfile) - }) + if context.viewState.dmRecipientInfo != nil { + switch context.viewState.dmRecipientInfo?.verificationState { + case .verified: + ListRow(label: .default(title: L10n.screenRoomDetailsProfileRowTitle, icon: \.userProfile), + details: .icon(CompoundIcon(\.verified).foregroundStyle(.compound.iconSuccessPrimary)), + kind: .navigationLink { + context.send(viewAction: .processTapRecipientProfile) + }) + case .verificationViolation: + ListRow(label: .default(title: L10n.screenRoomDetailsProfileRowTitle, icon: \.userProfile), + details: .icon(CompoundIcon(\.infoSolid).foregroundStyle(.compound.iconCriticalPrimary)), + kind: .navigationLink { + context.send(viewAction: .processTapRecipientProfile) + }) + default: + ListRow(label: .default(title: L10n.screenRoomDetailsProfileRowTitle, icon: \.userProfile), + kind: .navigationLink { + context.send(viewAction: .processTapRecipientProfile) + }) + } } } } @@ -216,17 +226,15 @@ struct RoomDetailsScreen: View { } if context.viewState.canSeeKnockingRequests { - ListRow(label: .default(title: L10n.screenRoomDetailsRequestsToJoinTitle, - icon: \.askToJoin), + ListRow(label: .default(title: L10n.screenRoomDetailsRequestsToJoinTitle, icon: \.askToJoin), details: context.viewState.knockRequestsCount > 0 ? .counter(context.viewState.knockRequestsCount) : nil, kind: .navigationLink { context.send(viewAction: .processTapRequestsToJoin) }) } - if context.viewState.canEditRolesOrPermissions, context.viewState.dmRecipient == nil { - ListRow(label: .default(title: L10n.screenRoomDetailsRolesAndPermissions, - icon: \.admin), + if context.viewState.canEditRolesOrPermissions, context.viewState.dmRecipientInfo == nil { + ListRow(label: .default(title: L10n.screenRoomDetailsRolesAndPermissions, icon: \.admin), kind: .navigationLink { context.send(viewAction: .processTapRolesAndPermissions) }) @@ -265,7 +273,7 @@ struct RoomDetailsScreen: View { } private var leaveRoomTitle: String { - context.viewState.dmRecipient == nil ? L10n.screenRoomDetailsLeaveRoomTitle : L10n.screenRoomDetailsLeaveConversationTitle + context.viewState.dmRecipientInfo == nil ? L10n.screenRoomDetailsLeaveRoomTitle : L10n.screenRoomDetailsLeaveConversationTitle } private var leaveRoomSection: some View { @@ -319,15 +327,55 @@ struct RoomDetailsScreen: View { // MARK: - Previews struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview { - static let genericRoomViewModel = { + static let genericRoomViewModel = makeGenericRoomViewModel() + static let simpleRoomViewModel = makeSimpleRoomViewModel() + static let dmRoomViewModel = makeDMViewModel(verificationState: .notVerified) + static let dmRoomVerifiedViewModel = makeDMViewModel(verificationState: .verified) + static let dmRoomVerificationViolationViewModel = makeDMViewModel(verificationState: .verificationViolation) + + static var previews: some View { + RoomDetailsScreen(context: genericRoomViewModel.context) + .snapshotPreferences(expect: genericRoomViewModel.context.$viewState.map { state in + state.shortcuts.contains(.invite) + }) + .previewDisplayName("Generic Room") + + RoomDetailsScreen(context: simpleRoomViewModel.context) + .snapshotPreferences(expect: simpleRoomViewModel.context.$viewState.map { state in + state.shortcuts.contains(.invite) + }) + .previewDisplayName("Simple Room") + + RoomDetailsScreen(context: dmRoomViewModel.context) + .snapshotPreferences(expect: dmRoomViewModel.context.$viewState.map { state in + state.accountOwner != nil + }) + .previewDisplayName("DM Room") + + RoomDetailsScreen(context: dmRoomVerifiedViewModel.context) + .snapshotPreferences(expect: dmRoomVerifiedViewModel.context.$viewState.map { state in + state.accountOwner != nil + }) + .previewDisplayName("DM Room Verified") + + RoomDetailsScreen(context: dmRoomVerificationViolationViewModel.context) + .snapshotPreferences(expect: dmRoomVerificationViolationViewModel.context.$viewState.map { state in + state.accountOwner != nil + }) + .previewDisplayName("DM Room Verification Violation") + } + + private static func makeGenericRoomViewModel() -> RoomDetailsScreenViewModel { ServiceLocator.shared.settings.knockingEnabled = true let knockRequests: [KnockRequestProxyMock] = [.init()] + let members: [RoomMemberProxyMock] = [ .mockMeAdmin, .mockAlice, .mockBob, .mockCharlie ] + let roomProxy = JoinedRoomProxyMock(.init(id: "room_a_id", name: "Room A", topic: """ @@ -345,50 +393,26 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview { knockRequestsState: .loaded(knockRequests), joinRule: .knock)) - var notificationSettingsProxyMockConfiguration = NotificationSettingsProxyMockConfiguration() + let notificationSettingsProxyMockConfiguration = NotificationSettingsProxyMockConfiguration() notificationSettingsProxyMockConfiguration.roomMode.isDefault = false - let notificationSettingsProxy = NotificationSettingsProxyMock(with: notificationSettingsProxyMockConfiguration) - - return RoomDetailsScreenViewModel(roomProxy: roomProxy, - clientProxy: ClientProxyMock(.init()), - mediaProvider: MediaProviderMock(configuration: .init()), - analyticsService: ServiceLocator.shared.analytics, - userIndicatorController: ServiceLocator.shared.userIndicatorController, - notificationSettingsProxy: notificationSettingsProxy, - attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()), - appMediator: AppMediatorMock.default, - appSettings: ServiceLocator.shared.settings) - }() - - static let dmRoomViewModel = { - let members: [RoomMemberProxyMock] = [ - .mockMe, - .mockDan - ] - let roomProxy = JoinedRoomProxyMock(.init(id: "dm_room_id", - name: "Dan", - topic: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", - isDirect: true, - isEncrypted: true, - members: members, - heroes: [.mockDan])) - let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init()) + let notificationSettingsProxy = NotificationSettingsProxyMock(with: notificationSettingsProxyMockConfiguration) - return RoomDetailsScreenViewModel(roomProxy: roomProxy, - clientProxy: ClientProxyMock(.init()), - mediaProvider: MediaProviderMock(configuration: .init()), - analyticsService: ServiceLocator.shared.analytics, - userIndicatorController: ServiceLocator.shared.userIndicatorController, - notificationSettingsProxy: notificationSettingsProxy, - attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()), - appMediator: AppMediatorMock.default, - appSettings: ServiceLocator.shared.settings) - }() + return .init(roomProxy: roomProxy, + clientProxy: ClientProxyMock(.init()), + mediaProvider: MediaProviderMock(configuration: .init()), + analyticsService: ServiceLocator.shared.analytics, + userIndicatorController: ServiceLocator.shared.userIndicatorController, + notificationSettingsProxy: notificationSettingsProxy, + attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()), + appMediator: AppMediatorMock.default, + appSettings: ServiceLocator.shared.settings) + } - static let simpleRoomViewModel = { - let knockRequests: [KnockRequestProxyMock] = [.init()] + private static func makeSimpleRoomViewModel() -> RoomDetailsScreenViewModel { ServiceLocator.shared.settings.knockingEnabled = true + let knockRequests: [KnockRequestProxyMock] = [.init()] + let members: [RoomMemberProxyMock] = [ .mockMeAdmin, .mockAlice, @@ -402,36 +426,57 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview { members: members, knockRequestsState: .loaded(knockRequests), joinRule: .knock)) + let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init()) - return RoomDetailsScreenViewModel(roomProxy: roomProxy, - clientProxy: ClientProxyMock(.init()), - mediaProvider: MediaProviderMock(configuration: .init()), - analyticsService: ServiceLocator.shared.analytics, - userIndicatorController: ServiceLocator.shared.userIndicatorController, - notificationSettingsProxy: notificationSettingsProxy, - attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()), - appMediator: AppMediatorMock.default, - appSettings: ServiceLocator.shared.settings) - }() + return .init(roomProxy: roomProxy, + clientProxy: ClientProxyMock(.init()), + mediaProvider: MediaProviderMock(configuration: .init()), + analyticsService: ServiceLocator.shared.analytics, + userIndicatorController: ServiceLocator.shared.userIndicatorController, + notificationSettingsProxy: notificationSettingsProxy, + attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()), + appMediator: AppMediatorMock.default, + appSettings: ServiceLocator.shared.settings) + } - static var previews: some View { - RoomDetailsScreen(context: simpleRoomViewModel.context) - .snapshotPreferences(expect: simpleRoomViewModel.context.$viewState.map { state in - state.shortcuts.contains(.invite) - }) - .previewDisplayName("Simple Room") + private static func makeDMViewModel(verificationState: UserIdentityVerificationState) -> RoomDetailsScreenViewModel { + let members: [RoomMemberProxyMock] = [ + .mockMe, + .mockDan + ] - RoomDetailsScreen(context: dmRoomViewModel.context) - .snapshotPreferences(expect: dmRoomViewModel.context.$viewState.map { state in - state.accountOwner != nil - }) - .previewDisplayName("DM Room") - - RoomDetailsScreen(context: genericRoomViewModel.context) - .snapshotPreferences(expect: genericRoomViewModel.context.$viewState.map { state in - state.shortcuts.contains(.invite) - }) - .previewDisplayName("Generic Room") + let roomProxy = JoinedRoomProxyMock(.init(id: "dm_room_id", + name: "Dan", + topic: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + isDirect: true, + isEncrypted: true, + members: members, + heroes: [.mockDan])) + + let clientProxyMock = ClientProxyMock(.init()) + + clientProxyMock.userIdentityForClosure = { userID in + let identity = switch userID { + case RoomMemberProxyMock.mockDan.userID: + UserIdentityProxyMock(configuration: .init(verificationState: verificationState)) + default: + UserIdentityProxyMock(configuration: .init()) + } + + return .success(identity) + } + + let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init()) + + return .init(roomProxy: roomProxy, + clientProxy: clientProxyMock, + mediaProvider: MediaProviderMock(configuration: .init()), + analyticsService: ServiceLocator.shared.analytics, + userIndicatorController: ServiceLocator.shared.userIndicatorController, + notificationSettingsProxy: notificationSettingsProxy, + attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()), + appMediator: AppMediatorMock.default, + appSettings: ServiceLocator.shared.settings) } } diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-en-GB.DM-Room-Verification-Violation.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-en-GB.DM-Room-Verification-Violation.png new file mode 100644 index 0000000000..ded94bf65e --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-en-GB.DM-Room-Verification-Violation.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bb50dfd4aca299415c05af27442b5ec1dc0281ad378f2c189f361de84f258cf7 +size 240022 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-en-GB.DM-Room-Verified.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-en-GB.DM-Room-Verified.png new file mode 100644 index 0000000000..61a7bc80e0 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-en-GB.DM-Room-Verified.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:13e8412072396fc4ed2bb04136d0d20804f1ae5b494d1ead4d5f86818c199d15 +size 240137 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-pseudo.DM-Room-Verification-Violation.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-pseudo.DM-Room-Verification-Violation.png new file mode 100644 index 0000000000..8bb27c6bf7 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-pseudo.DM-Room-Verification-Violation.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:24f852978c5ad569e15d3b665087a5ca314130d6d9d100ee469e26a4cf64e670 +size 244955 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-pseudo.DM-Room-Verified.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-pseudo.DM-Room-Verified.png new file mode 100644 index 0000000000..b59dbabdc4 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-pseudo.DM-Room-Verified.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:362438220db06a7a99b9d0e0060c08ba339a1423140e0dbc6c9f768870421b4b +size 245074 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-en-GB.DM-Room-Verification-Violation.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-en-GB.DM-Room-Verification-Violation.png new file mode 100644 index 0000000000..727acb88e8 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-en-GB.DM-Room-Verification-Violation.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5a56cde22f571594df1f155868532382656627f35b41dd50b0c1ff21c0c8895d +size 177717 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-en-GB.DM-Room-Verified.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-en-GB.DM-Room-Verified.png new file mode 100644 index 0000000000..582e08a9a0 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-en-GB.DM-Room-Verified.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:89e8859ab5f4855245be22aee09480bd7fc6e5b6affaa4aed903f4cbf7827377 +size 177821 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-pseudo.DM-Room-Verification-Violation.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-pseudo.DM-Room-Verification-Violation.png new file mode 100644 index 0000000000..72365c17d6 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-pseudo.DM-Room-Verification-Violation.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:12c5aed037f21552eba463602b62fa0e2098de89caa4a8a2b18ba8ef7b6e276e +size 188225 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-pseudo.DM-Room-Verified.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-pseudo.DM-Room-Verified.png new file mode 100644 index 0000000000..2c05e36ce6 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-pseudo.DM-Room-Verified.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fb62546b860070c1363366a575e452e414a8ba64cb1d05e2d2e1ff8263c7e30d +size 188322 diff --git a/UnitTests/Sources/RoomDetailsViewModelTests.swift b/UnitTests/Sources/RoomDetailsViewModelTests.swift index afbb10f875..a8c15f42d7 100644 --- a/UnitTests/Sources/RoomDetailsViewModelTests.swift +++ b/UnitTests/Sources/RoomDetailsViewModelTests.swift @@ -151,12 +151,12 @@ class RoomDetailsScreenViewModelTests: XCTestCase { appSettings: ServiceLocator.shared.settings) let deferred = deferFulfillment(viewModel.context.$viewState) { state in - state.dmRecipient != nil + state.dmRecipientInfo != nil } try await deferred.fulfill() - XCTAssertEqual(context.viewState.dmRecipient, RoomMemberDetails(withProxy: recipient)) + XCTAssertEqual(context.viewState.dmRecipientInfo?.member, RoomMemberDetails(withProxy: recipient)) } func testIgnoreSuccess() async throws { @@ -175,12 +175,12 @@ class RoomDetailsScreenViewModelTests: XCTestCase { appSettings: ServiceLocator.shared.settings) var deferred = deferFulfillment(viewModel.context.$viewState) { state in - state.dmRecipient != nil + state.dmRecipientInfo != nil } try await deferred.fulfill() - XCTAssertEqual(context.viewState.dmRecipient, RoomMemberDetails(withProxy: recipient)) + XCTAssertEqual(context.viewState.dmRecipientInfo?.member, RoomMemberDetails(withProxy: recipient)) deferred = deferFulfillment(viewModel.context.$viewState, keyPath: \.isProcessingIgnoreRequest, @@ -190,7 +190,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase { try await deferred.fulfill() - XCTAssert(context.viewState.dmRecipient?.isIgnored == true) + XCTAssert(context.viewState.dmRecipientInfo?.member.isIgnored == true) } func testIgnoreFailure() async throws { @@ -210,12 +210,12 @@ class RoomDetailsScreenViewModelTests: XCTestCase { appSettings: ServiceLocator.shared.settings) var deferred = deferFulfillment(viewModel.context.$viewState) { state in - state.dmRecipient != nil + state.dmRecipientInfo != nil } try await deferred.fulfill() - XCTAssertEqual(context.viewState.dmRecipient, RoomMemberDetails(withProxy: recipient)) + XCTAssertEqual(context.viewState.dmRecipientInfo?.member, RoomMemberDetails(withProxy: recipient)) deferred = deferFulfillment(viewModel.context.$viewState, keyPath: \.isProcessingIgnoreRequest, @@ -225,7 +225,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase { try await deferred.fulfill() - XCTAssert(context.viewState.dmRecipient?.isIgnored == false) + XCTAssert(context.viewState.dmRecipientInfo?.member.isIgnored == false) XCTAssertNotNil(context.alertInfo) } @@ -244,12 +244,12 @@ class RoomDetailsScreenViewModelTests: XCTestCase { appSettings: ServiceLocator.shared.settings) var deferred = deferFulfillment(viewModel.context.$viewState) { state in - state.dmRecipient != nil + state.dmRecipientInfo != nil } try await deferred.fulfill() - XCTAssertEqual(context.viewState.dmRecipient, RoomMemberDetails(withProxy: recipient)) + XCTAssertEqual(context.viewState.dmRecipientInfo?.member, RoomMemberDetails(withProxy: recipient)) deferred = deferFulfillment(viewModel.context.$viewState, keyPath: \.isProcessingIgnoreRequest, @@ -259,7 +259,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase { try await deferred.fulfill() - XCTAssert(context.viewState.dmRecipient?.isIgnored == false) + XCTAssert(context.viewState.dmRecipientInfo?.member.isIgnored == false) } func testUnignoreFailure() async throws { @@ -279,12 +279,12 @@ class RoomDetailsScreenViewModelTests: XCTestCase { appSettings: ServiceLocator.shared.settings) var deferred = deferFulfillment(viewModel.context.$viewState) { state in - state.dmRecipient != nil + state.dmRecipientInfo != nil } try await deferred.fulfill() - XCTAssertEqual(context.viewState.dmRecipient, RoomMemberDetails(withProxy: recipient)) + XCTAssertEqual(context.viewState.dmRecipientInfo?.member, RoomMemberDetails(withProxy: recipient)) deferred = deferFulfillment(viewModel.context.$viewState, keyPath: \.isProcessingIgnoreRequest, @@ -294,7 +294,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase { try await deferred.fulfill() - XCTAssert(context.viewState.dmRecipient?.isIgnored == true) + XCTAssert(context.viewState.dmRecipientInfo?.member.isIgnored == true) XCTAssertNotNil(context.alertInfo) } @@ -734,7 +734,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase { let deferred = deferFulfillment(context.$viewState) { state in state.knockRequestsCount == 2 && - state.dmRecipient == nil && + state.dmRecipientInfo == nil && !state.canSeeKnockingRequests && !state.canInviteUsers } @@ -760,7 +760,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase { let deferred = deferFulfillment(context.$viewState) { state in state.knockRequestsCount == 2 && !state.canSeeKnockingRequests && - state.dmRecipient != nil && + state.dmRecipientInfo != nil && state.canInviteUsers }