mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-06 04:18:31 +00:00
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:
@@ -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"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -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
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user