Use client.cachedAvatarUrl(), to be able to render the user avatar faster, and even if there is no network (#2488).
This commit is contained in:
parent
5c540c4af9
commit
1144d467e5
15 changed files with 105 additions and 124 deletions
|
|
@ -42,6 +42,7 @@ import java.io.Closeable
|
|||
interface MatrixClient : Closeable {
|
||||
val sessionId: SessionId
|
||||
val deviceId: String
|
||||
val userProfile: StateFlow<MatrixUser>
|
||||
val roomListService: RoomListService
|
||||
val mediaLoader: MatrixMediaLoader
|
||||
val sessionCoroutineScope: CoroutineScope
|
||||
|
|
@ -77,8 +78,11 @@ interface MatrixClient : Closeable {
|
|||
* @param ignoreSdkError if true, the SDK will ignore any error and delete the session data anyway.
|
||||
*/
|
||||
suspend fun logout(ignoreSdkError: Boolean): String?
|
||||
suspend fun loadUserDisplayName(): Result<String>
|
||||
suspend fun loadUserAvatarUrl(): Result<String?>
|
||||
|
||||
/**
|
||||
* Retrieve the user profile, will also eventually emit a new value to [userProfile].
|
||||
*/
|
||||
suspend fun getUserProfile(): Result<MatrixUser>
|
||||
suspend fun getAccountManagementUrl(action: AccountManagementAction?): Result<String?>
|
||||
suspend fun uploadMedia(mimeType: String, data: ByteArray, progressCallback: ProgressCallback?): Result<String>
|
||||
fun roomMembershipObserver(): RoomMembershipObserver
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.api.user
|
||||
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
|
||||
/**
|
||||
* Get the current user, as [MatrixUser], using [MatrixClient.loadUserAvatarUrl]
|
||||
* and [MatrixClient.loadUserDisplayName].
|
||||
*/
|
||||
suspend fun MatrixClient.getCurrentUser(): MatrixUser {
|
||||
val userAvatarUrl = loadUserAvatarUrl().getOrNull()
|
||||
val userDisplayName = loadUserDisplayName().getOrNull()
|
||||
return MatrixUser(
|
||||
userId = sessionId,
|
||||
displayName = userDisplayName,
|
||||
avatarUrl = userAvatarUrl,
|
||||
)
|
||||
}
|
||||
|
|
@ -73,7 +73,9 @@ import kotlinx.coroutines.CoroutineScope
|
|||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.buffer
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.first
|
||||
|
|
@ -249,6 +251,17 @@ class RustMatrixClient(
|
|||
|
||||
private val clientDelegateTaskHandle: TaskHandle? = client.setDelegate(clientDelegate)
|
||||
|
||||
private val _userProfile: MutableStateFlow<MatrixUser> = MutableStateFlow(
|
||||
MatrixUser(
|
||||
userId = sessionId,
|
||||
// TODO cache for displayName?
|
||||
displayName = null,
|
||||
avatarUrl = client.cachedAvatarUrl(),
|
||||
)
|
||||
)
|
||||
|
||||
override val userProfile: StateFlow<MatrixUser> = _userProfile
|
||||
|
||||
override val ignoredUsersFlow = mxCallbackFlow<ImmutableList<UserId>> {
|
||||
client.subscribeToIgnoredUsers(object : IgnoredUsersListener {
|
||||
override fun call(ignoredUserIds: List<String>) {
|
||||
|
|
@ -265,6 +278,10 @@ class RustMatrixClient(
|
|||
setupVerificationControllerIfNeeded()
|
||||
}
|
||||
}.launchIn(sessionCoroutineScope)
|
||||
sessionCoroutineScope.launch {
|
||||
// Force a refresh of the profile
|
||||
getUserProfile()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getRoom(roomId: RoomId): MatrixRoom? = withContext(sessionDispatcher) {
|
||||
|
|
@ -374,6 +391,9 @@ class RustMatrixClient(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getUserProfile(): Result<MatrixUser> = getProfile(sessionId)
|
||||
.onSuccess { _userProfile.tryEmit(it) }
|
||||
|
||||
override suspend fun searchUsers(searchTerm: String, limit: Long): Result<MatrixSearchUserResults> =
|
||||
withContext(sessionDispatcher) {
|
||||
runCatching {
|
||||
|
|
@ -471,18 +491,6 @@ class RustMatrixClient(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun loadUserDisplayName(): Result<String> = withContext(sessionDispatcher) {
|
||||
runCatching {
|
||||
client.displayName()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun loadUserAvatarUrl(): Result<String?> = withContext(sessionDispatcher) {
|
||||
runCatching {
|
||||
client.avatarUrl()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun uploadMedia(mimeType: String, data: ByteArray, progressCallback: ProgressCallback?): Result<String> = withContext(sessionDispatcher) {
|
||||
runCatching {
|
||||
client.uploadMedia(mimeType, data, progressCallback?.toProgressWatcher())
|
||||
|
|
|
|||
|
|
@ -48,14 +48,15 @@ import kotlinx.collections.immutable.persistentListOf
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
|
||||
class FakeMatrixClient(
|
||||
override val sessionId: SessionId = A_SESSION_ID,
|
||||
override val deviceId: String = "A_DEVICE_ID",
|
||||
override val sessionCoroutineScope: CoroutineScope = TestScope(),
|
||||
private val userDisplayName: Result<String> = Result.success(A_USER_NAME),
|
||||
private val userAvatarUrl: Result<String> = Result.success(AN_AVATAR_URL),
|
||||
private val userDisplayName: String? = A_USER_NAME,
|
||||
private val userAvatarUrl: String? = AN_AVATAR_URL,
|
||||
override val roomListService: RoomListService = FakeRoomListService(),
|
||||
override val mediaLoader: MatrixMediaLoader = FakeMediaLoader(),
|
||||
private val sessionVerificationService: FakeSessionVerificationService = FakeSessionVerificationService(),
|
||||
|
|
@ -73,6 +74,8 @@ class FakeMatrixClient(
|
|||
var removeAvatarCalled: Boolean = false
|
||||
private set
|
||||
|
||||
private val _userProfile: MutableStateFlow<MatrixUser> = MutableStateFlow(MatrixUser(sessionId, userDisplayName, userAvatarUrl))
|
||||
override val userProfile: StateFlow<MatrixUser> = _userProfile
|
||||
override val ignoredUsersFlow: MutableStateFlow<ImmutableList<UserId>> = MutableStateFlow(persistentListOf())
|
||||
|
||||
private var ignoreUserResult: Result<Unit> = Result.success(Unit)
|
||||
|
|
@ -140,12 +143,10 @@ class FakeMatrixClient(
|
|||
|
||||
override fun close() = Unit
|
||||
|
||||
override suspend fun loadUserDisplayName(): Result<String> {
|
||||
return userDisplayName
|
||||
}
|
||||
|
||||
override suspend fun loadUserAvatarUrl(): Result<String?> {
|
||||
return userAvatarUrl
|
||||
override suspend fun getUserProfile(): Result<MatrixUser> = simulateLongTask {
|
||||
val result = getProfileResults[sessionId]?.getOrNull() ?: MatrixUser(sessionId, userDisplayName, userAvatarUrl)
|
||||
_userProfile.tryEmit(result)
|
||||
return Result.success(result)
|
||||
}
|
||||
|
||||
override suspend fun getAccountManagementUrl(action: AccountManagementAction?): Result<String?> {
|
||||
|
|
|
|||
|
|
@ -292,23 +292,30 @@ class DefaultNotificationDrawerManager @Inject constructor(
|
|||
eventsForSessions.forEach { (sessionId, notifiableEvents) ->
|
||||
val client = matrixClientProvider.getOrRestore(sessionId).getOrThrow()
|
||||
val imageLoader = imageLoaderHolder.get(client)
|
||||
val currentUser = tryOrNull(
|
||||
onError = { Timber.tag(loggerTag.value).e(it, "Unable to retrieve info for user ${sessionId.value}") },
|
||||
operation = {
|
||||
// myUserDisplayName cannot be empty else NotificationCompat.MessagingStyle() will crash
|
||||
val myUserDisplayName = client.loadUserDisplayName().getOrNull() ?: sessionId.value
|
||||
val userAvatarUrl = client.loadUserAvatarUrl().getOrNull()
|
||||
MatrixUser(
|
||||
userId = sessionId,
|
||||
displayName = myUserDisplayName,
|
||||
avatarUrl = userAvatarUrl
|
||||
)
|
||||
}
|
||||
) ?: MatrixUser(
|
||||
userId = sessionId,
|
||||
displayName = sessionId.value,
|
||||
avatarUrl = null
|
||||
)
|
||||
val userFromCache = client.userProfile.value
|
||||
val currentUser = if (userFromCache.avatarUrl != null && userFromCache.displayName.isNullOrEmpty().not()) {
|
||||
// We have an avatar and a display name, use it
|
||||
userFromCache
|
||||
} else {
|
||||
tryOrNull(
|
||||
onError = { Timber.tag(loggerTag.value).e(it, "Unable to retrieve info for user ${sessionId.value}") },
|
||||
operation = {
|
||||
client.getUserProfile().getOrNull()
|
||||
?.let {
|
||||
// displayName cannot be empty else NotificationCompat.MessagingStyle() will crash
|
||||
if (it.displayName.isNullOrEmpty()) {
|
||||
it.copy(displayName = sessionId.value)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
}
|
||||
) ?: MatrixUser(
|
||||
userId = sessionId,
|
||||
displayName = sessionId.value,
|
||||
avatarUrl = null
|
||||
)
|
||||
}
|
||||
|
||||
notificationRenderer.render(currentUser, useCompleteNotificationFormat, notifiableEvents, imageLoader)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue