mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-10-31 16:24:11 +00:00
Combine the user's own display name/avatar into a single profile.
This is in preparation for adding the user status fields into the profile.
This commit is contained in:
@@ -715,7 +715,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
||||
Task {
|
||||
let credentials = SoftLogoutScreenCredentials(userID: userSession.clientProxy.userID,
|
||||
homeserverName: userSession.clientProxy.homeserver,
|
||||
userDisplayName: userSession.clientProxy.userDisplayNamePublisher.value ?? "",
|
||||
userDisplayName: userSession.clientProxy.userProfilePublisher.value.displayName ?? "",
|
||||
deviceID: userSession.clientProxy.deviceID)
|
||||
|
||||
let authenticationService = AuthenticationService(userSessionStore: userSessionStore,
|
||||
|
||||
@@ -64,8 +64,7 @@ extension ClientProxyMock {
|
||||
verificationStatePublisher = .init(.unknown)
|
||||
homeserverReachabilityPublisher = .init(.reachable)
|
||||
|
||||
userAvatarURLPublisher = .init(nil)
|
||||
userDisplayNamePublisher = .init("User display name")
|
||||
userProfilePublisher = .init(UserProfile(userID: configuration.userID, displayName: "User display name"))
|
||||
|
||||
ignoredUsersPublisher = .init([RoomMemberProxyMock].allMembers.map(\.userID))
|
||||
|
||||
@@ -85,9 +84,8 @@ extension ClientProxyMock {
|
||||
}
|
||||
joinRoomAliasReturnValue = .success(())
|
||||
uploadMediaReturnValue = .failure(.sdkError(ClientProxyMockError.generic))
|
||||
loadUserDisplayNameReturnValue = .failure(.sdkError(ClientProxyMockError.generic))
|
||||
loadUserProfileReturnValue = .success(())
|
||||
setUserDisplayNameReturnValue = .failure(.sdkError(ClientProxyMockError.generic))
|
||||
loadUserAvatarURLReturnValue = .success(())
|
||||
setUserAvatarMediaReturnValue = .success(())
|
||||
removeUserAvatarReturnValue = .success(())
|
||||
isAliasAvailableReturnValue = .success(true)
|
||||
|
||||
@@ -2291,16 +2291,11 @@ nonisolated class ClientProxyMock: ClientProxyProtocol, @unchecked Sendable {
|
||||
}
|
||||
nonisolated(unsafe) var underlyingCanDeactivateAccount: Bool!
|
||||
nonisolated(unsafe) var userIDServerName: String?
|
||||
var userDisplayNamePublisher: CurrentValuePublisher<String?, Never> {
|
||||
get { return underlyingUserDisplayNamePublisher }
|
||||
set(value) { underlyingUserDisplayNamePublisher = value }
|
||||
var userProfilePublisher: CurrentValuePublisher<UserProfile, Never> {
|
||||
get { return underlyingUserProfilePublisher }
|
||||
set(value) { underlyingUserProfilePublisher = value }
|
||||
}
|
||||
nonisolated(unsafe) var underlyingUserDisplayNamePublisher: CurrentValuePublisher<String?, Never>!
|
||||
var userAvatarURLPublisher: CurrentValuePublisher<URL?, Never> {
|
||||
get { return underlyingUserAvatarURLPublisher }
|
||||
set(value) { underlyingUserAvatarURLPublisher = value }
|
||||
}
|
||||
nonisolated(unsafe) var underlyingUserAvatarURLPublisher: CurrentValuePublisher<URL?, Never>!
|
||||
nonisolated(unsafe) var underlyingUserProfilePublisher: CurrentValuePublisher<UserProfile, Never>!
|
||||
var ignoredUsersPublisher: CurrentValuePublisher<[String]?, Never> {
|
||||
get { return underlyingIgnoredUsersPublisher }
|
||||
set(value) { underlyingIgnoredUsersPublisher = value }
|
||||
@@ -3174,33 +3169,33 @@ nonisolated class ClientProxyMock: ClientProxyProtocol, @unchecked Sendable {
|
||||
return reportRoomForIdentifierReasonReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - loadUserDisplayName
|
||||
//MARK: - loadUserProfile
|
||||
|
||||
private let loadUserDisplayNameCallsCountLock = NSLock()
|
||||
private nonisolated(unsafe) var loadUserDisplayNameUnderlyingCallsCount = 0
|
||||
var loadUserDisplayNameCallsCount: Int {
|
||||
get { loadUserDisplayNameCallsCountLock.withLock { loadUserDisplayNameUnderlyingCallsCount } }
|
||||
set { loadUserDisplayNameCallsCountLock.withLock { loadUserDisplayNameUnderlyingCallsCount = newValue } }
|
||||
private let loadUserProfileCallsCountLock = NSLock()
|
||||
private nonisolated(unsafe) var loadUserProfileUnderlyingCallsCount = 0
|
||||
var loadUserProfileCallsCount: Int {
|
||||
get { loadUserProfileCallsCountLock.withLock { loadUserProfileUnderlyingCallsCount } }
|
||||
set { loadUserProfileCallsCountLock.withLock { loadUserProfileUnderlyingCallsCount = newValue } }
|
||||
}
|
||||
var loadUserDisplayNameCalled: Bool {
|
||||
return loadUserDisplayNameCallsCount > 0
|
||||
var loadUserProfileCalled: Bool {
|
||||
return loadUserProfileCallsCount > 0
|
||||
}
|
||||
|
||||
private let loadUserDisplayNameReturnValueLock = NSLock()
|
||||
private nonisolated(unsafe) var loadUserDisplayNameUnderlyingReturnValue: Result<Void, ClientProxyError>!
|
||||
var loadUserDisplayNameReturnValue: Result<Void, ClientProxyError>! {
|
||||
get { loadUserDisplayNameReturnValueLock.withLock { loadUserDisplayNameUnderlyingReturnValue } }
|
||||
set { loadUserDisplayNameReturnValueLock.withLock { loadUserDisplayNameUnderlyingReturnValue = newValue } }
|
||||
private let loadUserProfileReturnValueLock = NSLock()
|
||||
private nonisolated(unsafe) var loadUserProfileUnderlyingReturnValue: Result<Void, ClientProxyError>!
|
||||
var loadUserProfileReturnValue: Result<Void, ClientProxyError>! {
|
||||
get { loadUserProfileReturnValueLock.withLock { loadUserProfileUnderlyingReturnValue } }
|
||||
set { loadUserProfileReturnValueLock.withLock { loadUserProfileUnderlyingReturnValue = newValue } }
|
||||
}
|
||||
nonisolated(unsafe) var loadUserDisplayNameClosure: (() async -> Result<Void, ClientProxyError>)?
|
||||
nonisolated(unsafe) var loadUserProfileClosure: (() async -> Result<Void, ClientProxyError>)?
|
||||
|
||||
@discardableResult
|
||||
@concurrent func loadUserDisplayName() async -> Result<Void, ClientProxyError> {
|
||||
loadUserDisplayNameCallsCountLock.withLock { loadUserDisplayNameUnderlyingCallsCount += 1 }
|
||||
if let loadUserDisplayNameClosure = loadUserDisplayNameClosure {
|
||||
return await loadUserDisplayNameClosure()
|
||||
@concurrent func loadUserProfile() async -> Result<Void, ClientProxyError> {
|
||||
loadUserProfileCallsCountLock.withLock { loadUserProfileUnderlyingCallsCount += 1 }
|
||||
if let loadUserProfileClosure = loadUserProfileClosure {
|
||||
return await loadUserProfileClosure()
|
||||
} else {
|
||||
return loadUserDisplayNameReturnValue
|
||||
return loadUserProfileReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - setUserDisplayName
|
||||
@@ -3245,35 +3240,6 @@ nonisolated class ClientProxyMock: ClientProxyProtocol, @unchecked Sendable {
|
||||
return setUserDisplayNameReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - loadUserAvatarURL
|
||||
|
||||
private let loadUserAvatarURLCallsCountLock = NSLock()
|
||||
private nonisolated(unsafe) var loadUserAvatarURLUnderlyingCallsCount = 0
|
||||
var loadUserAvatarURLCallsCount: Int {
|
||||
get { loadUserAvatarURLCallsCountLock.withLock { loadUserAvatarURLUnderlyingCallsCount } }
|
||||
set { loadUserAvatarURLCallsCountLock.withLock { loadUserAvatarURLUnderlyingCallsCount = newValue } }
|
||||
}
|
||||
var loadUserAvatarURLCalled: Bool {
|
||||
return loadUserAvatarURLCallsCount > 0
|
||||
}
|
||||
|
||||
private let loadUserAvatarURLReturnValueLock = NSLock()
|
||||
private nonisolated(unsafe) var loadUserAvatarURLUnderlyingReturnValue: Result<Void, ClientProxyError>!
|
||||
var loadUserAvatarURLReturnValue: Result<Void, ClientProxyError>! {
|
||||
get { loadUserAvatarURLReturnValueLock.withLock { loadUserAvatarURLUnderlyingReturnValue } }
|
||||
set { loadUserAvatarURLReturnValueLock.withLock { loadUserAvatarURLUnderlyingReturnValue = newValue } }
|
||||
}
|
||||
nonisolated(unsafe) var loadUserAvatarURLClosure: (() async -> Result<Void, ClientProxyError>)?
|
||||
|
||||
@discardableResult
|
||||
@concurrent func loadUserAvatarURL() async -> Result<Void, ClientProxyError> {
|
||||
loadUserAvatarURLCallsCountLock.withLock { loadUserAvatarURLUnderlyingCallsCount += 1 }
|
||||
if let loadUserAvatarURLClosure = loadUserAvatarURLClosure {
|
||||
return await loadUserAvatarURLClosure()
|
||||
} else {
|
||||
return loadUserAvatarURLReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - setUserAvatar
|
||||
|
||||
private let setUserAvatarMediaCallsCountLock = NSLock()
|
||||
|
||||
@@ -90,9 +90,7 @@ enum HomeScreenSecurityBannerMode: Equatable {
|
||||
}
|
||||
|
||||
struct HomeScreenViewState: BindableState {
|
||||
let userID: String
|
||||
var userDisplayName: String?
|
||||
var userAvatarURL: URL?
|
||||
var userProfile: UserProfile
|
||||
|
||||
var securityBannerMode = HomeScreenSecurityBannerMode.none
|
||||
var shouldShowNewSoundBanner = false
|
||||
|
||||
@@ -45,7 +45,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
||||
|
||||
roomSummaryProvider = userSession.clientProxy.roomSummaryProvider
|
||||
|
||||
super.init(initialViewState: .init(userID: userSession.clientProxy.userID,
|
||||
super.init(initialViewState: .init(userProfile: userSession.clientProxy.userProfilePublisher.value,
|
||||
bindings: .init(filtersState: .init(appSettings: appSettings))),
|
||||
mediaProvider: userSession.mediaProvider)
|
||||
|
||||
@@ -53,14 +53,9 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
||||
state.isRoomListSearchEnabled = false
|
||||
}
|
||||
|
||||
userSession.clientProxy.userAvatarURLPublisher
|
||||
userSession.clientProxy.userProfilePublisher
|
||||
.receive(on: DispatchQueue.main)
|
||||
.weakAssign(to: \.state.userAvatarURL, on: self)
|
||||
.store(in: &cancellables)
|
||||
|
||||
userSession.clientProxy.userDisplayNamePublisher
|
||||
.receive(on: DispatchQueue.main)
|
||||
.weakAssign(to: \.state.userDisplayName, on: self)
|
||||
.weakAssign(to: \.state.userProfile, on: self)
|
||||
.store(in: &cancellables)
|
||||
|
||||
userSession.sessionSecurityStatePublisher
|
||||
@@ -353,8 +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.loadUserAvatarURL()
|
||||
await self.userSession.clientProxy.loadUserDisplayName()
|
||||
await self.userSession.clientProxy.loadUserProfile()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,9 +85,9 @@ struct HomeScreen: View {
|
||||
Button {
|
||||
context.send(viewAction: .showSettings)
|
||||
} label: {
|
||||
LoadableAvatarImage(url: context.viewState.userAvatarURL,
|
||||
name: context.viewState.userDisplayName,
|
||||
contentID: context.viewState.userID,
|
||||
LoadableAvatarImage(url: context.viewState.userProfile.avatarURL,
|
||||
name: context.viewState.userProfile.displayName,
|
||||
contentID: context.viewState.userProfile.id,
|
||||
avatarSize: .user(on: .chats),
|
||||
mediaProvider: context.mediaProvider)
|
||||
.accessibilityIdentifier(A11yIdentifiers.homeScreen.userAvatar)
|
||||
|
||||
@@ -34,12 +34,10 @@ enum SettingsScreenSecuritySectionMode {
|
||||
|
||||
struct SettingsScreenViewState: BindableState {
|
||||
var deviceID: String?
|
||||
var userID: String
|
||||
var userProfile: UserProfile
|
||||
var showLinkNewDeviceButton: Bool
|
||||
var accountProfileURL: URL?
|
||||
var showAccountDeactivation: Bool
|
||||
var userAvatarURL: URL?
|
||||
var userDisplayName: String?
|
||||
var showDeveloperOptions: Bool
|
||||
|
||||
var securitySectionMode = SettingsScreenSecuritySectionMode.none
|
||||
|
||||
@@ -24,7 +24,7 @@ class SettingsScreenViewModel: SettingsScreenViewModelType, SettingsScreenViewMo
|
||||
self.appSettings = appSettings
|
||||
|
||||
super.init(initialViewState: .init(deviceID: userSession.clientProxy.deviceID,
|
||||
userID: userSession.clientProxy.userID,
|
||||
userProfile: userSession.clientProxy.userProfilePublisher.value,
|
||||
showLinkNewDeviceButton: appSettings.linkNewDeviceEnabled,
|
||||
showAccountDeactivation: userSession.clientProxy.canDeactivateAccount,
|
||||
showDeveloperOptions: appSettings.developerOptionsEnabled,
|
||||
@@ -41,14 +41,9 @@ class SettingsScreenViewModel: SettingsScreenViewModelType, SettingsScreenViewMo
|
||||
.weakAssign(to: \.state.showLinkNewDeviceButton, on: self)
|
||||
.store(in: &cancellables)
|
||||
|
||||
userSession.clientProxy.userAvatarURLPublisher
|
||||
userSession.clientProxy.userProfilePublisher
|
||||
.receive(on: DispatchQueue.main)
|
||||
.weakAssign(to: \.state.userAvatarURL, on: self)
|
||||
.store(in: &cancellables)
|
||||
|
||||
userSession.clientProxy.userDisplayNamePublisher
|
||||
.receive(on: DispatchQueue.main)
|
||||
.weakAssign(to: \.state.userDisplayName, on: self)
|
||||
.weakAssign(to: \.state.userProfile, on: self)
|
||||
.store(in: &cancellables)
|
||||
|
||||
userSession.sessionSecurityStatePublisher
|
||||
@@ -86,8 +81,7 @@ class SettingsScreenViewModel: SettingsScreenViewModelType, SettingsScreenViewMo
|
||||
.store(in: &cancellables)
|
||||
|
||||
Task {
|
||||
await userSession.clientProxy.loadUserAvatarURL()
|
||||
await userSession.clientProxy.loadUserDisplayName()
|
||||
await userSession.clientProxy.loadUserProfile()
|
||||
await state.accountProfileURL = userSession.clientProxy.accountURL(action: .profile)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,18 +51,18 @@ struct SettingsScreen: View {
|
||||
context.send(viewAction: .userDetails)
|
||||
} label: {
|
||||
HStack(spacing: 12) {
|
||||
LoadableAvatarImage(url: context.viewState.userAvatarURL,
|
||||
name: context.viewState.userDisplayName,
|
||||
contentID: context.viewState.userID,
|
||||
LoadableAvatarImage(url: context.viewState.userProfile.avatarURL,
|
||||
name: context.viewState.userProfile.displayName,
|
||||
contentID: context.viewState.userProfile.id,
|
||||
avatarSize: .user(on: .settings),
|
||||
mediaProvider: context.mediaProvider)
|
||||
.accessibilityHidden(true)
|
||||
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(context.viewState.userDisplayName ?? "")
|
||||
Text(context.viewState.userProfile.displayName ?? "")
|
||||
.font(.compound.headingMD)
|
||||
.foregroundColor(.compound.textPrimary)
|
||||
Text(context.viewState.userID)
|
||||
Text(context.viewState.userProfile.id)
|
||||
.font(.compound.bodySM)
|
||||
.foregroundColor(.compound.textSecondary)
|
||||
}
|
||||
|
||||
+3
-7
@@ -16,26 +16,22 @@ enum UserDetailsEditScreenViewModelAction {
|
||||
}
|
||||
|
||||
struct UserDetailsEditScreenViewState: BindableState {
|
||||
let userID: String
|
||||
var currentUserProfile: UserProfile
|
||||
|
||||
var canEditAvatar = true
|
||||
var canEditDisplayName = true
|
||||
|
||||
var currentAvatarURL: URL?
|
||||
var selectedAvatarURL: URL?
|
||||
|
||||
var currentDisplayName: String?
|
||||
|
||||
var localMedia: MediaInfo?
|
||||
|
||||
var bindings: UserDetailsEditScreenViewStateBindings
|
||||
|
||||
var nameDidChange: Bool {
|
||||
bindings.name != currentDisplayName
|
||||
bindings.name != currentUserProfile.displayName
|
||||
}
|
||||
|
||||
var avatarDidChange: Bool {
|
||||
localMedia != nil || selectedAvatarURL != currentAvatarURL
|
||||
localMedia != nil || selectedAvatarURL != currentUserProfile.avatarURL
|
||||
}
|
||||
|
||||
var canSave: Bool {
|
||||
|
||||
+7
-21
@@ -28,36 +28,22 @@ class UserDetailsEditScreenViewModel: UserDetailsEditScreenViewModelType, UserDe
|
||||
self.mediaUploadingPreprocessor = mediaUploadingPreprocessor
|
||||
self.userIndicatorController = userIndicatorController
|
||||
|
||||
super.init(initialViewState: UserDetailsEditScreenViewState(userID: clientProxy.userID,
|
||||
super.init(initialViewState: UserDetailsEditScreenViewState(currentUserProfile: clientProxy.userProfilePublisher.value,
|
||||
bindings: .init()), mediaProvider: userSession.mediaProvider)
|
||||
|
||||
clientProxy.userAvatarURLPublisher
|
||||
clientProxy.userProfilePublisher
|
||||
.receive(on: DispatchQueue.main)
|
||||
.weakAssign(to: \.state.currentAvatarURL, on: self)
|
||||
.store(in: &cancellables)
|
||||
|
||||
clientProxy.userAvatarURLPublisher
|
||||
.receive(on: DispatchQueue.main)
|
||||
.weakAssign(to: \.state.selectedAvatarURL, on: self)
|
||||
.store(in: &cancellables)
|
||||
|
||||
clientProxy.userDisplayNamePublisher
|
||||
.receive(on: DispatchQueue.main)
|
||||
.weakAssign(to: \.state.currentDisplayName, on: self)
|
||||
.store(in: &cancellables)
|
||||
|
||||
clientProxy.userDisplayNamePublisher
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] displayName in
|
||||
.sink { [weak self] profile in
|
||||
guard let self else { return }
|
||||
|
||||
state.bindings.name = displayName ?? ""
|
||||
state.currentUserProfile = profile
|
||||
state.selectedAvatarURL = profile.avatarURL
|
||||
state.bindings.name = profile.displayName ?? ""
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
Task {
|
||||
await self.clientProxy.loadUserAvatarURL()
|
||||
await self.clientProxy.loadUserDisplayName()
|
||||
await self.clientProxy.loadUserProfile()
|
||||
state.canEditAvatar = await clientProxy.capabilities.canChangeAvatar()
|
||||
state.canEditDisplayName = await clientProxy.capabilities.canChangeDisplayName()
|
||||
}
|
||||
|
||||
+3
-3
@@ -18,7 +18,7 @@ struct UserDetailsEditScreen: View {
|
||||
Section {
|
||||
avatar
|
||||
} footer: {
|
||||
Text(context.viewState.userID)
|
||||
Text(context.viewState.currentUserProfile.id)
|
||||
.frame(maxWidth: .infinity)
|
||||
.font(.compound.bodyLG)
|
||||
.foregroundColor(.compound.textPrimary)
|
||||
@@ -62,8 +62,8 @@ struct UserDetailsEditScreen: View {
|
||||
} label: {
|
||||
OverridableAvatarImage(overrideURL: context.viewState.localMedia?.thumbnailURL,
|
||||
url: context.viewState.selectedAvatarURL,
|
||||
name: context.viewState.currentDisplayName,
|
||||
contentID: context.viewState.userID,
|
||||
name: context.viewState.currentUserProfile.displayName,
|
||||
contentID: context.viewState.currentUserProfile.id,
|
||||
shape: .circle,
|
||||
avatarSize: .user(on: .editUserDetails),
|
||||
mediaProvider: context.mediaProvider)
|
||||
|
||||
@@ -15,9 +15,7 @@ enum SpacesScreenViewModelAction {
|
||||
}
|
||||
|
||||
struct SpacesScreenViewState: BindableState {
|
||||
let userID: String
|
||||
var userDisplayName: String?
|
||||
var userAvatarURL: URL?
|
||||
var userProfile: UserProfile
|
||||
|
||||
var topLevelSpaces: [SpaceServiceRoom]
|
||||
var selectedSpaceID: String?
|
||||
|
||||
@@ -29,7 +29,7 @@ class SpacesScreenViewModel: SpacesScreenViewModelType, SpacesScreenViewModelPro
|
||||
self.appSettings = appSettings
|
||||
self.userIndicatorController = userIndicatorController
|
||||
|
||||
super.init(initialViewState: SpacesScreenViewState(userID: userSession.clientProxy.userID,
|
||||
super.init(initialViewState: SpacesScreenViewState(userProfile: userSession.clientProxy.userProfilePublisher.value,
|
||||
topLevelSpaces: spaceServiceProxy.topLevelSpacesPublisher.value),
|
||||
mediaProvider: userSession.mediaProvider)
|
||||
|
||||
@@ -42,14 +42,9 @@ class SpacesScreenViewModel: SpacesScreenViewModelType, SpacesScreenViewModelPro
|
||||
.weakAssign(to: \.state.selectedSpaceID, on: self)
|
||||
.store(in: &cancellables)
|
||||
|
||||
userSession.clientProxy.userAvatarURLPublisher
|
||||
userSession.clientProxy.userProfilePublisher
|
||||
.receive(on: DispatchQueue.main)
|
||||
.weakAssign(to: \.state.userAvatarURL, on: self)
|
||||
.store(in: &cancellables)
|
||||
|
||||
userSession.clientProxy.userDisplayNamePublisher
|
||||
.receive(on: DispatchQueue.main)
|
||||
.weakAssign(to: \.state.userDisplayName, on: self)
|
||||
.weakAssign(to: \.state.userProfile, on: self)
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
|
||||
@@ -96,9 +96,9 @@ struct SpacesScreen: View {
|
||||
Button {
|
||||
context.send(viewAction: .showSettings)
|
||||
} label: {
|
||||
LoadableAvatarImage(url: context.viewState.userAvatarURL,
|
||||
name: context.viewState.userDisplayName,
|
||||
contentID: context.viewState.userID,
|
||||
LoadableAvatarImage(url: context.viewState.userProfile.avatarURL,
|
||||
name: context.viewState.userProfile.displayName,
|
||||
contentID: context.viewState.userProfile.id,
|
||||
avatarSize: .user(on: .spaces),
|
||||
mediaProvider: context.mediaProvider)
|
||||
.accessibilityIdentifier(A11yIdentifiers.homeScreen.userAvatar)
|
||||
|
||||
@@ -130,14 +130,9 @@ class ClientProxy: ClientProxyProtocol {
|
||||
}
|
||||
|
||||
private var loadCachedAvatarURLTask: Task<Void, Never>?
|
||||
private let userAvatarURLSubject = CurrentValueSubject<URL?, Never>(nil)
|
||||
var userAvatarURLPublisher: CurrentValuePublisher<URL?, Never> {
|
||||
userAvatarURLSubject.asCurrentValuePublisher()
|
||||
}
|
||||
|
||||
private let userDisplayNameSubject = CurrentValueSubject<String?, Never>(nil)
|
||||
var userDisplayNamePublisher: CurrentValuePublisher<String?, Never> {
|
||||
userDisplayNameSubject.asCurrentValuePublisher()
|
||||
private let userProfileSubject: CurrentValueSubject<UserProfile, Never>
|
||||
var userProfilePublisher: CurrentValuePublisher<UserProfile, Never> {
|
||||
userProfileSubject.asCurrentValuePublisher()
|
||||
}
|
||||
|
||||
private let ignoredUsersSubject = CurrentValueSubject<[String]?, Never>(nil)
|
||||
@@ -213,6 +208,8 @@ class ClientProxy: ClientProxyProtocol {
|
||||
self.appSettings = appSettings
|
||||
self.analyticsService = analyticsService
|
||||
|
||||
userProfileSubject = .init(UserProfile(userID: (try? client.userId()) ?? ""))
|
||||
|
||||
if appSettings.automaticBackPaginationEnabled {
|
||||
// Must be called before creating the sync service, timelines etc.
|
||||
client.enableAutomaticBackpagination()
|
||||
@@ -677,13 +674,19 @@ class ClientProxy: ClientProxyProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
func loadUserDisplayName() async -> Result<Void, ClientProxyError> {
|
||||
func loadUserProfile() async -> Result<Void, ClientProxyError> {
|
||||
do {
|
||||
let displayName = try await client.displayName()
|
||||
userDisplayNameSubject.send(displayName)
|
||||
async let displayName = client.displayName()
|
||||
async let avatarURLString = client.avatarUrl()
|
||||
|
||||
let profile = try await UserProfile(userID: userID,
|
||||
displayName: displayName,
|
||||
avatarURL: avatarURLString.flatMap(URL.init))
|
||||
loadCachedAvatarURLTask?.cancel()
|
||||
userProfileSubject.send(profile)
|
||||
return .success(())
|
||||
} catch {
|
||||
MXLog.error("Failed loading user display name with error: \(error)")
|
||||
MXLog.error("Failed loading user profile with error: \(error)")
|
||||
return .failure(.sdkError(error))
|
||||
}
|
||||
}
|
||||
@@ -692,7 +695,7 @@ class ClientProxy: ClientProxyProtocol {
|
||||
do {
|
||||
try await client.setDisplayName(name: name)
|
||||
Task {
|
||||
await self.loadUserDisplayName()
|
||||
await self.loadUserProfile()
|
||||
}
|
||||
return .success(())
|
||||
} catch {
|
||||
@@ -701,18 +704,6 @@ class ClientProxy: ClientProxyProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
func loadUserAvatarURL() async -> Result<Void, ClientProxyError> {
|
||||
do {
|
||||
let urlString = try await client.avatarUrl()
|
||||
loadCachedAvatarURLTask?.cancel()
|
||||
userAvatarURLSubject.send(urlString.flatMap(URL.init))
|
||||
return .success(())
|
||||
} catch {
|
||||
MXLog.error("Failed loading user avatar URL with error: \(error)")
|
||||
return .failure(.sdkError(error))
|
||||
}
|
||||
}
|
||||
|
||||
func setUserAvatar(media: MediaInfo) async -> Result<Void, ClientProxyError> {
|
||||
guard case let .image(imageURL, _, _) = media, let mimeType = media.mimeType else {
|
||||
MXLog.error("Failed uploading, invalid media: \(media)")
|
||||
@@ -723,7 +714,7 @@ class ClientProxy: ClientProxyProtocol {
|
||||
let data = try Data(contentsOf: imageURL)
|
||||
try await client.uploadAvatar(mimeType: mimeType, data: data)
|
||||
Task {
|
||||
await self.loadUserAvatarURL()
|
||||
await self.loadUserProfile()
|
||||
}
|
||||
return .success(())
|
||||
} catch {
|
||||
@@ -736,7 +727,7 @@ class ClientProxy: ClientProxyProtocol {
|
||||
do {
|
||||
try await client.removeAvatar()
|
||||
Task {
|
||||
await self.loadUserAvatarURL()
|
||||
await self.loadUserProfile()
|
||||
}
|
||||
return .success(())
|
||||
} catch {
|
||||
@@ -1172,7 +1163,10 @@ class ClientProxy: ClientProxyProtocol {
|
||||
do {
|
||||
let urlString = try await self.client.cachedAvatarUrl()
|
||||
guard !Task.isCancelled else { return }
|
||||
self.userAvatarURLSubject.value = urlString.flatMap(URL.init)
|
||||
let profile = self.userProfileSubject.value
|
||||
self.userProfileSubject.value = UserProfile(userID: profile.id,
|
||||
displayName: profile.displayName,
|
||||
avatarURL: urlString.flatMap(URL.init))
|
||||
} catch {
|
||||
MXLog.error("Failed to look for the avatar url in the cache: \(error)")
|
||||
}
|
||||
|
||||
@@ -120,9 +120,7 @@ protocol ClientProxyProtocol: AnyObject {
|
||||
|
||||
var userIDServerName: String? { get }
|
||||
|
||||
var userDisplayNamePublisher: CurrentValuePublisher<String?, Never> { get }
|
||||
|
||||
var userAvatarURLPublisher: CurrentValuePublisher<URL?, Never> { get }
|
||||
var userProfilePublisher: CurrentValuePublisher<UserProfile, Never> { get }
|
||||
|
||||
/// We delay fetching this until after the first sync. Nil until then
|
||||
var ignoredUsersPublisher: CurrentValuePublisher<[String]?, Never> { get }
|
||||
@@ -212,12 +210,10 @@ protocol ClientProxyProtocol: AnyObject {
|
||||
/// Will only work for rooms that are in our room list/local store
|
||||
func reportRoomForIdentifier(_ identifier: String, reason: String) async -> Result<Void, ClientProxyError>
|
||||
|
||||
@discardableResult func loadUserDisplayName() async -> Result<Void, ClientProxyError>
|
||||
@discardableResult func loadUserProfile() async -> Result<Void, ClientProxyError>
|
||||
|
||||
func setUserDisplayName(_ name: String) async -> Result<Void, ClientProxyError>
|
||||
|
||||
@discardableResult func loadUserAvatarURL() async -> Result<Void, ClientProxyError>
|
||||
|
||||
func setUserAvatar(media: MediaInfo) async -> Result<Void, ClientProxyError>
|
||||
|
||||
func removeUserAvatar() async -> Result<Void, ClientProxyError>
|
||||
|
||||
Reference in New Issue
Block a user