diff --git a/app/src/main/kotlin/net/primal/android/core/compose/AvatarThumbnailsRow.kt b/app/src/main/kotlin/net/primal/android/core/compose/AvatarThumbnailsRow.kt index 2cc3e4f9c..3906f1a91 100644 --- a/app/src/main/kotlin/net/primal/android/core/compose/AvatarThumbnailsRow.kt +++ b/app/src/main/kotlin/net/primal/android/core/compose/AvatarThumbnailsRow.kt @@ -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, @@ -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(), diff --git a/app/src/main/kotlin/net/primal/android/core/compose/PrimalTopAppBar.kt b/app/src/main/kotlin/net/primal/android/core/compose/PrimalTopAppBar.kt index 53e41b73e..c81014c4f 100644 --- a/app/src/main/kotlin/net/primal/android/core/compose/PrimalTopAppBar.kt +++ b/app/src/main/kotlin/net/primal/android/core/compose/PrimalTopAppBar.kt @@ -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 @@ -78,7 +77,7 @@ fun PrimalTopAppBar( ) { UniversalAvatarThumbnail( avatarCdnImage = avatarCdnImage, - modifier = Modifier.size(32.dp), + avatarSize = 32.dp, onClick = onNavigationIconClick, legendaryCustomization = legendaryCustomization, ) diff --git a/app/src/main/kotlin/net/primal/android/core/compose/AvatarThumbnailListItemImage.kt b/app/src/main/kotlin/net/primal/android/core/compose/UniversalAvatarThumbnail.kt similarity index 68% rename from app/src/main/kotlin/net/primal/android/core/compose/AvatarThumbnailListItemImage.kt rename to app/src/main/kotlin/net/primal/android/core/compose/UniversalAvatarThumbnail.kt index 42af14585..4585fc551 100644 --- a/app/src/main/kotlin/net/primal/android/core/compose/AvatarThumbnailListItemImage.kt +++ b/app/src/main/kotlin/net/primal/android/core/compose/UniversalAvatarThumbnail.kt @@ -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, @@ -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), @@ -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 @@ -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, diff --git a/app/src/main/kotlin/net/primal/android/explore/home/ExploreHomeScreen.kt b/app/src/main/kotlin/net/primal/android/explore/home/ExploreHomeScreen.kt index 638cc1b33..2fed90a1b 100644 --- a/app/src/main/kotlin/net/primal/android/explore/home/ExploreHomeScreen.kt +++ b/app/src/main/kotlin/net/primal/android/explore/home/ExploreHomeScreen.kt @@ -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 @@ -259,7 +258,7 @@ fun ExploreTopAppBar( ) { UniversalAvatarThumbnail( avatarCdnImage = avatarCdnImage, - modifier = Modifier.size(32.dp), + avatarSize = 32.dp, onClick = onNavigationIconClick, legendaryCustomization = avatarLegendaryCustomization, ) diff --git a/app/src/main/kotlin/net/primal/android/messages/chat/ChatScreen.kt b/app/src/main/kotlin/net/primal/android/messages/chat/ChatScreen.kt index 06b87b1c5..b86b8305a 100644 --- a/app/src/main/kotlin/net/primal/android/messages/chat/ChatScreen.kt +++ b/app/src/main/kotlin/net/primal/android/messages/chat/ChatScreen.kt @@ -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 @@ -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, ) diff --git a/app/src/main/kotlin/net/primal/android/profile/details/ui/ProfileTopCoverBar.kt b/app/src/main/kotlin/net/primal/android/profile/details/ui/ProfileTopCoverBar.kt index 4ea90967f..f7fbdc404 100644 --- a/app/src/main/kotlin/net/primal/android/profile/details/ui/ProfileTopCoverBar.kt +++ b/app/src/main/kotlin/net/primal/android/profile/details/ui/ProfileTopCoverBar.kt @@ -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 @@ -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, diff --git a/app/src/main/kotlin/net/primal/android/profile/qr/ui/ProfileQrCodeViewer.kt b/app/src/main/kotlin/net/primal/android/profile/qr/ui/ProfileQrCodeViewer.kt index 542b36748..eb1b61986 100644 --- a/app/src/main/kotlin/net/primal/android/profile/qr/ui/ProfileQrCodeViewer.kt +++ b/app/src/main/kotlin/net/primal/android/profile/qr/ui/ProfileQrCodeViewer.kt @@ -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,