diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 9479ff79c..b978e7534 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -10361,7 +10361,7 @@ repositoryURL = "https://github.com/element-hq/matrix-rust-components-swift"; requirement = { kind = exactVersion; - version = 26.07.15; + version = 26.07.20; }; }; 701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 77222abb5..6eb8fcb84 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "25b583bba526740b75c4ede29c7c1d25d7f9d2679fa8e9a795f995fd9f01ae5e", + "originHash" : "ed9be16560fed06ea598988a9fa7423a0bfb74cb245df6ae821e9bef9220a45d", "pins" : [ { "identity" : "compound-design-tokens", @@ -167,8 +167,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/element-hq/matrix-rust-components-swift", "state" : { - "revision" : "1bef4822a345b6c9d876f6f8bb5c5e26df6abf02", - "version" : "26.7.15" + "revision" : "bf41c1a369bae29f2a8e77c85c4cd9a718e1d973", + "version" : "26.7.20" } }, { diff --git a/ElementX/Sources/Mocks/ClientProxyMock.swift b/ElementX/Sources/Mocks/ClientProxyMock.swift index aef83ee8f..07db708f8 100644 --- a/ElementX/Sources/Mocks/ClientProxyMock.swift +++ b/ElementX/Sources/Mocks/ClientProxyMock.swift @@ -88,7 +88,7 @@ extension ClientProxyMock { } joinRoomAliasReturnValue = .success(()) uploadMediaReturnValue = .failure(.sdkError(ClientProxyMockError.generic)) - loadUserProfileReturnValue = .success(()) + loadUserProfileIfNeededReturnValue = .success(()) setUserDisplayNameReturnValue = .failure(.sdkError(ClientProxyMockError.generic)) setUserAvatarMediaReturnValue = .success(()) removeUserAvatarReturnValue = .success(()) diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index 2f7c3674d..7343d50be 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -3094,33 +3094,33 @@ nonisolated class ClientProxyMock: ClientProxyProtocol, @unchecked Sendable { return reportRoomForIdentifierReasonReturnValue } } - //MARK: - loadUserProfile + //MARK: - loadUserProfileIfNeeded - private let loadUserProfileCallsCountLock = NSLock() - private nonisolated(unsafe) var loadUserProfileUnderlyingCallsCount = 0 - var loadUserProfileCallsCount: Int { - get { loadUserProfileCallsCountLock.withLock { loadUserProfileUnderlyingCallsCount } } - set { loadUserProfileCallsCountLock.withLock { loadUserProfileUnderlyingCallsCount = newValue } } + private let loadUserProfileIfNeededCallsCountLock = NSLock() + private nonisolated(unsafe) var loadUserProfileIfNeededUnderlyingCallsCount = 0 + var loadUserProfileIfNeededCallsCount: Int { + get { loadUserProfileIfNeededCallsCountLock.withLock { loadUserProfileIfNeededUnderlyingCallsCount } } + set { loadUserProfileIfNeededCallsCountLock.withLock { loadUserProfileIfNeededUnderlyingCallsCount = newValue } } } - var loadUserProfileCalled: Bool { - return loadUserProfileCallsCount > 0 + var loadUserProfileIfNeededCalled: Bool { + return loadUserProfileIfNeededCallsCount > 0 } - private let loadUserProfileReturnValueLock = NSLock() - private nonisolated(unsafe) var loadUserProfileUnderlyingReturnValue: Result! - var loadUserProfileReturnValue: Result! { - get { loadUserProfileReturnValueLock.withLock { loadUserProfileUnderlyingReturnValue } } - set { loadUserProfileReturnValueLock.withLock { loadUserProfileUnderlyingReturnValue = newValue } } + private let loadUserProfileIfNeededReturnValueLock = NSLock() + private nonisolated(unsafe) var loadUserProfileIfNeededUnderlyingReturnValue: Result! + var loadUserProfileIfNeededReturnValue: Result! { + get { loadUserProfileIfNeededReturnValueLock.withLock { loadUserProfileIfNeededUnderlyingReturnValue } } + set { loadUserProfileIfNeededReturnValueLock.withLock { loadUserProfileIfNeededUnderlyingReturnValue = newValue } } } - nonisolated(unsafe) var loadUserProfileClosure: (() async -> Result)? + nonisolated(unsafe) var loadUserProfileIfNeededClosure: (() async -> Result)? @discardableResult - @concurrent func loadUserProfile() async -> Result { - loadUserProfileCallsCountLock.withLock { loadUserProfileUnderlyingCallsCount += 1 } - if let loadUserProfileClosure = loadUserProfileClosure { - return await loadUserProfileClosure() + @concurrent func loadUserProfileIfNeeded() async -> Result { + loadUserProfileIfNeededCallsCountLock.withLock { loadUserProfileIfNeededUnderlyingCallsCount += 1 } + if let loadUserProfileIfNeededClosure = loadUserProfileIfNeededClosure { + return await loadUserProfileIfNeededClosure() } else { - return loadUserProfileReturnValue + return loadUserProfileIfNeededReturnValue } } //MARK: - setUserDisplayName diff --git a/ElementX/Sources/Mocks/JoinedRoomProxyMock.swift b/ElementX/Sources/Mocks/JoinedRoomProxyMock.swift index cd0190b33..463a0150c 100644 --- a/ElementX/Sources/Mocks/JoinedRoomProxyMock.swift +++ b/ElementX/Sources/Mocks/JoinedRoomProxyMock.swift @@ -176,7 +176,9 @@ private extension RoomHero { init(from memberProxy: RoomMemberProxyMock) { self.init(userId: memberProxy.userID, displayName: memberProxy.displayName, - avatarUrl: memberProxy.avatarURL?.absoluteString) + avatarUrl: memberProxy.avatarURL?.absoluteString, + status: memberProxy.status.raw?.rustValue, + call: memberProxy.status.call?.rustValue) } } diff --git a/ElementX/Sources/Other/SDKListener.swift b/ElementX/Sources/Other/SDKListener.swift index 2de4c8716..25134348e 100644 --- a/ElementX/Sources/Other/SDKListener.swift +++ b/ElementX/Sources/Other/SDKListener.swift @@ -116,6 +116,12 @@ nonisolated extension SDKListener: VerificationStateListener where T == Verifica } } +nonisolated extension SDKListener: ProfileListener where T == MatrixRustSDK.UserProfile { + func onUpdate(profile: MatrixRustSDK.UserProfile) { + onUpdateClosure(profile) + } +} + nonisolated extension SDKListener: IgnoredUsersListener where T == [String] { func call(ignoredUserIds: [String]) { onUpdateClosure(ignoredUserIds) diff --git a/ElementX/Sources/Other/SwiftUI/Views/RoomHeaderView.swift b/ElementX/Sources/Other/SwiftUI/Views/RoomHeaderView.swift index a6563c5a7..ceaca8f92 100644 --- a/ElementX/Sources/Other/SwiftUI/Views/RoomHeaderView.swift +++ b/ElementX/Sources/Other/SwiftUI/Views/RoomHeaderView.swift @@ -12,7 +12,7 @@ import SwiftUI struct RoomHeaderView: View { struct DMRecipientDetails { - var status: UserStatus? + var statusEmoji: Character? var verification: UserIdentityVerificationState? } @@ -61,7 +61,7 @@ struct RoomHeaderView: View { .foregroundStyle(.compound.textPrimary) .accessibilityIdentifier(A11yIdentifiers.roomScreen.name) - if let statusEmoji = dmRecipientDetails.status?.displayed?.emoji { + if let statusEmoji = dmRecipientDetails.statusEmoji { Text(String(statusEmoji)) .font(.compound.bodyLG) .foregroundStyle(.compound.textPrimary) @@ -179,7 +179,8 @@ struct RoomHeaderView_Previews: PreviewProvider, TestablePreview { roomAvatar: .room(id: "1", name: roomName, avatarURL: avatarURL), - dmRecipientDetails: .init(status: userStatus, verification: verificationState), + dmRecipientDetails: .init(statusEmoji: userStatus?.displayed?.emoji, + verification: verificationState), roomHistorySharingState: historySharingState, mediaProvider: MediaProviderMock(.init())) { } diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift index 31dd16cb1..76a843f1a 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift @@ -348,7 +348,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol // Delay user profile detail loading until after the initial room list loads if roomListMode == .rooms { Task { - await self.userSession.clientProxy.loadUserProfile() + await self.userSession.clientProxy.loadUserProfileIfNeeded() } } } diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift index 82d248a82..23be4f835 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift @@ -335,6 +335,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol private func updateRoomInfo(_ roomInfo: RoomInfoProxyProtocol) { state.roomTitle = roomInfo.displayName ?? roomProxy.id state.roomAvatar = roomInfo.avatar + state.dmRecipientDetails.statusEmoji = roomInfo.statusEmoji state.hasOngoingCall = roomInfo.hasRoomCall state.activeRoomCallIntent = roomInfo.activeRoomCallIntent state.hasSuccessor = roomInfo.successor != nil diff --git a/ElementX/Sources/Screens/Settings/SettingsScreen/SettingsScreenViewModel.swift b/ElementX/Sources/Screens/Settings/SettingsScreen/SettingsScreenViewModel.swift index e8049c73b..3e8f11950 100644 --- a/ElementX/Sources/Screens/Settings/SettingsScreen/SettingsScreenViewModel.swift +++ b/ElementX/Sources/Screens/Settings/SettingsScreen/SettingsScreenViewModel.swift @@ -86,7 +86,7 @@ class SettingsScreenViewModel: SettingsScreenViewModelType, SettingsScreenViewMo .store(in: &cancellables) Task { - await userSession.clientProxy.loadUserProfile() + await userSession.clientProxy.loadUserProfileIfNeeded() await state.accountProfileURL = userSession.clientProxy.accountURL(action: .profile) } } diff --git a/ElementX/Sources/Screens/Settings/SettingsScreen/View/SettingsScreen.swift b/ElementX/Sources/Screens/Settings/SettingsScreen/View/SettingsScreen.swift index 94047717a..135912a10 100644 --- a/ElementX/Sources/Screens/Settings/SettingsScreen/View/SettingsScreen.swift +++ b/ElementX/Sources/Screens/Settings/SettingsScreen/View/SettingsScreen.swift @@ -98,6 +98,8 @@ struct SettingsScreen: View { SettingsScreenUserStatusPickerView { action in context.send(viewAction: .userStatus(action)) } + .presentationDetents([.medium]) // Stop using a List and calculate the exact height? + .presentationBackground(.compound.bgCanvasDefault) } } } diff --git a/ElementX/Sources/Screens/Settings/UserDetailsEditScreen/UserDetailsEditScreenViewModel.swift b/ElementX/Sources/Screens/Settings/UserDetailsEditScreen/UserDetailsEditScreenViewModel.swift index a7ddc5d9e..1987156db 100644 --- a/ElementX/Sources/Screens/Settings/UserDetailsEditScreen/UserDetailsEditScreenViewModel.swift +++ b/ElementX/Sources/Screens/Settings/UserDetailsEditScreen/UserDetailsEditScreenViewModel.swift @@ -46,7 +46,7 @@ class UserDetailsEditScreenViewModel: UserDetailsEditScreenViewModelType, UserDe .store(in: &cancellables) Task { - await self.clientProxy.loadUserProfile() + await self.clientProxy.loadUserProfileIfNeeded() state.canEditAvatar = await clientProxy.capabilities.canChangeAvatar() state.canEditDisplayName = await clientProxy.capabilities.canChangeDisplayName() } diff --git a/ElementX/Sources/Screens/ThreadTimelineScreen/ThreadTimelineScreenViewModel.swift b/ElementX/Sources/Screens/ThreadTimelineScreen/ThreadTimelineScreenViewModel.swift index 318953a0b..87a83e9a4 100644 --- a/ElementX/Sources/Screens/ThreadTimelineScreen/ThreadTimelineScreenViewModel.swift +++ b/ElementX/Sources/Screens/ThreadTimelineScreen/ThreadTimelineScreenViewModel.swift @@ -103,6 +103,8 @@ class ThreadTimelineScreenViewModel: ThreadTimelineScreenViewModelType, ThreadTi private func updateRoomInfo(_ roomInfo: RoomInfoProxyProtocol) { state.roomTitle = roomInfo.displayName ?? roomProxy.id state.roomAvatar = roomInfo.avatar + state.dmRecipientDetails.statusEmoji = roomInfo.statusEmoji + if let powerLevels = roomInfo.powerLevels { state.canSendMessage = powerLevels.canOwnUser(sendMessage: .roomMessage) } diff --git a/ElementX/Sources/Services/Client/ClientProxy.swift b/ElementX/Sources/Services/Client/ClientProxy.swift index eaef5724d..162ae4277 100644 --- a/ElementX/Sources/Services/Client/ClientProxy.swift +++ b/ElementX/Sources/Services/Client/ClientProxy.swift @@ -32,6 +32,9 @@ class ClientProxy: ClientProxyProtocol { // periphery: ignore - only for retain private var syncServiceStateUpdateTaskHandle: TaskHandle? + // periphery:ignore - required for instance retention in the rust codebase + private var userProfileListenerTaskHandle: TaskHandle? + // periphery:ignore - required for instance retention in the rust codebase private var ignoredUsersListenerTaskHandle: TaskHandle? @@ -278,9 +281,13 @@ class ClientProxy: ClientProxyProtocol { try await client.setUtdDelegate(utdDelegate: ClientDecryptionErrorDelegate(actionsSubject: actionsSubject)) - loadUserAvatarURLFromCache() + let canSubscribeToUserProfile = appSettings.userStatusEnabled // TODO: @pixlwave to add a /versions check too. - await setupSubscriptions() + if !canSubscribeToUserProfile { + loadUserAvatarURLFromCache() + } + + await setupSubscriptions(canSubscribeToUserProfile: canSubscribeToUserProfile) Task { do { @@ -686,14 +693,18 @@ class ClientProxy: ClientProxyProtocol { } } - func loadUserProfile() async -> Result { + func loadUserProfileIfNeeded() async -> Result { + // There's no need to load the profile if we're subscribed to it via /sync + guard userProfileListenerTaskHandle == nil else { return .success(()) } + do { async let displayName = client.displayName() async let avatarURLString = client.avatarUrl() let profile = try await UserProfile(userID: userID, displayName: displayName, - avatarURL: avatarURLString.flatMap(URL.init)) + avatarURL: avatarURLString.flatMap(URL.init), + status: userProfileSubject.value.status) loadCachedAvatarURLTask?.cancel() userProfileSubject.send(profile) return .success(()) @@ -706,7 +717,7 @@ class ClientProxy: ClientProxyProtocol { func setUserDisplayName(_ name: String) async -> Result { do { try await client.setDisplayName(name: name) - Task { await self.loadUserProfile() } + Task { await self.loadUserProfileIfNeeded() } return .success(()) } catch { MXLog.error("Failed setting user display name with error: \(error)") @@ -723,7 +734,7 @@ class ClientProxy: ClientProxyProtocol { do { let data = try Data(contentsOf: imageURL) try await client.uploadAvatar(mimeType: mimeType, data: data) - Task { await self.loadUserProfile() } + Task { await self.loadUserProfileIfNeeded() } return .success(()) } catch { MXLog.error("Failed setting user avatar with error: \(error)") @@ -734,7 +745,7 @@ class ClientProxy: ClientProxyProtocol { func removeUserAvatar() async -> Result { do { try await client.removeAvatar() - Task { await self.loadUserProfile() } + Task { await self.loadUserProfileIfNeeded() } return .success(()) } catch { MXLog.error("Failed removing user avatar with error: \(error)") @@ -746,7 +757,7 @@ class ClientProxy: ClientProxyProtocol { func setUserStatus(_ status: UserStatus.Raw) async -> Result { do { try await client.setUserStatus(status: status.rustValue) - Task { await self.loadUserProfile() } + // No need to refresh the profile, we only support user status with the profiles /sync extension. return .success(()) } catch { MXLog.error("Failed setting user status with error: \(error)") @@ -758,7 +769,7 @@ class ClientProxy: ClientProxyProtocol { func removeUserStatus() async -> Result { do { try await client.clearUserStatus() - Task { await self.loadUserProfile() } + Task { await self.loadUserProfileIfNeeded() } return .success(()) } catch { MXLog.error("Failed removing user status with error: \(error)") @@ -1017,7 +1028,7 @@ class ClientProxy: ClientProxyProtocol { // MARK: - Private - private func setupSubscriptions() async { + private func setupSubscriptions(canSubscribeToUserProfile: Bool) async { networkMonitor.reachabilityPublisher .removeDuplicates() .receive(on: DispatchQueue.main) @@ -1030,6 +1041,12 @@ class ClientProxy: ClientProxyProtocol { } .store(in: &cancellables) + if canSubscribeToUserProfile { + userProfileListenerTaskHandle = try? client.subscribeToOwnProfile(listener: SDKListener.onMainActor { [weak self] profile in + self?.userProfileSubject.send(.init(rustUserProfile: profile)) + }) + } + ignoredUsersListenerTaskHandle = client.subscribeToIgnoredUsers(listener: SDKListener.onMainActor { [weak self] ignoredUsers in self?.ignoredUsersSubject.send(ignoredUsers) }) @@ -1201,7 +1218,8 @@ class ClientProxy: ClientProxyProtocol { let profile = self.userProfileSubject.value self.userProfileSubject.value = UserProfile(userID: profile.id, displayName: profile.displayName, - avatarURL: urlString.flatMap(URL.init)) + avatarURL: urlString.flatMap(URL.init), + status: profile.status) } catch { MXLog.error("Failed to look for the avatar url in the cache: \(error)") } @@ -1481,11 +1499,14 @@ private struct ClientProxyServices { init(client: ClientProtocol, notificationSettings: NotificationSettingsProxyProtocol, appSettings: AppSettings) async throws { - let syncService = try await client + var syncServiceBuilder = client .syncService() .withOfflineMode() .withSharePos(enable: true) - .finish() + if appSettings.userStatusEnabled { + syncServiceBuilder = syncServiceBuilder.withProfilesExtension() + } + let syncService = try await syncServiceBuilder.finish() let roomListService = syncService.roomListService() diff --git a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift index b60554947..d8e0496bf 100644 --- a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift +++ b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift @@ -212,7 +212,11 @@ protocol ClientProxyProtocol: AnyObject { /// Will only work for rooms that are in our room list/local store func reportRoomForIdentifier(_ identifier: String, reason: String) async -> Result - @discardableResult func loadUserProfile() async -> Result + /// Loads the user's own profile when the server doesn't support MSC4262 and both returns the profile + /// as well as publishing it via ``userProfilePublisher``. + /// + /// When the server does support the MSC, then the client automatically publishes profile and keeps it up to date. + @discardableResult func loadUserProfileIfNeeded() async -> Result func setUserDisplayName(_ name: String) async -> Result func setUserAvatar(media: MediaInfo) async -> Result func removeUserAvatar() async -> Result diff --git a/ElementX/Sources/Services/Room/RoomInfoProxyProtocol.swift b/ElementX/Sources/Services/Room/RoomInfoProxyProtocol.swift index 209218cda..b21e84880 100644 --- a/ElementX/Sources/Services/Room/RoomInfoProxyProtocol.swift +++ b/ElementX/Sources/Services/Room/RoomInfoProxyProtocol.swift @@ -94,6 +94,11 @@ extension BaseRoomInfoProxyProtocol { return .room(id: id, name: displayName, avatarURL: avatarURL) } + + var statusEmoji: Character? { + guard case let .heroes(heroes) = avatar else { return nil } + return heroes.first?.status.displayed?.emoji + } } extension RoomInfoProxyProtocol { diff --git a/ElementX/Sources/Services/Room/RoomSummary/RoomSummary.swift b/ElementX/Sources/Services/Room/RoomSummary/RoomSummary.swift index 2b7bce69d..9459daeae 100644 --- a/ElementX/Sources/Services/Room/RoomSummary/RoomSummary.swift +++ b/ElementX/Sources/Services/Room/RoomSummary/RoomSummary.swift @@ -186,7 +186,7 @@ nonisolated extension RoomSummary { } var statusEmoji: Character? { - guard isDirect, case let .heroes(heroes) = avatar, heroes.count == 1 else { return nil } + guard case let .heroes(heroes) = avatar else { return nil } return heroes.first?.status.displayed?.emoji } } diff --git a/ElementX/Sources/Services/Users/UserProfile.swift b/ElementX/Sources/Services/Users/UserProfile.swift index e101c89dc..bad46009d 100644 --- a/ElementX/Sources/Services/Users/UserProfile.swift +++ b/ElementX/Sources/Services/Users/UserProfile.swift @@ -47,7 +47,7 @@ nonisolated struct UserProfile: Hashable, Identifiable { id = rustRoomHero.userId displayName = rustRoomHero.displayName avatarURL = rustRoomHero.avatarUrl.flatMap(URL.init(string:)) - status = .init() // Requires https://github.com/matrix-org/matrix-rust-sdk/pull/6733 + status = .init(rustStatus: rustRoomHero.status, rustCall: rustRoomHero.call) } init(member: RoomMemberProxyProtocol) { diff --git a/SDKMocks/Sources/Generated/SDKGeneratedMocks.swift b/SDKMocks/Sources/Generated/SDKGeneratedMocks.swift index a1cc50f56..8dcfc341e 100644 --- a/SDKMocks/Sources/Generated/SDKGeneratedMocks.swift +++ b/SDKMocks/Sources/Generated/SDKGeneratedMocks.swift @@ -3624,6 +3624,53 @@ open class ClientSDKMock: MatrixRustSDK.Client, @unchecked Sendable { } } + //MARK: - subscribeToOwnProfile + + open var subscribeToOwnProfileListenerThrowableError: Error? + private let subscribeToOwnProfileListenerCallsCountLock = NSLock() + private var subscribeToOwnProfileListenerUnderlyingCallsCount = 0 + open var subscribeToOwnProfileListenerCallsCount: Int { + get { subscribeToOwnProfileListenerCallsCountLock.withLock { subscribeToOwnProfileListenerUnderlyingCallsCount } } + set { subscribeToOwnProfileListenerCallsCountLock.withLock { subscribeToOwnProfileListenerUnderlyingCallsCount = newValue } } + } + open var subscribeToOwnProfileListenerCalled: Bool { + return subscribeToOwnProfileListenerCallsCount > 0 + } + private let subscribeToOwnProfileListenerReceivedListenerLock = NSLock() + private var subscribeToOwnProfileListenerUnderlyingReceivedListener: ProfileListener? + open var subscribeToOwnProfileListenerReceivedListener: ProfileListener? { + get { subscribeToOwnProfileListenerReceivedListenerLock.withLock { subscribeToOwnProfileListenerUnderlyingReceivedListener } } + set { subscribeToOwnProfileListenerReceivedListenerLock.withLock { subscribeToOwnProfileListenerUnderlyingReceivedListener = newValue } } + } + private let subscribeToOwnProfileListenerReceivedInvocationsLock = NSLock() + private var subscribeToOwnProfileListenerUnderlyingReceivedInvocations: [ProfileListener] = [] + open var subscribeToOwnProfileListenerReceivedInvocations: [ProfileListener] { + get { subscribeToOwnProfileListenerReceivedInvocationsLock.withLock { subscribeToOwnProfileListenerUnderlyingReceivedInvocations } } + set { subscribeToOwnProfileListenerReceivedInvocationsLock.withLock { subscribeToOwnProfileListenerUnderlyingReceivedInvocations = newValue } } + } + + private let subscribeToOwnProfileListenerReturnValueLock = NSLock() + open var subscribeToOwnProfileListenerUnderlyingReturnValue: TaskHandle! + open var subscribeToOwnProfileListenerReturnValue: TaskHandle! { + get { subscribeToOwnProfileListenerReturnValueLock.withLock { subscribeToOwnProfileListenerUnderlyingReturnValue } } + set { subscribeToOwnProfileListenerReturnValueLock.withLock { subscribeToOwnProfileListenerUnderlyingReturnValue = newValue } } + } + open var subscribeToOwnProfileListenerClosure: ((ProfileListener) throws -> TaskHandle)? + + open override func subscribeToOwnProfile(listener: ProfileListener) throws -> TaskHandle { + if let error = subscribeToOwnProfileListenerThrowableError { + throw error + } + subscribeToOwnProfileListenerCallsCountLock.withLock { subscribeToOwnProfileListenerUnderlyingCallsCount += 1 } + subscribeToOwnProfileListenerReceivedListener = listener + subscribeToOwnProfileListenerReceivedInvocationsLock.withLock { subscribeToOwnProfileListenerUnderlyingReceivedInvocations.append(listener) } + if let subscribeToOwnProfileListenerClosure = subscribeToOwnProfileListenerClosure { + return try subscribeToOwnProfileListenerClosure(listener) + } else { + return subscribeToOwnProfileListenerReturnValue + } + } + //MARK: - subscribeToRoomInfo open var subscribeToRoomInfoRoomIdListenerThrowableError: Error? @@ -9606,12 +9653,12 @@ open class RoomSDKMock: MatrixRustSDK.Room, @unchecked Sendable { get { heroesReturnValueLock.withLock { heroesUnderlyingReturnValue } } set { heroesReturnValueLock.withLock { heroesUnderlyingReturnValue = newValue } } } - open var heroesClosure: (() -> [RoomHero])? + open var heroesClosure: (() async -> [RoomHero])? - open override func heroes() -> [RoomHero] { + open override func heroes() async -> [RoomHero] { heroesCallsCountLock.withLock { heroesUnderlyingCallsCount += 1 } if let heroesClosure = heroesClosure { - return heroesClosure() + return await heroesClosure() } else { return heroesReturnValue } diff --git a/project.yml b/project.yml index b30ed7283..d44d68db2 100644 --- a/project.yml +++ b/project.yml @@ -74,7 +74,7 @@ packages: # Element/Matrix dependencies MatrixRustSDK: url: https://github.com/element-hq/matrix-rust-components-swift - exactVersion: 26.07.15 + exactVersion: 26.07.20 # path: ../matrix-rust-sdk Compound: path: compound-ios