Update the SDK and use RoomHero.status and subscribeToOwnProfile. (#5905)

* Update the SDK and hook up the status from Room Heroes.

Also start requesting profiles via sss when the feature flag is enabled.

* Add support for subscribing to your own profile.
This commit is contained in:
Doug
2026-07-22 12:18:02 +01:00
committed by GitHub
parent 4f03acab0c
commit a6466e80e0
20 changed files with 142 additions and 51 deletions
+1 -1
View File
@@ -10361,7 +10361,7 @@
repositoryURL = "https://github.com/element-hq/matrix-rust-components-swift"; repositoryURL = "https://github.com/element-hq/matrix-rust-components-swift";
requirement = { requirement = {
kind = exactVersion; kind = exactVersion;
version = 26.07.15; version = 26.07.20;
}; };
}; };
701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */ = { 701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */ = {
@@ -1,5 +1,5 @@
{ {
"originHash" : "25b583bba526740b75c4ede29c7c1d25d7f9d2679fa8e9a795f995fd9f01ae5e", "originHash" : "ed9be16560fed06ea598988a9fa7423a0bfb74cb245df6ae821e9bef9220a45d",
"pins" : [ "pins" : [
{ {
"identity" : "compound-design-tokens", "identity" : "compound-design-tokens",
@@ -167,8 +167,8 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/element-hq/matrix-rust-components-swift", "location" : "https://github.com/element-hq/matrix-rust-components-swift",
"state" : { "state" : {
"revision" : "1bef4822a345b6c9d876f6f8bb5c5e26df6abf02", "revision" : "bf41c1a369bae29f2a8e77c85c4cd9a718e1d973",
"version" : "26.7.15" "version" : "26.7.20"
} }
}, },
{ {
+1 -1
View File
@@ -88,7 +88,7 @@ extension ClientProxyMock {
} }
joinRoomAliasReturnValue = .success(()) joinRoomAliasReturnValue = .success(())
uploadMediaReturnValue = .failure(.sdkError(ClientProxyMockError.generic)) uploadMediaReturnValue = .failure(.sdkError(ClientProxyMockError.generic))
loadUserProfileReturnValue = .success(()) loadUserProfileIfNeededReturnValue = .success(())
setUserDisplayNameReturnValue = .failure(.sdkError(ClientProxyMockError.generic)) setUserDisplayNameReturnValue = .failure(.sdkError(ClientProxyMockError.generic))
setUserAvatarMediaReturnValue = .success(()) setUserAvatarMediaReturnValue = .success(())
removeUserAvatarReturnValue = .success(()) removeUserAvatarReturnValue = .success(())
@@ -3094,33 +3094,33 @@ nonisolated class ClientProxyMock: ClientProxyProtocol, @unchecked Sendable {
return reportRoomForIdentifierReasonReturnValue return reportRoomForIdentifierReasonReturnValue
} }
} }
//MARK: - loadUserProfile //MARK: - loadUserProfileIfNeeded
private let loadUserProfileCallsCountLock = NSLock() private let loadUserProfileIfNeededCallsCountLock = NSLock()
private nonisolated(unsafe) var loadUserProfileUnderlyingCallsCount = 0 private nonisolated(unsafe) var loadUserProfileIfNeededUnderlyingCallsCount = 0
var loadUserProfileCallsCount: Int { var loadUserProfileIfNeededCallsCount: Int {
get { loadUserProfileCallsCountLock.withLock { loadUserProfileUnderlyingCallsCount } } get { loadUserProfileIfNeededCallsCountLock.withLock { loadUserProfileIfNeededUnderlyingCallsCount } }
set { loadUserProfileCallsCountLock.withLock { loadUserProfileUnderlyingCallsCount = newValue } } set { loadUserProfileIfNeededCallsCountLock.withLock { loadUserProfileIfNeededUnderlyingCallsCount = newValue } }
} }
var loadUserProfileCalled: Bool { var loadUserProfileIfNeededCalled: Bool {
return loadUserProfileCallsCount > 0 return loadUserProfileIfNeededCallsCount > 0
} }
private let loadUserProfileReturnValueLock = NSLock() private let loadUserProfileIfNeededReturnValueLock = NSLock()
private nonisolated(unsafe) var loadUserProfileUnderlyingReturnValue: Result<Void, ClientProxyError>! private nonisolated(unsafe) var loadUserProfileIfNeededUnderlyingReturnValue: Result<Void, ClientProxyError>!
var loadUserProfileReturnValue: Result<Void, ClientProxyError>! { var loadUserProfileIfNeededReturnValue: Result<Void, ClientProxyError>! {
get { loadUserProfileReturnValueLock.withLock { loadUserProfileUnderlyingReturnValue } } get { loadUserProfileIfNeededReturnValueLock.withLock { loadUserProfileIfNeededUnderlyingReturnValue } }
set { loadUserProfileReturnValueLock.withLock { loadUserProfileUnderlyingReturnValue = newValue } } set { loadUserProfileIfNeededReturnValueLock.withLock { loadUserProfileIfNeededUnderlyingReturnValue = newValue } }
} }
nonisolated(unsafe) var loadUserProfileClosure: (() async -> Result<Void, ClientProxyError>)? nonisolated(unsafe) var loadUserProfileIfNeededClosure: (() async -> Result<Void, ClientProxyError>)?
@discardableResult @discardableResult
@concurrent func loadUserProfile() async -> Result<Void, ClientProxyError> { @concurrent func loadUserProfileIfNeeded() async -> Result<Void, ClientProxyError> {
loadUserProfileCallsCountLock.withLock { loadUserProfileUnderlyingCallsCount += 1 } loadUserProfileIfNeededCallsCountLock.withLock { loadUserProfileIfNeededUnderlyingCallsCount += 1 }
if let loadUserProfileClosure = loadUserProfileClosure { if let loadUserProfileIfNeededClosure = loadUserProfileIfNeededClosure {
return await loadUserProfileClosure() return await loadUserProfileIfNeededClosure()
} else { } else {
return loadUserProfileReturnValue return loadUserProfileIfNeededReturnValue
} }
} }
//MARK: - setUserDisplayName //MARK: - setUserDisplayName
@@ -176,7 +176,9 @@ private extension RoomHero {
init(from memberProxy: RoomMemberProxyMock) { init(from memberProxy: RoomMemberProxyMock) {
self.init(userId: memberProxy.userID, self.init(userId: memberProxy.userID,
displayName: memberProxy.displayName, displayName: memberProxy.displayName,
avatarUrl: memberProxy.avatarURL?.absoluteString) avatarUrl: memberProxy.avatarURL?.absoluteString,
status: memberProxy.status.raw?.rustValue,
call: memberProxy.status.call?.rustValue)
} }
} }
+6
View File
@@ -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] { nonisolated extension SDKListener: IgnoredUsersListener where T == [String] {
func call(ignoredUserIds: [String]) { func call(ignoredUserIds: [String]) {
onUpdateClosure(ignoredUserIds) onUpdateClosure(ignoredUserIds)
@@ -12,7 +12,7 @@ import SwiftUI
struct RoomHeaderView: View { struct RoomHeaderView: View {
struct DMRecipientDetails { struct DMRecipientDetails {
var status: UserStatus? var statusEmoji: Character?
var verification: UserIdentityVerificationState? var verification: UserIdentityVerificationState?
} }
@@ -61,7 +61,7 @@ struct RoomHeaderView: View {
.foregroundStyle(.compound.textPrimary) .foregroundStyle(.compound.textPrimary)
.accessibilityIdentifier(A11yIdentifiers.roomScreen.name) .accessibilityIdentifier(A11yIdentifiers.roomScreen.name)
if let statusEmoji = dmRecipientDetails.status?.displayed?.emoji { if let statusEmoji = dmRecipientDetails.statusEmoji {
Text(String(statusEmoji)) Text(String(statusEmoji))
.font(.compound.bodyLG) .font(.compound.bodyLG)
.foregroundStyle(.compound.textPrimary) .foregroundStyle(.compound.textPrimary)
@@ -179,7 +179,8 @@ struct RoomHeaderView_Previews: PreviewProvider, TestablePreview {
roomAvatar: .room(id: "1", roomAvatar: .room(id: "1",
name: roomName, name: roomName,
avatarURL: avatarURL), avatarURL: avatarURL),
dmRecipientDetails: .init(status: userStatus, verification: verificationState), dmRecipientDetails: .init(statusEmoji: userStatus?.displayed?.emoji,
verification: verificationState),
roomHistorySharingState: historySharingState, roomHistorySharingState: historySharingState,
mediaProvider: MediaProviderMock(.init())) { } mediaProvider: MediaProviderMock(.init())) { }
@@ -348,7 +348,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
// Delay user profile detail loading until after the initial room list loads // Delay user profile detail loading until after the initial room list loads
if roomListMode == .rooms { if roomListMode == .rooms {
Task { Task {
await self.userSession.clientProxy.loadUserProfile() await self.userSession.clientProxy.loadUserProfileIfNeeded()
} }
} }
} }
@@ -335,6 +335,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
private func updateRoomInfo(_ roomInfo: RoomInfoProxyProtocol) { private func updateRoomInfo(_ roomInfo: RoomInfoProxyProtocol) {
state.roomTitle = roomInfo.displayName ?? roomProxy.id state.roomTitle = roomInfo.displayName ?? roomProxy.id
state.roomAvatar = roomInfo.avatar state.roomAvatar = roomInfo.avatar
state.dmRecipientDetails.statusEmoji = roomInfo.statusEmoji
state.hasOngoingCall = roomInfo.hasRoomCall state.hasOngoingCall = roomInfo.hasRoomCall
state.activeRoomCallIntent = roomInfo.activeRoomCallIntent state.activeRoomCallIntent = roomInfo.activeRoomCallIntent
state.hasSuccessor = roomInfo.successor != nil state.hasSuccessor = roomInfo.successor != nil
@@ -86,7 +86,7 @@ class SettingsScreenViewModel: SettingsScreenViewModelType, SettingsScreenViewMo
.store(in: &cancellables) .store(in: &cancellables)
Task { Task {
await userSession.clientProxy.loadUserProfile() await userSession.clientProxy.loadUserProfileIfNeeded()
await state.accountProfileURL = userSession.clientProxy.accountURL(action: .profile) await state.accountProfileURL = userSession.clientProxy.accountURL(action: .profile)
} }
} }
@@ -98,6 +98,8 @@ struct SettingsScreen: View {
SettingsScreenUserStatusPickerView { action in SettingsScreenUserStatusPickerView { action in
context.send(viewAction: .userStatus(action)) context.send(viewAction: .userStatus(action))
} }
.presentationDetents([.medium]) // Stop using a List and calculate the exact height?
.presentationBackground(.compound.bgCanvasDefault)
} }
} }
} }
@@ -46,7 +46,7 @@ class UserDetailsEditScreenViewModel: UserDetailsEditScreenViewModelType, UserDe
.store(in: &cancellables) .store(in: &cancellables)
Task { Task {
await self.clientProxy.loadUserProfile() await self.clientProxy.loadUserProfileIfNeeded()
state.canEditAvatar = await clientProxy.capabilities.canChangeAvatar() state.canEditAvatar = await clientProxy.capabilities.canChangeAvatar()
state.canEditDisplayName = await clientProxy.capabilities.canChangeDisplayName() state.canEditDisplayName = await clientProxy.capabilities.canChangeDisplayName()
} }
@@ -103,6 +103,8 @@ class ThreadTimelineScreenViewModel: ThreadTimelineScreenViewModelType, ThreadTi
private func updateRoomInfo(_ roomInfo: RoomInfoProxyProtocol) { private func updateRoomInfo(_ roomInfo: RoomInfoProxyProtocol) {
state.roomTitle = roomInfo.displayName ?? roomProxy.id state.roomTitle = roomInfo.displayName ?? roomProxy.id
state.roomAvatar = roomInfo.avatar state.roomAvatar = roomInfo.avatar
state.dmRecipientDetails.statusEmoji = roomInfo.statusEmoji
if let powerLevels = roomInfo.powerLevels { if let powerLevels = roomInfo.powerLevels {
state.canSendMessage = powerLevels.canOwnUser(sendMessage: .roomMessage) state.canSendMessage = powerLevels.canOwnUser(sendMessage: .roomMessage)
} }
@@ -32,6 +32,9 @@ class ClientProxy: ClientProxyProtocol {
// periphery: ignore - only for retain // periphery: ignore - only for retain
private var syncServiceStateUpdateTaskHandle: TaskHandle? 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 // periphery:ignore - required for instance retention in the rust codebase
private var ignoredUsersListenerTaskHandle: TaskHandle? private var ignoredUsersListenerTaskHandle: TaskHandle?
@@ -278,9 +281,13 @@ class ClientProxy: ClientProxyProtocol {
try await client.setUtdDelegate(utdDelegate: ClientDecryptionErrorDelegate(actionsSubject: actionsSubject)) 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 { Task {
do { do {
@@ -686,14 +693,18 @@ class ClientProxy: ClientProxyProtocol {
} }
} }
func loadUserProfile() async -> Result<Void, ClientProxyError> { func loadUserProfileIfNeeded() async -> Result<Void, ClientProxyError> {
// There's no need to load the profile if we're subscribed to it via /sync
guard userProfileListenerTaskHandle == nil else { return .success(()) }
do { do {
async let displayName = client.displayName() async let displayName = client.displayName()
async let avatarURLString = client.avatarUrl() async let avatarURLString = client.avatarUrl()
let profile = try await UserProfile(userID: userID, let profile = try await UserProfile(userID: userID,
displayName: displayName, displayName: displayName,
avatarURL: avatarURLString.flatMap(URL.init)) avatarURL: avatarURLString.flatMap(URL.init),
status: userProfileSubject.value.status)
loadCachedAvatarURLTask?.cancel() loadCachedAvatarURLTask?.cancel()
userProfileSubject.send(profile) userProfileSubject.send(profile)
return .success(()) return .success(())
@@ -706,7 +717,7 @@ class ClientProxy: ClientProxyProtocol {
func setUserDisplayName(_ name: String) async -> Result<Void, ClientProxyError> { func setUserDisplayName(_ name: String) async -> Result<Void, ClientProxyError> {
do { do {
try await client.setDisplayName(name: name) try await client.setDisplayName(name: name)
Task { await self.loadUserProfile() } Task { await self.loadUserProfileIfNeeded() }
return .success(()) return .success(())
} catch { } catch {
MXLog.error("Failed setting user display name with error: \(error)") MXLog.error("Failed setting user display name with error: \(error)")
@@ -723,7 +734,7 @@ class ClientProxy: ClientProxyProtocol {
do { do {
let data = try Data(contentsOf: imageURL) let data = try Data(contentsOf: imageURL)
try await client.uploadAvatar(mimeType: mimeType, data: data) try await client.uploadAvatar(mimeType: mimeType, data: data)
Task { await self.loadUserProfile() } Task { await self.loadUserProfileIfNeeded() }
return .success(()) return .success(())
} catch { } catch {
MXLog.error("Failed setting user avatar with error: \(error)") MXLog.error("Failed setting user avatar with error: \(error)")
@@ -734,7 +745,7 @@ class ClientProxy: ClientProxyProtocol {
func removeUserAvatar() async -> Result<Void, ClientProxyError> { func removeUserAvatar() async -> Result<Void, ClientProxyError> {
do { do {
try await client.removeAvatar() try await client.removeAvatar()
Task { await self.loadUserProfile() } Task { await self.loadUserProfileIfNeeded() }
return .success(()) return .success(())
} catch { } catch {
MXLog.error("Failed removing user avatar with error: \(error)") MXLog.error("Failed removing user avatar with error: \(error)")
@@ -746,7 +757,7 @@ class ClientProxy: ClientProxyProtocol {
func setUserStatus(_ status: UserStatus.Raw) async -> Result<Void, ClientProxyError> { func setUserStatus(_ status: UserStatus.Raw) async -> Result<Void, ClientProxyError> {
do { do {
try await client.setUserStatus(status: status.rustValue) 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(()) return .success(())
} catch { } catch {
MXLog.error("Failed setting user status with error: \(error)") MXLog.error("Failed setting user status with error: \(error)")
@@ -758,7 +769,7 @@ class ClientProxy: ClientProxyProtocol {
func removeUserStatus() async -> Result<Void, ClientProxyError> { func removeUserStatus() async -> Result<Void, ClientProxyError> {
do { do {
try await client.clearUserStatus() try await client.clearUserStatus()
Task { await self.loadUserProfile() } Task { await self.loadUserProfileIfNeeded() }
return .success(()) return .success(())
} catch { } catch {
MXLog.error("Failed removing user status with error: \(error)") MXLog.error("Failed removing user status with error: \(error)")
@@ -1017,7 +1028,7 @@ class ClientProxy: ClientProxyProtocol {
// MARK: - Private // MARK: - Private
private func setupSubscriptions() async { private func setupSubscriptions(canSubscribeToUserProfile: Bool) async {
networkMonitor.reachabilityPublisher networkMonitor.reachabilityPublisher
.removeDuplicates() .removeDuplicates()
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
@@ -1030,6 +1041,12 @@ class ClientProxy: ClientProxyProtocol {
} }
.store(in: &cancellables) .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 ignoredUsersListenerTaskHandle = client.subscribeToIgnoredUsers(listener: SDKListener.onMainActor { [weak self] ignoredUsers in
self?.ignoredUsersSubject.send(ignoredUsers) self?.ignoredUsersSubject.send(ignoredUsers)
}) })
@@ -1201,7 +1218,8 @@ class ClientProxy: ClientProxyProtocol {
let profile = self.userProfileSubject.value let profile = self.userProfileSubject.value
self.userProfileSubject.value = UserProfile(userID: profile.id, self.userProfileSubject.value = UserProfile(userID: profile.id,
displayName: profile.displayName, displayName: profile.displayName,
avatarURL: urlString.flatMap(URL.init)) avatarURL: urlString.flatMap(URL.init),
status: profile.status)
} catch { } catch {
MXLog.error("Failed to look for the avatar url in the cache: \(error)") MXLog.error("Failed to look for the avatar url in the cache: \(error)")
} }
@@ -1481,11 +1499,14 @@ private struct ClientProxyServices {
init(client: ClientProtocol, init(client: ClientProtocol,
notificationSettings: NotificationSettingsProxyProtocol, notificationSettings: NotificationSettingsProxyProtocol,
appSettings: AppSettings) async throws { appSettings: AppSettings) async throws {
let syncService = try await client var syncServiceBuilder = client
.syncService() .syncService()
.withOfflineMode() .withOfflineMode()
.withSharePos(enable: true) .withSharePos(enable: true)
.finish() if appSettings.userStatusEnabled {
syncServiceBuilder = syncServiceBuilder.withProfilesExtension()
}
let syncService = try await syncServiceBuilder.finish()
let roomListService = syncService.roomListService() let roomListService = syncService.roomListService()
@@ -212,7 +212,11 @@ protocol ClientProxyProtocol: AnyObject {
/// Will only work for rooms that are in our room list/local store /// Will only work for rooms that are in our room list/local store
func reportRoomForIdentifier(_ identifier: String, reason: String) async -> Result<Void, ClientProxyError> func reportRoomForIdentifier(_ identifier: String, reason: String) async -> Result<Void, ClientProxyError>
@discardableResult func loadUserProfile() async -> Result<Void, ClientProxyError> /// 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<Void, ClientProxyError>
func setUserDisplayName(_ name: String) async -> Result<Void, ClientProxyError> func setUserDisplayName(_ name: String) async -> Result<Void, ClientProxyError>
func setUserAvatar(media: MediaInfo) async -> Result<Void, ClientProxyError> func setUserAvatar(media: MediaInfo) async -> Result<Void, ClientProxyError>
func removeUserAvatar() async -> Result<Void, ClientProxyError> func removeUserAvatar() async -> Result<Void, ClientProxyError>
@@ -94,6 +94,11 @@ extension BaseRoomInfoProxyProtocol {
return .room(id: id, name: displayName, avatarURL: avatarURL) 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 { extension RoomInfoProxyProtocol {
@@ -186,7 +186,7 @@ nonisolated extension RoomSummary {
} }
var statusEmoji: Character? { 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 return heroes.first?.status.displayed?.emoji
} }
} }
@@ -47,7 +47,7 @@ nonisolated struct UserProfile: Hashable, Identifiable {
id = rustRoomHero.userId id = rustRoomHero.userId
displayName = rustRoomHero.displayName displayName = rustRoomHero.displayName
avatarURL = rustRoomHero.avatarUrl.flatMap(URL.init(string:)) 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) { init(member: RoomMemberProxyProtocol) {
@@ -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 //MARK: - subscribeToRoomInfo
open var subscribeToRoomInfoRoomIdListenerThrowableError: Error? open var subscribeToRoomInfoRoomIdListenerThrowableError: Error?
@@ -9606,12 +9653,12 @@ open class RoomSDKMock: MatrixRustSDK.Room, @unchecked Sendable {
get { heroesReturnValueLock.withLock { heroesUnderlyingReturnValue } } get { heroesReturnValueLock.withLock { heroesUnderlyingReturnValue } }
set { heroesReturnValueLock.withLock { heroesUnderlyingReturnValue = newValue } } 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 } heroesCallsCountLock.withLock { heroesUnderlyingCallsCount += 1 }
if let heroesClosure = heroesClosure { if let heroesClosure = heroesClosure {
return heroesClosure() return await heroesClosure()
} else { } else {
return heroesReturnValue return heroesReturnValue
} }
+1 -1
View File
@@ -74,7 +74,7 @@ packages:
# Element/Matrix dependencies # Element/Matrix dependencies
MatrixRustSDK: MatrixRustSDK:
url: https://github.com/element-hq/matrix-rust-components-swift url: https://github.com/element-hq/matrix-rust-components-swift
exactVersion: 26.07.15 exactVersion: 26.07.20
# path: ../matrix-rust-sdk # path: ../matrix-rust-sdk
Compound: Compound:
path: compound-ios path: compound-ios