Skip to content

Commit

Permalink
Implement inner border on legend avatars
Browse files Browse the repository at this point in the history
  • Loading branch information
AleksandarIlic committed Dec 3, 2024
1 parent a1b3e44 commit c4dcd45
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ fun AvatarThumbnailsRow(

AvatarSpacer(width = (layoutIndex * avatarVisibleWidth.value).dp) {
UniversalAvatarThumbnail(
modifier = Modifier.size(avatarSize),
avatarCdnImage = imageCdnImage,
avatarSize = avatarSize,
hasBorder = hasAvatarBorder,
legendaryCustomization = runCatching { avatarLegendaryCustomizations[layoutIndex] }.getOrNull(),
fallbackBorderColor = avatarBorderColor,
Expand Down Expand Up @@ -111,10 +111,10 @@ private fun AvatarOverflowIndicator(
modifier = Modifier
.size(avatarSize)
.adjustAvatarBackground(
size = 48.dp,
hasBorder = hasAvatarBorder,
avatarSize = 48.dp,
hasOuterBorder = hasAvatarBorder,
borderBrush = Brush.linearGradient(listOf(avatarBorderColor, avatarBorderColor)),
borderSize = avatarBorderSize,
totalBorderSize = avatarBorderSize,
)
.background(color = moreBackgroundColor)
.fillMaxSize(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.ArrowForward
Expand Down Expand Up @@ -78,7 +77,7 @@ fun PrimalTopAppBar(
) {
UniversalAvatarThumbnail(
avatarCdnImage = avatarCdnImage,
modifier = Modifier.size(32.dp),
avatarSize = 32.dp,
onClick = onNavigationIconClick,
legendaryCustomization = legendaryCustomization,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,42 +48,58 @@ fun UniversalAvatarThumbnail(
val variant = avatarCdnImage?.variants?.minByOrNull { it.width }
val imageSource = variant?.mediaUrl ?: avatarCdnImage?.sourceUrl

val borderBrush = if (legendaryCustomization?.avatarGlow == true &&
val hasLegendBorder = legendaryCustomization?.avatarGlow == true &&
legendaryCustomization.legendaryStyle != LegendaryStyle.NO_CUSTOMIZATION
) {
legendaryCustomization.legendaryStyle?.brush

val borderBrush = if (hasLegendBorder) {
legendaryCustomization?.legendaryStyle?.brush
} else {
null
}

val totalBorderSize = avatarSize.resolveOuterBorderSizeFromAvatarSize() +
avatarSize.resolveInnerBorderSizeFromAvatarSize()

AvatarThumbnailListItemImage(
modifier = modifier,
avatarSize = avatarSize,
source = imageSource,
hasBorder = hasBorder,
hasOuterBorder = hasBorder && avatarSize > 0.dp,
hasInnerBorder = hasLegendBorder && avatarSize > 0.dp,
borderBrush = borderBrush ?: Brush.linearGradient(
colors = listOf(
fallbackBorderColor,
fallbackBorderColor,
),
),
borderSize = borderSizeOverride ?: avatarSize.mapAvatarSizeToBorderSize(),
totalBorderSize = borderSizeOverride ?: totalBorderSize,
backgroundColor = backgroundColor,
onClick = onClick,
defaultAvatar = defaultAvatar,
)
}

@Composable
private fun defaultBorderBrush() =
Brush.linearGradient(
listOf(AppTheme.colorScheme.primary, AppTheme.colorScheme.primary),
)

@Composable
private fun transparentBorderBrush() =
Brush.linearGradient(
listOf(Color.Transparent, Color.Transparent),
)

@Composable
private fun AvatarThumbnailListItemImage(
source: Any?,
modifier: Modifier = Modifier,
avatarSize: Dp = 48.dp,
hasBorder: Boolean = false,
borderBrush: Brush = Brush.linearGradient(
listOf(AppTheme.colorScheme.primary, AppTheme.colorScheme.primary),
),
borderSize: Dp = 2.dp,
totalBorderSize: Dp = 2.dp,
hasOuterBorder: Boolean = false,
hasInnerBorder: Boolean = false,
borderBrush: Brush = defaultBorderBrush(),
backgroundColor: Color = AppTheme.extraColorScheme.surfaceVariantAlt1,
onClick: (() -> Unit)? = null,
defaultAvatar: @Composable () -> Unit,
Expand All @@ -101,10 +117,11 @@ private fun AvatarThumbnailListItemImage(
imageLoader = imageLoader,
modifier = modifier
.adjustAvatarBackground(
size = avatarSize,
hasBorder = hasBorder,
borderSize = borderSize,
avatarSize = avatarSize,
totalBorderSize = totalBorderSize,
borderBrush = borderBrush,
hasOuterBorder = hasOuterBorder,
hasInnerBorder = hasInnerBorder,
)
.clickable(enabled = onClick != null, onClick = { onClick?.invoke() }),
contentDescription = stringResource(id = R.string.accessibility_profile_image),
Expand All @@ -120,39 +137,53 @@ private fun AvatarThumbnailListItemImage(
)
}

@Composable
fun Modifier.adjustAvatarBackground(
size: Dp = 48.dp,
hasBorder: Boolean = false,
borderSize: Dp = 2.dp,
avatarSize: Dp,
totalBorderSize: Dp,
borderBrush: Brush,
hasOuterBorder: Boolean = false,
hasInnerBorder: Boolean = false,
): Modifier {
return if (hasBorder) {
return if (hasOuterBorder || hasInnerBorder) {
val innerBorderSize = avatarSize.resolveInnerBorderSizeFromAvatarSize()
val outerBorderSize = totalBorderSize - innerBorderSize
this
.size(size + borderSize * 2)
.size(avatarSize + totalBorderSize * 2)
.border(
width = borderSize,
width = outerBorderSize,
brush = borderBrush,
shape = CircleShape,
)
.padding(borderSize)
.padding(outerBorderSize)
.then(
if (hasInnerBorder) {
Modifier
.border(
width = innerBorderSize,
color = AppTheme.colorScheme.background,
shape = CircleShape,
)
} else {
Modifier
},
)
.clip(CircleShape)
} else {
this
.size(size + borderSize * 2)
.size(avatarSize + totalBorderSize * 2)
.border(
width = borderSize,
brush = Brush.linearGradient(
listOf(Color.Transparent, Color.Transparent),
),
width = totalBorderSize,
brush = transparentBorderBrush(),
shape = CircleShape,
)
.padding(borderSize)
.padding(totalBorderSize)
.clip(CircleShape)
}
}

@Suppress("MagicNumber")
private fun Dp.mapAvatarSizeToBorderSize(): Dp =
private fun Dp.resolveOuterBorderSizeFromAvatarSize(): Dp =
when {
this >= 112.dp -> 4.dp
this >= 80.dp -> 3.dp
Expand All @@ -161,6 +192,15 @@ private fun Dp.mapAvatarSizeToBorderSize(): Dp =
else -> 1.dp
}

@Suppress("MagicNumber")
private fun Dp.resolveInnerBorderSizeFromAvatarSize(): Dp =
when {
this >= 100.dp -> (1.5).dp
this >= 40.dp -> 1.dp
this >= 32.dp -> (0.5).dp
else -> 0.dp
}

@Composable
fun DefaultAvatarThumbnailPlaceholderListItemImage(
backgroundColor: Color = AppTheme.extraColorScheme.surfaceVariantAlt1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.PagerState
Expand Down Expand Up @@ -259,7 +258,7 @@ fun ExploreTopAppBar(
) {
UniversalAvatarThumbnail(
avatarCdnImage = avatarCdnImage,
modifier = Modifier.size(32.dp),
avatarSize = 32.dp,
onClick = onNavigationIconClick,
legendaryCustomization = avatarLegendaryCustomization,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.lazy.LazyColumn
Expand Down Expand Up @@ -140,7 +139,7 @@ fun ChatScreen(
) {
UniversalAvatarThumbnail(
avatarCdnImage = state.participantProfile?.avatarCdnImage,
modifier = Modifier.size(32.dp),
avatarSize = 32.dp,
onClick = { noteCallbacks.onProfileClick?.invoke(state.participantId) },
legendaryCustomization = state.participantProfile?.legendaryCustomization,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.TopAppBar
Expand Down Expand Up @@ -118,13 +117,13 @@ fun ProfileTopCoverBar(
) {
UniversalAvatarThumbnail(
modifier = Modifier
.size(avatarValues.avatarSize)
.padding(
top = avatarValues.avatarPadding * 0 / 3,
bottom = avatarValues.avatarPadding * 3 / 3,
start = avatarValues.avatarPadding * 1 / 8,
end = avatarValues.avatarPadding * 7 / 8,
),
avatarSize = avatarValues.avatarSize,
onClick = { state.profileDetails?.avatarCdnImage?.sourceUrl?.let { onMediaItemClick(it) } },
avatarCdnImage = state.profileDetails?.avatarCdnImage,
fallbackBorderColor = Color.White,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ fun ProfileQrCodeViewer(
horizontalAlignment = Alignment.CenterHorizontally,
) {
UniversalAvatarThumbnail(
modifier = Modifier.size(108.dp),
avatarSize = 108.dp,
avatarCdnImage = profileDetails?.avatarCdnImage,
hasBorder = true,
fallbackBorderColor = Color.White,
Expand Down

0 comments on commit c4dcd45

Please sign in to comment.