Rename UserProfileProxy to UserProfile. It's a pure struct and proxies nothing.

Also remove the redundant `userID` property given `id` already exists (for Identifiable conformance).
This commit is contained in:
Doug
2026-07-01 15:18:03 +01:00
committed by Doug
parent bdf53b64a8
commit 3ef66cdd05
55 changed files with 203 additions and 209 deletions
+4 -4
View File
@@ -100,6 +100,7 @@
0DC815CA24E1BD7F408F37D3 /* CollapsibleTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7C4EA55DA62F9D0F984A2AE /* CollapsibleTimelineItem.swift */; }; 0DC815CA24E1BD7F408F37D3 /* CollapsibleTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7C4EA55DA62F9D0F984A2AE /* CollapsibleTimelineItem.swift */; };
0DFCEAB8C82B151718F71D49 /* FrequentlyUsedEmoji.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A2074C0449B83D5858BD2D7 /* FrequentlyUsedEmoji.swift */; }; 0DFCEAB8C82B151718F71D49 /* FrequentlyUsedEmoji.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A2074C0449B83D5858BD2D7 /* FrequentlyUsedEmoji.swift */; };
0E08BB72B2258652CF501A8B /* Version in Frameworks */ = {isa = PBXBuildFile; productRef = 2B9ACE4FCACB5A8812154424 /* Version */; }; 0E08BB72B2258652CF501A8B /* Version in Frameworks */ = {isa = PBXBuildFile; productRef = 2B9ACE4FCACB5A8812154424 /* Version */; };
0E6B97E4F4DAC3E5B64B215F /* UserProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66653C5417592A727B549533 /* UserProfile.swift */; };
0E8C480700870BB34A2A360F /* DeviceKit in Frameworks */ = {isa = PBXBuildFile; productRef = 4003BC24B24C9E63D3304177 /* DeviceKit */; }; 0E8C480700870BB34A2A360F /* DeviceKit in Frameworks */ = {isa = PBXBuildFile; productRef = 4003BC24B24C9E63D3304177 /* DeviceKit */; };
0EA6537A07E2DC882AEA5962 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 187853A7E643995EE49FAD43 /* Localizable.stringsdict */; }; 0EA6537A07E2DC882AEA5962 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 187853A7E643995EE49FAD43 /* Localizable.stringsdict */; };
0EBF0A411E1C46D4BFA2B087 /* ManageAuthorizedSpacesScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB9CD3A0A39BC063438E12D4 /* ManageAuthorizedSpacesScreenCoordinator.swift */; }; 0EBF0A411E1C46D4BFA2B087 /* ManageAuthorizedSpacesScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB9CD3A0A39BC063438E12D4 /* ManageAuthorizedSpacesScreenCoordinator.swift */; };
@@ -1353,7 +1354,6 @@
E1C67E5D9E22135A8FEBBD60 /* StackedAvatarsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8558D41DD4B553A752C868A /* StackedAvatarsView.swift */; }; E1C67E5D9E22135A8FEBBD60 /* StackedAvatarsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8558D41DD4B553A752C868A /* StackedAvatarsView.swift */; };
E1DE8A5422643F759385290F /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BD5523BDEDB247E29228476 /* AppSettings.swift */; }; E1DE8A5422643F759385290F /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BD5523BDEDB247E29228476 /* AppSettings.swift */; };
E1DF24D085572A55C9758A2D /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6E89E530A8E92EC44301CA1 /* Bundle.swift */; }; E1DF24D085572A55C9758A2D /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6E89E530A8E92EC44301CA1 /* Bundle.swift */; };
E21FE4C5B614F311C0955859 /* UserProfileProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C454AE59914B551A6D02C0 /* UserProfileProxy.swift */; };
E26E702F325F0AA49B8A5CFD /* RoomPowerLevelsProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E944F717FC10A428D027074D /* RoomPowerLevelsProxyMock.swift */; }; E26E702F325F0AA49B8A5CFD /* RoomPowerLevelsProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E944F717FC10A428D027074D /* RoomPowerLevelsProxyMock.swift */; };
E27C4D1A1F8BB77CA790B403 /* InviteUsersScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A861DA5932B128FE1DCB5CE2 /* InviteUsersScreenCoordinator.swift */; }; E27C4D1A1F8BB77CA790B403 /* InviteUsersScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A861DA5932B128FE1DCB5CE2 /* InviteUsersScreenCoordinator.swift */; };
E290C78E7F09F47FD2662986 /* Task.swift in Sources */ = {isa = PBXBuildFile; fileRef = A40C19719687984FD9478FBE /* Task.swift */; }; E290C78E7F09F47FD2662986 /* Task.swift in Sources */ = {isa = PBXBuildFile; fileRef = A40C19719687984FD9478FBE /* Task.swift */; };
@@ -2114,7 +2114,6 @@
510E89B989477E5EE8E503C0 /* PinnedEventsTimelineScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinnedEventsTimelineScreenViewModelProtocol.swift; sourceTree = "<group>"; }; 510E89B989477E5EE8E503C0 /* PinnedEventsTimelineScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinnedEventsTimelineScreenViewModelProtocol.swift; sourceTree = "<group>"; };
514363244AE7D68080D44C6F /* NotificationSettingsScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsScreenViewModelTests.swift; sourceTree = "<group>"; }; 514363244AE7D68080D44C6F /* NotificationSettingsScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsScreenViewModelTests.swift; sourceTree = "<group>"; };
51C2BCE0BC1FC69C1B36E688 /* BugReportScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportScreenModels.swift; sourceTree = "<group>"; }; 51C2BCE0BC1FC69C1B36E688 /* BugReportScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportScreenModels.swift; sourceTree = "<group>"; };
51C454AE59914B551A6D02C0 /* UserProfileProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfileProxy.swift; sourceTree = "<group>"; };
51F14C0F5FD4A1814D5527E6 /* fa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fa; path = fa.lproj/InfoPlist.strings; sourceTree = "<group>"; }; 51F14C0F5FD4A1814D5527E6 /* fa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fa; path = fa.lproj/InfoPlist.strings; sourceTree = "<group>"; };
52135BD9E0E7A091688F627A /* MessageForwardingScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageForwardingScreenModels.swift; sourceTree = "<group>"; }; 52135BD9E0E7A091688F627A /* MessageForwardingScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageForwardingScreenModels.swift; sourceTree = "<group>"; };
5221DFDF809142A2D6AC82B9 /* RoomScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreen.swift; sourceTree = "<group>"; }; 5221DFDF809142A2D6AC82B9 /* RoomScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreen.swift; sourceTree = "<group>"; };
@@ -2222,6 +2221,7 @@
664ABD745A746C45CB842158 /* CallInviteRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallInviteRoomTimelineView.swift; sourceTree = "<group>"; }; 664ABD745A746C45CB842158 /* CallInviteRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallInviteRoomTimelineView.swift; sourceTree = "<group>"; };
664D3C710B4A12CE4E623645 /* DeactivateAccountScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeactivateAccountScreenModels.swift; sourceTree = "<group>"; }; 664D3C710B4A12CE4E623645 /* DeactivateAccountScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeactivateAccountScreenModels.swift; sourceTree = "<group>"; };
6654859746B0BE9611459391 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = cs; path = cs.lproj/Localizable.stringsdict; sourceTree = "<group>"; }; 6654859746B0BE9611459391 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = cs; path = cs.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
66653C5417592A727B549533 /* UserProfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfile.swift; sourceTree = "<group>"; };
667DD3A9D932D7D9EB380CAA /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = sk; path = sk.lproj/Localizable.stringsdict; sourceTree = "<group>"; }; 667DD3A9D932D7D9EB380CAA /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = sk; path = sk.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
6695C64F066628411EAD21E9 /* RoomPreviewProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPreviewProxyMock.swift; sourceTree = "<group>"; }; 6695C64F066628411EAD21E9 /* RoomPreviewProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPreviewProxyMock.swift; sourceTree = "<group>"; };
669F35C505ACE1110589F875 /* MediaUploadingPreprocessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaUploadingPreprocessor.swift; sourceTree = "<group>"; }; 669F35C505ACE1110589F875 /* MediaUploadingPreprocessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaUploadingPreprocessor.swift; sourceTree = "<group>"; };
@@ -6740,7 +6740,7 @@
7F615A00DB223FF3280204D2 /* UserDiscoveryServiceProtocol.swift */, 7F615A00DB223FF3280204D2 /* UserDiscoveryServiceProtocol.swift */,
964093C7CA8823CAB7FFD88E /* UserIdentityProxy.swift */, 964093C7CA8823CAB7FFD88E /* UserIdentityProxy.swift */,
0F793C422BDACE0C60C774F4 /* UserIdentityProxyProtocol.swift */, 0F793C422BDACE0C60C774F4 /* UserIdentityProxyProtocol.swift */,
51C454AE59914B551A6D02C0 /* UserProfileProxy.swift */, 66653C5417592A727B549533 /* UserProfile.swift */,
); );
path = Users; path = Users;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -9182,8 +9182,8 @@
A8DB299C5C891EDAE74A22F4 /* UserLocationCell.swift in Sources */, A8DB299C5C891EDAE74A22F4 /* UserLocationCell.swift in Sources */,
E3291AD16D7A5CB14781819C /* UserNotificationCenterProtocol.swift in Sources */, E3291AD16D7A5CB14781819C /* UserNotificationCenterProtocol.swift in Sources */,
80DEA2A4B20F9E279EAE6B2B /* UserProfile+Mock.swift in Sources */, 80DEA2A4B20F9E279EAE6B2B /* UserProfile+Mock.swift in Sources */,
0E6B97E4F4DAC3E5B64B215F /* UserProfile.swift in Sources */,
ED90A59F068FD0CA27E602ED /* UserProfileListRow.swift in Sources */, ED90A59F068FD0CA27E602ED /* UserProfileListRow.swift in Sources */,
E21FE4C5B614F311C0955859 /* UserProfileProxy.swift in Sources */,
1A3B073568D1DC8F76F1F3A0 /* UserProfileScreen.swift in Sources */, 1A3B073568D1DC8F76F1F3A0 /* UserProfileScreen.swift in Sources */,
6AEB650311F694A5702255C9 /* UserProfileScreenCoordinator.swift in Sources */, 6AEB650311F694A5702255C9 /* UserProfileScreenCoordinator.swift in Sources */,
D4CB979EB4FE26AAD9F9A72B /* UserProfileScreenModels.swift in Sources */, D4CB979EB4FE26AAD9F9A72B /* UserProfileScreenModels.swift in Sources */,
@@ -64,7 +64,7 @@ enum InviteUsersFlow: Hashable {
/// Invite people into the current room. /// Invite people into the current room.
case existingRoom case existingRoom
/// Start a new room with the given invitee pre-selected and mandatory. /// Start a new room with the given invitee pre-selected and mandatory.
case newRoom(mandatoryInvitee: UserProfileProxy) case newRoom(mandatoryInvitee: UserProfile)
} }
// swiftlint:disable:next type_body_length // swiftlint:disable:next type_body_length
@@ -405,7 +405,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
MXLog.info("Received session verification request") MXLog.info("Received session verification request")
if details.senderProfile.userID == userSession.clientProxy.userID { if details.senderProfile.id == userSession.clientProxy.userID {
presentSessionVerificationScreen(flow: .deviceResponder(requestDetails: details)) presentSessionVerificationScreen(flow: .deviceResponder(requestDetails: details))
} else { } else {
presentSessionVerificationScreen(flow: .userResponder(requestDetails: details)) presentSessionVerificationScreen(flow: .userResponder(requestDetails: details))
@@ -3533,14 +3533,14 @@ nonisolated class ClientProxyMock: ClientProxyProtocol, @unchecked Sendable {
} }
private let profileForReturnValueLock = NSLock() private let profileForReturnValueLock = NSLock()
private nonisolated(unsafe) var profileForUnderlyingReturnValue: Result<UserProfileProxy, ClientProxyError>! private nonisolated(unsafe) var profileForUnderlyingReturnValue: Result<UserProfile, ClientProxyError>!
var profileForReturnValue: Result<UserProfileProxy, ClientProxyError>! { var profileForReturnValue: Result<UserProfile, ClientProxyError>! {
get { profileForReturnValueLock.withLock { profileForUnderlyingReturnValue } } get { profileForReturnValueLock.withLock { profileForUnderlyingReturnValue } }
set { profileForReturnValueLock.withLock { profileForUnderlyingReturnValue = newValue } } set { profileForReturnValueLock.withLock { profileForUnderlyingReturnValue = newValue } }
} }
nonisolated(unsafe) var profileForClosure: ((String) async -> Result<UserProfileProxy, ClientProxyError>)? nonisolated(unsafe) var profileForClosure: ((String) async -> Result<UserProfile, ClientProxyError>)?
@concurrent func profile(for userID: String) async -> Result<UserProfileProxy, ClientProxyError> { @concurrent func profile(for userID: String) async -> Result<UserProfile, ClientProxyError> {
profileForCallsCountLock.withLock { profileForUnderlyingCallsCount += 1 } profileForCallsCountLock.withLock { profileForUnderlyingCallsCount += 1 }
profileForReceivedUserID = userID profileForReceivedUserID = userID
profileForReceivedInvocationsLock.withLock { profileForUnderlyingReceivedInvocations.append(userID) } profileForReceivedInvocationsLock.withLock { profileForUnderlyingReceivedInvocations.append(userID) }
@@ -3972,14 +3972,14 @@ nonisolated class ClientProxyMock: ClientProxyProtocol, @unchecked Sendable {
} }
private let recentConversationCounterpartsReturnValueLock = NSLock() private let recentConversationCounterpartsReturnValueLock = NSLock()
private nonisolated(unsafe) var recentConversationCounterpartsUnderlyingReturnValue: [UserProfileProxy]! private nonisolated(unsafe) var recentConversationCounterpartsUnderlyingReturnValue: [UserProfile]!
var recentConversationCounterpartsReturnValue: [UserProfileProxy]! { var recentConversationCounterpartsReturnValue: [UserProfile]! {
get { recentConversationCounterpartsReturnValueLock.withLock { recentConversationCounterpartsUnderlyingReturnValue } } get { recentConversationCounterpartsReturnValueLock.withLock { recentConversationCounterpartsUnderlyingReturnValue } }
set { recentConversationCounterpartsReturnValueLock.withLock { recentConversationCounterpartsUnderlyingReturnValue = newValue } } set { recentConversationCounterpartsReturnValueLock.withLock { recentConversationCounterpartsUnderlyingReturnValue = newValue } }
} }
nonisolated(unsafe) var recentConversationCounterpartsClosure: (() async -> [UserProfileProxy])? nonisolated(unsafe) var recentConversationCounterpartsClosure: (() async -> [UserProfile])?
@concurrent func recentConversationCounterparts() async -> [UserProfileProxy] { @concurrent func recentConversationCounterparts() async -> [UserProfile] {
recentConversationCounterpartsCallsCountLock.withLock { recentConversationCounterpartsUnderlyingCallsCount += 1 } recentConversationCounterpartsCallsCountLock.withLock { recentConversationCounterpartsUnderlyingCallsCount += 1 }
if let recentConversationCounterpartsClosure = recentConversationCounterpartsClosure { if let recentConversationCounterpartsClosure = recentConversationCounterpartsClosure {
return await recentConversationCounterpartsClosure() return await recentConversationCounterpartsClosure()
@@ -14057,14 +14057,14 @@ nonisolated class UserDiscoveryServiceMock: UserDiscoveryServiceProtocol, @unche
} }
private let searchProfilesWithReturnValueLock = NSLock() private let searchProfilesWithReturnValueLock = NSLock()
private nonisolated(unsafe) var searchProfilesWithUnderlyingReturnValue: Result<[UserProfileProxy], UserDiscoveryErrorType>! private nonisolated(unsafe) var searchProfilesWithUnderlyingReturnValue: Result<[UserProfile], UserDiscoveryErrorType>!
var searchProfilesWithReturnValue: Result<[UserProfileProxy], UserDiscoveryErrorType>! { var searchProfilesWithReturnValue: Result<[UserProfile], UserDiscoveryErrorType>! {
get { searchProfilesWithReturnValueLock.withLock { searchProfilesWithUnderlyingReturnValue } } get { searchProfilesWithReturnValueLock.withLock { searchProfilesWithUnderlyingReturnValue } }
set { searchProfilesWithReturnValueLock.withLock { searchProfilesWithUnderlyingReturnValue = newValue } } set { searchProfilesWithReturnValueLock.withLock { searchProfilesWithUnderlyingReturnValue = newValue } }
} }
nonisolated(unsafe) var searchProfilesWithClosure: ((String) async -> Result<[UserProfileProxy], UserDiscoveryErrorType>)? nonisolated(unsafe) var searchProfilesWithClosure: ((String) async -> Result<[UserProfile], UserDiscoveryErrorType>)?
@concurrent func searchProfiles(with searchQuery: String) async -> Result<[UserProfileProxy], UserDiscoveryErrorType> { @concurrent func searchProfiles(with searchQuery: String) async -> Result<[UserProfile], UserDiscoveryErrorType> {
searchProfilesWithCallsCountLock.withLock { searchProfilesWithUnderlyingCallsCount += 1 } searchProfilesWithCallsCountLock.withLock { searchProfilesWithUnderlyingCallsCount += 1 }
searchProfilesWithReceivedSearchQuery = searchQuery searchProfilesWithReceivedSearchQuery = searchQuery
searchProfilesWithReceivedInvocationsLock.withLock { searchProfilesWithUnderlyingReceivedInvocations.append(searchQuery) } searchProfilesWithReceivedInvocationsLock.withLock { searchProfilesWithUnderlyingReceivedInvocations.append(searchQuery) }
@@ -8,29 +8,29 @@
import Foundation import Foundation
extension UserProfileProxy { extension UserProfile {
/// Mocks /// Mocks
static var mockAlice: UserProfileProxy { static var mockAlice: UserProfile {
.init(userID: "@alice:matrix.org", displayName: "Alice", avatarURL: "mxc://matrix.org/UcCimidcvpFvWkPzvjXMQPHA") .init(userID: "@alice:matrix.org", displayName: "Alice", avatarURL: "mxc://matrix.org/UcCimidcvpFvWkPzvjXMQPHA")
} }
static var mockBob: UserProfileProxy { static var mockBob: UserProfile {
.init(userID: "@bob:matrix.org", displayName: "Bob", avatarURL: nil) .init(userID: "@bob:matrix.org", displayName: "Bob", avatarURL: nil)
} }
static var mockBobby: UserProfileProxy { static var mockBobby: UserProfile {
.init(userID: "@bobby:matrix.org", displayName: "Bobby", avatarURL: nil) .init(userID: "@bobby:matrix.org", displayName: "Bobby", avatarURL: nil)
} }
static var mockCharlie: UserProfileProxy { static var mockCharlie: UserProfile {
.init(userID: "@charlie:matrix.org", displayName: "Charlie", avatarURL: nil) .init(userID: "@charlie:matrix.org", displayName: "Charlie", avatarURL: nil)
} }
static var mockDan: UserProfileProxy { static var mockDan: UserProfile {
.init(userID: "@dan:matrix.org", displayName: "Dan", avatarURL: .mockMXCUserAvatar) .init(userID: "@dan:matrix.org", displayName: "Dan", avatarURL: .mockMXCUserAvatar)
} }
static var mockVerbose: UserProfileProxy { static var mockVerbose: UserProfile {
.init(userID: "@charlie:matrix.org", displayName: "Charlie is the best display name", avatarURL: nil) .init(userID: "@charlie:matrix.org", displayName: "Charlie is the best display name", avatarURL: nil)
} }
} }
@@ -13,7 +13,7 @@ import SwiftUI
struct AvatarHeaderView<Footer: View>: View { struct AvatarHeaderView<Footer: View>: View {
private enum AvatarInfo { private enum AvatarInfo {
case room(RoomAvatar) case room(RoomAvatar)
case user(UserProfileProxy) case user(UserProfile)
} }
private enum Badge: Hashable { private enum Badge: Hashable {
@@ -44,7 +44,7 @@ struct AvatarHeaderView<Footer: View>: View {
if let roomAlias = room.canonicalAlias { if let roomAlias = room.canonicalAlias {
subtitle = roomAlias subtitle = roomAlias
} else if room.isDirect, case let .heroes(heroes) = room.avatar, heroes.count == 1 { } else if room.isDirect, case let .heroes(heroes) = room.avatar, heroes.count == 1 {
subtitle = heroes[0].userID subtitle = heroes[0].id
} else { } else {
subtitle = nil subtitle = nil
} }
@@ -70,10 +70,10 @@ struct AvatarHeaderView<Footer: View>: View {
mediaProvider: MediaProviderProtocol? = nil, mediaProvider: MediaProviderProtocol? = nil,
onAvatarTap: ((URL) -> Void)? = nil, onAvatarTap: ((URL) -> Void)? = nil,
@ViewBuilder footer: @escaping () -> Footer) { @ViewBuilder footer: @escaping () -> Footer) {
let dmRecipientProfile = UserProfileProxy(member: dmRecipient) let dmRecipientProfile = UserProfile(member: dmRecipient)
avatarInfo = .room(.heroes([dmRecipientProfile, UserProfileProxy(member: accountOwner)])) avatarInfo = .room(.heroes([dmRecipientProfile, UserProfile(member: accountOwner)]))
title = dmRecipientProfile.displayName ?? dmRecipientProfile.userID title = dmRecipientProfile.displayName ?? dmRecipientProfile.id
subtitle = dmRecipientProfile.displayName == nil ? nil : dmRecipientProfile.userID subtitle = dmRecipientProfile.displayName == nil ? nil : dmRecipientProfile.id
avatarSize = .user(on: .dmDetails) avatarSize = .user(on: .dmDetails)
self.mediaProvider = mediaProvider self.mediaProvider = mediaProvider
@@ -89,7 +89,7 @@ struct AvatarHeaderView<Footer: View>: View {
mediaProvider: MediaProviderProtocol? = nil, mediaProvider: MediaProviderProtocol? = nil,
onAvatarTap: ((URL) -> Void)? = nil, onAvatarTap: ((URL) -> Void)? = nil,
@ViewBuilder footer: @escaping () -> Footer) { @ViewBuilder footer: @escaping () -> Footer) {
let profile = UserProfileProxy(member: member) let profile = UserProfile(member: member)
self.init(user: profile, self.init(user: profile,
isVerified: isVerified, isVerified: isVerified,
@@ -99,15 +99,15 @@ struct AvatarHeaderView<Footer: View>: View {
footer: footer) footer: footer)
} }
init(user: UserProfileProxy, init(user: UserProfile,
isVerified: Bool, isVerified: Bool,
avatarSize: Avatars.Size, avatarSize: Avatars.Size,
mediaProvider: MediaProviderProtocol? = nil, mediaProvider: MediaProviderProtocol? = nil,
onAvatarTap: ((URL) -> Void)? = nil, onAvatarTap: ((URL) -> Void)? = nil,
@ViewBuilder footer: @escaping () -> Footer) { @ViewBuilder footer: @escaping () -> Footer) {
avatarInfo = .user(user) avatarInfo = .user(user)
title = user.displayName ?? user.userID title = user.displayName ?? user.id
subtitle = user.displayName == nil ? nil : user.userID subtitle = user.displayName == nil ? nil : user.id
self.avatarSize = avatarSize self.avatarSize = avatarSize
self.mediaProvider = mediaProvider self.mediaProvider = mediaProvider
@@ -123,7 +123,7 @@ struct AvatarHeaderView<Footer: View>: View {
mediaProvider: MediaProviderProtocol? = nil, mediaProvider: MediaProviderProtocol? = nil,
onAvatarTap: ((URL) -> Void)? = nil, onAvatarTap: ((URL) -> Void)? = nil,
@ViewBuilder footer: @escaping () -> Footer) { @ViewBuilder footer: @escaping () -> Footer) {
let profile = UserProfileProxy(sender: sender) let profile = UserProfile(sender: sender)
self.init(user: profile, self.init(user: profile,
isVerified: false, isVerified: false,
@@ -180,8 +180,8 @@ struct AvatarHeaderView<Footer: View>: View {
switch avatarInfo { switch avatarInfo {
case .room(let roomAvatar): case .room(let roomAvatar):
return roomAvatar.hasURL ? L10n.a11yViewAvatar : L10n.a11yAvatar return roomAvatar.hasURL ? L10n.a11yViewAvatar : L10n.a11yAvatar
case .user(let userProfileProxy): case .user(let userProfile):
return userProfileProxy.avatarURL != nil ? L10n.a11yViewAvatar : L10n.a11yAvatar return userProfile.avatarURL != nil ? L10n.a11yViewAvatar : L10n.a11yAvatar
} }
} }
@@ -198,7 +198,7 @@ struct AvatarHeaderView<Footer: View>: View {
case .user(let userProfile): case .user(let userProfile):
LoadableAvatarImage(url: userProfile.avatarURL, LoadableAvatarImage(url: userProfile.avatarURL,
name: userProfile.displayName, name: userProfile.displayName,
contentID: userProfile.userID, contentID: userProfile.id,
avatarSize: avatarSize, avatarSize: avatarSize,
mediaProvider: mediaProvider, mediaProvider: mediaProvider,
onTap: onAvatarTap) onTap: onAvatarTap)
@@ -10,7 +10,7 @@ import Compound
import SwiftUI import SwiftUI
struct JoinedMembersBadgeView: View { struct JoinedMembersBadgeView: View {
let heroes: [UserProfileProxy] let heroes: [UserProfile]
var shouldHideAvatars = false var shouldHideAvatars = false
let joinedCount: Int let joinedCount: Int
@@ -46,7 +46,7 @@ struct JoinedMembersBadgeView: View {
ForEach(heroes.prefix(3).reversed()) { hero in ForEach(heroes.prefix(3).reversed()) { hero in
LoadableAvatarImage(url: shouldHideAvatars ? nil : hero.avatarURL, LoadableAvatarImage(url: shouldHideAvatars ? nil : hero.avatarURL,
name: hero.displayName, name: hero.displayName,
contentID: hero.userID, contentID: hero.id,
avatarSize: .user(on: .spaceHeader), avatarSize: .user(on: .spaceHeader),
mediaProvider: mediaProvider) mediaProvider: mediaProvider)
.mask { .mask {
@@ -69,7 +69,7 @@ struct LocationMarkerView: View {
if let userProfile = kind.userProfile { if let userProfile = kind.userProfile {
LoadableAvatarImage(url: userProfile.avatarURL, LoadableAvatarImage(url: userProfile.avatarURL,
name: userProfile.displayName, name: userProfile.displayName,
contentID: userProfile.userID, contentID: userProfile.id,
avatarSize: .user(on: .map), avatarSize: .user(on: .map),
mediaProvider: mediaProvider) mediaProvider: mediaProvider)
.overlay { .overlay {
@@ -13,7 +13,7 @@ enum RoomAvatar: Equatable {
/// An avatar generated from a Room's details. /// An avatar generated from a Room's details.
case room(id: String, name: String?, avatarURL: URL?) case room(id: String, name: String?, avatarURL: URL?)
/// An avatar generated from a collection of room heroes. /// An avatar generated from a collection of room heroes.
case heroes([UserProfileProxy]) case heroes([UserProfile])
/// An avatar generated from a Space's details. /// An avatar generated from a Space's details.
case space(id: String, name: String?, avatarURL: URL?) case space(id: String, name: String?, avatarURL: URL?)
/// A static avatar for a tombstoned room. /// A static avatar for a tombstoned room.
@@ -24,7 +24,7 @@ enum RoomAvatar: Equatable {
case let .room(id, name, _): case let .room(id, name, _):
.room(id: id, name: name, avatarURL: nil) .room(id: id, name: name, avatarURL: nil)
case let .heroes(users): case let .heroes(users):
.heroes(users.map { .init(userID: $0.userID, displayName: $0.displayName, avatarURL: nil) }) .heroes(users.map { .init(userID: $0.id, displayName: $0.displayName, avatarURL: nil) })
case .space(let id, let name, _): case .space(let id, let name, _):
.space(id: id, name: name, avatarURL: nil) .space(id: id, name: name, avatarURL: nil)
case .tombstoned: case .tombstoned:
@@ -76,7 +76,7 @@ struct RoomAvatarImage: View {
ZStack { ZStack {
LoadableAvatarImage(url: users[0].avatarURL, LoadableAvatarImage(url: users[0].avatarURL,
name: users[0].displayName, name: users[0].displayName,
contentID: users[0].userID, contentID: users[0].id,
avatarSize: avatarSize, avatarSize: avatarSize,
mediaProvider: mediaProvider, mediaProvider: mediaProvider,
onTap: onAvatarTap) onTap: onAvatarTap)
@@ -84,7 +84,7 @@ struct RoomAvatarImage: View {
LoadableAvatarImage(url: users[1].avatarURL, LoadableAvatarImage(url: users[1].avatarURL,
name: users[1].displayName, name: users[1].displayName,
contentID: users[1].userID, contentID: users[1].id,
avatarSize: avatarSize, avatarSize: avatarSize,
mediaProvider: mediaProvider, mediaProvider: mediaProvider,
onTap: onAvatarTap) onTap: onAvatarTap)
@@ -107,7 +107,7 @@ struct RoomAvatarImage: View {
} else { } else {
LoadableAvatarImage(url: users[0].avatarURL, LoadableAvatarImage(url: users[0].avatarURL,
name: users[0].displayName, name: users[0].displayName,
contentID: users[0].userID, contentID: users[0].id,
avatarSize: avatarSize, avatarSize: avatarSize,
mediaProvider: mediaProvider, mediaProvider: mediaProvider,
onTap: onAvatarTap) onTap: onAvatarTap)
@@ -11,7 +11,7 @@ import MatrixRustSDK
import SwiftUI import SwiftUI
struct UserProfileListRow: View { struct UserProfileListRow: View {
let user: UserProfileProxy let user: UserProfile
let membership: MembershipState? let membership: MembershipState?
let mediaProvider: MediaProviderProtocol? let mediaProvider: MediaProviderProtocol?
@@ -27,14 +27,14 @@ struct UserProfileListRow: View {
if let membershipText = membership?.localizedDescription { if let membershipText = membership?.localizedDescription {
return membershipText return membershipText
} else if user.displayName != nil { } else if user.displayName != nil {
return user.userID return user.id
} else { } else {
return nil return nil
} }
} }
var body: some View { var body: some View {
ListRow(label: .avatar(title: user.displayName ?? user.userID, ListRow(label: .avatar(title: user.displayName ?? user.id,
description: subtitle, description: subtitle,
icon: avatar, icon: avatar,
role: isUnknownProfile ? .error : nil), role: isUnknownProfile ? .error : nil),
@@ -44,7 +44,7 @@ struct UserProfileListRow: View {
var avatar: LoadableAvatarImage { var avatar: LoadableAvatarImage {
LoadableAvatarImage(url: user.avatarURL, LoadableAvatarImage(url: user.avatarURL,
name: user.displayName, name: user.displayName,
contentID: user.userID, contentID: user.id,
avatarSize: .user(on: .startChat), avatarSize: .user(on: .startChat),
mediaProvider: mediaProvider) mediaProvider: mediaProvider)
} }
@@ -9,7 +9,7 @@
import Foundation import Foundation
struct BlockedUsersScreenViewState: BindableState { struct BlockedUsersScreenViewState: BindableState {
var blockedUsers: [UserProfileProxy] var blockedUsers: [UserProfile]
var processingUserID: String? var processingUserID: String?
var bindings = BlockedUsersScreenViewStateBindings() var bindings = BlockedUsersScreenViewStateBindings()
@@ -20,7 +20,7 @@ struct BlockedUsersScreenViewStateBindings {
} }
enum BlockedUsersScreenViewAction { enum BlockedUsersScreenViewAction {
case unblockUser(UserProfileProxy) case unblockUser(UserProfile)
} }
enum BlockedUsersScreenViewStateAlertType: Hashable { enum BlockedUsersScreenViewStateAlertType: Hashable {
@@ -23,7 +23,7 @@ class BlockedUsersScreenViewModel: BlockedUsersScreenViewModelType, BlockedUsers
clientProxy = userSession.clientProxy clientProxy = userSession.clientProxy
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
let ignoredUsers = clientProxy.ignoredUsersPublisher.value?.map { UserProfileProxy(userID: $0) } let ignoredUsers = clientProxy.ignoredUsersPublisher.value?.map { UserProfile(userID: $0) }
super.init(initialViewState: BlockedUsersScreenViewState(blockedUsers: ignoredUsers ?? []), super.init(initialViewState: BlockedUsersScreenViewState(blockedUsers: ignoredUsers ?? []),
mediaProvider: userSession.mediaProvider) mediaProvider: userSession.mediaProvider)
@@ -64,16 +64,16 @@ class BlockedUsersScreenViewModel: BlockedUsersScreenViewModelType, BlockedUsers
defer { hideLoadingIndicator() } defer { hideLoadingIndicator() }
if hideProfiles { if hideProfiles {
state.blockedUsers = blockedUsers.map { UserProfileProxy(userID: $0) } state.blockedUsers = blockedUsers.map { UserProfile(userID: $0) }
} else { } else {
state.blockedUsers = await withTaskGroup(of: UserProfileProxy.self) { group in state.blockedUsers = await withTaskGroup(of: UserProfile.self) { group in
for userID in blockedUsers { for userID in blockedUsers {
group.addTask { group.addTask {
await self.profile(for: userID) await self.profile(for: userID)
} }
} }
var profiles = [UserProfileProxy]() var profiles = [UserProfile]()
for await profile in group { for await profile in group {
profiles.append(profile) profiles.append(profile)
} }
@@ -84,19 +84,19 @@ class BlockedUsersScreenViewModel: BlockedUsersScreenViewModelType, BlockedUsers
/// The client proxy isn't Sendable, fetch through this helper so that it /// The client proxy isn't Sendable, fetch through this helper so that it
/// never leaves the main actor when running calls in parallel. /// never leaves the main actor when running calls in parallel.
private func profile(for userID: String) async -> UserProfileProxy { private func profile(for userID: String) async -> UserProfile {
switch await clientProxy.profile(for: userID) { switch await clientProxy.profile(for: userID) {
case .success(let profile): profile case .success(let profile): profile
case .failure: UserProfileProxy(userID: userID) case .failure: UserProfile(userID: userID)
} }
} }
private func unblockUser(_ user: UserProfileProxy) { private func unblockUser(_ user: UserProfile) {
showLoadingIndicator() showLoadingIndicator()
state.processingUserID = user.userID state.processingUserID = user.id
Task { Task {
if case .failure = await clientProxy.unignoreUser(user.userID) { if case .failure = await clientProxy.unignoreUser(user.id) {
state.bindings.alertInfo = .init(id: .error) state.bindings.alertInfo = .init(id: .error)
} }
@@ -33,20 +33,20 @@ struct BlockedUsersScreen: View {
} else { } else {
Form { Form {
ForEach(context.viewState.blockedUsers, id: \.self) { user in ForEach(context.viewState.blockedUsers, id: \.self) { user in
ListRow(label: .avatar(title: user.displayName ?? user.userID, ListRow(label: .avatar(title: user.displayName ?? user.id,
description: user.displayName != nil ? user.userID : nil, description: user.displayName != nil ? user.id : nil,
icon: avatar(for: user)), icon: avatar(for: user)),
details: .isWaiting(context.viewState.processingUserID == user.userID), details: .isWaiting(context.viewState.processingUserID == user.id),
kind: .button { context.send(viewAction: .unblockUser(user)) }) kind: .button { context.send(viewAction: .unblockUser(user)) })
} }
} }
} }
} }
private func avatar(for user: UserProfileProxy) -> some View { private func avatar(for user: UserProfile) -> some View {
LoadableAvatarImage(url: user.avatarURL, LoadableAvatarImage(url: user.avatarURL,
name: user.displayName, name: user.displayName,
contentID: user.userID, contentID: user.id,
avatarSize: .user(on: .blockedUsers), avatarSize: .user(on: .blockedUsers),
mediaProvider: context.mediaProvider) mediaProvider: context.mediaProvider)
.accessibilityHidden(true) .accessibilityHidden(true)
@@ -20,7 +20,7 @@ enum InviteUsersScreenViewModelAction {
} }
enum InviteUsersScreenRoomType { enum InviteUsersScreenRoomType {
case draft(mandatoryInvitees: [UserProfileProxy]) case draft(mandatoryInvitees: [UserProfile])
case existingRoom(roomProxy: JoinedRoomProxyProtocol) case existingRoom(roomProxy: JoinedRoomProxyProtocol)
} }
@@ -29,10 +29,10 @@ struct InviteUsersScreenViewState: BindableState {
var usersSection: UserDiscoverySection = .init(type: .suggestions, users: []) var usersSection: UserDiscoverySection = .init(type: .suggestions, users: [])
var selectedUsers: [UserProfileProxy] = [] var selectedUsers: [UserProfile] = []
var mandatoryInvitees: [UserProfileProxy] = [] var mandatoryInvitees: [UserProfile] = []
var membershipState: [String: MembershipState] = .init() var membershipState: [String: MembershipState] = .init()
var usersToConfirm: [UserProfileProxy] = [] var usersToConfirm: [UserProfile] = []
var isSearching = false var isSearching = false
@@ -44,11 +44,11 @@ struct InviteUsersScreenViewState: BindableState {
selectedUsers.contains { !isInviteeMandatory($0) } selectedUsers.contains { !isInviteeMandatory($0) }
} }
func isUserSelected(_ user: UserProfileProxy) -> Bool { func isUserSelected(_ user: UserProfile) -> Bool {
isUserDisabled(user) || selectedUsers.contains { $0.userID == user.userID } isUserDisabled(user) || selectedUsers.contains { $0.id == user.id }
} }
func isUserDisabled(_ user: UserProfileProxy) -> Bool { func isUserDisabled(_ user: UserProfile) -> Bool {
if isInviteeMandatory(user) { if isInviteeMandatory(user) {
return true return true
} }
@@ -56,12 +56,12 @@ struct InviteUsersScreenViewState: BindableState {
return membershipState == .invite || membershipState == .join return membershipState == .invite || membershipState == .join
} }
func isInviteeMandatory(_ user: UserProfileProxy) -> Bool { func isInviteeMandatory(_ user: UserProfile) -> Bool {
mandatoryInvitees.contains { $0.userID == user.userID } mandatoryInvitees.contains { $0.id == user.id }
} }
func membershipState(_ user: UserProfileProxy) -> MembershipState? { func membershipState(_ user: UserProfile) -> MembershipState? {
membershipState[user.userID] membershipState[user.id]
} }
let isSkippable: Bool let isSkippable: Bool
@@ -83,5 +83,5 @@ enum InviteUsersScreenViewAction {
case proceed case proceed
case removeUnknownUsers case removeUnknownUsers
case confirmUnknownUsers case confirmUnknownUsers
case toggleUser(UserProfileProxy) case toggleUser(UserProfile)
} }
@@ -19,7 +19,7 @@ class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScr
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private let appSettings: AppSettings private let appSettings: AppSettings
private var suggestedUsers = [UserProfileProxy]() private var suggestedUsers = [UserProfile]()
private let actionsSubject: PassthroughSubject<InviteUsersScreenViewModelAction, Never> = .init() private let actionsSubject: PassthroughSubject<InviteUsersScreenViewModelAction, Never> = .init()
var actions: AnyPublisher<InviteUsersScreenViewModelAction, Never> { var actions: AnyPublisher<InviteUsersScreenViewModelAction, Never> {
@@ -38,7 +38,7 @@ class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScr
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
self.appSettings = appSettings self.appSettings = appSettings
let mandatoryInvitees: [UserProfileProxy] = if case .draft(let invitees) = roomType { let mandatoryInvitees: [UserProfile] = if case .draft(let invitees) = roomType {
invitees invitees
} else { } else {
[] []
@@ -70,12 +70,12 @@ class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScr
case .proceed: case .proceed:
switch roomType { switch roomType {
case .draft: case .draft:
createDraftRoom(mandatoryUserIDs: state.selectedUsers.map(\.userID)) createDraftRoom(mandatoryUserIDs: state.selectedUsers.map(\.id))
case .existingRoom(let roomProxy): case .existingRoom(let roomProxy):
guard roomProxy.details.historySharingState != RoomHistorySharingState.hidden, guard roomProxy.details.historySharingState != RoomHistorySharingState.hidden,
!state.usersToConfirm.isEmpty, !state.usersToConfirm.isEmpty,
!state.isSkippable else { !state.isSkippable else {
inviteUsers(state.selectedUsers.map(\.userID), roomProxy: roomProxy) inviteUsers(state.selectedUsers.map(\.id), roomProxy: roomProxy)
return return
} }
state.bindings.presentConfirmationDialog = true state.bindings.presentConfirmationDialog = true
@@ -83,14 +83,14 @@ class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScr
case .removeUnknownUsers: case .removeUnknownUsers:
state.bindings.presentConfirmationDialog = false state.bindings.presentConfirmationDialog = false
state.selectedUsers.removeAll { user in state.selectedUsers.removeAll { user in
state.usersToConfirm.contains { $0.userID == user.userID } state.usersToConfirm.contains { $0.id == user.id }
} }
state.usersToConfirm = [] state.usersToConfirm = []
case .confirmUnknownUsers: case .confirmUnknownUsers:
state.bindings.presentConfirmationDialog = false state.bindings.presentConfirmationDialog = false
state.usersToConfirm = [] state.usersToConfirm = []
if case .existingRoom(let roomProxy) = roomType { if case .existingRoom(let roomProxy) = roomType {
inviteUsers(state.selectedUsers.map(\.userID), roomProxy: roomProxy) inviteUsers(state.selectedUsers.map(\.id), roomProxy: roomProxy)
} }
case .toggleUser(let user): case .toggleUser(let user):
toggleUser(user) toggleUser(user)
@@ -99,16 +99,16 @@ class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScr
// MARK: - Private // MARK: - Private
private func toggleUser(_ user: UserProfileProxy) { private func toggleUser(_ user: UserProfile) {
guard !state.isInviteeMandatory(user) else { return } guard !state.isInviteeMandatory(user) else { return }
if state.selectedUsers.contains(user) { if state.selectedUsers.contains(user) {
state.selectedUsers.removeAll { $0.userID == user.userID } state.selectedUsers.removeAll { $0.id == user.id }
} else { } else {
state.selectedUsers.append(user) state.selectedUsers.append(user)
withElementAnimation(.easeInOut) { state.bindings.selectedUsersPosition = user.userID } withElementAnimation(.easeInOut) { state.bindings.selectedUsersPosition = user.id }
Task { Task {
let identityUnknown = if case .success(let identity) = await self.clientProxy.userIdentity(for: user.userID, fallBackToServer: false) { let identityUnknown = if case .success(let identity) = await self.clientProxy.userIdentity(for: user.id, fallBackToServer: false) {
identity == nil identity == nil
} else { } else {
true true
@@ -12,7 +12,7 @@ struct InviteUsersConfirmationSheetView: View {
@ObservedObject var context: InviteUsersScreenViewModel.Context @ObservedObject var context: InviteUsersScreenViewModel.Context
/// The users whose identities we wish the user to confirm. /// The users whose identities we wish the user to confirm.
var users: [UserProfileProxy] var users: [UserProfile]
var body: some View { var body: some View {
FullscreenDialog(topPadding: 24, horizontalPadding: 24) { FullscreenDialog(topPadding: 24, horizontalPadding: 24) {
@@ -22,7 +22,7 @@ struct InviteUsersConfirmationSheetView: View {
icon: \.userAddSolid, icon: \.userAddSolid,
iconStyle: .defaultSolid) iconStyle: .defaultSolid)
VStack(spacing: 0) { VStack(spacing: 0) {
ForEach(users, id: \.userID) { user in ForEach(users, id: \.id) { user in
UserProfileListRow(user: user, UserProfileListRow(user: user,
membership: nil, membership: nil,
mediaProvider: context.mediaProvider, mediaProvider: context.mediaProvider,
@@ -83,7 +83,7 @@ struct InviteUsersScreen: View {
private var usersSection: some View { private var usersSection: some View {
if !context.viewState.usersSection.users.isEmpty { if !context.viewState.usersSection.users.isEmpty {
Section { Section {
ForEach(context.viewState.usersSection.users, id: \.userID) { user in ForEach(context.viewState.usersSection.users, id: \.id) { user in
UserProfileListRow(user: user, UserProfileListRow(user: user,
membership: context.viewState.membershipState(user), membership: context.viewState.membershipState(user),
mediaProvider: context.mediaProvider, mediaProvider: context.mediaProvider,
@@ -109,7 +109,7 @@ struct InviteUsersScreen: View {
private var selectedUsersSection: some View { private var selectedUsersSection: some View {
ScrollView(.horizontal, showsIndicators: false) { ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 8) { HStack(spacing: 8) {
ForEach(context.viewState.selectedUsers, id: \.userID) { user in ForEach(context.viewState.selectedUsers, id: \.id) { user in
InviteUsersScreenSelectedItem(user: user, InviteUsersScreenSelectedItem(user: user,
mediaProvider: context.mediaProvider, mediaProvider: context.mediaProvider,
isLocked: context.viewState.isInviteeMandatory(user)) { isLocked: context.viewState.isInviteeMandatory(user)) {
@@ -150,7 +150,7 @@ struct InviteUsersScreen: View {
} }
} }
private func deselect(_ user: UserProfileProxy) { private func deselect(_ user: UserProfile) {
context.send(viewAction: .toggleUser(user)) context.send(viewAction: .toggleUser(user))
} }
} }
@@ -10,7 +10,7 @@ import Compound
import SwiftUI import SwiftUI
struct InviteUsersScreenSelectedItem: View { struct InviteUsersScreenSelectedItem: View {
let user: UserProfileProxy let user: UserProfile
let mediaProvider: MediaProviderProtocol? let mediaProvider: MediaProviderProtocol?
var isLocked = false var isLocked = false
let dismissAction: () -> Void let dismissAction: () -> Void
@@ -20,7 +20,7 @@ struct InviteUsersScreenSelectedItem: View {
avatar avatar
.accessibilityHidden(true) .accessibilityHidden(true)
Text(user.displayName ?? user.userID) Text(user.displayName ?? user.id)
.font(.compound.bodySM) .font(.compound.bodySM)
.foregroundColor(.compound.textSecondary) .foregroundColor(.compound.textSecondary)
.lineLimit(1) .lineLimit(1)
@@ -39,7 +39,7 @@ struct InviteUsersScreenSelectedItem: View {
var avatar: some View { var avatar: some View {
let avatarImage = LoadableAvatarImage(url: user.avatarURL, let avatarImage = LoadableAvatarImage(url: user.avatarURL,
name: user.displayName, name: user.displayName,
contentID: user.userID, contentID: user.id,
avatarSize: .user(on: .inviteUsers), avatarSize: .user(on: .inviteUsers),
mediaProvider: mediaProvider) mediaProvider: mediaProvider)
if isLocked { if isLocked {
@@ -58,12 +58,12 @@ struct InviteUsersScreenSelectedItem: View {
} }
struct InviteUsersScreenSelectedItem_Previews: PreviewProvider, TestablePreview { struct InviteUsersScreenSelectedItem_Previews: PreviewProvider, TestablePreview {
static let people: [UserProfileProxy] = [.mockAlice, .mockVerbose] static let people: [UserProfile] = [.mockAlice, .mockVerbose]
static var previews: some View { static var previews: some View {
ScrollView(.horizontal) { ScrollView(.horizontal) {
HStack(spacing: 8) { HStack(spacing: 8) {
ForEach(people, id: \.userID) { user in ForEach(people, id: \.id) { user in
InviteUsersScreenSelectedItem(user: user, mediaProvider: MediaProviderMock(.init())) { } InviteUsersScreenSelectedItem(user: user, mediaProvider: MediaProviderMock(.init())) { }
.frame(width: 80) .frame(width: 80)
} }
@@ -43,7 +43,7 @@ struct JoinRoomScreenRoomDetails {
let canonicalAlias: String? let canonicalAlias: String?
let avatar: RoomAvatar? let avatar: RoomAvatar?
let memberCount: Int? let memberCount: Int?
let heroes: [UserProfileProxy] let heroes: [UserProfile]
let inviter: RoomInviterDetails? let inviter: RoomInviterDetails?
let isDirect: Bool? let isDirect: Bool?
@@ -52,7 +52,7 @@ struct LocationSharingScreenViewState: BindableState {
self.mapURLBuilder = mapURLBuilder self.mapURLBuilder = mapURLBuilder
self.ownUserID = ownUserID self.ownUserID = ownUserID
let initialProfile: UserProfileProxy = switch interactionMode { let initialProfile: UserProfile = switch interactionMode {
case .viewStatic(let locationData): case .viewStatic(let locationData):
.init(sender: locationData.sender) .init(sender: locationData.sender)
case .viewLive(let sender, _): case .viewLive(let sender, _):
@@ -64,7 +64,7 @@ struct LocationSharingScreenViewState: BindableState {
case .picker: case .picker:
.init(userID: ownUserID) .init(userID: ownUserID)
} }
userProfiles = [initialProfile.userID: initialProfile] userProfiles = [initialProfile.id: initialProfile]
if case .viewLive(_, let initialLiveLocationShare) = interactionMode, let initialLiveLocationShare { if case .viewLive(_, let initialLiveLocationShare) = interactionMode, let initialLiveLocationShare {
liveLocationShares = [initialLiveLocationShare] liveLocationShares = [initialLiveLocationShare]
@@ -79,7 +79,7 @@ struct LocationSharingScreenViewState: BindableState {
let interactionMode: LocationSharingInteractionMode let interactionMode: LocationSharingInteractionMode
let mapURLBuilder: MapTilerURLBuilderProtocol let mapURLBuilder: MapTilerURLBuilderProtocol
let ownUserID: String let ownUserID: String
var userProfiles: [String: UserProfileProxy] var userProfiles: [String: UserProfile]
var liveLocationShares: [LiveLocationShare] = [] var liveLocationShares: [LiveLocationShare] = []
var isStoppingLiveLocation = false var isStoppingLiveLocation = false
/// Whether this device has an active live location sharing session in the room. /// Whether this device has an active live location sharing session in the room.
@@ -103,10 +103,10 @@ struct LocationSharingScreenViewState: BindableState {
return nil return nil
} }
let profile = userProfiles[share.userID] ?? UserProfileProxy(userID: share.userID) let profile = userProfiles[share.userID] ?? UserProfile(userID: share.userID)
let kind = LocationMarkerKind.liveUser(profile) let kind = LocationMarkerKind.liveUser(profile)
let coordinate = CLLocationCoordinate2D(latitude: geoURI.latitude, longitude: geoURI.longitude) let coordinate = CLLocationCoordinate2D(latitude: geoURI.latitude, longitude: geoURI.longitude)
return LocationAnnotation(id: profile.userID, coordinate: coordinate, kind: kind) return LocationAnnotation(id: profile.id, coordinate: coordinate, kind: kind)
} }
case .picker: case .picker:
return [] return []
@@ -287,8 +287,8 @@ extension AlertInfo where T == LocationSharingViewAlert {
enum LocationMarkerKind: Equatable { enum LocationMarkerKind: Equatable {
case pin case pin
case staticUser(UserProfileProxy) case staticUser(UserProfile)
case liveUser(UserProfileProxy) case liveUser(UserProfile)
case placeholder case placeholder
var id: String { var id: String {
@@ -296,7 +296,7 @@ enum LocationMarkerKind: Equatable {
case .pin, .placeholder: case .pin, .placeholder:
UUID().uuidString UUID().uuidString
case .staticUser(let profile), .liveUser(let profile): case .staticUser(let profile), .liveUser(let profile):
profile.userID profile.id
} }
} }
@@ -309,7 +309,7 @@ enum LocationMarkerKind: Equatable {
} }
} }
var userProfile: UserProfileProxy? { var userProfile: UserProfile? {
switch self { switch self {
case .pin, .placeholder: case .pin, .placeholder:
nil nil
@@ -194,11 +194,11 @@ class LocationSharingScreenViewModel: LocationSharingScreenViewModelType, Locati
private func updateUserProfiles(members: [RoomMemberProxyProtocol]) { private func updateUserProfiles(members: [RoomMemberProxyProtocol]) {
switch state.interactionMode { switch state.interactionMode {
case .picker: case .picker:
let ownUser = members.first { $0.userID == roomProxy.ownUserID }.map(UserProfileProxy.init) ?? .init(userID: roomProxy.ownUserID) let ownUser = members.first { $0.userID == roomProxy.ownUserID }.map(UserProfile.init) ?? .init(userID: roomProxy.ownUserID)
state.userProfiles = [ownUser.userID: ownUser] state.userProfiles = [ownUser.id: ownUser]
case .viewStatic(let location): case .viewStatic(let location):
let sender = members.first { $0.userID == location.sender.id }.map(UserProfileProxy.init) ?? .init(sender: location.sender) let sender = members.first { $0.userID == location.sender.id }.map(UserProfile.init) ?? .init(sender: location.sender)
state.userProfiles = [sender.userID: sender] state.userProfiles = [sender.id: sender]
case .viewLive(let sender, _): case .viewLive(let sender, _):
var userIDs = Set(state.liveLocationShares.map(\.userID)) var userIDs = Set(state.liveLocationShares.map(\.userID))
if let senderID = sender?.id { if let senderID = sender?.id {
@@ -206,9 +206,9 @@ class LocationSharingScreenViewModel: LocationSharingScreenViewModelType, Locati
} }
state.userProfiles = userIDs.reduce(into: [:]) { dict, userID in state.userProfiles = userIDs.reduce(into: [:]) { dict, userID in
if let member = members.first(where: { $0.userID == userID }) { if let member = members.first(where: { $0.userID == userID }) {
dict[userID] = UserProfileProxy(member: member) dict[userID] = UserProfile(member: member)
} else { } else {
dict[userID] = UserProfileProxy(userID: userID) dict[userID] = UserProfile(userID: userID)
} }
} }
} }
@@ -16,7 +16,7 @@ struct LocationShareSheet: View {
} }
private var senderName: String? { private var senderName: String? {
annotation.kind.displayName ?? annotation.kind.userProfile?.userID annotation.kind.displayName ?? annotation.kind.userProfile?.id
} }
var body: some View { var body: some View {
@@ -40,8 +40,8 @@ struct LocationShareSheet: View {
// MARK: - Previews // MARK: - Previews
struct LocationShareSheet_Previews: PreviewProvider { struct LocationShareSheet_Previews: PreviewProvider {
static let profile = UserProfileProxy(userID: "@alice:example.com", displayName: "Alice") static let profile = UserProfile(userID: "@alice:example.com", displayName: "Alice")
static let annotation = LocationAnnotation(id: profile.userID, static let annotation = LocationAnnotation(id: profile.id,
coordinate: .init(latitude: 51.509865, longitude: -0.118092), coordinate: .init(latitude: 51.509865, longitude: -0.118092),
kind: .liveUser(profile)) kind: .liveUser(profile))
@@ -41,7 +41,7 @@ struct StaticLocationSheet: View {
longitude: location.geoURI.longitude))) longitude: location.geoURI.longitude)))
} label: { } label: {
UserLocationCell(profile: profile, UserLocationCell(profile: profile,
isOwnUser: context.viewState.isOwnUser(profile.userID), isOwnUser: context.viewState.isOwnUser(profile.id),
kind: .static(isUserLocation: location.kind == .sender, kind: .static(isUserLocation: location.kind == .sender,
timestamp: location.timestamp), timestamp: location.timestamp),
mediaProvider: context.mediaProvider, mediaProvider: context.mediaProvider,
@@ -9,7 +9,7 @@ import Compound
import SwiftUI import SwiftUI
struct UserLocationCell: View { struct UserLocationCell: View {
let profile: UserProfileProxy let profile: UserProfile
let isOwnUser: Bool let isOwnUser: Bool
let kind: Kind let kind: Kind
var mediaProvider: MediaProviderProtocol? var mediaProvider: MediaProviderProtocol?
@@ -23,7 +23,7 @@ struct UserLocationCell: View {
} }
private var name: String { private var name: String {
isOwnUser ? L10n.commonYou : profile.displayName ?? profile.userID isOwnUser ? L10n.commonYou : profile.displayName ?? profile.id
} }
var body: some View { var body: some View {
@@ -30,18 +30,18 @@ struct SessionVerificationRequestDetailsView: View {
HStack(spacing: 12) { HStack(spacing: 12) {
LoadableAvatarImage(url: details.senderProfile.avatarURL, LoadableAvatarImage(url: details.senderProfile.avatarURL,
name: details.senderProfile.displayName, name: details.senderProfile.displayName,
contentID: details.senderProfile.userID, contentID: details.senderProfile.id,
avatarSize: .user(on: .sessionVerification), avatarSize: .user(on: .sessionVerification),
mediaProvider: mediaProvider) mediaProvider: mediaProvider)
.accessibilityHidden(true) .accessibilityHidden(true)
VStack(alignment: .leading, spacing: 0) { VStack(alignment: .leading, spacing: 0) {
Text(details.senderProfile.displayName ?? details.senderProfile.userID) Text(details.senderProfile.displayName ?? details.senderProfile.id)
.font(.compound.bodySM) .font(.compound.bodySM)
.foregroundColor(.compound.textSecondary) .foregroundColor(.compound.textSecondary)
if details.senderProfile.displayName != nil { if details.senderProfile.displayName != nil {
Text(details.senderProfile.userID) Text(details.senderProfile.id)
.font(.compound.bodyMD) .font(.compound.bodyMD)
.foregroundColor(.compound.textPrimary) .foregroundColor(.compound.textPrimary)
} }
@@ -66,7 +66,7 @@ struct SessionVerificationRequestDetailsView: View {
.clipShape(RoundedRectangle(cornerRadius: 8)) .clipShape(RoundedRectangle(cornerRadius: 8))
let displayName = isUserVerification ? details.senderProfile.displayName : details.deviceDisplayName let displayName = isUserVerification ? details.senderProfile.displayName : details.deviceDisplayName
Text(displayName ?? details.senderProfile.userID) Text(displayName ?? details.senderProfile.id)
.font(.compound.bodyMDSemibold) .font(.compound.bodyMDSemibold)
.foregroundColor(.compound.textPrimary) .foregroundColor(.compound.textPrimary)
.frame(maxWidth: .infinity, alignment: .leading) .frame(maxWidth: .infinity, alignment: .leading)
@@ -110,9 +110,9 @@ struct SessionVerificationRequestDetailsView: View {
} }
struct SessionVerificationRequestDetailsView_Previews: PreviewProvider, TestablePreview { struct SessionVerificationRequestDetailsView_Previews: PreviewProvider, TestablePreview {
static let details = SessionVerificationRequestDetails(senderProfile: UserProfileProxy(userID: "@bob:matrix.org", static let details = SessionVerificationRequestDetails(senderProfile: UserProfile(userID: "@bob:matrix.org",
displayName: "Billy bob", displayName: "Billy bob",
avatarURL: .mockMXCUserAvatar), avatarURL: .mockMXCUserAvatar),
flowID: "123", flowID: "123",
deviceID: "CODEMISTAKE", deviceID: "CODEMISTAKE",
deviceDisplayName: "Bob's Element X iOS", deviceDisplayName: "Bob's Element X iOS",
@@ -232,9 +232,9 @@ struct SessionVerification_Previews: PreviewProvider, TestablePreview {
sessionVerificationScreen(state: .initial, flow: .userInitiator(userID: "@bob:matrix.org")) sessionVerificationScreen(state: .initial, flow: .userInitiator(userID: "@bob:matrix.org"))
.previewDisplayName("Initial - User Initiator") .previewDisplayName("Initial - User Initiator")
let details = SessionVerificationRequestDetails(senderProfile: UserProfileProxy(userID: "@bob:matrix.org", let details = SessionVerificationRequestDetails(senderProfile: UserProfile(userID: "@bob:matrix.org",
displayName: "Billy Bob", displayName: "Billy Bob",
avatarURL: .mockMXCUserAvatar), avatarURL: .mockMXCUserAvatar),
flowID: "123", flowID: "123",
deviceID: "CODEMISTAKE", deviceID: "CODEMISTAKE",
deviceDisplayName: "Bob's Element X iOS", deviceDisplayName: "Bob's Element X iOS",
@@ -26,7 +26,7 @@ enum RoomDetailsScreenCoordinatorAction {
case presentRoomDetailsEditScreen case presentRoomDetailsEditScreen
case presentNotificationSettingsScreen case presentNotificationSettingsScreen
case presentInviteUsersScreen case presentInviteUsersScreen
case presentInviteToNewRoom(invitee: UserProfileProxy) case presentInviteToNewRoom(invitee: UserProfile)
case presentPollsHistory case presentPollsHistory
case presentRolesAndPermissionsScreen case presentRolesAndPermissionsScreen
case presentCall(isVoiceCall: Bool) case presentCall(isVoiceCall: Bool)
@@ -18,7 +18,7 @@ enum RoomDetailsScreenViewModelAction: Equatable {
case requestMemberDetailsPresentation case requestMemberDetailsPresentation
case requestRecipientDetailsPresentation(userID: String) case requestRecipientDetailsPresentation(userID: String)
case requestInvitePeoplePresentation case requestInvitePeoplePresentation
case requestInviteToNewRoomPresentation(selectedInvitee: UserProfileProxy) case requestInviteToNewRoomPresentation(selectedInvitee: UserProfile)
case leftRoom case leftRoom
case requestEditDetailsPresentation case requestEditDetailsPresentation
case requestPollsHistoryPresentation case requestPollsHistoryPresentation
@@ -202,7 +202,7 @@ class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, Ro
} else if roomProxy.details.historySharingState != RoomHistorySharingState.hidden { } else if roomProxy.details.historySharingState != RoomHistorySharingState.hidden {
Task { Task {
let identity = await self.userSession.clientProxy.userIdentity(for: roomMemberProxy.userID, fallBackToServer: false) let identity = await self.userSession.clientProxy.userIdentity(for: roomMemberProxy.userID, fallBackToServer: false)
let user: UserProfileProxy = .init(userID: roomMemberProxy.userID, displayName: roomMemberProxy.displayName, avatarURL: roomMemberProxy.avatarURL) let user: UserProfile = .init(userID: roomMemberProxy.userID, displayName: roomMemberProxy.displayName, avatarURL: roomMemberProxy.avatarURL)
let isUnknown = if case .success(let identity) = identity { let isUnknown = if case .success(let identity) = identity {
identity == nil identity == nil
} else { } else {
@@ -59,7 +59,7 @@ struct RoomMemberDetailsScreen: View {
.padding(.top, 24) .padding(.top, 24)
} }
} else { } else {
AvatarHeaderView(user: UserProfileProxy(userID: context.viewState.userID), AvatarHeaderView(user: UserProfile(userID: context.viewState.userID),
isVerified: context.viewState.showVerifiedBadge, isVerified: context.viewState.showVerifiedBadge,
avatarSize: .user(on: .memberDetails), avatarSize: .user(on: .memberDetails),
mediaProvider: context.mediaProvider) { } mediaProvider: context.mediaProvider) { }
@@ -85,7 +85,7 @@ class SearchScreenViewModel: SearchScreenViewModelType, SearchScreenViewModelPro
// Show the matrix identifier as the subtitle: the other member's user ID for DMs, // Show the matrix identifier as the subtitle: the other member's user ID for DMs,
// otherwise the room's canonical alias. // otherwise the room's canonical alias.
let identifier = if summary.isDirect { let identifier = if summary.isDirect {
summary.heroes.first?.userID ?? summary.canonicalAlias summary.heroes.first?.id ?? summary.canonicalAlias
} else { } else {
summary.canonicalAlias summary.canonicalAlias
} }
@@ -50,8 +50,8 @@ struct StartChatScreenViewStateBindings {
enum StartChatScreenViewAction { enum StartChatScreenViewAction {
case close case close
case createRoom case createRoom
case createDM(user: UserProfileProxy) case createDM(user: UserProfile)
case selectUser(UserProfileProxy) case selectUser(UserProfile)
case joinRoomByAddress case joinRoomByAddress
case openRoomDirectorySearch case openRoomDirectorySearch
} }
@@ -19,7 +19,7 @@ class StartChatScreenViewModel: StartChatScreenViewModelType, StartChatScreenVie
private let userDiscoveryService: UserDiscoveryServiceProtocol private let userDiscoveryService: UserDiscoveryServiceProtocol
private let appSettings: AppSettings private let appSettings: AppSettings
private var suggestedUsers = [UserProfileProxy]() private var suggestedUsers = [UserProfile]()
private let actionsSubject: PassthroughSubject<StartChatScreenViewModelAction, Never> = .init() private let actionsSubject: PassthroughSubject<StartChatScreenViewModelAction, Never> = .init()
var actions: AnyPublisher<StartChatScreenViewModelAction, Never> { var actions: AnyPublisher<StartChatScreenViewModelAction, Never> {
@@ -61,7 +61,7 @@ class StartChatScreenViewModel: StartChatScreenViewModelType, StartChatScreenVie
case .selectUser(let user): case .selectUser(let user):
showLoadingIndicator(delay: .milliseconds(200)) showLoadingIndicator(delay: .milliseconds(200))
let currentDirectRoom = userSession.clientProxy.directRoomForUserID(user.userID) let currentDirectRoom = userSession.clientProxy.directRoomForUserID(user.id)
switch currentDirectRoom { switch currentDirectRoom {
case .success(.some(let roomId)): case .success(.some(let roomId)):
hideLoadingIndicator() hideLoadingIndicator()
@@ -69,7 +69,7 @@ class StartChatScreenViewModel: StartChatScreenViewModelType, StartChatScreenVie
case .success: case .success:
Task { Task {
// If an error occured while fetching the identity, assume they are unknown. // If an error occured while fetching the identity, assume they are unknown.
let isUnknown = if case .success(let identity) = await self.userSession.clientProxy.userIdentity(for: user.userID, fallBackToServer: false) { let isUnknown = if case .success(let identity) = await self.userSession.clientProxy.userIdentity(for: user.id, fallBackToServer: false) {
identity == nil identity == nil
} else { } else {
true true
@@ -186,12 +186,12 @@ class StartChatScreenViewModel: StartChatScreenViewModelType, StartChatScreenVie
} }
} }
private func createDirectRoom(user: UserProfileProxy) async { private func createDirectRoom(user: UserProfile) async {
defer { defer {
hideLoadingIndicator() hideLoadingIndicator()
} }
showLoadingIndicator() showLoadingIndicator()
switch await userSession.clientProxy.createDirectRoom(with: user.userID, expectedRoomName: user.displayName) { switch await userSession.clientProxy.createDirectRoom(with: user.id, expectedRoomName: user.displayName) {
case .success(let roomId): case .success(let roomId):
analytics.trackCreatedRoom(isDM: true) analytics.trackCreatedRoom(isDM: true)
actionsSubject.send(.showRoom(roomID: roomId)) actionsSubject.send(.showRoom(roomID: roomId))
@@ -112,7 +112,7 @@ struct StartChatScreen: View {
private var usersSection: some View { private var usersSection: some View {
if !context.viewState.usersSection.users.isEmpty { if !context.viewState.usersSection.users.isEmpty {
Section { Section {
ForEach(context.viewState.usersSection.users, id: \.userID) { user in ForEach(context.viewState.usersSection.users, id: \.id) { user in
UserProfileListRow(user: user, UserProfileListRow(user: user,
membership: nil, membership: nil,
mediaProvider: context.mediaProvider, mediaProvider: context.mediaProvider,
@@ -19,7 +19,7 @@ struct UserProfileScreenViewState: BindableState {
let isOwnUser: Bool let isOwnUser: Bool
let isPresentedModally: Bool let isPresentedModally: Bool
var userProfile: UserProfileProxy? var userProfile: UserProfile?
var isIdentityKnown = false var isIdentityKnown = false
var isVerified: Bool? var isVerified: Bool?
var permalink: URL? var permalink: URL?
@@ -77,7 +77,7 @@ class UserProfileScreenViewModel: UserProfileScreenViewModelType, UserProfileScr
// The proxies aren't Sendable, fetch through these helpers so that // The proxies aren't Sendable, fetch through these helpers so that
// they never leave the main actor when running calls in parallel. // they never leave the main actor when running calls in parallel.
private func fetchProfile() async -> Result<UserProfileProxy, ClientProxyError> { private func fetchProfile() async -> Result<UserProfile, ClientProxyError> {
await userSession.clientProxy.profile(for: state.userID) await userSession.clientProxy.profile(for: state.userID)
} }
@@ -94,7 +94,7 @@ class UserProfileScreenViewModel: UserProfileScreenViewModelType, UserProfileScr
state.userProfile = userProfile state.userProfile = userProfile
state.permalink = (try? matrixToUserPermalink(userId: state.userID)).flatMap(URL.init(string:)) state.permalink = (try? matrixToUserPermalink(userId: state.userID)).flatMap(URL.init(string:))
switch userSession.clientProxy.directRoomForUserID(userProfile.userID) { switch userSession.clientProxy.directRoomForUserID(userProfile.id) {
case .success(let roomID): case .success(let roomID):
state.dmRoomID = roomID state.dmRoomID = roomID
case .failure: case .failure:
@@ -133,13 +133,13 @@ class UserProfileScreenViewModel: UserProfileScreenViewModelType, UserProfileScr
showLoadingIndicator(allowsInteraction: false) showLoadingIndicator(allowsInteraction: false)
defer { hideLoadingIndicator() } defer { hideLoadingIndicator() }
switch userSession.clientProxy.directRoomForUserID(userProfile.userID) { switch userSession.clientProxy.directRoomForUserID(userProfile.id) {
case .success(let roomID): case .success(let roomID):
if let roomID { if let roomID {
actionsSubject.send(.openDirectChat(roomID: roomID)) actionsSubject.send(.openDirectChat(roomID: roomID))
} else { } else {
Task { Task {
let isUnknown = if case let .success(identity) = await userSession.clientProxy.userIdentity(for: userProfile.userID, fallBackToServer: false) { let isUnknown = if case let .success(identity) = await userSession.clientProxy.userIdentity(for: userProfile.id, fallBackToServer: false) {
identity == nil identity == nil
} else { } else {
true true
@@ -158,7 +158,7 @@ class UserProfileScreenViewModel: UserProfileScreenViewModelType, UserProfileScr
showLoadingIndicator(allowsInteraction: false) showLoadingIndicator(allowsInteraction: false)
defer { hideLoadingIndicator() } defer { hideLoadingIndicator() }
switch await userSession.clientProxy.createDirectRoom(with: userProfile.userID, expectedRoomName: userProfile.displayName) { switch await userSession.clientProxy.createDirectRoom(with: userProfile.id, expectedRoomName: userProfile.displayName) {
case .success(let roomID): case .success(let roomID):
analytics.trackCreatedRoom(isDM: true) analytics.trackCreatedRoom(isDM: true)
actionsSubject.send(.openDirectChat(roomID: roomID)) actionsSubject.send(.openDirectChat(roomID: roomID))
@@ -45,7 +45,7 @@ struct UserProfileScreen: View {
otherUserFooter otherUserFooter
} }
} else { } else {
AvatarHeaderView(user: UserProfileProxy(userID: context.viewState.userID), AvatarHeaderView(user: UserProfile(userID: context.viewState.userID),
isVerified: context.viewState.showVerifiedBadge, isVerified: context.viewState.showVerifiedBadge,
avatarSize: .user(on: .memberDetails), avatarSize: .user(on: .memberDetails),
mediaProvider: context.mediaProvider) { } mediaProvider: context.mediaProvider) { }
@@ -786,7 +786,7 @@ class ClientProxy: ClientProxyProtocol {
} }
} }
func profile(for userID: String) async -> Result<UserProfileProxy, ClientProxyError> { func profile(for userID: String) async -> Result<UserProfile, ClientProxyError> {
do { do {
return try await .success(.init(sdkUserProfile: client.getProfile(userId: userID))) return try await .success(.init(sdkUserProfile: client.getProfile(userId: userID)))
} catch { } catch {
@@ -933,14 +933,14 @@ class ClientProxy: ClientProxyProtocol {
return rooms return rooms
} }
func recentConversationCounterparts() async -> [UserProfileProxy] { func recentConversationCounterparts() async -> [UserProfile] {
let maxResultsToReturn = 5 let maxResultsToReturn = 5
guard case let .success(roomIdentifiers) = await recentlyVisitedRoomIDs() else { guard case let .success(roomIdentifiers) = await recentlyVisitedRoomIDs() else {
return [] return []
} }
var users: OrderedSet<UserProfileProxy> = [] var users: OrderedSet<UserProfile> = []
for roomID in roomIdentifiers { for roomID in roomIdentifiers {
guard case let .joined(roomProxy) = await roomForIdentifier(roomID), guard case let .joined(roomProxy) = await roomForIdentifier(roomID),
@@ -232,7 +232,7 @@ protocol ClientProxyProtocol: AnyObject {
func searchUsers(searchTerm: String, limit: UInt) async -> Result<SearchUsersResultsProxy, ClientProxyError> func searchUsers(searchTerm: String, limit: UInt) async -> Result<SearchUsersResultsProxy, ClientProxyError>
func profile(for userID: String) async -> Result<UserProfileProxy, ClientProxyError> func profile(for userID: String) async -> Result<UserProfile, ClientProxyError>
func roomDirectorySearchProxy() -> RoomDirectorySearchProxyProtocol func roomDirectorySearchProxy() -> RoomDirectorySearchProxyProtocol
@@ -261,7 +261,7 @@ protocol ClientProxyProtocol: AnyObject {
func trackRecentlyVisitedRoom(_ roomID: String) async -> Result<Void, ClientProxyError> func trackRecentlyVisitedRoom(_ roomID: String) async -> Result<Void, ClientProxyError>
func recentlyVisitedRooms(filter: @Sendable (JoinedRoomProxyProtocol) -> Bool) async -> [JoinedRoomProxyProtocol] func recentlyVisitedRooms(filter: @Sendable (JoinedRoomProxyProtocol) -> Bool) async -> [JoinedRoomProxyProtocol]
func recentConversationCounterparts() async -> [UserProfileProxy] func recentConversationCounterparts() async -> [UserProfile]
// MARK: - Crypto // MARK: - Crypto
@@ -78,7 +78,7 @@ extension BaseRoomInfoProxyProtocol {
} }
if isDirect, avatarURL == nil, heroes.count == 1 { if isDirect, avatarURL == nil, heroes.count == 1 {
return .heroes(heroes.map(UserProfileProxy.init)) return .heroes(heroes.map(UserProfile.init))
} }
return .room(id: id, name: displayName, avatarURL: avatarURL) return .room(id: id, name: displayName, avatarURL: avatarURL)
@@ -43,7 +43,7 @@ nonisolated struct RoomSummary {
let isSpace: Bool let isSpace: Bool
let avatarURL: URL? let avatarURL: URL?
let heroes: [UserProfileProxy] let heroes: [UserProfile]
let activeMembersCount: UInt let activeMembersCount: UInt
let lastMessage: AttributedString? let lastMessage: AttributedString?
@@ -354,7 +354,7 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol {
isDirect: roomInfo.isDirect, isDirect: roomInfo.isDirect,
isSpace: roomInfo.isSpace, isSpace: roomInfo.isSpace,
avatarURL: roomInfo.avatarUrl.flatMap(URL.init(string:)), avatarURL: roomInfo.avatarUrl.flatMap(URL.init(string:)),
heroes: roomInfo.heroes.map(UserProfileProxy.init), heroes: roomInfo.heroes.map(UserProfile.init),
activeMembersCount: UInt(roomInfo.activeMembersCount), activeMembersCount: UInt(roomInfo.activeMembersCount),
lastMessage: attributedLastMessage, lastMessage: attributedLastMessage,
lastMessageDate: lastMessageDate, lastMessageDate: lastMessageDate,
@@ -9,7 +9,7 @@ import Foundation
struct UserToInvite: Identifiable { struct UserToInvite: Identifiable {
/// The profile of the user being invited. /// The profile of the user being invited.
var user: UserProfileProxy var user: UserProfile
/// The ID of the user being invited. /// The ID of the user being invited.
var id: String { var id: String {
@@ -89,7 +89,7 @@ class SessionVerificationControllerProxy: SessionVerificationControllerProxyProt
MXLog.info("Acknowledging verification request") MXLog.info("Acknowledging verification request")
do { do {
try await sessionVerificationController.acknowledgeVerificationRequest(senderId: details.senderProfile.userID, flowId: details.flowID) try await sessionVerificationController.acknowledgeVerificationRequest(senderId: details.senderProfile.id, flowId: details.flowID)
return .success(()) return .success(())
} catch { } catch {
MXLog.error("Failed requesting session verification with error: \(error)") MXLog.error("Failed requesting session verification with error: \(error)")
@@ -190,7 +190,7 @@ class SessionVerificationControllerProxy: SessionVerificationControllerProxyProt
fileprivate func didReceiveVerificationRequest(details: MatrixRustSDK.SessionVerificationRequestDetails) { fileprivate func didReceiveVerificationRequest(details: MatrixRustSDK.SessionVerificationRequestDetails) {
MXLog.info("Received verification request \(details)") MXLog.info("Received verification request \(details)")
let details = SessionVerificationRequestDetails(senderProfile: UserProfileProxy(sdkUserProfile: details.senderProfile), let details = SessionVerificationRequestDetails(senderProfile: UserProfile(sdkUserProfile: details.senderProfile),
flowID: details.flowId, flowID: details.flowId,
deviceID: details.deviceId, deviceID: details.deviceId,
deviceDisplayName: details.deviceDisplayName, deviceDisplayName: details.deviceDisplayName,
@@ -31,7 +31,7 @@ enum SessionVerificationControllerProxyAction: Equatable {
} }
struct SessionVerificationRequestDetails: Equatable { struct SessionVerificationRequestDetails: Equatable {
let senderProfile: UserProfileProxy let senderProfile: UserProfile
let flowID: String let flowID: String
let deviceID: String let deviceID: String
let deviceDisplayName: String? let deviceDisplayName: String?
@@ -28,7 +28,7 @@ struct SpaceServiceRoom {
var childrenCount: Int var childrenCount: Int
var joinedMembersCount: Int var joinedMembersCount: Int
var heroes: [UserProfileProxy] var heroes: [UserProfile]
var topic: String? var topic: String?
var canonicalAlias: String? var canonicalAlias: String?
@@ -75,7 +75,7 @@ extension SpaceServiceRoom {
childrenCount = Int(spaceRoom.childrenCount) childrenCount = Int(spaceRoom.childrenCount)
joinedMembersCount = Int(spaceRoom.numJoinedMembers) joinedMembersCount = Int(spaceRoom.numJoinedMembers)
heroes = (spaceRoom.heroes ?? []).map(UserProfileProxy.init) heroes = (spaceRoom.heroes ?? []).map(UserProfile.init)
topic = spaceRoom.topic topic = spaceRoom.topic
canonicalAlias = spaceRoom.canonicalAlias canonicalAlias = spaceRoom.canonicalAlias
@@ -97,7 +97,7 @@ extension SpaceServiceRoom {
isDM: Bool? = nil, isDM: Bool? = nil,
childrenCount: Int = 0, childrenCount: Int = 0,
joinedMembersCount: Int = 0, joinedMembersCount: Int = 0,
heroes: [UserProfileProxy] = [], heroes: [UserProfile] = [],
topic: String? = nil, topic: String? = nil,
canonicalAlias: String? = nil, canonicalAlias: String? = nil,
joinRule: JoinRule? = nil, joinRule: JoinRule? = nil,
@@ -10,7 +10,7 @@ import Foundation
struct UserDiscoverySection { struct UserDiscoverySection {
let type: UserDiscoverySectionType let type: UserDiscoverySectionType
let users: [UserProfileProxy] let users: [UserProfile]
var title: String? { var title: String? {
switch type { switch type {
@@ -15,7 +15,7 @@ final class UserDiscoveryService: UserDiscoveryServiceProtocol {
self.clientProxy = clientProxy self.clientProxy = clientProxy
} }
func searchProfiles(with searchQuery: String) async -> Result<[UserProfileProxy], UserDiscoveryErrorType> { func searchProfiles(with searchQuery: String) async -> Result<[UserProfile], UserDiscoveryErrorType> {
async let queriedProfile = profileIfPossible(with: searchQuery) async let queriedProfile = profileIfPossible(with: searchQuery)
do { do {
@@ -32,7 +32,7 @@ final class UserDiscoveryService: UserDiscoveryServiceProtocol {
} }
} }
private func merge(queriedProfile: UserProfileProxy?, searchResults: SearchUsersResultsProxy) -> [UserProfileProxy] { private func merge(queriedProfile: UserProfile?, searchResults: SearchUsersResultsProxy) -> [UserProfile] {
let searchResults = searchResults.results let searchResults = searchResults.results
guard let queriedProfile else { guard let queriedProfile else {
@@ -40,13 +40,13 @@ final class UserDiscoveryService: UserDiscoveryServiceProtocol {
} }
let filteredSearchResult = searchResults.filter { let filteredSearchResult = searchResults.filter {
$0.userID != queriedProfile.userID $0.id != queriedProfile.id
} }
return [queriedProfile] + filteredSearchResult return [queriedProfile] + filteredSearchResult
} }
private func profileIfPossible(with searchQuery: String) async -> UserProfileProxy? { private func profileIfPossible(with searchQuery: String) async -> UserProfile? {
guard searchQuery.isMatrixIdentifier, searchQuery != clientProxy.userID else { guard searchQuery.isMatrixIdentifier, searchQuery != clientProxy.userID else {
return nil return nil
} }
@@ -57,9 +57,9 @@ final class UserDiscoveryService: UserDiscoveryServiceProtocol {
return getProfileResult ?? .init(userID: searchQuery) return getProfileResult ?? .init(userID: searchQuery)
} }
private func filterAccountOwner(_ profiles: [UserProfileProxy]) -> [UserProfileProxy] { private func filterAccountOwner(_ profiles: [UserProfile]) -> [UserProfile] {
let accountOwnerID = clientProxy.userID let accountOwnerID = clientProxy.userID
return profiles.filter { $0.userID != accountOwnerID } return profiles.filter { $0.id != accountOwnerID }
} }
} }
@@ -14,5 +14,5 @@ enum UserDiscoveryErrorType: Error {
// sourcery: AutoMockable // sourcery: AutoMockable
protocol UserDiscoveryServiceProtocol { protocol UserDiscoveryServiceProtocol {
func searchProfiles(with searchQuery: String) async -> Result<[UserProfileProxy], UserDiscoveryErrorType> func searchProfiles(with searchQuery: String) async -> Result<[UserProfile], UserDiscoveryErrorType>
} }
@@ -9,37 +9,37 @@
import Foundation import Foundation
import MatrixRustSDK import MatrixRustSDK
nonisolated struct UserProfileProxy: Equatable, Hashable { nonisolated struct UserProfile: Hashable, Identifiable {
let userID: String let id: String
let displayName: String? let displayName: String?
let avatarURL: URL? let avatarURL: URL?
init(userID: String, displayName: String? = nil, avatarURL: URL? = nil) { init(userID: String, displayName: String? = nil, avatarURL: URL? = nil) {
self.userID = userID id = userID
self.displayName = displayName self.displayName = displayName
self.avatarURL = avatarURL self.avatarURL = avatarURL
} }
init(member: RoomMemberDetails) { init(member: RoomMemberDetails) {
userID = member.id id = member.id
displayName = member.isBanned ? nil : member.name displayName = member.isBanned ? nil : member.name
avatarURL = member.isBanned ? nil : member.avatarURL avatarURL = member.isBanned ? nil : member.avatarURL
} }
init(sender: TimelineItemSender) { init(sender: TimelineItemSender) {
userID = sender.id id = sender.id
displayName = sender.displayName displayName = sender.displayName
avatarURL = sender.avatarURL avatarURL = sender.avatarURL
} }
init(sdkUserProfile: MatrixRustSDK.UserProfile) { init(sdkUserProfile: MatrixRustSDK.UserProfile) {
userID = sdkUserProfile.userId id = sdkUserProfile.userId
displayName = sdkUserProfile.displayName displayName = sdkUserProfile.displayName
avatarURL = sdkUserProfile.avatarUrl.flatMap(URL.init(string:)) avatarURL = sdkUserProfile.avatarUrl.flatMap(URL.init(string:))
} }
init(sdkRoomHero: MatrixRustSDK.RoomHero) { init(sdkRoomHero: MatrixRustSDK.RoomHero) {
userID = sdkRoomHero.userId id = sdkRoomHero.userId
displayName = sdkRoomHero.displayName displayName = sdkRoomHero.displayName
avatarURL = sdkRoomHero.avatarUrl.flatMap(URL.init(string:)) avatarURL = sdkRoomHero.avatarUrl.flatMap(URL.init(string:))
} }
@@ -56,19 +56,13 @@ nonisolated struct UserProfileProxy: Equatable, Hashable {
} }
struct SearchUsersResultsProxy { struct SearchUsersResultsProxy {
let results: [UserProfileProxy] let results: [UserProfile]
let limited: Bool let limited: Bool
} }
extension SearchUsersResultsProxy { extension SearchUsersResultsProxy {
init(sdkResults: MatrixRustSDK.SearchUsersResults) { init(sdkResults: MatrixRustSDK.SearchUsersResults) {
results = sdkResults.results.map(UserProfileProxy.init) results = sdkResults.results.map(UserProfile.init)
limited = sdkResults.limited limited = sdkResults.limited
} }
} }
extension UserProfileProxy: Identifiable {
var id: String {
userID
}
}
@@ -19,7 +19,7 @@ final class CreateRoomScreenViewModelTests {
private let appSettings: AppSettings private let appSettings: AppSettings
private let usersSubject = CurrentValueSubject<[UserProfileProxy], Never>([]) private let usersSubject = CurrentValueSubject<[UserProfile], Never>([])
var context: CreateRoomScreenViewModel.Context { var context: CreateRoomScreenViewModel.Context {
viewModel.context viewModel.context
@@ -29,7 +29,7 @@ final class InviteUsersScreenViewModelTests {
#expect(context.viewState.selectedUsers.isEmpty) #expect(context.viewState.selectedUsers.isEmpty)
context.send(viewAction: .toggleUser(.mockAlice)) context.send(viewAction: .toggleUser(.mockAlice))
#expect(context.viewState.selectedUsers.count == 1) #expect(context.viewState.selectedUsers.count == 1)
#expect(context.viewState.selectedUsers.first?.userID == UserProfileProxy.mockAlice.userID) #expect(context.viewState.selectedUsers.first?.id == UserProfile.mockAlice.id)
} }
@Test @Test
@@ -41,7 +41,7 @@ final class InviteUsersScreenViewModelTests {
#expect(context.viewState.selectedUsers.isEmpty) #expect(context.viewState.selectedUsers.isEmpty)
context.send(viewAction: .toggleUser(.mockAlice)) context.send(viewAction: .toggleUser(.mockAlice))
#expect(context.viewState.selectedUsers.count == 1) #expect(context.viewState.selectedUsers.count == 1)
#expect(context.viewState.selectedUsers.first?.userID == UserProfileProxy.mockAlice.userID) #expect(context.viewState.selectedUsers.first?.id == UserProfile.mockAlice.id)
context.send(viewAction: .toggleUser(.mockAlice)) context.send(viewAction: .toggleUser(.mockAlice))
#expect(context.viewState.selectedUsers.isEmpty) #expect(context.viewState.selectedUsers.isEmpty)
} }
@@ -55,7 +55,7 @@ final class InviteUsersScreenViewModelTests {
#expect(context.viewState.selectedUsers.isEmpty) #expect(context.viewState.selectedUsers.isEmpty)
context.send(viewAction: .toggleUser(.mockAlice)) context.send(viewAction: .toggleUser(.mockAlice))
#expect(context.viewState.selectedUsers.count == 1) #expect(context.viewState.selectedUsers.count == 1)
#expect(context.viewState.selectedUsers.first?.userID == UserProfileProxy.mockAlice.userID) #expect(context.viewState.selectedUsers.first?.id == UserProfile.mockAlice.id)
context.send(viewAction: .toggleUser(.mockAlice)) context.send(viewAction: .toggleUser(.mockAlice))
#expect(context.viewState.selectedUsers.isEmpty) #expect(context.viewState.selectedUsers.isEmpty)
} }
@@ -174,7 +174,7 @@ final class InviteUsersScreenViewModelTests {
appSettings: .volatile()) appSettings: .volatile())
// The locked invitee starts pre-selected and locked. // The locked invitee starts pre-selected and locked.
#expect(context.viewState.selectedUsers.map(\.userID) == [UserProfileProxy.mockAlice.userID]) #expect(context.viewState.selectedUsers.map(\.id) == [UserProfile.mockAlice.id])
#expect(context.viewState.isInviteeMandatory(.mockAlice)) #expect(context.viewState.isInviteeMandatory(.mockAlice))
// The proceed button is disabled while the only selected user is the locked invitee. // The proceed button is disabled while the only selected user is the locked invitee.
#expect(!context.viewState.hasInvitableSelectedUsers) #expect(!context.viewState.hasInvitableSelectedUsers)
@@ -218,7 +218,7 @@ final class InviteUsersScreenViewModelTests {
#expect(args.topic == nil) #expect(args.topic == nil)
#expect(args.accessType == .private) #expect(args.accessType == .private)
#expect(args.isSpace == false) #expect(args.isSpace == false)
#expect(args.userIDs == [UserProfileProxy.mockAlice.userID, UserProfileProxy.mockBob.userID]) #expect(args.userIDs == [UserProfile.mockAlice.id, UserProfile.mockBob.id])
#expect(args.avatarURL == nil) #expect(args.avatarURL == nil)
#expect(args.aliasLocalPart == nil) #expect(args.aliasLocalPart == nil)
} }
+1 -1
View File
@@ -15,7 +15,7 @@ import Testing
struct RoomSummaryTests { struct RoomSummaryTests {
// swiftlint:disable:next large_tuple // swiftlint:disable:next large_tuple
let roomDetails: (id: String, name: String, avatarURL: URL) = ("room_id", "Room Name", "mxc://hs.tld/room/avatar") let roomDetails: (id: String, name: String, avatarURL: URL) = ("room_id", "Room Name", "mxc://hs.tld/room/avatar")
let heroes = [UserProfileProxy(userID: "hero_1", displayName: "Hero 1", avatarURL: "mxc://hs.tld/user/avatar")] let heroes = [UserProfile(userID: "hero_1", displayName: "Hero 1", avatarURL: "mxc://hs.tld/user/avatar")]
@Test @Test
func roomAvatar() { func roomAvatar() {
@@ -14,7 +14,7 @@ struct UserDiscoveryServiceTest {
private var service: UserDiscoveryService private var service: UserDiscoveryService
private var clientProxy: ClientProxyMock private var clientProxy: ClientProxyMock
private var searchResults: [UserProfileProxy] { private var searchResults: [UserProfile] {
[.mockAlice, .mockBob, .mockCharlie] [.mockAlice, .mockBob, .mockCharlie]
} }
@@ -25,7 +25,7 @@ struct UserDiscoveryServiceTest {
@Test @Test
func queryShowingResults() async { func queryShowingResults() async {
clientProxy.searchUsersSearchTermLimitReturnValue = .success(.init(results: [UserProfileProxy.mockAlice], limited: true)) clientProxy.searchUsersSearchTermLimitReturnValue = .success(.init(results: [UserProfile.mockAlice], limited: true))
let results = await (try? search(query: "AAA").get()) ?? [] let results = await (try? search(query: "AAA").get()) ?? []
assertSearchResults(results, toBe: 1) assertSearchResults(results, toBe: 1)
@@ -33,7 +33,7 @@ struct UserDiscoveryServiceTest {
@Test @Test
func ownerIsFiltered() async { func ownerIsFiltered() async {
clientProxy.searchUsersSearchTermLimitReturnValue = .success(.init(results: [UserProfileProxy(userID: "@foo:matrix.org")], limited: true)) clientProxy.searchUsersSearchTermLimitReturnValue = .success(.init(results: [UserProfile(userID: "@foo:matrix.org")], limited: true))
let results = await (try? search(query: "AAA").get()) ?? [] let results = await (try? search(query: "AAA").get()) ?? []
assertSearchResults(results, toBe: 0) assertSearchResults(results, toBe: 0)
@@ -104,7 +104,7 @@ struct UserDiscoveryServiceTest {
let results = await (try? search(query: "@a:b.com").get()) ?? [] let results = await (try? search(query: "@a:b.com").get()) ?? []
assertSearchResults(results, toBe: 3) assertSearchResults(results, toBe: 3)
let firstUserID = results.first?.userID let firstUserID = results.first?.id
#expect(firstUserID == "@bob:matrix.org") #expect(firstUserID == "@bob:matrix.org")
#expect(clientProxy.profileForCalled) #expect(clientProxy.profileForCalled)
} }
@@ -116,20 +116,20 @@ struct UserDiscoveryServiceTest {
let results = await (try? search(query: "@a:b.com").get()) ?? [] let results = await (try? search(query: "@a:b.com").get()) ?? []
let firstUserID = results.first?.userID let firstUserID = results.first?.id
#expect(firstUserID == "@a:b.com") #expect(firstUserID == "@a:b.com")
#expect(clientProxy.profileForCalled) #expect(clientProxy.profileForCalled)
} }
// MARK: - Private // MARK: - Private
private func assertSearchResults(_ results: [UserProfileProxy], toBe count: Int) { private func assertSearchResults(_ results: [UserProfile], toBe count: Int) {
#expect(count >= 0) #expect(count >= 0)
#expect(results.count == count) #expect(results.count == count)
#expect(results.isEmpty == (count == 0)) #expect(results.isEmpty == (count == 0))
} }
private func search(query: String) async -> Result<[UserProfileProxy], UserDiscoveryErrorType> { private func search(query: String) async -> Result<[UserProfile], UserDiscoveryErrorType> {
await service.searchProfiles(with: query) await service.searchProfiles(with: query)
} }
} }
@@ -15,11 +15,11 @@ struct UserProfileScreenViewModelTests {
func initialState() async throws { func initialState() async throws {
let userIndicatorController = UserIndicatorControllerMock() let userIndicatorController = UserIndicatorControllerMock()
let profile = UserProfileProxy(userID: "@alice:matrix.org", displayName: "Alice", avatarURL: .mockMXCAvatar) let profile = UserProfile(userID: "@alice:matrix.org", displayName: "Alice", avatarURL: .mockMXCAvatar)
let clientProxy = ClientProxyMock(.init()) let clientProxy = ClientProxyMock(.init())
clientProxy.profileForReturnValue = .success(profile) clientProxy.profileForReturnValue = .success(profile)
let viewModel = UserProfileScreenViewModel(userID: profile.userID, let viewModel = UserProfileScreenViewModel(userID: profile.id,
isPresentedModally: false, isPresentedModally: false,
userSession: UserSessionMock(.init(clientProxy: clientProxy)), userSession: UserSessionMock(.init(clientProxy: clientProxy)),
userIndicatorController: userIndicatorController, userIndicatorController: userIndicatorController,
@@ -39,11 +39,11 @@ struct UserProfileScreenViewModelTests {
func initialStateAccountOwner() async throws { func initialStateAccountOwner() async throws {
let userIndicatorController = UserIndicatorControllerMock() let userIndicatorController = UserIndicatorControllerMock()
let profile = UserProfileProxy(userID: RoomMemberProxyMock.mockMe.userID, displayName: "Me", avatarURL: .mockMXCAvatar) let profile = UserProfile(userID: RoomMemberProxyMock.mockMe.userID, displayName: "Me", avatarURL: .mockMXCAvatar)
let clientProxy = ClientProxyMock(.init()) let clientProxy = ClientProxyMock(.init())
clientProxy.profileForReturnValue = .success(profile) clientProxy.profileForReturnValue = .success(profile)
let viewModel = UserProfileScreenViewModel(userID: profile.userID, let viewModel = UserProfileScreenViewModel(userID: profile.id,
isPresentedModally: false, isPresentedModally: false,
userSession: UserSessionMock(.init(clientProxy: clientProxy)), userSession: UserSessionMock(.init(clientProxy: clientProxy)),
userIndicatorController: userIndicatorController, userIndicatorController: userIndicatorController,
@@ -63,13 +63,13 @@ struct UserProfileScreenViewModelTests {
func startingDmWithUnknownUserFetchesIdentity() async throws { func startingDmWithUnknownUserFetchesIdentity() async throws {
let userIndicatorController = UserIndicatorControllerMock() let userIndicatorController = UserIndicatorControllerMock()
let profile = UserProfileProxy.mockAlice let profile = UserProfile.mockAlice
let clientProxy = ClientProxyMock(.init()) let clientProxy = ClientProxyMock(.init())
clientProxy.directRoomForUserIDReturnValue = .success(nil) clientProxy.directRoomForUserIDReturnValue = .success(nil)
clientProxy.userIdentityForFallBackToServerReturnValue = .success(nil) clientProxy.userIdentityForFallBackToServerReturnValue = .success(nil)
let viewModel = UserProfileScreenViewModel(userID: profile.userID, let viewModel = UserProfileScreenViewModel(userID: profile.id,
isPresentedModally: false, isPresentedModally: false,
userSession: UserSessionMock(.init(clientProxy: clientProxy)), userSession: UserSessionMock(.init(clientProxy: clientProxy)),
userIndicatorController: userIndicatorController, userIndicatorController: userIndicatorController,