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
@@ -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<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 {
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<Void, ClientProxyError> {
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<Void, ClientProxyError> {
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<Void, ClientProxyError> {
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<Void, ClientProxyError> {
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()