feat: Update ChatDetailScreen and ChatsListScreen for improved UI responsiveness and consistency; add custom verified badge icon
This commit is contained in:
@@ -58,6 +58,7 @@ import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -142,7 +143,7 @@ fun ChatDetailScreen(
|
||||
val backgroundColor = if (isDarkTheme) Color(0xFF1A1A1A) else Color(0xFFF2F2F7)
|
||||
val textColor = if (isDarkTheme) Color.White else Color.Black
|
||||
val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF8E8E93)
|
||||
val headerIconColor = if (isDarkTheme) Color(0xFF0A84FF) else Color(0xFF007AFF)
|
||||
val headerIconColor = Color.White
|
||||
|
||||
// 🔥 Keyboard & Emoji Coordinator
|
||||
val coordinator = remember { KeyboardTransitionCoordinator() }
|
||||
@@ -232,7 +233,7 @@ fun ChatDetailScreen(
|
||||
if (window != null && view != null) {
|
||||
val ic = androidx.core.view.WindowCompat.getInsetsController(window, view)
|
||||
window.statusBarColor = android.graphics.Color.TRANSPARENT
|
||||
ic.isAppearanceLightStatusBars = !isDarkTheme
|
||||
ic.isAppearanceLightStatusBars = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -659,7 +660,7 @@ fun ChatDetailScreen(
|
||||
// 🚀 Весь контент (swipe-back обрабатывается в SwipeBackContainer)
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
// Telegram-style solid header background (без blur)
|
||||
val headerBackground = if (isDarkTheme) Color(0xFF212121) else Color(0xFFFFFFFF)
|
||||
val headerBackground = if (isDarkTheme) Color(0xFF2A2A2A) else Color(0xFF228BE6)
|
||||
|
||||
Scaffold(
|
||||
contentWindowInsets = WindowInsets(0.dp),
|
||||
@@ -669,13 +670,7 @@ fun ChatDetailScreen(
|
||||
Box(
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.background(
|
||||
if (isSelectionMode) {
|
||||
if (isDarkTheme)
|
||||
Color(0xFF212121)
|
||||
else Color.White
|
||||
} else headerBackground
|
||||
)
|
||||
.background(headerBackground)
|
||||
// 🎨 statusBarsPadding ПОСЛЕ background =
|
||||
// хедер начинается под статус баром
|
||||
.statusBarsPadding()
|
||||
@@ -737,12 +732,7 @@ fun ChatDetailScreen(
|
||||
fontWeight =
|
||||
FontWeight
|
||||
.Bold,
|
||||
color =
|
||||
if (isDarkTheme
|
||||
)
|
||||
Color.White
|
||||
else
|
||||
Color.Black
|
||||
color = Color.White
|
||||
)
|
||||
}
|
||||
|
||||
@@ -880,14 +870,7 @@ fun ChatDetailScreen(
|
||||
imageVector = TablerIcons.ChevronLeft,
|
||||
contentDescription =
|
||||
"Back",
|
||||
tint =
|
||||
if (isDarkTheme
|
||||
)
|
||||
Color.White
|
||||
else
|
||||
Color(
|
||||
0xFF007AFF
|
||||
),
|
||||
tint = Color.White,
|
||||
modifier =
|
||||
Modifier.size(
|
||||
28.dp
|
||||
@@ -1007,15 +990,14 @@ fun ChatDetailScreen(
|
||||
CircleShape
|
||||
)
|
||||
.background(
|
||||
PrimaryBlue
|
||||
Color(0xFF228BE6)
|
||||
),
|
||||
contentAlignment =
|
||||
Alignment
|
||||
.Center
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default
|
||||
.Bookmark,
|
||||
painter = painterResource(R.drawable.bookmark_outlined),
|
||||
contentDescription =
|
||||
null,
|
||||
tint =
|
||||
@@ -1102,7 +1084,7 @@ fun ChatDetailScreen(
|
||||
FontWeight
|
||||
.SemiBold,
|
||||
color =
|
||||
textColor,
|
||||
Color.White,
|
||||
maxLines =
|
||||
1,
|
||||
overflow =
|
||||
@@ -1125,7 +1107,8 @@ fun ChatDetailScreen(
|
||||
size =
|
||||
16,
|
||||
isDarkTheme =
|
||||
isDarkTheme
|
||||
isDarkTheme,
|
||||
badgeTint = if (isDarkTheme) null else Color(0xFFACD2F9)
|
||||
)
|
||||
}
|
||||
// 🔇 Mute icon
|
||||
@@ -1135,7 +1118,7 @@ fun ChatDetailScreen(
|
||||
painter = TelegramIcons.Mute,
|
||||
contentDescription = "Muted",
|
||||
modifier = Modifier.size(16.dp),
|
||||
tint = secondaryTextColor
|
||||
tint = Color.White.copy(alpha = 0.7f)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1155,13 +1138,13 @@ fun ChatDetailScreen(
|
||||
color =
|
||||
when {
|
||||
isSavedMessages ->
|
||||
secondaryTextColor
|
||||
Color.White.copy(alpha = 0.7f)
|
||||
isOnline ->
|
||||
Color(
|
||||
0xFF38B24D
|
||||
) // Зелёный когда онлайн
|
||||
0xFF7FE084
|
||||
) // Зелёный когда онлайн (светлый на синем фоне)
|
||||
else ->
|
||||
secondaryTextColor // Серый
|
||||
Color.White.copy(alpha = 0.7f) // Белый полупрозрачный
|
||||
// для
|
||||
// offline
|
||||
},
|
||||
@@ -1182,14 +1165,7 @@ fun ChatDetailScreen(
|
||||
.Call,
|
||||
contentDescription =
|
||||
"Call",
|
||||
tint =
|
||||
if (isDarkTheme
|
||||
)
|
||||
Color.White
|
||||
else
|
||||
Color(
|
||||
0xFF007AFF
|
||||
)
|
||||
tint = Color.White
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1221,14 +1197,7 @@ fun ChatDetailScreen(
|
||||
.MoreVert,
|
||||
contentDescription =
|
||||
"More",
|
||||
tint =
|
||||
if (isDarkTheme
|
||||
)
|
||||
Color.White
|
||||
else
|
||||
Color(
|
||||
0xFF007AFF
|
||||
),
|
||||
tint = Color.White,
|
||||
modifier =
|
||||
Modifier.size(
|
||||
26.dp
|
||||
@@ -1287,15 +1256,7 @@ fun ChatDetailScreen(
|
||||
.fillMaxWidth()
|
||||
.height(0.5.dp)
|
||||
.background(
|
||||
if (isDarkTheme)
|
||||
Color.White.copy(
|
||||
alpha =
|
||||
0.15f
|
||||
)
|
||||
else
|
||||
Color.Black.copy(
|
||||
alpha = 0.1f
|
||||
)
|
||||
Color.White.copy(alpha = 0.15f)
|
||||
)
|
||||
)
|
||||
} // Закрытие Box unified header
|
||||
|
||||
@@ -73,6 +73,8 @@ import com.rosetta.messenger.ui.components.AvatarImage
|
||||
import com.rosetta.messenger.ui.components.VerifiedBadge
|
||||
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
||||
import com.rosetta.messenger.ui.onboarding.PrimaryBlueDark
|
||||
import com.rosetta.messenger.update.UpdateManager
|
||||
import com.rosetta.messenger.update.UpdateState
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
@@ -245,6 +247,10 @@ fun ChatsListScreen(
|
||||
val focusManager = androidx.compose.ui.platform.LocalFocusManager.current
|
||||
val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
|
||||
val scope = rememberCoroutineScope()
|
||||
val sduUpdateState by UpdateManager.updateState.collectAsState()
|
||||
val sduDownloadProgress by UpdateManager.downloadProgress.collectAsState()
|
||||
val sduDebugLogs by UpdateManager.debugLogs.collectAsState()
|
||||
var showSduLogs by remember { mutableStateOf(false) }
|
||||
val themeRevealRadius = remember { Animatable(0f) }
|
||||
var rootSize by remember { mutableStateOf(IntSize.Zero) }
|
||||
var themeToggleCenterInRoot by remember { mutableStateOf<Offset?>(null) }
|
||||
@@ -253,6 +259,73 @@ fun ChatsListScreen(
|
||||
var themeRevealCenter by remember { mutableStateOf(Offset.Zero) }
|
||||
var themeRevealSnapshot by remember { mutableStateOf<ImageBitmap?>(null) }
|
||||
|
||||
// ═══════════════ SDU Debug Log Dialog ═══════════════
|
||||
if (showSduLogs) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { showSduLogs = false },
|
||||
title = {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text("SDU Logs", fontWeight = FontWeight.Bold, fontSize = 16.sp)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Text(
|
||||
"state: ${sduUpdateState::class.simpleName}",
|
||||
fontSize = 11.sp,
|
||||
color = Color.Gray
|
||||
)
|
||||
}
|
||||
},
|
||||
text = {
|
||||
val scrollState = rememberScrollState()
|
||||
LaunchedEffect(sduDebugLogs.size) {
|
||||
scrollState.animateScrollTo(scrollState.maxValue)
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(max = 400.dp)
|
||||
.verticalScroll(scrollState)
|
||||
) {
|
||||
if (sduDebugLogs.isEmpty()) {
|
||||
Text(
|
||||
"Нет логов. SDU ещё не инициализирован\nили пакет 0x0A не пришёл.",
|
||||
fontSize = 13.sp,
|
||||
color = Color.Gray
|
||||
)
|
||||
} else {
|
||||
sduDebugLogs.forEach { line ->
|
||||
Text(
|
||||
text = line,
|
||||
fontSize = 11.sp,
|
||||
fontFamily = androidx.compose.ui.text.font.FontFamily.Monospace,
|
||||
color = when {
|
||||
"ERROR" in line || "EXCEPTION" in line -> Color(0xFFFF5555)
|
||||
"WARNING" in line -> Color(0xFFFFAA33)
|
||||
"State ->" in line -> Color(0xFF55BB55)
|
||||
else -> if (isDarkTheme) Color(0xFFCCCCCC) else Color(0xFF333333)
|
||||
},
|
||||
modifier = Modifier.padding(vertical = 1.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
Row {
|
||||
TextButton(onClick = {
|
||||
// Retry: force re-request SDU
|
||||
UpdateManager.requestSduServer()
|
||||
}) {
|
||||
Text("Retry SDU")
|
||||
}
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
TextButton(onClick = { showSduLogs = false }) {
|
||||
Text("Close")
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun startThemeReveal() {
|
||||
if (themeRevealActive) {
|
||||
return
|
||||
@@ -801,7 +874,7 @@ fun ChatsListScreen(
|
||||
VerifiedBadge(
|
||||
verified = 1,
|
||||
size = 15,
|
||||
badgeTint = Color.White
|
||||
badgeTint = Color(0xFFACD2F9)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1078,9 +1151,15 @@ fun ChatsListScreen(
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// FOOTER - Version
|
||||
// FOOTER - Version + Update Banner
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
Column(modifier = Modifier.fillMaxWidth().navigationBarsPadding()) {
|
||||
// Telegram-style update banner
|
||||
val curUpdate = sduUpdateState
|
||||
val showUpdateBanner = curUpdate is UpdateState.UpdateAvailable ||
|
||||
curUpdate is UpdateState.Downloading ||
|
||||
curUpdate is UpdateState.ReadyToInstall
|
||||
|
||||
Divider(
|
||||
color =
|
||||
if (isDarkTheme)
|
||||
@@ -1089,29 +1168,137 @@ fun ChatsListScreen(
|
||||
thickness = 0.5.dp
|
||||
)
|
||||
|
||||
// Version info
|
||||
Box(
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = 20.dp,
|
||||
vertical = 12.dp
|
||||
),
|
||||
contentAlignment =
|
||||
Alignment.CenterStart
|
||||
) {
|
||||
Text(
|
||||
text = "Rosetta v${BuildConfig.VERSION_NAME}",
|
||||
fontSize = 12.sp,
|
||||
color =
|
||||
if (isDarkTheme)
|
||||
Color(0xFF666666)
|
||||
else
|
||||
Color(0xFF999999)
|
||||
)
|
||||
// Version info — прячем когда есть баннер обновления
|
||||
if (!showUpdateBanner) {
|
||||
Row(
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = 20.dp,
|
||||
vertical = 12.dp
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = "Rosetta v${BuildConfig.VERSION_NAME}",
|
||||
fontSize = 12.sp,
|
||||
color =
|
||||
if (isDarkTheme)
|
||||
Color(0xFF666666)
|
||||
else
|
||||
Color(0xFF999999)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
Text(
|
||||
text = "(статус: ${sduUpdateState::class.simpleName})",
|
||||
fontSize = 10.sp,
|
||||
color =
|
||||
if (isDarkTheme)
|
||||
Color(0xFF555555)
|
||||
else
|
||||
Color(0xFFAAAAAA)
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
// Debug log button
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(28.dp)
|
||||
.clip(CircleShape)
|
||||
.background(
|
||||
if (isDarkTheme) Color(0xFF2A2A2A) else Color(0xFFEEEEEE)
|
||||
)
|
||||
.clickable { showSduLogs = true },
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
imageVector = TablerIcons.Bug,
|
||||
contentDescription = "SDU Logs",
|
||||
modifier = Modifier.size(16.dp),
|
||||
tint = if (isDarkTheme) Color(0xFF888888) else Color(0xFF999999)
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
if (showUpdateBanner) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(50.dp)
|
||||
.background(
|
||||
Brush.horizontalGradient(
|
||||
colors = listOf(
|
||||
Color(0xFF69BF72),
|
||||
Color(0xFF53B3AD)
|
||||
)
|
||||
)
|
||||
)
|
||||
.clickable {
|
||||
when (curUpdate) {
|
||||
is UpdateState.UpdateAvailable ->
|
||||
UpdateManager.downloadAndInstall(context)
|
||||
is UpdateState.Downloading ->
|
||||
UpdateManager.cancelDownload()
|
||||
is UpdateState.ReadyToInstall ->
|
||||
UpdateManager.downloadAndInstall(context)
|
||||
else -> {}
|
||||
}
|
||||
},
|
||||
contentAlignment = Alignment.CenterStart
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
imageVector = when (curUpdate) {
|
||||
is UpdateState.Downloading -> TablerIcons.X
|
||||
else -> TablerIcons.Download
|
||||
},
|
||||
contentDescription = null,
|
||||
tint = Color.White,
|
||||
modifier = Modifier.size(22.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
Text(
|
||||
text = when (curUpdate) {
|
||||
is UpdateState.Downloading ->
|
||||
"Downloading... ${curUpdate.progress}%"
|
||||
is UpdateState.ReadyToInstall ->
|
||||
"Install Update"
|
||||
is UpdateState.UpdateAvailable ->
|
||||
"Update Rosetta"
|
||||
else -> ""
|
||||
},
|
||||
color = Color.White,
|
||||
fontSize = 15.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
if (curUpdate is UpdateState.UpdateAvailable) {
|
||||
Text(
|
||||
text = curUpdate.version,
|
||||
color = Color.White.copy(alpha = 0.8f),
|
||||
fontSize = 13.sp,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
}
|
||||
if (curUpdate is UpdateState.Downloading) {
|
||||
CircularProgressIndicator(
|
||||
progress = curUpdate.progress / 100f,
|
||||
modifier = Modifier.size(20.dp),
|
||||
color = Color.White,
|
||||
trackColor = Color.White.copy(alpha = 0.3f),
|
||||
strokeWidth = 2.dp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1757,7 +1944,10 @@ fun ChatsListScreen(
|
||||
state = chatListState,
|
||||
modifier =
|
||||
Modifier.fillMaxSize()
|
||||
.nestedScroll(requestsNestedScroll)
|
||||
.then(
|
||||
if (requestsCount > 0) Modifier.nestedScroll(requestsNestedScroll)
|
||||
else Modifier
|
||||
)
|
||||
.background(
|
||||
listBackgroundColor
|
||||
)
|
||||
@@ -3384,7 +3574,7 @@ fun DialogItemContent(
|
||||
modifier =
|
||||
Modifier.fillMaxSize()
|
||||
.clip(CircleShape)
|
||||
.background(PrimaryBlue),
|
||||
.background(Color(0xFF228BE6)),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
|
||||
@@ -347,7 +347,7 @@ private fun ForwardDialogItem(
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.clip(CircleShape)
|
||||
.background(PrimaryBlue),
|
||||
.background(Color(0xFF228BE6)),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
|
||||
@@ -18,11 +18,13 @@ import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.airbnb.lottie.compose.*
|
||||
import com.airbnb.lottie.LottieComposition
|
||||
import com.rosetta.messenger.network.SearchUser
|
||||
import com.rosetta.messenger.R
|
||||
import com.rosetta.messenger.ui.components.VerifiedBadge
|
||||
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
||||
|
||||
@@ -182,12 +184,12 @@ private fun SearchResultItem(
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.clip(CircleShape)
|
||||
.background(if (isOwnAccount) PrimaryBlue else avatarColors.backgroundColor),
|
||||
.background(if (isOwnAccount) Color(0xFF228BE6) else avatarColors.backgroundColor),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
if (isOwnAccount) {
|
||||
Icon(
|
||||
Icons.Default.Bookmark,
|
||||
painter = painterResource(R.drawable.bookmark_outlined),
|
||||
contentDescription = "Saved Messages",
|
||||
tint = Color.White,
|
||||
modifier = Modifier.size(20.dp)
|
||||
|
||||
@@ -30,6 +30,7 @@ import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import com.rosetta.messenger.repository.AvatarRepository
|
||||
import com.rosetta.messenger.ui.components.AvatarImage
|
||||
import com.rosetta.messenger.ui.components.VerifiedBadge
|
||||
import com.rosetta.messenger.database.RosettaDatabase
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
@@ -438,11 +439,9 @@ private fun RecentUserItem(
|
||||
)
|
||||
if (user.verified != 0) {
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
Icon(
|
||||
Icons.Default.Verified,
|
||||
contentDescription = "Verified",
|
||||
tint = PrimaryBlue,
|
||||
modifier = Modifier.size(16.dp)
|
||||
VerifiedBadge(
|
||||
verified = user.verified,
|
||||
size = 16
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,16 +4,17 @@ import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Verified
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import com.rosetta.messenger.R
|
||||
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
||||
|
||||
/**
|
||||
* Значок верификации пользователя
|
||||
@@ -35,14 +36,9 @@ fun VerifiedBadge(
|
||||
|
||||
var showDialog by remember { mutableStateOf(false) }
|
||||
|
||||
// Цвет в зависимости от уровня верификации
|
||||
// Цвет верификации: в тёмной теме — как индикаторы прочтения (PrimaryBlue), в светлой — #ACD2F9
|
||||
val badgeColor =
|
||||
badgeTint
|
||||
?: when (verified) {
|
||||
1 -> Color(0xFF1DA1F2) // Стандартная верификация (синий как в Twitter/Telegram)
|
||||
2 -> Color(0xFFFFD700) // Золотая верификация
|
||||
else -> Color(0xFF4CAF50) // Зеленая для других уровней
|
||||
}
|
||||
badgeTint ?: if (isDarkTheme) PrimaryBlue else Color(0xFFACD2F9)
|
||||
|
||||
// Текст аннотации
|
||||
val annotationText = when (verified) {
|
||||
@@ -52,7 +48,7 @@ fun VerifiedBadge(
|
||||
}
|
||||
|
||||
Icon(
|
||||
Icons.Default.Verified,
|
||||
painter = painterResource(id = R.drawable.ic_rosette_discount_check),
|
||||
contentDescription = "Verified",
|
||||
tint = badgeColor,
|
||||
modifier = modifier
|
||||
@@ -73,7 +69,7 @@ fun VerifiedBadge(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Verified,
|
||||
painter = painterResource(id = R.drawable.ic_rosette_discount_check),
|
||||
contentDescription = null,
|
||||
tint = badgeColor,
|
||||
modifier = Modifier.size(32.dp)
|
||||
|
||||
@@ -1935,8 +1935,7 @@ private fun CollapsingOtherProfileHeader(
|
||||
VerifiedBadge(
|
||||
verified = if (verified > 0) verified else 1,
|
||||
size = (nameFontSize.value * 0.8f).toInt(),
|
||||
isDarkTheme = isDarkTheme,
|
||||
badgeTint = Color.White
|
||||
isDarkTheme = isDarkTheme
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1402,8 +1402,7 @@ private fun CollapsingProfileHeader(
|
||||
VerifiedBadge(
|
||||
verified = 2,
|
||||
size = (nameFontSize.value * 0.8f).toInt(),
|
||||
isDarkTheme = isDarkTheme,
|
||||
badgeTint = Color.White
|
||||
isDarkTheme = isDarkTheme
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,20 +7,21 @@ import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.*
|
||||
import com.rosetta.messenger.ui.icons.TelegramIcons
|
||||
import compose.icons.TablerIcons
|
||||
import compose.icons.tablericons.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.rosetta.messenger.BuildConfig
|
||||
import com.rosetta.messenger.update.UpdateManager
|
||||
import com.rosetta.messenger.update.UpdateState
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun UpdatesScreen(
|
||||
@@ -28,6 +29,8 @@ fun UpdatesScreen(
|
||||
onBack: () -> Unit
|
||||
) {
|
||||
val view = LocalView.current
|
||||
val context = LocalContext.current
|
||||
|
||||
if (!view.isInEditMode) {
|
||||
SideEffect {
|
||||
val window = (view.context as android.app.Activity).window
|
||||
@@ -43,8 +46,11 @@ fun UpdatesScreen(
|
||||
val surfaceColor = if (isDarkTheme) Color(0xFF2C2C2E) else Color(0xFFF2F2F7)
|
||||
val textColor = if (isDarkTheme) Color.White else Color.Black
|
||||
val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666)
|
||||
|
||||
// Handle back gesture
|
||||
|
||||
val updateState by UpdateManager.updateState.collectAsState()
|
||||
val downloadProgress by UpdateManager.downloadProgress.collectAsState()
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
BackHandler { onBack() }
|
||||
|
||||
Column(
|
||||
@@ -87,29 +93,12 @@ fun UpdatesScreen(
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(16.dp)
|
||||
) {
|
||||
// Info Card
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
color = Color(0xFF2E7D32).copy(alpha = 0.2f),
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "✓ App is up to date",
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF2E7D32)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Text(
|
||||
text = "You're using the latest version",
|
||||
fontSize = 12.sp,
|
||||
color = secondaryTextColor
|
||||
)
|
||||
}
|
||||
}
|
||||
// Status Card
|
||||
UpdateStatusCard(
|
||||
state = updateState,
|
||||
progress = downloadProgress,
|
||||
secondaryTextColor = secondaryTextColor
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
@@ -168,23 +157,191 @@ fun UpdatesScreen(
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
// Check for updates button
|
||||
Button(
|
||||
onClick = { /* TODO: Implement update check */ },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(48.dp),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = Color(0xFF007AFF)
|
||||
),
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "Check for Updates",
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
// Action Button
|
||||
when (val state = updateState) {
|
||||
is UpdateState.Idle, is UpdateState.UpToDate, is UpdateState.Checking, is UpdateState.Error -> {
|
||||
Button(
|
||||
onClick = {
|
||||
coroutineScope.launch {
|
||||
UpdateManager.checkForUpdates()
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(48.dp),
|
||||
enabled = state !is UpdateState.Checking,
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = Color(0xFF007AFF)
|
||||
),
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
) {
|
||||
if (state is UpdateState.Checking) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(20.dp),
|
||||
color = Color.White,
|
||||
strokeWidth = 2.dp
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text("Checking...", fontSize = 16.sp, fontWeight = FontWeight.Medium)
|
||||
} else {
|
||||
Text("Check for Updates", fontSize = 16.sp, fontWeight = FontWeight.Medium)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is UpdateState.UpdateAvailable -> {
|
||||
Button(
|
||||
onClick = { UpdateManager.downloadAndInstall(context) },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(48.dp),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = Color(0xFF34C759)
|
||||
),
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = TablerIcons.Download,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
"Download ${state.version}",
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is UpdateState.Downloading -> {
|
||||
Column {
|
||||
LinearProgressIndicator(
|
||||
progress = state.progress / 100f,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(6.dp),
|
||||
color = Color(0xFF007AFF),
|
||||
trackColor = if (isDarkTheme) Color(0xFF3A3A3A) else Color(0xFFE0E0E0),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(
|
||||
"Downloading... ${state.progress}%",
|
||||
fontSize = 14.sp,
|
||||
color = textColor
|
||||
)
|
||||
TextButton(onClick = { UpdateManager.cancelDownload() }) {
|
||||
Text("Cancel", color = Color(0xFFFF3B30), fontSize = 14.sp)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is UpdateState.ReadyToInstall -> {
|
||||
Button(
|
||||
onClick = {
|
||||
UpdateManager.downloadAndInstall(context)
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(48.dp),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = Color(0xFF34C759)
|
||||
),
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
) {
|
||||
Text(
|
||||
"Install Update",
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UpdateStatusCard(
|
||||
state: UpdateState,
|
||||
progress: Int,
|
||||
secondaryTextColor: Color
|
||||
) {
|
||||
val (bgColor, iconColor, title, subtitle) = when (state) {
|
||||
is UpdateState.Idle -> StatusCardData(
|
||||
bg = Color(0xFF2E7D32).copy(alpha = 0.2f),
|
||||
icon = Color(0xFF2E7D32),
|
||||
title = "✓ App is up to date",
|
||||
subtitle = "You're using the latest version"
|
||||
)
|
||||
is UpdateState.UpToDate -> StatusCardData(
|
||||
bg = Color(0xFF2E7D32).copy(alpha = 0.2f),
|
||||
icon = Color(0xFF2E7D32),
|
||||
title = "✓ App is up to date",
|
||||
subtitle = "You're using the latest version"
|
||||
)
|
||||
is UpdateState.Checking -> StatusCardData(
|
||||
bg = Color(0xFF007AFF).copy(alpha = 0.2f),
|
||||
icon = Color(0xFF007AFF),
|
||||
title = "Checking for updates...",
|
||||
subtitle = "Connecting to update server"
|
||||
)
|
||||
is UpdateState.UpdateAvailable -> StatusCardData(
|
||||
bg = Color(0xFF007AFF).copy(alpha = 0.2f),
|
||||
icon = Color(0xFF007AFF),
|
||||
title = "Update available: ${state.version}",
|
||||
subtitle = "A new version is ready to download"
|
||||
)
|
||||
is UpdateState.Downloading -> StatusCardData(
|
||||
bg = Color(0xFF007AFF).copy(alpha = 0.2f),
|
||||
icon = Color(0xFF007AFF),
|
||||
title = "Downloading... $progress%",
|
||||
subtitle = "Please wait while the update is downloading"
|
||||
)
|
||||
is UpdateState.ReadyToInstall -> StatusCardData(
|
||||
bg = Color(0xFF34C759).copy(alpha = 0.2f),
|
||||
icon = Color(0xFF34C759),
|
||||
title = "Ready to install",
|
||||
subtitle = "Tap the button below to install the update"
|
||||
)
|
||||
is UpdateState.Error -> StatusCardData(
|
||||
bg = Color(0xFFFF3B30).copy(alpha = 0.2f),
|
||||
icon = Color(0xFFFF3B30),
|
||||
title = "Update check failed",
|
||||
subtitle = state.message
|
||||
)
|
||||
}
|
||||
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
color = bgColor,
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
) {
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Text(
|
||||
text = title,
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = iconColor
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Text(
|
||||
text = subtitle,
|
||||
fontSize = 12.sp,
|
||||
color = secondaryTextColor
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private data class StatusCardData(
|
||||
val bg: Color,
|
||||
val icon: Color,
|
||||
val title: String,
|
||||
val subtitle: String
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user