Show DM recipient verification badges on the room details screen profile button (#3824)

This commit is contained in:
Stefan Ceriu 2025-02-26 13:55:59 +02:00 committed by GitHub
parent 2db80b6858
commit 5b2f6cfbf4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 219 additions and 132 deletions

View File

@ -30,6 +30,11 @@ enum RoomDetailsScreenViewModelAction: Equatable {
// MARK: View // MARK: View
struct DMRecipientInfo {
var member: RoomMemberDetails
var verificationState: UserIdentityVerificationState?
}
struct RoomDetailsScreenViewState: BindableState { struct RoomDetailsScreenViewState: BindableState {
var details: RoomDetails var details: RoomDetails
@ -60,11 +65,11 @@ struct RoomDetailsScreenViewState: BindableState {
var knockRequestsCount = 0 var knockRequestsCount = 0
var canSeeKnockingRequests: Bool { var canSeeKnockingRequests: Bool {
knockingEnabled && dmRecipient == nil && isKnockableRoom && (canInviteUsers || canKickUsers || canBanUsers) knockingEnabled && dmRecipientInfo == nil && isKnockableRoom && (canInviteUsers || canKickUsers || canBanUsers)
} }
var canSeeSecurityAndPrivacy: Bool { var canSeeSecurityAndPrivacy: Bool {
knockingEnabled && dmRecipient == nil && canEditRolesOrPermissions knockingEnabled && dmRecipientInfo == nil && canEditRolesOrPermissions
} }
var canEdit: Bool { var canEdit: Bool {
@ -77,7 +82,7 @@ struct RoomDetailsScreenViewState: BindableState {
var bindings: RoomDetailsScreenViewStateBindings var bindings: RoomDetailsScreenViewStateBindings
var dmRecipient: RoomMemberDetails? var dmRecipientInfo: DMRecipientInfo?
var accountOwner: RoomMemberDetails? var accountOwner: RoomMemberDetails?
var shortcuts: [RoomDetailsScreenViewShortcut] { var shortcuts: [RoomDetailsScreenViewShortcut] {
@ -85,10 +90,10 @@ struct RoomDetailsScreenViewState: BindableState {
if !ProcessInfo.processInfo.isiOSAppOnMac, canJoinCall { if !ProcessInfo.processInfo.isiOSAppOnMac, canJoinCall {
shortcuts.append(.call) shortcuts.append(.call)
} }
if dmRecipient == nil, canInviteUsers { if dmRecipientInfo == nil, canInviteUsers {
shortcuts.append(.invite) shortcuts.append(.invite)
} }
if let permalink = dmRecipient?.permalink { if let permalink = dmRecipientInfo?.member.permalink {
shortcuts.append(.share(link: permalink)) shortcuts.append(.share(link: permalink))
} else if let permalink { } else if let permalink {
shortcuts.append(.share(link: permalink)) shortcuts.append(.share(link: permalink))

View File

@ -20,7 +20,6 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
private let attributedStringBuilder: AttributedStringBuilderProtocol private let attributedStringBuilder: AttributedStringBuilderProtocol
private let appSettings: AppSettings private let appSettings: AppSettings
private var dmRecipient: RoomMemberProxyProtocol?
private var pinnedEventsTimelineProvider: TimelineProviderProtocol? { private var pinnedEventsTimelineProvider: TimelineProviderProtocol? {
didSet { didSet {
guard let pinnedEventsTimelineProvider else { guard let pinnedEventsTimelineProvider else {
@ -171,7 +170,7 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
case .processTapSecurityAndPrivacy: case .processTapSecurityAndPrivacy:
actionsSubject.send(.displaySecurityAndPrivacy) actionsSubject.send(.displaySecurityAndPrivacy)
case .processTapRecipientProfile: case .processTapRecipientProfile:
guard let userID = dmRecipient?.userID else { guard let userID = state.dmRecipientInfo?.member.id else {
return return
} }
actionsSubject.send(.requestRecipientDetailsPresentation(userID: userID)) actionsSubject.send(.requestRecipientDetailsPresentation(userID: userID))
@ -234,11 +233,16 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
.sink { [weak self, ownUserID = roomProxy.ownUserID] members in .sink { [weak self, ownUserID = roomProxy.ownUserID] members in
guard let self else { return } guard let self else { return }
let accountOwner = members.first { $0.userID == ownUserID }
let dmRecipient = members.first { $0.userID != ownUserID } if let accountOwner = members.first(where: { $0.userID == ownUserID }) {
self.dmRecipient = dmRecipient self.state.accountOwner = .init(withProxy: accountOwner)
self.state.dmRecipient = dmRecipient.map(RoomMemberDetails.init(withProxy:)) }
self.state.accountOwner = accountOwner.map(RoomMemberDetails.init(withProxy:))
if let dmRecipient = members.first(where: { $0.userID != ownUserID }) {
self.state.dmRecipientInfo = .init(member: .init(withProxy: dmRecipient))
Task { await self.updateMemberIdentityVerificationStates() }
}
} }
.store(in: &cancellables) .store(in: &cancellables)
@ -251,9 +255,17 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
return 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 { for member in roomProxy.membersPublisher.value {
if case let .success(identity) = await clientProxy.userIdentity(for: member.userID) { if case let .success(userIdentity) = await clientProxy.userIdentity(for: member.userID) {
if identity?.verificationState == .verificationViolation { if userIdentity?.verificationState == .verificationViolation {
state.hasMemberIdentityVerificationStateViolations = true state.hasMemberIdentityVerificationStateViolations = true
return return
} }
@ -262,6 +274,7 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
state.hasMemberIdentityVerificationStateViolations = false state.hasMemberIdentityVerificationStateViolations = false
} }
}
private func updatePowerLevelPermissions() async { private func updatePowerLevelPermissions() async {
state.canEditRoomName = await (try? roomProxy.canUser(userID: roomProxy.ownUserID, sendStateEvent: .roomName).get()) == true state.canEditRoomName = await (try? roomProxy.canUser(userID: roomProxy.ownUserID, sendStateEvent: .roomName).get()) == true
@ -357,7 +370,7 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
} }
private func ignore() async { 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") MXLog.error("Attempting to ignore a nil DM Recipient")
state.bindings.alertInfo = .init(id: .unknown) state.bindings.alertInfo = .init(id: .unknown)
return return
@ -369,16 +382,16 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
switch result { switch result {
case .success: case .success:
// Mutating the optional in place when built for Release crashes 🤷 // Mutating the optional in place when built for Release crashes 🤷
var dmRecipient = state.dmRecipient var dmRecipientInfo = state.dmRecipientInfo
dmRecipient?.isIgnored = true dmRecipientInfo?.member.isIgnored = true
state.dmRecipient = dmRecipient state.dmRecipientInfo = dmRecipientInfo
case .failure: case .failure:
state.bindings.alertInfo = .init(id: .unknown) state.bindings.alertInfo = .init(id: .unknown)
} }
} }
private func unignore() async { 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") MXLog.error("Attempting to unignore a nil DM Recipient")
state.bindings.alertInfo = .init(id: .unknown) state.bindings.alertInfo = .init(id: .unknown)
return return
@ -390,9 +403,9 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
switch result { switch result {
case .success: case .success:
// Mutating the optional in place when built for Release crashes 🤷 // Mutating the optional in place when built for Release crashes 🤷
var dmRecipient = state.dmRecipient var dmRecipientInfo = state.dmRecipientInfo
dmRecipient?.isIgnored = false dmRecipientInfo?.member.isIgnored = false
state.dmRecipient = dmRecipient state.dmRecipientInfo = dmRecipientInfo
case .failure: case .failure:
state.bindings.alertInfo = .init(id: .unknown) state.bindings.alertInfo = .init(id: .unknown)
} }

View File

@ -21,7 +21,7 @@ struct RoomDetailsScreen: View {
configurationSection configurationSection
if context.viewState.dmRecipient == nil { if context.viewState.dmRecipientInfo == nil {
peopleSection peopleSection
} }
@ -29,7 +29,7 @@ struct RoomDetailsScreen: View {
securitySection securitySection
if let recipient = context.viewState.dmRecipient { if let recipient = context.viewState.dmRecipientInfo?.member {
ignoreUserSection(user: recipient) ignoreUserSection(user: recipient)
} }
@ -136,16 +136,14 @@ struct RoomDetailsScreen: View {
private var aboutSection: some View { private var aboutSection: some View {
Section { Section {
ListRow(label: .default(title: L10n.screenRoomDetailsPinnedEventsRowTitle, ListRow(label: .default(title: L10n.screenRoomDetailsPinnedEventsRowTitle, icon: \.pin),
icon: \.pin),
details: context.viewState.pinnedEventsActionState.isLoading ? .isWaiting(true) : .title(context.viewState.pinnedEventsActionState.count), details: context.viewState.pinnedEventsActionState.isLoading ? .isWaiting(true) : .title(context.viewState.pinnedEventsActionState.count),
kind: context.viewState.pinnedEventsActionState.isLoading ? .label : .navigationLink { kind: context.viewState.pinnedEventsActionState.isLoading ? .label : .navigationLink {
context.send(viewAction: .processTapPinnedEvents) context.send(viewAction: .processTapPinnedEvents)
}) })
.disabled(context.viewState.pinnedEventsActionState.isLoading) .disabled(context.viewState.pinnedEventsActionState.isLoading)
ListRow(label: .default(title: L10n.screenPollsHistoryTitle, ListRow(label: .default(title: L10n.screenPollsHistoryTitle, icon: \.polls),
icon: \.polls),
kind: .navigationLink { kind: .navigationLink {
context.send(viewAction: .processTapPolls) context.send(viewAction: .processTapPolls)
}) })
@ -160,8 +158,7 @@ struct RoomDetailsScreen: View {
private var configurationSection: some View { private var configurationSection: some View {
Section { Section {
ListRow(label: .default(title: L10n.screenRoomDetailsNotificationTitle, ListRow(label: .default(title: L10n.screenRoomDetailsNotificationTitle, icon: \.notifications),
icon: \.notifications),
details: context.viewState.notificationSettingsState.isLoading ? .isWaiting(true) details: context.viewState.notificationSettingsState.isLoading ? .isWaiting(true)
: context.viewState.notificationSettingsState.isError ? .systemIcon(.exclamationmarkCircle) : context.viewState.notificationSettingsState.isError ? .systemIcon(.exclamationmarkCircle)
: .title(context.viewState.notificationSettingsState.label), : .title(context.viewState.notificationSettingsState.label),
@ -179,19 +176,32 @@ struct RoomDetailsScreen: View {
} }
if context.viewState.canSeeSecurityAndPrivacy { if context.viewState.canSeeSecurityAndPrivacy {
ListRow(label: .default(title: L10n.screenRoomDetailsSecurityAndPrivacyTitle, ListRow(label: .default(title: L10n.screenRoomDetailsSecurityAndPrivacyTitle, icon: \.lock),
icon: \.lock),
kind: .navigationLink { kind: .navigationLink {
context.send(viewAction: .processTapSecurityAndPrivacy) context.send(viewAction: .processTapSecurityAndPrivacy)
}) })
} }
if context.viewState.dmRecipient != nil { if context.viewState.dmRecipientInfo != nil {
ListRow(label: .default(title: L10n.screenRoomDetailsProfileRowTitle, switch context.viewState.dmRecipientInfo?.verificationState {
icon: \.userProfile), case .verified:
ListRow(label: .default(title: L10n.screenRoomDetailsProfileRowTitle, icon: \.userProfile),
details: .icon(CompoundIcon(\.verified).foregroundStyle(.compound.iconSuccessPrimary)),
kind: .navigationLink { kind: .navigationLink {
context.send(viewAction: .processTapRecipientProfile) 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 { if context.viewState.canSeeKnockingRequests {
ListRow(label: .default(title: L10n.screenRoomDetailsRequestsToJoinTitle, ListRow(label: .default(title: L10n.screenRoomDetailsRequestsToJoinTitle, icon: \.askToJoin),
icon: \.askToJoin),
details: context.viewState.knockRequestsCount > 0 ? .counter(context.viewState.knockRequestsCount) : nil, details: context.viewState.knockRequestsCount > 0 ? .counter(context.viewState.knockRequestsCount) : nil,
kind: .navigationLink { kind: .navigationLink {
context.send(viewAction: .processTapRequestsToJoin) context.send(viewAction: .processTapRequestsToJoin)
}) })
} }
if context.viewState.canEditRolesOrPermissions, context.viewState.dmRecipient == nil { if context.viewState.canEditRolesOrPermissions, context.viewState.dmRecipientInfo == nil {
ListRow(label: .default(title: L10n.screenRoomDetailsRolesAndPermissions, ListRow(label: .default(title: L10n.screenRoomDetailsRolesAndPermissions, icon: \.admin),
icon: \.admin),
kind: .navigationLink { kind: .navigationLink {
context.send(viewAction: .processTapRolesAndPermissions) context.send(viewAction: .processTapRolesAndPermissions)
}) })
@ -265,7 +273,7 @@ struct RoomDetailsScreen: View {
} }
private var leaveRoomTitle: String { 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 { private var leaveRoomSection: some View {
@ -319,15 +327,55 @@ struct RoomDetailsScreen: View {
// MARK: - Previews // MARK: - Previews
struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview { 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 ServiceLocator.shared.settings.knockingEnabled = true
let knockRequests: [KnockRequestProxyMock] = [.init()] let knockRequests: [KnockRequestProxyMock] = [.init()]
let members: [RoomMemberProxyMock] = [ let members: [RoomMemberProxyMock] = [
.mockMeAdmin, .mockMeAdmin,
.mockAlice, .mockAlice,
.mockBob, .mockBob,
.mockCharlie .mockCharlie
] ]
let roomProxy = JoinedRoomProxyMock(.init(id: "room_a_id", let roomProxy = JoinedRoomProxyMock(.init(id: "room_a_id",
name: "Room A", name: "Room A",
topic: """ topic: """
@ -345,11 +393,12 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview {
knockRequestsState: .loaded(knockRequests), knockRequestsState: .loaded(knockRequests),
joinRule: .knock)) joinRule: .knock))
var notificationSettingsProxyMockConfiguration = NotificationSettingsProxyMockConfiguration() let notificationSettingsProxyMockConfiguration = NotificationSettingsProxyMockConfiguration()
notificationSettingsProxyMockConfiguration.roomMode.isDefault = false notificationSettingsProxyMockConfiguration.roomMode.isDefault = false
let notificationSettingsProxy = NotificationSettingsProxyMock(with: notificationSettingsProxyMockConfiguration) let notificationSettingsProxy = NotificationSettingsProxyMock(with: notificationSettingsProxyMockConfiguration)
return RoomDetailsScreenViewModel(roomProxy: roomProxy, return .init(roomProxy: roomProxy,
clientProxy: ClientProxyMock(.init()), clientProxy: ClientProxyMock(.init()),
mediaProvider: MediaProviderMock(configuration: .init()), mediaProvider: MediaProviderMock(configuration: .init()),
analyticsService: ServiceLocator.shared.analytics, analyticsService: ServiceLocator.shared.analytics,
@ -358,37 +407,12 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview {
attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()), attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()),
appMediator: AppMediatorMock.default, appMediator: AppMediatorMock.default,
appSettings: ServiceLocator.shared.settings) appSettings: ServiceLocator.shared.settings)
}() }
static let dmRoomViewModel = { private static func makeSimpleRoomViewModel() -> RoomDetailsScreenViewModel {
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())
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 simpleRoomViewModel = {
let knockRequests: [KnockRequestProxyMock] = [.init()]
ServiceLocator.shared.settings.knockingEnabled = true ServiceLocator.shared.settings.knockingEnabled = true
let knockRequests: [KnockRequestProxyMock] = [.init()]
let members: [RoomMemberProxyMock] = [ let members: [RoomMemberProxyMock] = [
.mockMeAdmin, .mockMeAdmin,
.mockAlice, .mockAlice,
@ -402,9 +426,10 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview {
members: members, members: members,
knockRequestsState: .loaded(knockRequests), knockRequestsState: .loaded(knockRequests),
joinRule: .knock)) joinRule: .knock))
let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init()) let notificationSettingsProxy = NotificationSettingsProxyMock(with: .init())
return RoomDetailsScreenViewModel(roomProxy: roomProxy, return .init(roomProxy: roomProxy,
clientProxy: ClientProxyMock(.init()), clientProxy: ClientProxyMock(.init()),
mediaProvider: MediaProviderMock(configuration: .init()), mediaProvider: MediaProviderMock(configuration: .init()),
analyticsService: ServiceLocator.shared.analytics, analyticsService: ServiceLocator.shared.analytics,
@ -413,25 +438,45 @@ struct RoomDetailsScreen_Previews: PreviewProvider, TestablePreview {
attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()), attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()),
appMediator: AppMediatorMock.default, appMediator: AppMediatorMock.default,
appSettings: ServiceLocator.shared.settings) appSettings: ServiceLocator.shared.settings)
}() }
static var previews: some View { private static func makeDMViewModel(verificationState: UserIdentityVerificationState) -> RoomDetailsScreenViewModel {
RoomDetailsScreen(context: simpleRoomViewModel.context) let members: [RoomMemberProxyMock] = [
.snapshotPreferences(expect: simpleRoomViewModel.context.$viewState.map { state in .mockMe,
state.shortcuts.contains(.invite) .mockDan
}) ]
.previewDisplayName("Simple Room")
RoomDetailsScreen(context: dmRoomViewModel.context) let roomProxy = JoinedRoomProxyMock(.init(id: "dm_room_id",
.snapshotPreferences(expect: dmRoomViewModel.context.$viewState.map { state in name: "Dan",
state.accountOwner != nil topic: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
}) isDirect: true,
.previewDisplayName("DM Room") isEncrypted: true,
members: members,
heroes: [.mockDan]))
RoomDetailsScreen(context: genericRoomViewModel.context) let clientProxyMock = ClientProxyMock(.init())
.snapshotPreferences(expect: genericRoomViewModel.context.$viewState.map { state in
state.shortcuts.contains(.invite) clientProxyMock.userIdentityForClosure = { userID in
}) let identity = switch userID {
.previewDisplayName("Generic Room") 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)
} }
} }

View File

@ -151,12 +151,12 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
appSettings: ServiceLocator.shared.settings) appSettings: ServiceLocator.shared.settings)
let deferred = deferFulfillment(viewModel.context.$viewState) { state in let deferred = deferFulfillment(viewModel.context.$viewState) { state in
state.dmRecipient != nil state.dmRecipientInfo != nil
} }
try await deferred.fulfill() try await deferred.fulfill()
XCTAssertEqual(context.viewState.dmRecipient, RoomMemberDetails(withProxy: recipient)) XCTAssertEqual(context.viewState.dmRecipientInfo?.member, RoomMemberDetails(withProxy: recipient))
} }
func testIgnoreSuccess() async throws { func testIgnoreSuccess() async throws {
@ -175,12 +175,12 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
appSettings: ServiceLocator.shared.settings) appSettings: ServiceLocator.shared.settings)
var deferred = deferFulfillment(viewModel.context.$viewState) { state in var deferred = deferFulfillment(viewModel.context.$viewState) { state in
state.dmRecipient != nil state.dmRecipientInfo != nil
} }
try await deferred.fulfill() try await deferred.fulfill()
XCTAssertEqual(context.viewState.dmRecipient, RoomMemberDetails(withProxy: recipient)) XCTAssertEqual(context.viewState.dmRecipientInfo?.member, RoomMemberDetails(withProxy: recipient))
deferred = deferFulfillment(viewModel.context.$viewState, deferred = deferFulfillment(viewModel.context.$viewState,
keyPath: \.isProcessingIgnoreRequest, keyPath: \.isProcessingIgnoreRequest,
@ -190,7 +190,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
try await deferred.fulfill() try await deferred.fulfill()
XCTAssert(context.viewState.dmRecipient?.isIgnored == true) XCTAssert(context.viewState.dmRecipientInfo?.member.isIgnored == true)
} }
func testIgnoreFailure() async throws { func testIgnoreFailure() async throws {
@ -210,12 +210,12 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
appSettings: ServiceLocator.shared.settings) appSettings: ServiceLocator.shared.settings)
var deferred = deferFulfillment(viewModel.context.$viewState) { state in var deferred = deferFulfillment(viewModel.context.$viewState) { state in
state.dmRecipient != nil state.dmRecipientInfo != nil
} }
try await deferred.fulfill() try await deferred.fulfill()
XCTAssertEqual(context.viewState.dmRecipient, RoomMemberDetails(withProxy: recipient)) XCTAssertEqual(context.viewState.dmRecipientInfo?.member, RoomMemberDetails(withProxy: recipient))
deferred = deferFulfillment(viewModel.context.$viewState, deferred = deferFulfillment(viewModel.context.$viewState,
keyPath: \.isProcessingIgnoreRequest, keyPath: \.isProcessingIgnoreRequest,
@ -225,7 +225,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
try await deferred.fulfill() try await deferred.fulfill()
XCTAssert(context.viewState.dmRecipient?.isIgnored == false) XCTAssert(context.viewState.dmRecipientInfo?.member.isIgnored == false)
XCTAssertNotNil(context.alertInfo) XCTAssertNotNil(context.alertInfo)
} }
@ -244,12 +244,12 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
appSettings: ServiceLocator.shared.settings) appSettings: ServiceLocator.shared.settings)
var deferred = deferFulfillment(viewModel.context.$viewState) { state in var deferred = deferFulfillment(viewModel.context.$viewState) { state in
state.dmRecipient != nil state.dmRecipientInfo != nil
} }
try await deferred.fulfill() try await deferred.fulfill()
XCTAssertEqual(context.viewState.dmRecipient, RoomMemberDetails(withProxy: recipient)) XCTAssertEqual(context.viewState.dmRecipientInfo?.member, RoomMemberDetails(withProxy: recipient))
deferred = deferFulfillment(viewModel.context.$viewState, deferred = deferFulfillment(viewModel.context.$viewState,
keyPath: \.isProcessingIgnoreRequest, keyPath: \.isProcessingIgnoreRequest,
@ -259,7 +259,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
try await deferred.fulfill() try await deferred.fulfill()
XCTAssert(context.viewState.dmRecipient?.isIgnored == false) XCTAssert(context.viewState.dmRecipientInfo?.member.isIgnored == false)
} }
func testUnignoreFailure() async throws { func testUnignoreFailure() async throws {
@ -279,12 +279,12 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
appSettings: ServiceLocator.shared.settings) appSettings: ServiceLocator.shared.settings)
var deferred = deferFulfillment(viewModel.context.$viewState) { state in var deferred = deferFulfillment(viewModel.context.$viewState) { state in
state.dmRecipient != nil state.dmRecipientInfo != nil
} }
try await deferred.fulfill() try await deferred.fulfill()
XCTAssertEqual(context.viewState.dmRecipient, RoomMemberDetails(withProxy: recipient)) XCTAssertEqual(context.viewState.dmRecipientInfo?.member, RoomMemberDetails(withProxy: recipient))
deferred = deferFulfillment(viewModel.context.$viewState, deferred = deferFulfillment(viewModel.context.$viewState,
keyPath: \.isProcessingIgnoreRequest, keyPath: \.isProcessingIgnoreRequest,
@ -294,7 +294,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
try await deferred.fulfill() try await deferred.fulfill()
XCTAssert(context.viewState.dmRecipient?.isIgnored == true) XCTAssert(context.viewState.dmRecipientInfo?.member.isIgnored == true)
XCTAssertNotNil(context.alertInfo) XCTAssertNotNil(context.alertInfo)
} }
@ -734,7 +734,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
let deferred = deferFulfillment(context.$viewState) { state in let deferred = deferFulfillment(context.$viewState) { state in
state.knockRequestsCount == 2 && state.knockRequestsCount == 2 &&
state.dmRecipient == nil && state.dmRecipientInfo == nil &&
!state.canSeeKnockingRequests && !state.canSeeKnockingRequests &&
!state.canInviteUsers !state.canInviteUsers
} }
@ -760,7 +760,7 @@ class RoomDetailsScreenViewModelTests: XCTestCase {
let deferred = deferFulfillment(context.$viewState) { state in let deferred = deferFulfillment(context.$viewState) { state in
state.knockRequestsCount == 2 && state.knockRequestsCount == 2 &&
!state.canSeeKnockingRequests && !state.canSeeKnockingRequests &&
state.dmRecipient != nil && state.dmRecipientInfo != nil &&
state.canInviteUsers state.canInviteUsers
} }