Gate user status behind server support as well as the feature flag. (#5920)

This commit is contained in:
Doug
2026-07-24 13:23:54 +01:00
committed by GitHub
parent 4a9cd02fbc
commit 5d0343c480
7 changed files with 84 additions and 4 deletions
@@ -46,6 +46,7 @@ enum ClientProxyMockError: Error {
} }
extension ClientProxyMock { extension ClientProxyMock {
// swiftlint:disable:next function_body_length
convenience init(_ configuration: ClientProxyMockConfiguration) { convenience init(_ configuration: ClientProxyMockConfiguration) {
self.init() self.init()
@@ -91,6 +92,8 @@ extension ClientProxyMock {
loadUserProfileIfNeededReturnValue = .success(()) loadUserProfileIfNeededReturnValue = .success(())
setUserDisplayNameReturnValue = .failure(.sdkError(ClientProxyMockError.generic)) setUserDisplayNameReturnValue = .failure(.sdkError(ClientProxyMockError.generic))
setUserAvatarMediaReturnValue = .success(()) setUserAvatarMediaReturnValue = .success(())
isUserStatusSupportedReturnValue = .success(false)
setUserStatusReturnValue = .failure(.sdkError(ClientProxyMockError.generic))
removeUserAvatarReturnValue = .success(()) removeUserAvatarReturnValue = .success(())
isAliasAvailableReturnValue = .success(true) isAliasAvailableReturnValue = .success(true)
searchUsersSearchTermLimitReturnValue = .success(.init(results: [], limited: false)) searchUsersSearchTermLimitReturnValue = .success(.init(results: [], limited: false))
@@ -3235,6 +3235,34 @@ nonisolated class ClientProxyMock: ClientProxyProtocol, @unchecked Sendable {
return removeUserAvatarReturnValue return removeUserAvatarReturnValue
} }
} }
//MARK: - isUserStatusSupported
private let isUserStatusSupportedCallsCountLock = NSLock()
private nonisolated(unsafe) var isUserStatusSupportedUnderlyingCallsCount = 0
var isUserStatusSupportedCallsCount: Int {
get { isUserStatusSupportedCallsCountLock.withLock { isUserStatusSupportedUnderlyingCallsCount } }
set { isUserStatusSupportedCallsCountLock.withLock { isUserStatusSupportedUnderlyingCallsCount = newValue } }
}
var isUserStatusSupportedCalled: Bool {
return isUserStatusSupportedCallsCount > 0
}
private let isUserStatusSupportedReturnValueLock = NSLock()
private nonisolated(unsafe) var isUserStatusSupportedUnderlyingReturnValue: Result<Bool, ClientProxyError>!
var isUserStatusSupportedReturnValue: Result<Bool, ClientProxyError>! {
get { isUserStatusSupportedReturnValueLock.withLock { isUserStatusSupportedUnderlyingReturnValue } }
set { isUserStatusSupportedReturnValueLock.withLock { isUserStatusSupportedUnderlyingReturnValue = newValue } }
}
nonisolated(unsafe) var isUserStatusSupportedClosure: (() async -> Result<Bool, ClientProxyError>)?
@concurrent func isUserStatusSupported() async -> Result<Bool, ClientProxyError> {
isUserStatusSupportedCallsCountLock.withLock { isUserStatusSupportedUnderlyingCallsCount += 1 }
if let isUserStatusSupportedClosure = isUserStatusSupportedClosure {
return await isUserStatusSupportedClosure()
} else {
return isUserStatusSupportedReturnValue
}
}
//MARK: - setUserStatus //MARK: - setUserStatus
private let setUserStatusCallsCountLock = NSLock() private let setUserStatusCallsCountLock = NSLock()
@@ -36,7 +36,7 @@ enum SettingsScreenSecuritySectionMode {
struct SettingsScreenViewState: BindableState { struct SettingsScreenViewState: BindableState {
var deviceID: String? var deviceID: String?
var userProfile: UserProfile var userProfile: UserProfile
var showUserStatus: Bool var showUserStatus = false
var showLinkNewDeviceButton: Bool var showLinkNewDeviceButton: Bool
var accountProfileURL: URL? var accountProfileURL: URL?
var showAccountDeactivation: Bool var showAccountDeactivation: Bool
@@ -29,7 +29,6 @@ class SettingsScreenViewModel: SettingsScreenViewModelType, SettingsScreenViewMo
super.init(initialViewState: .init(deviceID: userSession.clientProxy.deviceID, super.init(initialViewState: .init(deviceID: userSession.clientProxy.deviceID,
userProfile: userSession.clientProxy.userProfilePublisher.value, userProfile: userSession.clientProxy.userProfilePublisher.value,
showUserStatus: appSettings.userStatusEnabled,
showLinkNewDeviceButton: appSettings.linkNewDeviceEnabled, showLinkNewDeviceButton: appSettings.linkNewDeviceEnabled,
showAccountDeactivation: userSession.clientProxy.canDeactivateAccount, showAccountDeactivation: userSession.clientProxy.canDeactivateAccount,
showDeveloperOptions: appSettings.developerOptionsEnabled, showDeveloperOptions: appSettings.developerOptionsEnabled,
@@ -86,9 +85,12 @@ class SettingsScreenViewModel: SettingsScreenViewModelType, SettingsScreenViewMo
.store(in: &cancellables) .store(in: &cancellables)
Task { Task {
await userSession.clientProxy.loadUserProfileIfNeeded() if appSettings.userStatusEnabled, case .success(true) = await userSession.clientProxy.isUserStatusSupported() {
await state.accountProfileURL = userSession.clientProxy.accountURL(action: .profile) state.showUserStatus = true
} }
await userSession.clientProxy.loadUserProfileIfNeeded()
}
Task { await state.accountProfileURL = userSession.clientProxy.accountURL(action: .profile) }
} }
override func process(viewAction: SettingsScreenViewAction) { override func process(viewAction: SettingsScreenViewAction) {
@@ -281,7 +281,11 @@ class ClientProxy: ClientProxyProtocol {
try await client.setUtdDelegate(utdDelegate: ClientDecryptionErrorDelegate(actionsSubject: actionsSubject)) try await client.setUtdDelegate(utdDelegate: ClientDecryptionErrorDelegate(actionsSubject: actionsSubject))
let canSubscribeToUserProfile = appSettings.userStatusEnabled // TODO: @pixlwave to add a /versions check too. let canSubscribeToUserProfile = if appSettings.userStatusEnabled, case .success(true) = await isUserStatusSupported() {
true
} else {
false
}
if !canSubscribeToUserProfile { if !canSubscribeToUserProfile {
loadUserAvatarURLFromCache() loadUserAvatarURLFromCache()
@@ -753,6 +757,15 @@ class ClientProxy: ClientProxyProtocol {
} }
} }
func isUserStatusSupported() async -> Result<Bool, ClientProxyError> {
do {
return try await .success(client.isUserStatusSupported())
} catch {
MXLog.error("Failed detecting user status support with error: \(error)")
return .failure(.sdkError(error))
}
}
// periphery:ignore - might be useful to have // periphery:ignore - might be useful to have
func setUserStatus(_ status: UserStatus.Raw) async -> Result<Void, ClientProxyError> { func setUserStatus(_ status: UserStatus.Raw) async -> Result<Void, ClientProxyError> {
do { do {
@@ -226,6 +226,7 @@ protocol ClientProxyProtocol: AnyObject {
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>
func isUserStatusSupported() async -> Result<Bool, ClientProxyError>
func setUserStatus(_ status: UserStatus.Raw) async -> Result<Void, ClientProxyError> func setUserStatus(_ status: UserStatus.Raw) async -> Result<Void, ClientProxyError>
func removeUserStatus() async -> Result<Void, ClientProxyError> func removeUserStatus() async -> Result<Void, ClientProxyError>
@@ -1835,6 +1835,39 @@ open class ClientSDKMock: MatrixRustSDK.Client, @unchecked Sendable {
} }
} }
//MARK: - isUserStatusSupported
open var isUserStatusSupportedThrowableError: Error?
private let isUserStatusSupportedCallsCountLock = NSLock()
private var isUserStatusSupportedUnderlyingCallsCount = 0
open var isUserStatusSupportedCallsCount: Int {
get { isUserStatusSupportedCallsCountLock.withLock { isUserStatusSupportedUnderlyingCallsCount } }
set { isUserStatusSupportedCallsCountLock.withLock { isUserStatusSupportedUnderlyingCallsCount = newValue } }
}
open var isUserStatusSupportedCalled: Bool {
return isUserStatusSupportedCallsCount > 0
}
private let isUserStatusSupportedReturnValueLock = NSLock()
open var isUserStatusSupportedUnderlyingReturnValue: Bool!
open var isUserStatusSupportedReturnValue: Bool! {
get { isUserStatusSupportedReturnValueLock.withLock { isUserStatusSupportedUnderlyingReturnValue } }
set { isUserStatusSupportedReturnValueLock.withLock { isUserStatusSupportedUnderlyingReturnValue = newValue } }
}
open var isUserStatusSupportedClosure: (() async throws -> Bool)?
open override func isUserStatusSupported() async throws -> Bool {
if let error = isUserStatusSupportedThrowableError {
throw error
}
isUserStatusSupportedCallsCountLock.withLock { isUserStatusSupportedUnderlyingCallsCount += 1 }
if let isUserStatusSupportedClosure = isUserStatusSupportedClosure {
return try await isUserStatusSupportedClosure()
} else {
return isUserStatusSupportedReturnValue
}
}
//MARK: - joinRoomById //MARK: - joinRoomById
open var joinRoomByIdRoomIdThrowableError: Error? open var joinRoomByIdRoomIdThrowableError: Error?