Релиз 1.2.0: синхронизированы статусы, скролл и UI-выравнивание
All checks were successful
Android Kernel Build / build (push) Successful in 17h2m4s
All checks were successful
Android Kernel Build / build (push) Successful in 17h2m4s
- Поднята версия приложения до 1.2.0 (versionCode 22)\n- Синхронизированы статусы отправки между чат-листом и диалогом: SENT отображается как часы до delivery, ERROR теперь стабильно приходит в открытый диалог\n- Доработан Telegram-подобный skeleton в диалоге: shimmer, геометрия пузырей, поддержка групповых аватаров\n- Добавлен плавный автоскролл к баннеру подтверждения нового устройства в чат-листе\n- Выровнены verified-галочки с именами в профилях и в сайдбаре\n- Кнопка Copy Seed Phrase в светлой теме приведена к белому тексту\n- Мелкие UI-правки в чате и компонентах ввода/эмодзи
This commit is contained in:
@@ -23,8 +23,8 @@ val gitShortSha = safeGitOutput("rev-parse", "--short", "HEAD") ?: "unknown"
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// Rosetta versioning — bump here on each release
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
val rosettaVersionName = "1.1.9"
|
||||
val rosettaVersionCode = 21 // Increment on each release
|
||||
val rosettaVersionName = "1.2.0"
|
||||
val rosettaVersionCode = 22 // Increment on each release
|
||||
|
||||
android {
|
||||
namespace = "com.rosetta.messenger"
|
||||
|
||||
@@ -600,6 +600,9 @@ class MessageRepository private constructor(private val context: Context) {
|
||||
// При ошибке обновляем статус
|
||||
messageDao.updateDeliveryStatus(account, messageId, DeliveryStatus.ERROR.value)
|
||||
updateMessageStatus(dialogKey, messageId, DeliveryStatus.ERROR)
|
||||
_deliveryStatusEvents.tryEmit(
|
||||
DeliveryStatusUpdate(dialogKey, messageId, DeliveryStatus.ERROR)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1140,6 +1143,9 @@ class MessageRepository private constructor(private val context: Context) {
|
||||
messageDao.updateDeliveryStatus(account, entity.messageId, DeliveryStatus.ERROR.value)
|
||||
val dialogKey = getDialogKey(entity.toPublicKey)
|
||||
updateMessageStatus(dialogKey, entity.messageId, DeliveryStatus.ERROR)
|
||||
_deliveryStatusEvents.tryEmit(
|
||||
DeliveryStatusUpdate(dialogKey, entity.messageId, DeliveryStatus.ERROR)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1164,7 +1164,7 @@ fun ChatDetailScreen(
|
||||
Row(
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.height(56.dp)
|
||||
.height(64.dp)
|
||||
.padding(
|
||||
horizontal =
|
||||
4.dp
|
||||
@@ -1336,7 +1336,7 @@ fun ChatDetailScreen(
|
||||
Row(
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.height(56.dp)
|
||||
.height(64.dp)
|
||||
.padding(
|
||||
horizontal =
|
||||
4.dp
|
||||
@@ -2310,6 +2310,7 @@ fun ChatDetailScreen(
|
||||
isLoading -> {
|
||||
MessageSkeletonList(
|
||||
isDarkTheme = isDarkTheme,
|
||||
isGroupChat = isGroupChat,
|
||||
modifier =
|
||||
Modifier.fillMaxSize()
|
||||
)
|
||||
|
||||
@@ -425,6 +425,10 @@ class ChatViewModel(application: Application) : AndroidViewModel(application) {
|
||||
// Обновляем конкретное сообщение
|
||||
updateMessageStatus(update.messageId, MessageStatus.DELIVERED)
|
||||
}
|
||||
DeliveryStatus.ERROR -> {
|
||||
// Синхронизируем ошибку отправки с открытым диалогом
|
||||
updateMessageStatus(update.messageId, MessageStatus.ERROR)
|
||||
}
|
||||
DeliveryStatus.READ -> {
|
||||
// Помечаем все исходящие как прочитанные
|
||||
markAllOutgoingAsRead()
|
||||
|
||||
@@ -928,26 +928,27 @@ fun ChatsListScreen(
|
||||
// Display name
|
||||
if (accountName.isNotEmpty()) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
horizontalArrangement = Arrangement.Start
|
||||
) {
|
||||
Text(
|
||||
text = accountName,
|
||||
fontSize = 15.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = Color.White
|
||||
color = Color.White,
|
||||
modifier = Modifier.alignByBaseline()
|
||||
)
|
||||
if (accountVerified > 0 || isRosettaOfficial || isFreddyOfficial) {
|
||||
Spacer(
|
||||
Box(
|
||||
modifier =
|
||||
Modifier.width(
|
||||
6.dp
|
||||
)
|
||||
)
|
||||
VerifiedBadge(
|
||||
verified = if (accountVerified > 0) accountVerified else 1,
|
||||
size = 15,
|
||||
badgeTint = if (isDarkTheme) PrimaryBlue else Color.White
|
||||
)
|
||||
Modifier.padding(start = 2.dp)
|
||||
.alignBy { (it.measuredHeight * 0.78f).toInt() }
|
||||
) {
|
||||
VerifiedBadge(
|
||||
verified = if (accountVerified > 0) accountVerified else 1,
|
||||
size = 15,
|
||||
badgeTint = if (isDarkTheme) PrimaryBlue else Color.White
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2066,8 +2067,34 @@ fun ChatsListScreen(
|
||||
// Track scroll direction to hide/show Requests
|
||||
val chatListState = rememberLazyListState()
|
||||
var isRequestsVisible by remember { mutableStateOf(true) }
|
||||
var lastAutoScrolledVerificationId by remember {
|
||||
mutableStateOf<String?>(null)
|
||||
}
|
||||
val hapticFeedback = LocalHapticFeedback.current
|
||||
|
||||
// When a new device confirmation banner appears at the top,
|
||||
// smoothly bring the list to top so the banner is visible.
|
||||
LaunchedEffect(pendingDeviceVerification?.deviceId) {
|
||||
val verificationId =
|
||||
pendingDeviceVerification?.deviceId
|
||||
if (verificationId.isNullOrBlank()) {
|
||||
lastAutoScrolledVerificationId = null
|
||||
return@LaunchedEffect
|
||||
}
|
||||
if (verificationId == lastAutoScrolledVerificationId) {
|
||||
return@LaunchedEffect
|
||||
}
|
||||
|
||||
val alreadyAtTop =
|
||||
chatListState.firstVisibleItemIndex == 0 &&
|
||||
chatListState.firstVisibleItemScrollOffset <= 2
|
||||
if (!alreadyAtTop) {
|
||||
chatListState.animateScrollToItem(0)
|
||||
}
|
||||
|
||||
lastAutoScrolledVerificationId = verificationId
|
||||
}
|
||||
|
||||
// NestedScroll — ловим направление свайпа даже без скролла
|
||||
// Для появления: накапливаем pull down дельту, нужен сильный жест
|
||||
val requestsNestedScroll = remember(hapticFeedback) {
|
||||
|
||||
@@ -1898,12 +1898,12 @@ fun AnimatedMessageStatus(
|
||||
}
|
||||
} else {
|
||||
Icon(
|
||||
painter =
|
||||
painter =
|
||||
when (currentStatus) {
|
||||
MessageStatus.SENDING ->
|
||||
TelegramIcons.Clock
|
||||
MessageStatus.SENT ->
|
||||
TelegramIcons.Done
|
||||
TelegramIcons.Clock
|
||||
MessageStatus.DELIVERED ->
|
||||
TelegramIcons.Done
|
||||
else -> TelegramIcons.Clock
|
||||
@@ -2573,68 +2573,130 @@ private fun ForwardedImagePreview(
|
||||
|
||||
/** Message skeleton loader with shimmer animation */
|
||||
@Composable
|
||||
fun MessageSkeletonList(isDarkTheme: Boolean, modifier: Modifier = Modifier) {
|
||||
val skeletonColor = if (isDarkTheme) Color(0xFF3A3A3C) else Color(0xFFE0E0E0)
|
||||
|
||||
val infiniteTransition = rememberInfiniteTransition(label = "shimmer")
|
||||
val shimmerAlpha by
|
||||
infiniteTransition.animateFloat(
|
||||
initialValue = 0.4f,
|
||||
targetValue = 0.8f,
|
||||
fun MessageSkeletonList(
|
||||
isDarkTheme: Boolean,
|
||||
isGroupChat: Boolean,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val transition = rememberInfiniteTransition(label = "telegramSkeleton")
|
||||
val shimmerProgress by
|
||||
transition.animateFloat(
|
||||
initialValue = -1f,
|
||||
targetValue = 2f,
|
||||
animationSpec =
|
||||
infiniteRepeatable(
|
||||
animation = tween(800, easing = FastOutSlowInEasing),
|
||||
repeatMode = RepeatMode.Reverse
|
||||
animation = tween(1300, easing = LinearEasing),
|
||||
repeatMode = RepeatMode.Restart
|
||||
),
|
||||
label = "shimmerAlpha"
|
||||
label = "telegramSkeletonProgress"
|
||||
)
|
||||
|
||||
Box(modifier = modifier) {
|
||||
// Telegram-style deterministic pseudo-randomness.
|
||||
val widthRandom = remember { floatArrayOf(0.18f, 0.74f, 0.32f, 0.61f, 0.27f, 0.84f, 0.49f, 0.12f) }
|
||||
val heightRandom = remember { floatArrayOf(0.25f, 0.68f, 0.14f, 0.52f, 0.37f, 0.79f, 0.29f, 0.57f) }
|
||||
|
||||
val bubbleShape =
|
||||
remember {
|
||||
RoundedCornerShape(
|
||||
topStart = TelegramBubbleSpec.bubbleRadius,
|
||||
topEnd = TelegramBubbleSpec.bubbleRadius,
|
||||
bottomStart = TelegramBubbleSpec.nearRadius,
|
||||
bottomEnd = TelegramBubbleSpec.bubbleRadius
|
||||
)
|
||||
}
|
||||
|
||||
BoxWithConstraints(modifier = modifier.fillMaxSize()) {
|
||||
val density = LocalDensity.current
|
||||
val maxWidthPx = with(density) { maxWidth.toPx() }.coerceAtLeast(1f)
|
||||
val gradientWidthPx = with(density) { 200.dp.toPx() }
|
||||
val shimmerX = shimmerProgress * maxWidthPx
|
||||
|
||||
val color0 =
|
||||
if (isDarkTheme) Color(0x36FFFFFF) else Color(0x1F000000)
|
||||
val color1 =
|
||||
if (isDarkTheme) Color(0x1CFFFFFF) else Color(0x12000000)
|
||||
val outlineColor =
|
||||
if (isDarkTheme) Color(0x40FFFFFF) else Color(0x24FFFFFF)
|
||||
|
||||
val shimmerBrush =
|
||||
Brush.linearGradient(
|
||||
colorStops =
|
||||
arrayOf(
|
||||
0f to color1,
|
||||
0.4f to color0,
|
||||
0.6f to color0,
|
||||
1f to color1
|
||||
),
|
||||
start = Offset(shimmerX - gradientWidthPx, 0f),
|
||||
end = Offset(shimmerX, 0f)
|
||||
)
|
||||
|
||||
val bottomInset = 58.dp
|
||||
val containerWidth = this@BoxWithConstraints.maxWidth
|
||||
val bubbleMaxWidth = (containerWidth * 0.8f) - if (isGroupChat) 42.dp else 0.dp
|
||||
val avatarGap = 6.dp
|
||||
|
||||
val availableHeight = (maxHeight - bottomInset).coerceAtLeast(0.dp)
|
||||
var usedHeight = 0.dp
|
||||
var rowCount = 0
|
||||
while (rowCount < 24 && usedHeight < availableHeight) {
|
||||
val randomHeight = heightRandom[rowCount % heightRandom.size]
|
||||
usedHeight += (64.dp + (randomHeight * 64f).dp + 3.dp)
|
||||
rowCount++
|
||||
}
|
||||
if (rowCount < 6) rowCount = 6
|
||||
|
||||
Column(
|
||||
modifier =
|
||||
Modifier.align(Alignment.BottomCenter)
|
||||
Modifier.align(Alignment.BottomStart)
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 8.dp)
|
||||
.padding(bottom = 80.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(6.dp)
|
||||
.padding(start = 3.dp, end = 8.dp, bottom = bottomInset),
|
||||
verticalArrangement = Arrangement.spacedBy(3.dp)
|
||||
) {
|
||||
SkeletonBubble(true, 0.45f, skeletonColor, shimmerAlpha)
|
||||
SkeletonBubble(false, 0.55f, skeletonColor, shimmerAlpha)
|
||||
SkeletonBubble(true, 0.35f, skeletonColor, shimmerAlpha)
|
||||
SkeletonBubble(false, 0.50f, skeletonColor, shimmerAlpha)
|
||||
SkeletonBubble(true, 0.60f, skeletonColor, shimmerAlpha)
|
||||
SkeletonBubble(false, 0.40f, skeletonColor, shimmerAlpha)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SkeletonBubble(
|
||||
isOutgoing: Boolean,
|
||||
widthFraction: Float,
|
||||
bubbleColor: Color,
|
||||
alpha: Float
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = if (isOutgoing) Arrangement.End else Arrangement.Start
|
||||
) {
|
||||
Box(
|
||||
modifier =
|
||||
Modifier.fillMaxWidth(widthFraction)
|
||||
.defaultMinSize(minHeight = 44.dp)
|
||||
.clip(
|
||||
RoundedCornerShape(
|
||||
topStart = 18.dp,
|
||||
topEnd = 18.dp,
|
||||
bottomStart =
|
||||
if (isOutgoing) 18.dp else 6.dp,
|
||||
bottomEnd = if (isOutgoing) 6.dp else 18.dp
|
||||
)
|
||||
repeat(rowCount) { index ->
|
||||
val lineWidth =
|
||||
minOf(
|
||||
bubbleMaxWidth,
|
||||
42.dp +
|
||||
containerWidth *
|
||||
(0.4f +
|
||||
widthRandom[index % widthRandom.size] *
|
||||
0.35f)
|
||||
)
|
||||
.background(bubbleColor.copy(alpha = alpha))
|
||||
.padding(horizontal = 14.dp, vertical = 10.dp)
|
||||
)
|
||||
val lineHeight =
|
||||
64.dp +
|
||||
(heightRandom[index % heightRandom.size] * 64f).dp
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.Bottom
|
||||
) {
|
||||
if (isGroupChat) {
|
||||
Box(
|
||||
modifier =
|
||||
Modifier.size(42.dp)
|
||||
.clip(CircleShape)
|
||||
.background(shimmerBrush)
|
||||
.border(1.dp, outlineColor, CircleShape)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(avatarGap))
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier =
|
||||
Modifier.width(lineWidth)
|
||||
.height(lineHeight)
|
||||
.clip(bubbleShape)
|
||||
.background(shimmerBrush)
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = outlineColor,
|
||||
shape = bubbleShape
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Paint
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import android.text.Editable
|
||||
import android.text.SpannableStringBuilder
|
||||
@@ -12,6 +14,7 @@ import android.text.TextPaint
|
||||
import android.text.TextWatcher
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.text.style.ClickableSpan
|
||||
import android.text.style.DynamicDrawableSpan
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.text.style.ImageSpan
|
||||
import android.util.AttributeSet
|
||||
@@ -32,6 +35,43 @@ import androidx.compose.foundation.background
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import java.util.regex.Pattern
|
||||
|
||||
private class TelegramLikeEmojiSpan(
|
||||
emojiDrawable: Drawable,
|
||||
private var sourceFontMetrics: Paint.FontMetricsInt?
|
||||
) : ImageSpan(emojiDrawable, DynamicDrawableSpan.ALIGN_BOTTOM) {
|
||||
|
||||
private var sizePx: Int = resolveSize(sourceFontMetrics)
|
||||
|
||||
private fun resolveSize(metrics: Paint.FontMetricsInt?): Int {
|
||||
val metricsSize = metrics?.let { kotlin.math.abs(it.descent) + kotlin.math.abs(it.ascent) } ?: 0
|
||||
if (metricsSize > 0) return metricsSize
|
||||
val intrinsic = drawable.intrinsicHeight
|
||||
return if (intrinsic > 0) intrinsic else 20
|
||||
}
|
||||
|
||||
override fun getSize(
|
||||
paint: Paint,
|
||||
text: CharSequence,
|
||||
start: Int,
|
||||
end: Int,
|
||||
fm: Paint.FontMetricsInt?
|
||||
): Int {
|
||||
val metrics = sourceFontMetrics ?: paint.fontMetricsInt
|
||||
sourceFontMetrics = metrics
|
||||
sizePx = resolveSize(metrics)
|
||||
val scaledSize = sizePx.coerceAtLeast(1)
|
||||
drawable.setBounds(0, 0, scaledSize, scaledSize)
|
||||
fm?.let {
|
||||
it.ascent = metrics.ascent
|
||||
it.descent = metrics.descent
|
||||
it.top = metrics.top
|
||||
it.bottom = metrics.bottom
|
||||
it.leading = metrics.leading
|
||||
}
|
||||
return scaledSize
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apple Emoji EditText - кастомный EditText с PNG эмодзи
|
||||
* Заменяет системные эмодзи на Apple PNG изображения из assets
|
||||
@@ -98,9 +138,8 @@ class AppleEmojiEditTextView @JvmOverloads constructor(
|
||||
// 🔥 Паттерн для :emoji_XXXX: формата (как в десктопе)
|
||||
val EMOJI_CODE_PATTERN: Pattern = Pattern.compile(":emoji_([a-fA-F0-9_-]+):")
|
||||
|
||||
// Кэш для bitmap и drawable
|
||||
// Кэш bitmap
|
||||
private val bitmapCache = LruCache<String, Bitmap>(500)
|
||||
private val drawableCache = LruCache<String, BitmapDrawable>(500)
|
||||
}
|
||||
|
||||
init {
|
||||
@@ -195,27 +234,17 @@ class AppleEmojiEditTextView @JvmOverloads constructor(
|
||||
if (existingSpans.isNotEmpty()) continue
|
||||
|
||||
val unified = match.unified
|
||||
var drawable = drawableCache.get(unified)
|
||||
|
||||
if (drawable == null) {
|
||||
var bitmap = bitmapCache.get(unified)
|
||||
if (bitmap == null) {
|
||||
bitmap = loadFromAssets(unified)
|
||||
if (bitmap != null) {
|
||||
bitmapCache.put(unified, bitmap)
|
||||
}
|
||||
}
|
||||
|
||||
var bitmap = bitmapCache.get(unified)
|
||||
if (bitmap == null) {
|
||||
bitmap = loadFromAssets(unified)
|
||||
if (bitmap != null) {
|
||||
drawable = BitmapDrawable(getContext().resources, bitmap)
|
||||
val size = (textSize * 1.15).toInt()
|
||||
drawable.setBounds(0, 0, size, size)
|
||||
drawableCache.put(unified, drawable)
|
||||
bitmapCache.put(unified, bitmap)
|
||||
}
|
||||
}
|
||||
|
||||
if (drawable != null && start < editable.length && end <= editable.length) {
|
||||
val imageSpan = ImageSpan(drawable, ImageSpan.ALIGN_CENTER)
|
||||
if (bitmap != null && start < editable.length && end <= editable.length) {
|
||||
val drawable = BitmapDrawable(resources, bitmap)
|
||||
val imageSpan = TelegramLikeEmojiSpan(drawable, paint.fontMetricsInt)
|
||||
editable.setSpan(imageSpan, start, end, SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
}
|
||||
@@ -577,12 +606,8 @@ class AppleEmojiTextView @JvmOverloads constructor(
|
||||
for (match in emojiMatches) {
|
||||
val bitmap = loadEmojiBitmap(match.unified)
|
||||
if (bitmap != null) {
|
||||
val size = (textSize * 1.3).toInt()
|
||||
val scaledBitmap = Bitmap.createScaledBitmap(bitmap, size, size, true)
|
||||
val drawable = BitmapDrawable(resources, scaledBitmap)
|
||||
drawable.setBounds(0, 0, size, size)
|
||||
|
||||
val span = ImageSpan(drawable, ImageSpan.ALIGN_BOTTOM)
|
||||
val drawable = BitmapDrawable(resources, bitmap)
|
||||
val span = TelegramLikeEmojiSpan(drawable, paint.fontMetricsInt)
|
||||
|
||||
// Для :emoji_XXXX: заменяем весь текст на пробел + span
|
||||
// Для Unicode эмодзи оставляем символ как есть
|
||||
|
||||
@@ -253,7 +253,8 @@ fun BackupScreen(
|
||||
.fillMaxWidth()
|
||||
.height(52.dp),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = Color(0xFF248AE6)
|
||||
containerColor = Color(0xFF248AE6),
|
||||
contentColor = Color.White
|
||||
),
|
||||
shape = RoundedCornerShape(10.dp)
|
||||
) {
|
||||
@@ -266,7 +267,8 @@ fun BackupScreen(
|
||||
Text(
|
||||
text = "Copy Seed Phrase",
|
||||
fontSize = 15.sp,
|
||||
fontWeight = FontWeight.Medium
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color.White
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -2138,9 +2138,9 @@ private fun CollapsingOtherProfileHeader(
|
||||
Modifier.align(Alignment.TopCenter).offset(y = textY),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
val verifiedBadgeSize = (nameFontSize.value * 0.8f).toInt()
|
||||
// Name + Verified Badge
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
Text(
|
||||
@@ -2150,18 +2150,23 @@ private fun CollapsingOtherProfileHeader(
|
||||
color = textColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.widthIn(max = 220.dp),
|
||||
modifier = Modifier.widthIn(max = 220.dp).alignByBaseline(),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
if (verified > 0 || isRosettaOfficial || isFreddyOfficial) {
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
VerifiedBadge(
|
||||
verified = if (verified > 0) verified else 1,
|
||||
size = (nameFontSize.value * 0.8f).toInt(),
|
||||
isDarkTheme = isDarkTheme,
|
||||
badgeTint = if (isDarkTheme) Color.White else PrimaryBlue
|
||||
)
|
||||
Box(
|
||||
modifier =
|
||||
Modifier.padding(start = 4.dp)
|
||||
.alignBy { (it.measuredHeight * 0.78f).toInt() }
|
||||
) {
|
||||
VerifiedBadge(
|
||||
verified = if (verified > 0) verified else 1,
|
||||
size = verifiedBadgeSize,
|
||||
isDarkTheme = isDarkTheme,
|
||||
badgeTint = Color.White
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1389,8 +1389,8 @@ private fun CollapsingProfileHeader(
|
||||
Modifier.align(Alignment.TopCenter).offset(y = textY),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
val verifiedBadgeSize = (nameFontSize.value * 0.8f).toInt()
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
Text(
|
||||
@@ -1400,17 +1400,22 @@ private fun CollapsingProfileHeader(
|
||||
color = textColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.widthIn(max = 220.dp),
|
||||
modifier = Modifier.widthIn(max = 220.dp).alignByBaseline(),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
if (verified > 0 || isRosettaOfficial || isFreddyOfficial) {
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
VerifiedBadge(
|
||||
verified = if (verified > 0) verified else 2,
|
||||
size = (nameFontSize.value * 0.8f).toInt(),
|
||||
isDarkTheme = isDarkTheme,
|
||||
badgeTint = if (isDarkTheme) Color.White else PrimaryBlue
|
||||
)
|
||||
Box(
|
||||
modifier =
|
||||
Modifier.padding(start = 4.dp)
|
||||
.alignBy { (it.measuredHeight * 0.78f).toInt() }
|
||||
) {
|
||||
VerifiedBadge(
|
||||
verified = if (verified > 0) verified else 2,
|
||||
size = verifiedBadgeSize,
|
||||
isDarkTheme = isDarkTheme,
|
||||
badgeTint = Color.White
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user