Implement a in-memory cache for user avatar colors.

This commit is contained in:
Benoit Marty 2023-09-08 13:51:46 +02:00 committed by Benoit Marty
parent 06a607abf8
commit 5a85efc458
5 changed files with 42 additions and 23 deletions

View file

@ -43,7 +43,7 @@ import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMo
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
import io.element.android.libraries.designsystem.colors.AvatarColors import io.element.android.libraries.designsystem.colors.AvatarColors
import io.element.android.libraries.designsystem.colors.avatarColors import io.element.android.libraries.designsystem.colors.AvatarColorsProvider
import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
@ -119,21 +119,21 @@ private fun NotificationsOptInContent(
) { ) {
NotificationRow( NotificationRow(
avatarLetter = "M", avatarLetter = "M",
avatarColors = avatarColors("5"), avatarColors = AvatarColorsProvider.provide("5", ElementTheme.isLightTheme),
firstRowPercent = 1f, firstRowPercent = 1f,
secondRowPercent = 0.4f secondRowPercent = 0.4f
) )
NotificationRow( NotificationRow(
avatarLetter = "A", avatarLetter = "A",
avatarColors = avatarColors("1"), avatarColors = AvatarColorsProvider.provide("1", ElementTheme.isLightTheme),
firstRowPercent = 1f, firstRowPercent = 1f,
secondRowPercent = 1f secondRowPercent = 1f
) )
NotificationRow( NotificationRow(
avatarLetter = "T", avatarLetter = "T",
avatarColors = avatarColors("4"), avatarColors = AvatarColorsProvider.provide("4", ElementTheme.isLightTheme),
firstRowPercent = 0.65f, firstRowPercent = 0.65f,
secondRowPercent = 0f secondRowPercent = 0f
) )

View file

@ -75,7 +75,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemPollContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemPollContent
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
import io.element.android.libraries.designsystem.colors.avatarColors import io.element.android.libraries.designsystem.colors.AvatarColorsProvider
import io.element.android.libraries.designsystem.components.EqualWidthColumn import io.element.android.libraries.designsystem.components.EqualWidthColumn
import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
@ -328,7 +328,7 @@ private fun MessageSenderInformation(
) { ) {
val avatarStrokeColor = MaterialTheme.colorScheme.background val avatarStrokeColor = MaterialTheme.colorScheme.background
val avatarSize = senderAvatar.size.dp val avatarSize = senderAvatar.size.dp
val avatarColors = avatarColors(senderAvatar.id) val avatarColors = AvatarColorsProvider.provide(senderAvatar.id, ElementTheme.isLightTheme)
Box( Box(
modifier = modifier modifier = modifier
) { ) {

View file

@ -16,9 +16,8 @@
package io.element.android.libraries.designsystem.colors package io.element.android.libraries.designsystem.colors
import androidx.compose.runtime.Composable import androidx.collection.LruCache
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.theme.colors.avatarColorsDark import io.element.android.libraries.theme.colors.avatarColorsDark
import io.element.android.libraries.theme.colors.avatarColorsLight import io.element.android.libraries.theme.colors.avatarColorsLight
@ -27,18 +26,37 @@ data class AvatarColors(
val foreground: Color, val foreground: Color,
) )
@Composable object AvatarColorsProvider {
fun avatarColors(userId: String): AvatarColors { private val cache = LruCache<String, AvatarColors>(200)
val hash = userId.toHash() private var currentThemeIsLight = true
val colors = if (ElementTheme.isLightTheme) {
avatarColorsLight[hash] fun provide(id: String, isLightTheme: Boolean): AvatarColors {
} else { if (currentThemeIsLight != isLightTheme) {
avatarColorsDark[hash] currentThemeIsLight = isLightTheme
cache.evictAll()
}
val valueFromCache = cache.get(id)
return if (valueFromCache != null) {
valueFromCache
} else {
val colors = avatarColors(id, isLightTheme)
cache.put(id, colors)
colors
}
}
private fun avatarColors(id: String, isLightTheme: Boolean): AvatarColors {
val hash = id.toHash()
val colors = if (isLightTheme) {
avatarColorsLight[hash]
} else {
avatarColorsDark[hash]
}
return AvatarColors(
background = colors.first,
foreground = colors.second,
)
} }
return AvatarColors(
background = colors.first,
foreground = colors.second,
)
} }
internal fun String.toHash(): Int { internal fun String.toHash(): Int {

View file

@ -33,7 +33,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import coil.compose.AsyncImage import coil.compose.AsyncImage
import io.element.android.libraries.designsystem.colors.AvatarColors import io.element.android.libraries.designsystem.colors.AvatarColors
import io.element.android.libraries.designsystem.colors.avatarColors import io.element.android.libraries.designsystem.colors.AvatarColorsProvider
import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.preview.debugPlaceholderAvatar import io.element.android.libraries.designsystem.preview.debugPlaceholderAvatar
@ -53,7 +53,7 @@ fun Avatar(
.size(avatarData.size.dp) .size(avatarData.size.dp)
.clip(CircleShape) .clip(CircleShape)
if (avatarData.url.isNullOrBlank()) { if (avatarData.url.isNullOrBlank()) {
val avatarColors = initialAvatarColors ?: avatarColors(avatarData.id) val avatarColors = initialAvatarColors ?: AvatarColorsProvider.provide(avatarData.id, ElementTheme.isLightTheme)
InitialsAvatar( InitialsAvatar(
avatarData = avatarData, avatarData = avatarData,
avatarColors = avatarColors, avatarColors = avatarColors,

View file

@ -24,10 +24,11 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.colors.avatarColors import io.element.android.libraries.designsystem.colors.AvatarColorsProvider
import io.element.android.libraries.designsystem.preview.DayNightPreviews import io.element.android.libraries.designsystem.preview.DayNightPreviews
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.theme.colors.avatarColorsLight import io.element.android.libraries.theme.colors.avatarColorsLight
@DayNightPreviews @DayNightPreviews
@ -43,7 +44,7 @@ internal fun UserAvatarPreview() = ElementPreview {
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
// Note: it's OK, since the hash of "0" is 0, the hash of "1" is 1, etc. // Note: it's OK, since the hash of "0" is 0, the hash of "1" is 1, etc.
Avatar(anAvatarData(), initialAvatarColors = avatarColors("$it")) Avatar(anAvatarData(), initialAvatarColors = AvatarColorsProvider.provide("$it", ElementTheme.isLightTheme))
Text(text = "Color index $it") Text(text = "Color index $it")
} }
} }