From a4ea552a83cc0d3e9b23b90e9ec6cdd3ab5b7f33 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Mon, 21 Oct 2024 16:07:19 +0100 Subject: [PATCH] Show a verification badge on the Room Member/User Profile screens. (#3427) * Add a badge for verified users/room members. * Reorder subviews. * Add a (disabled) button to verify other users. * PR comments. * Update the SDK. * Adopt the SDK changes introduced in matrix-rust-sdk/pull/4100 --------- Co-authored-by: Stefan Ceriu --- ElementX.xcodeproj/project.pbxproj | 6 +- .../xcshareddata/swiftpm/Package.resolved | 4 +- ElementX/Sources/Mocks/ClientProxyMock.swift | 2 + .../Mocks/Generated/GeneratedMocks.swift | 70 +++++ .../Mocks/Generated/SDKGeneratedMocks.swift | 250 ++++++++++-------- .../Mocks/SDK/UserIdentitySDKMock.swift | 21 ++ .../SwiftUI/Views/AvatarHeaderView.swift | 33 ++- .../RoomMemberDetailsScreenModels.swift | 13 + .../RoomMemberDetailsScreenViewModel.swift | 54 ++-- .../View/RoomMemberDetailsScreen.swift | 53 ++-- .../ComposerToolbarModels.swift | 7 +- .../ComposerToolbarViewModel.swift | 4 +- .../View/MessageComposer.swift | 2 +- .../Timeline/TimelineInteractionHandler.swift | 2 +- .../Screens/Timeline/TimelineViewModel.swift | 5 +- .../UserProfileScreenModels.swift | 13 + .../UserProfileScreenViewModel.swift | 46 ++-- .../View/UserProfileScreen.swift | 52 +++- .../Sources/Services/Client/ClientProxy.swift | 11 +- .../Services/Client/ClientProxyProtocol.swift | 2 + .../MockRoomTimelineController.swift | 1 - .../RoomTimelineController.swift | 25 +- .../RoomTimelineControllerProtocol.swift | 1 - .../Services/Timeline/TimelineProxy.swift | 18 +- ...st_avatarHeaderView-iPad-en-GB.Members.png | 4 +- ...t_avatarHeaderView-iPad-pseudo.Members.png | 4 +- ...atarHeaderView-iPhone-16-en-GB.Members.png | 4 +- ...tarHeaderView-iPhone-16-pseudo.Members.png | 4 +- ...rDetailsScreen-iPad-en-GB.Ignored-User.png | 4 +- ...berDetailsScreen-iPad-en-GB.Other-User.png | 4 +- ...DetailsScreen-iPad-en-GB.Verified-User.png | 3 + ...DetailsScreen-iPad-pseudo.Ignored-User.png | 4 +- ...erDetailsScreen-iPad-pseudo.Other-User.png | 4 +- ...etailsScreen-iPad-pseudo.Verified-User.png | 3 + ...ilsScreen-iPhone-16-en-GB.Ignored-User.png | 4 +- ...tailsScreen-iPhone-16-en-GB.Other-User.png | 4 +- ...lsScreen-iPhone-16-en-GB.Verified-User.png | 3 + ...lsScreen-iPhone-16-pseudo.Ignored-User.png | 4 +- ...ailsScreen-iPhone-16-pseudo.Other-User.png | 4 +- ...sScreen-iPhone-16-pseudo.Verified-User.png | 3 + ...serProfileScreen-iPad-en-GB.Other-User.png | 4 +- ...ProfileScreen-iPad-en-GB.Verified-User.png | 3 + ...erProfileScreen-iPad-pseudo.Other-User.png | 4 +- ...rofileScreen-iPad-pseudo.Verified-User.png | 3 + ...ofileScreen-iPhone-16-en-GB.Other-User.png | 4 +- ...leScreen-iPhone-16-en-GB.Verified-User.png | 3 + ...fileScreen-iPhone-16-pseudo.Other-User.png | 4 +- ...eScreen-iPhone-16-pseudo.Verified-User.png | 3 + .../ComposerToolbarViewModelTests.swift | 12 +- project.yml | 2 +- 50 files changed, 522 insertions(+), 275 deletions(-) create mode 100644 ElementX/Sources/Mocks/SDK/UserIdentitySDKMock.swift create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Verified-User.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Verified-User.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Verified-User.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Verified-User.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-en-GB.Verified-User.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-pseudo.Verified-User.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-en-GB.Verified-User.png create mode 100644 PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-pseudo.Verified-User.png diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index e68a0d5e07..0e2bed091a 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -899,6 +899,7 @@ C76892321558E75101E68ED6 /* ReadableFrameModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 398817652FA8ABAE0A31AC6D /* ReadableFrameModifier.swift */; }; C7774720A4B2E34693E3227C /* RoomNotificationSettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8896CDD20CA2D87EA3B848A1 /* RoomNotificationSettingsScreen.swift */; }; C7ABEBECDC513F7887DACF66 /* ProgressMaskModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68010886142843705E342645 /* ProgressMaskModifier.swift */; }; + C7B07EBA0F12B5912DA9BB97 /* UserIdentitySDKMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8DE9D0D480D087D0F676B52 /* UserIdentitySDKMock.swift */; }; C80E06ED97CE52704A46C148 /* ClientBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1C33355FFB0F0953C35036 /* ClientBuilder.swift */; }; C85C7A201E4CFDA477ACEBEB /* AppLockSetupSettingsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8610C1D21565C950BCA6A454 /* AppLockSetupSettingsScreenViewModelProtocol.swift */; }; C8A9C595038AFA2D707AC8C1 /* NotificationPermissionsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20E69F67D2A70ABD08CA6D54 /* NotificationPermissionsScreenViewModelProtocol.swift */; }; @@ -2221,6 +2222,7 @@ E8A1F98AE670377B20679FF5 /* MediaPlayerProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPlayerProvider.swift; sourceTree = ""; }; E8AE4B3273BA189FDCD4055C /* UserIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIndicator.swift; sourceTree = ""; }; E8CA187FE656EE5A3F6C7DE5 /* UIFont+AttributedStringBuilder.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIFont+AttributedStringBuilder.m"; sourceTree = ""; }; + E8DE9D0D480D087D0F676B52 /* UserIdentitySDKMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIdentitySDKMock.swift; sourceTree = ""; }; E96ED747FF90332EA1333C22 /* RoomTimelineItemFixtures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemFixtures.swift; sourceTree = ""; }; E992D7B8BE54B2AB454613AF /* XCUIElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCUIElement.swift; sourceTree = ""; }; E9A3D3CFA199FA7897364547 /* CallInviteRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallInviteRoomTimelineItem.swift; sourceTree = ""; }; @@ -5276,6 +5278,7 @@ isa = PBXGroup; children = ( 8EAF4A49F3ACD8BB8B0D2371 /* ClientSDKMock.swift */, + E8DE9D0D480D087D0F676B52 /* UserIdentitySDKMock.swift */, ); path = SDK; sourceTree = ""; @@ -7007,6 +7010,7 @@ 828EA5009557C2B9DCD4CA0F /* UserDiscoverySection.swift in Sources */, 044DD8F80231BC30570F7965 /* UserDiscoveryService.swift in Sources */, 1C409A26A99F0371C47AFA51 /* UserDiscoveryServiceProtocol.swift in Sources */, + C7B07EBA0F12B5912DA9BB97 /* UserIdentitySDKMock.swift in Sources */, 988BA75A182738150894A23F /* UserIndicator.swift in Sources */, C4E0D03DF88242697545A9B7 /* UserIndicatorController.swift in Sources */, 3467FEE8210D301FF1B77001 /* UserIndicatorControllerMock.swift in Sources */, @@ -7818,7 +7822,7 @@ repositoryURL = "https://github.com/element-hq/matrix-rust-components-swift"; requirement = { kind = exactVersion; - version = 1.0.58; + version = 1.0.59; }; }; 701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 0c07855fa0..46b5123a2c 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -149,8 +149,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/element-hq/matrix-rust-components-swift", "state" : { - "revision" : "753c5381ce88b3549cbd8ed9b839109ff143ecdd", - "version" : "1.0.58" + "revision" : "acd36f68f107f608c33b4d5b46be4ddea5537b1f", + "version" : "1.0.59" } }, { diff --git a/ElementX/Sources/Mocks/ClientProxyMock.swift b/ElementX/Sources/Mocks/ClientProxyMock.swift index 507a21b0a9..ac086c1c43 100644 --- a/ElementX/Sources/Mocks/ClientProxyMock.swift +++ b/ElementX/Sources/Mocks/ClientProxyMock.swift @@ -93,5 +93,7 @@ extension ClientProxyMock { return await .joined(JoinedRoomProxyMock(.init(id: room.id, name: room.name))) } + + userIdentityForReturnValue = .success(UserIdentitySDKMock(configuration: .init())) } } diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index 039932ee7f..f5d9d148ff 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -4700,6 +4700,76 @@ class ClientProxyMock: ClientProxyProtocol { return resetIdentityReturnValue } } + //MARK: - userIdentity + + var userIdentityForUnderlyingCallsCount = 0 + var userIdentityForCallsCount: Int { + get { + if Thread.isMainThread { + return userIdentityForUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = userIdentityForUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + userIdentityForUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + userIdentityForUnderlyingCallsCount = newValue + } + } + } + } + var userIdentityForCalled: Bool { + return userIdentityForCallsCount > 0 + } + var userIdentityForReceivedUserID: String? + var userIdentityForReceivedInvocations: [String] = [] + + var userIdentityForUnderlyingReturnValue: Result! + var userIdentityForReturnValue: Result! { + get { + if Thread.isMainThread { + return userIdentityForUnderlyingReturnValue + } else { + var returnValue: Result? = nil + DispatchQueue.main.sync { + returnValue = userIdentityForUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + userIdentityForUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + userIdentityForUnderlyingReturnValue = newValue + } + } + } + } + var userIdentityForClosure: ((String) async -> Result)? + + func userIdentity(for userID: String) async -> Result { + userIdentityForCallsCount += 1 + userIdentityForReceivedUserID = userID + DispatchQueue.main.async { + self.userIdentityForReceivedInvocations.append(userID) + } + if let userIdentityForClosure = userIdentityForClosure { + return await userIdentityForClosure(userID) + } else { + return userIdentityForReturnValue + } + } //MARK: - loadMediaContentForSource var loadMediaContentForSourceThrowableError: Error? diff --git a/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift index 52c51b578b..968f3fd9c8 100644 --- a/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift @@ -6214,81 +6214,6 @@ open class EncryptionSDKMock: MatrixRustSDK.Encryption { } } - //MARK: - getUserIdentity - - open var getUserIdentityUserIdThrowableError: Error? - var getUserIdentityUserIdUnderlyingCallsCount = 0 - open var getUserIdentityUserIdCallsCount: Int { - get { - if Thread.isMainThread { - return getUserIdentityUserIdUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = getUserIdentityUserIdUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - getUserIdentityUserIdUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - getUserIdentityUserIdUnderlyingCallsCount = newValue - } - } - } - } - open var getUserIdentityUserIdCalled: Bool { - return getUserIdentityUserIdCallsCount > 0 - } - open var getUserIdentityUserIdReceivedUserId: String? - open var getUserIdentityUserIdReceivedInvocations: [String] = [] - - var getUserIdentityUserIdUnderlyingReturnValue: UserIdentity? - open var getUserIdentityUserIdReturnValue: UserIdentity? { - get { - if Thread.isMainThread { - return getUserIdentityUserIdUnderlyingReturnValue - } else { - var returnValue: UserIdentity?? = nil - DispatchQueue.main.sync { - returnValue = getUserIdentityUserIdUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - getUserIdentityUserIdUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - getUserIdentityUserIdUnderlyingReturnValue = newValue - } - } - } - } - open var getUserIdentityUserIdClosure: ((String) async throws -> UserIdentity?)? - - open override func getUserIdentity(userId: String) async throws -> UserIdentity? { - if let error = getUserIdentityUserIdThrowableError { - throw error - } - getUserIdentityUserIdCallsCount += 1 - getUserIdentityUserIdReceivedUserId = userId - DispatchQueue.main.async { - self.getUserIdentityUserIdReceivedInvocations.append(userId) - } - if let getUserIdentityUserIdClosure = getUserIdentityUserIdClosure { - return try await getUserIdentityUserIdClosure(userId) - } else { - return getUserIdentityUserIdReturnValue - } - } - //MARK: - isLastDevice open var isLastDeviceThrowableError: Error? @@ -6753,6 +6678,81 @@ open class EncryptionSDKMock: MatrixRustSDK.Encryption { } } + //MARK: - userIdentity + + open var userIdentityUserIdThrowableError: Error? + var userIdentityUserIdUnderlyingCallsCount = 0 + open var userIdentityUserIdCallsCount: Int { + get { + if Thread.isMainThread { + return userIdentityUserIdUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = userIdentityUserIdUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + userIdentityUserIdUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + userIdentityUserIdUnderlyingCallsCount = newValue + } + } + } + } + open var userIdentityUserIdCalled: Bool { + return userIdentityUserIdCallsCount > 0 + } + open var userIdentityUserIdReceivedUserId: String? + open var userIdentityUserIdReceivedInvocations: [String] = [] + + var userIdentityUserIdUnderlyingReturnValue: UserIdentity? + open var userIdentityUserIdReturnValue: UserIdentity? { + get { + if Thread.isMainThread { + return userIdentityUserIdUnderlyingReturnValue + } else { + var returnValue: UserIdentity?? = nil + DispatchQueue.main.sync { + returnValue = userIdentityUserIdUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + userIdentityUserIdUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + userIdentityUserIdUnderlyingReturnValue = newValue + } + } + } + } + open var userIdentityUserIdClosure: ((String) async throws -> UserIdentity?)? + + open override func userIdentity(userId: String) async throws -> UserIdentity? { + if let error = userIdentityUserIdThrowableError { + throw error + } + userIdentityUserIdCallsCount += 1 + userIdentityUserIdReceivedUserId = userId + DispatchQueue.main.async { + self.userIdentityUserIdReceivedInvocations.append(userId) + } + if let userIdentityUserIdClosure = userIdentityUserIdClosure { + return try await userIdentityUserIdClosure(userId) + } else { + return userIdentityUserIdReturnValue + } + } + //MARK: - verificationState var verificationStateUnderlyingCallsCount = 0 @@ -18042,34 +18042,9 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { } open var editEventOrTransactionIdNewContentReceivedArguments: (eventOrTransactionId: EventOrTransactionId, newContent: EditedContent)? open var editEventOrTransactionIdNewContentReceivedInvocations: [(eventOrTransactionId: EventOrTransactionId, newContent: EditedContent)] = [] + open var editEventOrTransactionIdNewContentClosure: ((EventOrTransactionId, EditedContent) async throws -> Void)? - var editEventOrTransactionIdNewContentUnderlyingReturnValue: Bool! - open var editEventOrTransactionIdNewContentReturnValue: Bool! { - get { - if Thread.isMainThread { - return editEventOrTransactionIdNewContentUnderlyingReturnValue - } else { - var returnValue: Bool? = nil - DispatchQueue.main.sync { - returnValue = editEventOrTransactionIdNewContentUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - editEventOrTransactionIdNewContentUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - editEventOrTransactionIdNewContentUnderlyingReturnValue = newValue - } - } - } - } - open var editEventOrTransactionIdNewContentClosure: ((EventOrTransactionId, EditedContent) async throws -> Bool)? - - open override func edit(eventOrTransactionId: EventOrTransactionId, newContent: EditedContent) async throws -> Bool { + open override func edit(eventOrTransactionId: EventOrTransactionId, newContent: EditedContent) async throws { if let error = editEventOrTransactionIdNewContentThrowableError { throw error } @@ -18078,11 +18053,7 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline { DispatchQueue.main.async { self.editEventOrTransactionIdNewContentReceivedInvocations.append((eventOrTransactionId: eventOrTransactionId, newContent: newContent)) } - if let editEventOrTransactionIdNewContentClosure = editEventOrTransactionIdNewContentClosure { - return try await editEventOrTransactionIdNewContentClosure(eventOrTransactionId, newContent) - } else { - return editEventOrTransactionIdNewContentReturnValue - } + try await editEventOrTransactionIdNewContentClosure?(eventOrTransactionId, newContent) } //MARK: - endPoll @@ -20976,6 +20947,71 @@ open class UserIdentitySDKMock: MatrixRustSDK.UserIdentity { fileprivate var pointer: UnsafeMutableRawPointer! + //MARK: - isVerified + + var isVerifiedUnderlyingCallsCount = 0 + open var isVerifiedCallsCount: Int { + get { + if Thread.isMainThread { + return isVerifiedUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = isVerifiedUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + isVerifiedUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + isVerifiedUnderlyingCallsCount = newValue + } + } + } + } + open var isVerifiedCalled: Bool { + return isVerifiedCallsCount > 0 + } + + var isVerifiedUnderlyingReturnValue: Bool! + open var isVerifiedReturnValue: Bool! { + get { + if Thread.isMainThread { + return isVerifiedUnderlyingReturnValue + } else { + var returnValue: Bool? = nil + DispatchQueue.main.sync { + returnValue = isVerifiedUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + isVerifiedUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + isVerifiedUnderlyingReturnValue = newValue + } + } + } + } + open var isVerifiedClosure: (() -> Bool)? + + open override func isVerified() -> Bool { + isVerifiedCallsCount += 1 + if let isVerifiedClosure = isVerifiedClosure { + return isVerifiedClosure() + } else { + return isVerifiedReturnValue + } + } + //MARK: - masterKey var masterKeyUnderlyingCallsCount = 0 diff --git a/ElementX/Sources/Mocks/SDK/UserIdentitySDKMock.swift b/ElementX/Sources/Mocks/SDK/UserIdentitySDKMock.swift new file mode 100644 index 0000000000..885cf1a418 --- /dev/null +++ b/ElementX/Sources/Mocks/SDK/UserIdentitySDKMock.swift @@ -0,0 +1,21 @@ +// +// Copyright 2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import Foundation +import MatrixRustSDK + +extension UserIdentitySDKMock { + struct Configuration { + var isVerified = false + } + + convenience init(configuration: Configuration) { + self.init() + + isVerifiedReturnValue = configuration.isVerified + } +} diff --git a/ElementX/Sources/Other/SwiftUI/Views/AvatarHeaderView.swift b/ElementX/Sources/Other/SwiftUI/Views/AvatarHeaderView.swift index 26d8443bf6..2717af4a82 100644 --- a/ElementX/Sources/Other/SwiftUI/Views/AvatarHeaderView.swift +++ b/ElementX/Sources/Other/SwiftUI/Views/AvatarHeaderView.swift @@ -17,6 +17,7 @@ struct AvatarHeaderView: View { private enum Badge: Hashable { case encrypted(Bool) case `public` + case verified } private let avatarInfo: AvatarInfo @@ -70,6 +71,7 @@ struct AvatarHeaderView: View { } init(member: RoomMemberDetails, + isVerified: Bool = false, avatarSize: AvatarSize, mediaProvider: MediaProviderProtocol? = nil, onAvatarTap: ((URL) -> Void)? = nil, @@ -77,6 +79,7 @@ struct AvatarHeaderView: View { let profile = UserProfileProxy(member: member) self.init(user: profile, + isVerified: isVerified, avatarSize: avatarSize, mediaProvider: mediaProvider, onAvatarTap: onAvatarTap, @@ -84,6 +87,7 @@ struct AvatarHeaderView: View { } init(user: UserProfileProxy, + isVerified: Bool, avatarSize: AvatarSize, mediaProvider: MediaProviderProtocol? = nil, onAvatarTap: ((URL) -> Void)? = nil, @@ -96,27 +100,29 @@ struct AvatarHeaderView: View { self.mediaProvider = mediaProvider self.onAvatarTap = onAvatarTap self.footer = footer - badges = [] + badges = isVerified ? [.verified] : [] } private var badgesStack: some View { HStack(spacing: 8) { ForEach(badges, id: \.self) { badge in switch badge { - case .encrypted(let isEncrypted): - if isEncrypted { - BadgeLabel(title: L10n.screenRoomDetailsBadgeEncrypted, - icon: \.lockSolid, - isHighlighted: true) - } else { - BadgeLabel(title: L10n.screenRoomDetailsBadgeNotEncrypted, - icon: \.lockOff, - isHighlighted: false) - } + case .encrypted(true): + BadgeLabel(title: L10n.screenRoomDetailsBadgeEncrypted, + icon: \.lockSolid, + isHighlighted: true) + case .encrypted(false): + BadgeLabel(title: L10n.screenRoomDetailsBadgeNotEncrypted, + icon: \.lockOff, + isHighlighted: false) case .public: BadgeLabel(title: L10n.screenRoomDetailsBadgePublic, icon: \.public, isHighlighted: false) + case .verified: + BadgeLabel(title: L10n.commonVerified, + icon: \.verified, + isHighlighted: true) } } } @@ -220,6 +226,11 @@ struct AvatarHeaderView_Previews: PreviewProvider, TestablePreview { avatarSize: .room(on: .details), mediaProvider: MediaProviderMock(configuration: .init())) { Text("") } + AvatarHeaderView(member: RoomMemberDetails(withProxy: RoomMemberProxyMock.mockBob), + isVerified: true, + avatarSize: .room(on: .details), + mediaProvider: MediaProviderMock(configuration: .init())) { Text("") } + AvatarHeaderView(member: RoomMemberDetails(withProxy: RoomMemberProxyMock.mockBanned[3]), avatarSize: .room(on: .details), mediaProvider: MediaProviderMock(configuration: .init())) { Text("") } diff --git a/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenModels.swift b/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenModels.swift index 0b33b653c8..e55252e65e 100644 --- a/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenModels.swift +++ b/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenModels.swift @@ -16,11 +16,24 @@ enum RoomMemberDetailsScreenViewModelAction { struct RoomMemberDetailsScreenViewState: BindableState { let userID: String var memberDetails: RoomMemberDetails? + var isVerified: Bool? var isOwnMemberDetails = false var isProcessingIgnoreRequest = false var dmRoomID: String? var bindings: RoomMemberDetailsScreenViewStateBindings + + var showVerifiedBadge: Bool { + isVerified == true // We purposely show the badge on your own account for consistency with Web. + } + + var showVerificationSection: Bool { + isVerified == false && !isOwnMemberDetails + } + + var verifyButtonTitle: String { + L10n.screenRoomMemberDetailsVerifyButtonTitle(memberDetails?.name ?? "") + } } struct RoomMemberDetailsScreenViewStateBindings { diff --git a/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenViewModel.swift b/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenViewModel.swift index 1bd2dc81d9..178183a853 100644 --- a/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomMemberDetailsScreen/RoomMemberDetailsScreenViewModel.swift @@ -43,25 +43,8 @@ class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, Ro showMemberLoadingIndicator() Task { - defer { - hideMemberLoadingIndicator() - } - - switch await roomProxy.getMember(userID: userID) { - case .success(let member): - roomMemberProxy = member - state.memberDetails = RoomMemberDetails(withProxy: member) - state.isOwnMemberDetails = member.userID == roomProxy.ownUserID - switch await clientProxy.directRoomForUserID(member.userID) { - case .success(let roomID): - state.dmRoomID = roomID - case .failure: - break - } - case .failure(let error): - MXLog.warning("Failed to find member: \(error)") - actionsSubject.send(.openUserProfile) - } + await loadMember() + hideMemberLoadingIndicator() } } @@ -94,8 +77,37 @@ class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, Ro } // MARK: - Private - - @MainActor + + private func loadMember() async { + async let memberResult = roomProxy.getMember(userID: state.userID) + async let identityResult = clientProxy.userIdentity(for: state.userID) + + switch await memberResult { + case .success(let member): + roomMemberProxy = member + state.memberDetails = RoomMemberDetails(withProxy: member) + state.isOwnMemberDetails = member.userID == roomProxy.ownUserID + switch await clientProxy.directRoomForUserID(member.userID) { + case .success(let roomID): + state.dmRoomID = roomID + case .failure: + break + } + case .failure(let error): + MXLog.warning("Failed to find member: \(error)") + // As we didn't find a member with the specified user ID in this room we instead + // fall back to showing a generic user profile screen as the source is likely + // a message containing a permalink to someone who's not in this room. + actionsSubject.send(.openUserProfile) + } + + if case let .success(.some(identity)) = await identityResult { + state.isVerified = identity.isVerified() + } else { + MXLog.error("Failed to find the member's identity.") + } + } + private func ignoreUser() async { guard let roomMemberProxy else { fatalError() diff --git a/ElementX/Sources/Screens/RoomMemberDetailsScreen/View/RoomMemberDetailsScreen.swift b/ElementX/Sources/Screens/RoomMemberDetailsScreen/View/RoomMemberDetailsScreen.swift index 680aea69e5..0e7be2d2f8 100644 --- a/ElementX/Sources/Screens/RoomMemberDetailsScreen/View/RoomMemberDetailsScreen.swift +++ b/ElementX/Sources/Screens/RoomMemberDetailsScreen/View/RoomMemberDetailsScreen.swift @@ -15,6 +15,8 @@ struct RoomMemberDetailsScreen: View { Form { headerSection + verificationSection + if context.viewState.memberDetails != nil, !context.viewState.isOwnMemberDetails { blockUserSection } @@ -30,6 +32,25 @@ struct RoomMemberDetailsScreen: View { // MARK: - Private @ViewBuilder + private var headerSection: some View { + if let memberDetails = context.viewState.memberDetails { + AvatarHeaderView(member: memberDetails, + isVerified: context.viewState.showVerifiedBadge, + avatarSize: .user(on: .memberDetails), + mediaProvider: context.mediaProvider) { url in + context.send(viewAction: .displayAvatar(url)) + } footer: { + otherUserFooter + } + } else { + AvatarHeaderView(user: UserProfileProxy(userID: context.viewState.userID), + isVerified: context.viewState.showVerifiedBadge, + avatarSize: .user(on: .memberDetails), + mediaProvider: context.mediaProvider, + footer: { }) + } + } + private var otherUserFooter: some View { HStack(spacing: 8) { if context.viewState.memberDetails != nil, !context.viewState.isOwnMemberDetails { @@ -62,20 +83,15 @@ struct RoomMemberDetailsScreen: View { } @ViewBuilder - private var headerSection: some View { - if let memberDetails = context.viewState.memberDetails { - AvatarHeaderView(member: memberDetails, - avatarSize: .user(on: .memberDetails), - mediaProvider: context.mediaProvider) { url in - context.send(viewAction: .displayAvatar(url)) - } footer: { - otherUserFooter + var verificationSection: some View { + if context.viewState.showVerificationSection { + Section { + ListRow(label: .default(title: context.viewState.verifyButtonTitle, + description: L10n.screenRoomMemberDetailsVerifyButtonSubtitle, + icon: \.lock), + kind: .button { }) + .disabled(true) } - } else { - AvatarHeaderView(user: UserProfileProxy(userID: context.viewState.userID), - avatarSize: .user(on: .memberDetails), - mediaProvider: context.mediaProvider, - footer: { }) } } @@ -117,11 +133,15 @@ struct RoomMemberDetailsScreen: View { // MARK: - Previews struct RoomMemberDetailsScreen_Previews: PreviewProvider, TestablePreview { - static let otherUserViewModel = makeViewModel(member: .mockDan) + static let verifiedUserViewModel = makeViewModel(member: .mockDan) + static let otherUserViewModel = makeViewModel(member: .mockAlice) static let accountOwnerViewModel = makeViewModel(member: .mockMe) static let ignoredUserViewModel = makeViewModel(member: .mockIgnored) static var previews: some View { + RoomMemberDetailsScreen(context: verifiedUserViewModel.context) + .previewDisplayName("Verified User") + .snapshotPreferences(delay: 0.25) RoomMemberDetailsScreen(context: otherUserViewModel.context) .previewDisplayName("Other User") .snapshotPreferences(delay: 0.25) @@ -136,9 +156,12 @@ struct RoomMemberDetailsScreen_Previews: PreviewProvider, TestablePreview { static func makeViewModel(member: RoomMemberProxyMock) -> RoomMemberDetailsScreenViewModel { let roomProxyMock = JoinedRoomProxyMock(.init(name: "")) roomProxyMock.getMemberUserIDReturnValue = .success(member) - let clientProxyMock = ClientProxyMock(.init()) + clientProxyMock.userIdentityForClosure = { userID in + let isVerified = userID == RoomMemberProxyMock.mockDan.userID + return .success(UserIdentitySDKMock(configuration: .init(isVerified: isVerified))) + } // to avoid mock the call state for the account owner test case if member.userID != RoomMemberProxyMock.mockMe.userID { clientProxyMock.directRoomForUserIDReturnValue = .success("roomID") diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarModels.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarModels.swift index 8f5fc54e37..3459fb1c81 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarModels.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarModels.swift @@ -284,14 +284,9 @@ extension FormatType { } enum ComposerMode: Equatable { - enum EditSource { - case timeline - case draftService - } - case `default` case reply(eventID: String, replyDetails: TimelineItemReplyDetails, isThread: Bool) - case edit(originalEventOrTransactionID: EventOrTransactionId, source: EditSource) + case edit(originalEventOrTransactionID: EventOrTransactionId) case recordVoiceMessage(state: AudioRecorderState) case previewVoiceMessage(state: AudioPlayerState, waveform: WaveformSource, isUploading: Bool) diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarViewModel.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarViewModel.swift index c15a17b65b..36471da729 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarViewModel.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarViewModel.swift @@ -258,7 +258,7 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool case .newMessage: set(mode: .default) case .edit(let eventID): - set(mode: .edit(originalEventOrTransactionID: .eventId(eventId: eventID), source: .draftService)) + set(mode: .edit(originalEventOrTransactionID: .eventId(eventId: eventID))) case .reply(let eventID): set(mode: .reply(eventID: eventID, replyDetails: .loading(eventID: eventID), isThread: false)) replyLoadingTask = Task { @@ -314,7 +314,7 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool switch state.composerMode { case .default: type = .newMessage - case .edit(.eventId(let originalEventID), _): + case .edit(.eventId(let originalEventID)): type = .edit(eventID: originalEventID) case .reply(let eventID, _, _): type = .reply(eventID: eventID) diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposer.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposer.swift index 16f2c0f711..0a142b37a1 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposer.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposer.swift @@ -275,7 +275,7 @@ struct MessageComposer_Previews: PreviewProvider, TestablePreview { messageComposer() messageComposer(.init(string: "Some message"), - mode: .edit(originalEventOrTransactionID: .eventId(eventId: UUID().uuidString), source: .timeline)) + mode: .edit(originalEventOrTransactionID: .eventId(eventId: UUID().uuidString))) messageComposer(mode: .reply(eventID: UUID().uuidString, replyDetails: .loaded(sender: .init(id: "Kirk"), diff --git a/ElementX/Sources/Screens/Timeline/TimelineInteractionHandler.swift b/ElementX/Sources/Screens/Timeline/TimelineInteractionHandler.swift index 281183664f..36d3429c32 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineInteractionHandler.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineInteractionHandler.swift @@ -212,7 +212,7 @@ class TimelineInteractionHandler { } // Always update the mode first and then the text so that the composer has time to save the text draft - actionsSubject.send(.composer(action: .setMode(mode: .edit(originalEventOrTransactionID: eventOrTransactionID, source: .timeline)))) + actionsSubject.send(.composer(action: .setMode(mode: .edit(originalEventOrTransactionID: eventOrTransactionID)))) actionsSubject.send(.composer(action: .setText(plainText: text, htmlText: htmlText))) } diff --git a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift index 80c2460361..7d35cb7a73 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift @@ -595,16 +595,15 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { } actionsSubject.send(.composer(action: .clear)) - + switch mode { case .reply(let eventID, _, _): await timelineController.sendMessage(message, html: html, inReplyToEventID: eventID, intentionalMentions: intentionalMentions) - case .edit(let originalEventOrTransactionID, let source): + case .edit(let originalEventOrTransactionID): await timelineController.edit(originalEventOrTransactionID, - useTimeline: source == .timeline, message: message, html: html, intentionalMentions: intentionalMentions) diff --git a/ElementX/Sources/Screens/UserProfileScreen/UserProfileScreenModels.swift b/ElementX/Sources/Screens/UserProfileScreen/UserProfileScreenModels.swift index 4cb0647dd8..ba3ffd6e4e 100644 --- a/ElementX/Sources/Screens/UserProfileScreen/UserProfileScreenModels.swift +++ b/ElementX/Sources/Screens/UserProfileScreen/UserProfileScreenModels.swift @@ -19,10 +19,23 @@ struct UserProfileScreenViewState: BindableState { let isPresentedModally: Bool var userProfile: UserProfileProxy? + var isVerified: Bool? var permalink: URL? var dmRoomID: String? var bindings: UserProfileScreenViewStateBindings + + var showVerifiedBadge: Bool { + isVerified == true // We purposely show the badge on your own account for consistency with Web. + } + + var showVerificationSection: Bool { + isVerified == false && !isOwnUser + } + + var verifyButtonTitle: String { + L10n.screenRoomMemberDetailsVerifyButtonTitle(userProfile?.displayName ?? "") + } } struct UserProfileScreenViewStateBindings { diff --git a/ElementX/Sources/Screens/UserProfileScreen/UserProfileScreenViewModel.swift b/ElementX/Sources/Screens/UserProfileScreen/UserProfileScreenViewModel.swift index c409a72890..01a0f10337 100644 --- a/ElementX/Sources/Screens/UserProfileScreen/UserProfileScreenViewModel.swift +++ b/ElementX/Sources/Screens/UserProfileScreen/UserProfileScreenViewModel.swift @@ -42,24 +42,8 @@ class UserProfileScreenViewModel: UserProfileScreenViewModelType, UserProfileScr showLoadingIndicator(allowsInteraction: true) Task { - defer { - hideLoadingIndicator() - } - - switch await clientProxy.profile(for: userID) { - case .success(let userProfile): - state.userProfile = userProfile - state.permalink = (try? matrixToUserPermalink(userId: userID)).flatMap(URL.init(string:)) - switch await clientProxy.directRoomForUserID(userProfile.userID) { - case .success(let roomID): - state.dmRoomID = roomID - case .failure: - break - } - case .failure(let error): - state.bindings.alertInfo = .init(id: .unknown) - MXLog.error("Failed to find user profile: \(error)") - } + await loadProfile() + hideLoadingIndicator() } } @@ -87,6 +71,32 @@ class UserProfileScreenViewModel: UserProfileScreenViewModelType, UserProfileScr // MARK: - Private + private func loadProfile() async { + async let profileResult = clientProxy.profile(for: state.userID) + async let identityResult = clientProxy.userIdentity(for: state.userID) + + switch await profileResult { + case .success(let userProfile): + state.userProfile = userProfile + state.permalink = (try? matrixToUserPermalink(userId: state.userID)).flatMap(URL.init(string:)) + switch await clientProxy.directRoomForUserID(userProfile.userID) { + case .success(let roomID): + state.dmRoomID = roomID + case .failure: + break + } + case .failure(let error): + state.bindings.alertInfo = .init(id: .unknown) + MXLog.error("Failed to find user profile: \(error)") + } + + if case let .success(.some(identity)) = await identityResult { + state.isVerified = identity.isVerified() + } else { + MXLog.error("Failed to find the user's identity.") + } + } + private func displayFullScreenAvatar(_ url: URL) async { guard let userProfile = state.userProfile else { fatalError() } diff --git a/ElementX/Sources/Screens/UserProfileScreen/View/UserProfileScreen.swift b/ElementX/Sources/Screens/UserProfileScreen/View/UserProfileScreen.swift index b8d7f749a7..075b53a51c 100644 --- a/ElementX/Sources/Screens/UserProfileScreen/View/UserProfileScreen.swift +++ b/ElementX/Sources/Screens/UserProfileScreen/View/UserProfileScreen.swift @@ -14,6 +14,8 @@ struct UserProfileScreen: View { var body: some View { Form { headerSection + + verificationSection } .compoundList() .navigationTitle(L10n.screenRoomMemberDetailsTitle) @@ -27,6 +29,25 @@ struct UserProfileScreen: View { // MARK: - Private @ViewBuilder + private var headerSection: some View { + if let userProfile = context.viewState.userProfile { + AvatarHeaderView(user: userProfile, + isVerified: context.viewState.showVerifiedBadge, + avatarSize: .user(on: .memberDetails), + mediaProvider: context.mediaProvider) { url in + context.send(viewAction: .displayAvatar(url)) + } footer: { + otherUserFooter + } + } else { + AvatarHeaderView(user: UserProfileProxy(userID: context.viewState.userID), + isVerified: context.viewState.showVerifiedBadge, + avatarSize: .user(on: .memberDetails), + mediaProvider: context.mediaProvider, + footer: { }) + } + } + private var otherUserFooter: some View { HStack(spacing: 8) { if context.viewState.userProfile != nil, !context.viewState.isOwnUser { @@ -59,20 +80,15 @@ struct UserProfileScreen: View { } @ViewBuilder - private var headerSection: some View { - if let userProfile = context.viewState.userProfile { - AvatarHeaderView(user: userProfile, - avatarSize: .user(on: .memberDetails), - mediaProvider: context.mediaProvider) { url in - context.send(viewAction: .displayAvatar(url)) - } footer: { - otherUserFooter + var verificationSection: some View { + if context.viewState.showVerificationSection { + Section { + ListRow(label: .default(title: context.viewState.verifyButtonTitle, + description: L10n.screenRoomMemberDetailsVerifyButtonSubtitle, + icon: \.lock), + kind: .button { }) + .disabled(true) } - } else { - AvatarHeaderView(user: UserProfileProxy(userID: context.viewState.userID), - avatarSize: .user(on: .memberDetails), - mediaProvider: context.mediaProvider, - footer: { }) } } @@ -91,10 +107,14 @@ struct UserProfileScreen: View { // MARK: - Previews struct UserProfileScreen_Previews: PreviewProvider, TestablePreview { - static let otherUserViewModel = makeViewModel(userID: RoomMemberProxyMock.mockDan.userID) + static let verifiedUserViewModel = makeViewModel(userID: RoomMemberProxyMock.mockDan.userID) + static let otherUserViewModel = makeViewModel(userID: RoomMemberProxyMock.mockAlice.userID) static let accountOwnerViewModel = makeViewModel(userID: RoomMemberProxyMock.mockMe.userID) static var previews: some View { + UserProfileScreen(context: verifiedUserViewModel.context) + .previewDisplayName("Verified User") + .snapshotPreferences(delay: 0.25) UserProfileScreen(context: otherUserViewModel.context) .previewDisplayName("Other User") .snapshotPreferences(delay: 0.25) @@ -105,6 +125,10 @@ struct UserProfileScreen_Previews: PreviewProvider, TestablePreview { static func makeViewModel(userID: String) -> UserProfileScreenViewModel { let clientProxyMock = ClientProxyMock(.init()) + clientProxyMock.userIdentityForClosure = { userID in + let isVerified = userID == RoomMemberProxyMock.mockDan.userID + return .success(UserIdentitySDKMock(configuration: .init(isVerified: isVerified))) + } if userID != RoomMemberProxyMock.mockMe.userID { clientProxyMock.directRoomForUserIDReturnValue = .success("roomID") } diff --git a/ElementX/Sources/Services/Client/ClientProxy.swift b/ElementX/Sources/Services/Client/ClientProxy.swift index 06972a1ec7..1551008861 100644 --- a/ElementX/Sources/Services/Client/ClientProxy.swift +++ b/ElementX/Sources/Services/Client/ClientProxy.swift @@ -933,7 +933,7 @@ class ClientProxy: ClientProxyProtocol { MXLog.info("Pinning current identity for user: \(userID)") do { - guard let userIdentity = try await client.encryption().getUserIdentity(userId: userID) else { + guard let userIdentity = try await client.encryption().userIdentity(userId: userID) else { MXLog.error("Failed retrieving identity for user: \(userID)") return .failure(.failedRetrievingUserIdentity) } @@ -952,6 +952,15 @@ class ClientProxy: ClientProxyProtocol { return .failure(.sdkError(error)) } } + + func userIdentity(for userID: String) async -> Result { + do { + return try await .success(client.encryption().userIdentity(userId: userID)) + } catch { + MXLog.error("Failed retrieving user identity: \(error)") + return .failure(.sdkError(error)) + } + } } extension ClientProxy: MediaLoaderProtocol { diff --git a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift index aa7663a533..876cfcee3a 100644 --- a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift +++ b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift @@ -204,4 +204,6 @@ protocol ClientProxyProtocol: AnyObject, MediaLoaderProtocol { func pinUserIdentity(_ userID: String) async -> Result func resetIdentity() async -> Result + + func userIdentity(for userID: String) async -> Result } diff --git a/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift b/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift index c5d18c0729..e615165936 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift @@ -87,7 +87,6 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol { func toggleReaction(_ reaction: String, to eventID: EventOrTransactionId) async { } func edit(_ eventOrTransactionID: EventOrTransactionId, - useTimeline: Bool, message: String, html: String?, intentionalMentions: IntentionalMentions) async { } diff --git a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift index f9738a1735..b7e704324c 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift @@ -170,7 +170,6 @@ class RoomTimelineController: RoomTimelineControllerProtocol { } func edit(_ eventOrTransactionID: EventOrTransactionId, - useTimeline: Bool, message: String, html: String?, intentionalMentions: IntentionalMentions) async { @@ -181,25 +180,11 @@ class RoomTimelineController: RoomTimelineControllerProtocol { html: html, intentionalMentions: intentionalMentions.toRustMentions()) - if useTimeline { - switch await activeTimeline.edit(eventOrTransactionID, newContent: messageContent) { - case .success: - MXLog.info("Finished editing message by event") - case let .failure(error): - MXLog.error("Failed editing message by event with error: \(error)") - } - } else { - guard case let .eventId(eventID) = eventOrTransactionID else { - MXLog.error("Failed editing message, missing eventID.") - return - } - - switch await roomProxy.edit(eventID: eventID, newContent: messageContent) { - case .success: - MXLog.info("Finished editing message by event ID") - case let .failure(error): - MXLog.error("Failed editing message by event ID with error: \(error)") - } + switch await activeTimeline.edit(eventOrTransactionID, newContent: messageContent) { + case .success: + MXLog.info("Finished editing message by event") + case let .failure(error): + MXLog.error("Failed editing message by event with error: \(error)") } } diff --git a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift index 4b47bacf44..125454d3cc 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift @@ -52,7 +52,6 @@ protocol RoomTimelineControllerProtocol { intentionalMentions: IntentionalMentions) async func edit(_ eventOrTransactionID: EventOrTransactionId, - useTimeline: Bool, message: String, html: String?, intentionalMentions: IntentionalMentions) async diff --git a/ElementX/Sources/Services/Timeline/TimelineProxy.swift b/ElementX/Sources/Services/Timeline/TimelineProxy.swift index 160eff7911..659a12d1ea 100644 --- a/ElementX/Sources/Services/Timeline/TimelineProxy.swift +++ b/ElementX/Sources/Services/Timeline/TimelineProxy.swift @@ -166,12 +166,10 @@ final class TimelineProxy: TimelineProxyProtocol { func edit(_ eventOrTransactionID: EventOrTransactionId, newContent: RoomMessageEventContentWithoutRelation) async -> Result { do { - guard try await timeline.edit(eventOrTransactionId: eventOrTransactionID, newContent: .roomMessage(content: newContent)) == true else { - return .failure(.failedEditing) - } + try await timeline.edit(eventOrTransactionId: eventOrTransactionID, newContent: .roomMessage(content: newContent)) MXLog.info("Finished editing timeline item: \(eventOrTransactionID)") - + return .success(()) } catch { MXLog.error("Failed editing timeline item: \(eventOrTransactionID) with error: \(error)") @@ -460,13 +458,11 @@ final class TimelineProxy: TimelineProxyProtocol { do { let originalEvent = try await timeline.getEventTimelineItemByEventId(eventId: eventID) - guard try await timeline.edit(eventOrTransactionId: originalEvent.eventOrTransactionId, - newContent: .pollStart(pollData: .init(question: question, - answers: answers, - maxSelections: 1, - pollKind: .init(pollKind: pollKind)))) else { - return .failure(.failedEditing) - } + try await timeline.edit(eventOrTransactionId: originalEvent.eventOrTransactionId, + newContent: .pollStart(pollData: .init(question: question, + answers: answers, + maxSelections: 1, + pollKind: .init(pollKind: pollKind)))) MXLog.info("Finished editing poll with eventID: \(eventID)") diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPad-en-GB.Members.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPad-en-GB.Members.png index d4a360cab6..eb6d007ce4 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPad-en-GB.Members.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPad-en-GB.Members.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b32488a5a7c90b0c4b750d8cd822153782fa8493ac84d54af3496086a856d530 -size 65677 +oid sha256:347ca4459368e8a76e3e5952ef0cdbbb76c89e099cd48f726a11bfa341b32c57 +size 106299 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPad-pseudo.Members.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPad-pseudo.Members.png index d4a360cab6..cd747a2b50 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPad-pseudo.Members.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPad-pseudo.Members.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b32488a5a7c90b0c4b750d8cd822153782fa8493ac84d54af3496086a856d530 -size 65677 +oid sha256:80805199a27b76e9e958197da199105217920310803d839e1347463e0f48e690 +size 106516 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPhone-16-en-GB.Members.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPhone-16-en-GB.Members.png index 932e29de00..65489b2f14 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPhone-16-en-GB.Members.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPhone-16-en-GB.Members.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7dbc686c75e95b8b0fbabad6f707d23bb5f0f5a6f37db69a98fd7a0138b4d0f3 -size 39633 +oid sha256:c039a8c3cb9967aa355ef8472e30469e053a88f08b232df77bc1e1129ff58d60 +size 64744 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPhone-16-pseudo.Members.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPhone-16-pseudo.Members.png index 932e29de00..4057f2f60c 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPhone-16-pseudo.Members.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_avatarHeaderView-iPhone-16-pseudo.Members.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7dbc686c75e95b8b0fbabad6f707d23bb5f0f5a6f37db69a98fd7a0138b4d0f3 -size 39633 +oid sha256:246a69ad32fdbd7a2cae5851b9152d21cdf9263b0746cdbb9c3f2cbf38edd9cc +size 64987 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Ignored-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Ignored-User.png index 6b79b99a5f..d89dcfc938 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Ignored-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Ignored-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:51a163146eacfa1147d3ca6b08a6f713c1d6e466ce2f990b7339a88edadc4bb9 -size 109846 +oid sha256:c4a9d05d2aea48c83dd1f0891eccbdc2d99a9b38176971efd097597e69fda28f +size 121678 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Other-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Other-User.png index 193397aad6..c820c177a8 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Other-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Other-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:057db6fe0f5d5a95482906bbe069f25ceeab1c8f5cc173f3c94014ac84fd828e -size 147921 +oid sha256:3f2806abf81f7d716dd4a740bde06ca6e7d488db513f42bcb70ca4ef8a5c2505 +size 120435 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Verified-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Verified-User.png new file mode 100644 index 0000000000..a4b9b6b419 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-en-GB.Verified-User.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bf3638bf8eabd4c30cf9cc67bd55a71d3d7968dba02913b6a1c285b66abe86ca +size 151472 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Ignored-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Ignored-User.png index 9ce01526b5..1775940a9a 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Ignored-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Ignored-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2abc9dd95da1d42cc4e20163504253e48079a96253f0f6b020c9214cab3fc177 -size 111394 +oid sha256:b9d5ad23bdf44dbe689d44c03ad2a6f12e993bcf6d8245d614b3d9e3b004b8b9 +size 127141 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Other-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Other-User.png index 61ffbcf25a..e2b6c81cd0 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Other-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Other-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce62f3154f37ec185e2323765a4191c6afe165fbb054f7638ed59d8617fff3e8 -size 149231 +oid sha256:1dcb000d6d4caae5edbfef5e9007d327b72332708a98fc1df54a35622f8eb220 +size 125657 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Verified-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Verified-User.png new file mode 100644 index 0000000000..b954c48cde --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPad-pseudo.Verified-User.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0d0880d9fa7868f87803bf1a212194a7ca365ed7f10e85ab71561c19f735b121 +size 152996 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Ignored-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Ignored-User.png index d93686c93d..a2845556dd 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Ignored-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Ignored-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9cf7ca6c0fa48a76bca67226963823759bd14dc871dea6a5eb373533d7d6b0e5 -size 61377 +oid sha256:baa7127ec68c63490624fba7d6204063cdf950e941a55066291766e887c2281c +size 71929 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Other-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Other-User.png index 41c5c0a1a3..858e2bfb03 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Other-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Other-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a8128c33e14930422fd2047b329c2a496a2d561a45d4bbafc674013d57c6716 -size 93275 +oid sha256:de7639ceab51d7eb8306c7df3a9a6899c1e78a120e22358908f95f9791cb417f +size 70569 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Verified-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Verified-User.png new file mode 100644 index 0000000000..ec0c34d4ca --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-en-GB.Verified-User.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7b065c8fb99d1a1f5045db9cc52b328d1f2a48c3c062349c2d0348a28eec9640 +size 96032 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Ignored-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Ignored-User.png index 0e6d76799a..5920bc5e6a 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Ignored-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Ignored-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9d16eb4167974cc8231caaf02a91151eb22eab006953e0e43d8da3f047e32652 -size 68228 +oid sha256:d624776c5e6a14ddb5c21e7349a9e2b29499d45137c4dbe906f3ad1014d9c6d2 +size 84082 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Other-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Other-User.png index b0a0da8f1a..d1c0729d73 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Other-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Other-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:35e4e002682e32a283edf58eb593463813b7a78d24bb17635eb07ce6d0365698 -size 100293 +oid sha256:ddcf366a4fea427cd38681cff49842d7f5bc73ebc218efbe7ea9bc5914ae7b62 +size 82982 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Verified-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Verified-User.png new file mode 100644 index 0000000000..c9274153cd --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomMemberDetailsScreen-iPhone-16-pseudo.Verified-User.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:abbeec988102eb396909234e06388c1ab21c6f61875789722ab85a29cca1cc6a +size 104122 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-en-GB.Other-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-en-GB.Other-User.png index 718f7844e8..4ca05e10c7 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-en-GB.Other-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-en-GB.Other-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6a5d377d251dfe4cd7303973b586786db8a1185bd11061c31d78c5ab910a11c5 -size 101504 +oid sha256:d5995f233036158f3adaff3e86899e86ccace5c6b6991626c78e1514eb63c5d5 +size 115587 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-en-GB.Verified-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-en-GB.Verified-User.png new file mode 100644 index 0000000000..ca8377be1a --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-en-GB.Verified-User.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8cfc307f7565b4f1e5cdef15055090205d69dabf36b19b3f538a331444e1cede +size 104896 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-pseudo.Other-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-pseudo.Other-User.png index 1c291cf490..c750e0cb89 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-pseudo.Other-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-pseudo.Other-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:903d91265fa95a4ad40c4131a90680e805c895e1f57b846838b317200b5e14cd -size 102481 +oid sha256:207bad6ef21783ebcc7c8189af09c233d191b01ddf7dc2fd92d3bbd1df1dd2ae +size 120261 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-pseudo.Verified-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-pseudo.Verified-User.png new file mode 100644 index 0000000000..a4c0e35fb1 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPad-pseudo.Verified-User.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:71303b4e0a9a1757e7658b0490a90d1cf438aade4466402e8777de94afa6d905 +size 106067 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-en-GB.Other-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-en-GB.Other-User.png index f359d9eb43..892b09ce16 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-en-GB.Other-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-en-GB.Other-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d2733defffabec358c0d86d9cbade1aba498c312db2c7f93eb045085d8ebb83c -size 53177 +oid sha256:5bd27ed946027cecc6286c1b4a0212b92addba260e7a17a650bb1f8a00ac0df9 +size 65717 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-en-GB.Verified-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-en-GB.Verified-User.png new file mode 100644 index 0000000000..cc46530ed3 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-en-GB.Verified-User.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4b4e47701bd9f9b3829f2d5a131aba025ab90a6c0a4b6e06ccfabcbf73e87c87 +size 55937 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-pseudo.Other-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-pseudo.Other-User.png index c78221e885..3b7dc05d9c 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-pseudo.Other-User.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-pseudo.Other-User.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5ce7e2162f0210a0d8ed409e089f7ab5dd62427c45728f5b543c24a5d70f7c6b -size 58590 +oid sha256:a5dca5794cc839ed0d28f7868e044ef5aba075e5e44e0b3e43c9b70df7e642b8 +size 78100 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-pseudo.Verified-User.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-pseudo.Verified-User.png new file mode 100644 index 0000000000..41c40c9880 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/test_userProfileScreen-iPhone-16-pseudo.Verified-User.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d0412eab0f79b3baa004db621bf76b824c6817c2b90b7d7f8f4ab18f94bb2315 +size 62260 diff --git a/UnitTests/Sources/ComposerToolbarViewModelTests.swift b/UnitTests/Sources/ComposerToolbarViewModelTests.swift index 49aa528bfa..9779ae8bf0 100644 --- a/UnitTests/Sources/ComposerToolbarViewModelTests.swift +++ b/UnitTests/Sources/ComposerToolbarViewModelTests.swift @@ -41,14 +41,14 @@ class ComposerToolbarViewModelTests: XCTestCase { } func testComposerFocus() { - viewModel.process(timelineAction: .setMode(mode: .edit(originalEventOrTransactionID: .eventId(eventId: "mock"), source: .timeline))) + viewModel.process(timelineAction: .setMode(mode: .edit(originalEventOrTransactionID: .eventId(eventId: "mock")))) XCTAssertTrue(viewModel.state.bindings.composerFocused) viewModel.process(timelineAction: .removeFocus) XCTAssertFalse(viewModel.state.bindings.composerFocused) } func testComposerMode() { - let mode: ComposerMode = .edit(originalEventOrTransactionID: .eventId(eventId: "mock"), source: .timeline) + let mode: ComposerMode = .edit(originalEventOrTransactionID: .eventId(eventId: "mock")) viewModel.process(timelineAction: .setMode(mode: mode)) XCTAssertEqual(viewModel.state.composerMode, mode) viewModel.process(timelineAction: .clear) @@ -56,7 +56,7 @@ class ComposerToolbarViewModelTests: XCTestCase { } func testComposerModeIsPublished() { - let mode: ComposerMode = .edit(originalEventOrTransactionID: .eventId(eventId: "mock"), source: .timeline) + let mode: ComposerMode = .edit(originalEventOrTransactionID: .eventId(eventId: "mock")) let expectation = expectation(description: "Composer mode is published") let cancellable = viewModel .context @@ -236,7 +236,7 @@ class ComposerToolbarViewModelTests: XCTestCase { } viewModel.context.composerFormattingEnabled = false - viewModel.process(timelineAction: .setMode(mode: .edit(originalEventOrTransactionID: .eventId(eventId: "testID"), source: .timeline))) + viewModel.process(timelineAction: .setMode(mode: .edit(originalEventOrTransactionID: .eventId(eventId: "testID")))) viewModel.context.plainComposerText = .init(string: "Hello world!") viewModel.saveDraft() @@ -395,7 +395,7 @@ class ComposerToolbarViewModelTests: XCTestCase { await fulfillment(of: [expectation], timeout: 10) XCTAssertFalse(viewModel.context.composerFormattingEnabled) - XCTAssertEqual(viewModel.state.composerMode, .edit(originalEventOrTransactionID: .eventId(eventId: "testID"), source: .draftService)) + XCTAssertEqual(viewModel.state.composerMode, .edit(originalEventOrTransactionID: .eventId(eventId: "testID"))) XCTAssertEqual(viewModel.context.plainComposerText, NSAttributedString(string: "Hello world!")) } @@ -483,7 +483,7 @@ class ComposerToolbarViewModelTests: XCTestCase { func testSaveVolatileDraftWhenEditing() { viewModel.context.composerFormattingEnabled = false viewModel.context.plainComposerText = .init(string: "Hello world!") - viewModel.process(timelineAction: .setMode(mode: .edit(originalEventOrTransactionID: .eventId(eventId: UUID().uuidString), source: .timeline))) + viewModel.process(timelineAction: .setMode(mode: .edit(originalEventOrTransactionID: .eventId(eventId: UUID().uuidString)))) let draft = draftServiceMock.saveVolatileDraftReceivedDraft XCTAssertNotNil(draft) diff --git a/project.yml b/project.yml index f7fde5a70e..adbf58d8a6 100644 --- a/project.yml +++ b/project.yml @@ -60,7 +60,7 @@ packages: # Element/Matrix dependencies MatrixRustSDK: url: https://github.com/element-hq/matrix-rust-components-swift - exactVersion: 1.0.58 + exactVersion: 1.0.59 # path: ../matrix-rust-sdk Compound: url: https://github.com/element-hq/compound-ios