mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 21:39:12 +00:00
Show DM recipient verification badges on the room details screen profile button (#3824)
This commit is contained in:
parent
2db80b6858
commit
5b2f6cfbf4
@ -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))
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-en-GB.DM-Room-Verification-Violation.png
(Stored with Git LFS)
Normal file
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-en-GB.DM-Room-Verification-Violation.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-en-GB.DM-Room-Verified.png
(Stored with Git LFS)
Normal file
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-en-GB.DM-Room-Verified.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-pseudo.DM-Room-Verification-Violation.png
(Stored with Git LFS)
Normal file
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-pseudo.DM-Room-Verification-Violation.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-pseudo.DM-Room-Verified.png
(Stored with Git LFS)
Normal file
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPad-pseudo.DM-Room-Verified.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-en-GB.DM-Room-Verification-Violation.png
(Stored with Git LFS)
Normal file
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-en-GB.DM-Room-Verification-Violation.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-en-GB.DM-Room-Verified.png
(Stored with Git LFS)
Normal file
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-en-GB.DM-Room-Verified.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-pseudo.DM-Room-Verification-Violation.png
(Stored with Git LFS)
Normal file
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-pseudo.DM-Room-Verification-Violation.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-pseudo.DM-Room-Verified.png
(Stored with Git LFS)
Normal file
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomDetailsScreen-iPhone-16-pseudo.DM-Room-Verified.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user