Add new drawable resources for icons and themes
- Created `archive_filled.xml` for filled archive icon. - Added `bookmark_outlined.xml` for outlined bookmark icon. - Introduced `day_theme_filled.xml` for day theme icon. - Added `folder_outlined.xml` for outlined folder icon. - Created `gear_outlined.xml` for outlined gear icon. - Introduced `night_mode.xml` for night mode icon.
This commit is contained in:
@@ -21,6 +21,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.clipToBounds
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.graphics.lerp
|
||||
import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
@@ -36,6 +37,8 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.airbnb.lottie.compose.*
|
||||
import com.rosetta.messenger.R
|
||||
import com.rosetta.messenger.data.AccountManager
|
||||
import com.rosetta.messenger.data.EncryptedAccount
|
||||
import com.rosetta.messenger.data.RecentSearchesManager
|
||||
import com.rosetta.messenger.network.ProtocolManager
|
||||
import com.rosetta.messenger.network.ProtocolState
|
||||
@@ -50,6 +53,8 @@ import com.rosetta.messenger.ui.onboarding.PrimaryBlueDark
|
||||
import com.rosetta.messenger.ui.settings.BackgroundBlurPresets
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import compose.icons.TablerIcons
|
||||
import compose.icons.tablericons.*
|
||||
import java.text.SimpleDateFormat
|
||||
@@ -176,7 +181,9 @@ fun ChatsListScreen(
|
||||
onTogglePin: (String) -> Unit = {},
|
||||
chatsViewModel: ChatsListViewModel = androidx.lifecycle.viewmodel.compose.viewModel(),
|
||||
avatarRepository: com.rosetta.messenger.repository.AvatarRepository? = null,
|
||||
onLogout: () -> Unit
|
||||
onLogout: () -> Unit,
|
||||
onAddAccount: () -> Unit = {},
|
||||
onSwitchAccount: (String) -> Unit = {}
|
||||
) {
|
||||
// Theme transition state
|
||||
var hasInitialized by remember { mutableStateOf(false) }
|
||||
@@ -213,14 +220,14 @@ fun ChatsListScreen(
|
||||
focusManager.clearFocus()
|
||||
}
|
||||
|
||||
// Update status bar appearance
|
||||
LaunchedEffect(isDarkTheme) {
|
||||
if (!view.isInEditMode) {
|
||||
// Update status bar appearance — SideEffect overrides global Theme.kt SideEffect
|
||||
if (!view.isInEditMode) {
|
||||
SideEffect {
|
||||
val window = (view.context as android.app.Activity).window
|
||||
val insetsController =
|
||||
androidx.core.view.WindowCompat.getInsetsController(window, view)
|
||||
|
||||
// Status bar
|
||||
// Status bar — always white icons (header is blue)
|
||||
insetsController.isAppearanceLightStatusBars = false
|
||||
window.statusBarColor = android.graphics.Color.TRANSPARENT
|
||||
|
||||
@@ -270,6 +277,17 @@ fun ChatsListScreen(
|
||||
// 📬 Requests screen state
|
||||
var showRequestsScreen by remember { mutableStateOf(false) }
|
||||
|
||||
// 📂 Accounts section expanded state (arrow toggle)
|
||||
var accountsSectionExpanded by remember { mutableStateOf(false) }
|
||||
|
||||
// 👥 Load all accounts for sidebar (current account always first)
|
||||
var allAccounts by remember { mutableStateOf<List<EncryptedAccount>>(emptyList()) }
|
||||
LaunchedEffect(accountPublicKey) {
|
||||
val accountManager = AccountManager(context)
|
||||
val accounts = accountManager.getAllAccounts()
|
||||
allAccounts = accounts.sortedByDescending { it.publicKey == accountPublicKey }
|
||||
}
|
||||
|
||||
// 🔥 Используем rememberSaveable чтобы сохранить состояние при навигации
|
||||
// Header сразу visible = true, без анимации при возврате из чата
|
||||
var visible by rememberSaveable { mutableStateOf(true) }
|
||||
@@ -538,18 +556,18 @@ fun ChatsListScreen(
|
||||
// Theme toggle icon
|
||||
IconButton(
|
||||
onClick = { onToggleTheme() },
|
||||
modifier = Modifier.size(40.dp)
|
||||
modifier = Modifier.size(48.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector =
|
||||
if (isDarkTheme)
|
||||
TablerIcons.Sun
|
||||
else TablerIcons.Moon,
|
||||
painter = painterResource(
|
||||
id = if (isDarkTheme) R.drawable.day_theme_filled
|
||||
else R.drawable.night_mode
|
||||
),
|
||||
contentDescription =
|
||||
if (isDarkTheme) "Light Mode"
|
||||
else "Dark Mode",
|
||||
tint = Color.White,
|
||||
modifier = Modifier.size(22.dp)
|
||||
modifier = Modifier.size(28.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -559,35 +577,191 @@ fun ChatsListScreen(
|
||||
Modifier.height(8.dp)
|
||||
)
|
||||
|
||||
// Display name
|
||||
if (accountName.isNotEmpty()) {
|
||||
Text(
|
||||
text = accountName,
|
||||
fontSize = 16.sp,
|
||||
fontWeight =
|
||||
FontWeight.SemiBold,
|
||||
color = Color.White
|
||||
)
|
||||
}
|
||||
// Display name + arrow row
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
// Display name
|
||||
if (accountName.isNotEmpty()) {
|
||||
Text(
|
||||
text = accountName,
|
||||
fontSize = 15.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = Color.White
|
||||
)
|
||||
}
|
||||
|
||||
// Username
|
||||
if (accountUsername.isNotEmpty()) {
|
||||
Spacer(
|
||||
modifier =
|
||||
Modifier.height(4.dp)
|
||||
)
|
||||
Text(
|
||||
text =
|
||||
"@$accountUsername",
|
||||
fontSize = 13.sp,
|
||||
color = Color.White
|
||||
// Username
|
||||
if (accountUsername.isNotEmpty()) {
|
||||
Spacer(modifier = Modifier.height(2.dp))
|
||||
Text(
|
||||
text = "@$accountUsername",
|
||||
fontSize = 13.sp,
|
||||
color = Color.White.copy(alpha = 0.7f)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Chevron arrow (toggles accounts section)
|
||||
val arrowRotation by animateFloatAsState(
|
||||
targetValue = if (accountsSectionExpanded) 180f else 0f,
|
||||
animationSpec = tween(300),
|
||||
label = "arrowRotation"
|
||||
)
|
||||
IconButton(
|
||||
onClick = { accountsSectionExpanded = !accountsSectionExpanded },
|
||||
modifier = Modifier.size(40.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = TablerIcons.ChevronDown,
|
||||
contentDescription = if (accountsSectionExpanded) "Collapse" else "Expand",
|
||||
tint = Color.White,
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.graphicsLayer { rotationZ = arrowRotation }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// 📱 MENU ITEMS
|
||||
// <EFBFBD> ACCOUNTS SECTION (like Telegram)
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
AnimatedVisibility(
|
||||
visible = accountsSectionExpanded,
|
||||
enter = expandVertically(animationSpec = tween(250)) + fadeIn(animationSpec = tween(250)),
|
||||
exit = shrinkVertically(animationSpec = tween(250)) + fadeOut(animationSpec = tween(200))
|
||||
) {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
// All accounts list
|
||||
allAccounts.forEach { account ->
|
||||
val isCurrentAccount = account.publicKey == accountPublicKey
|
||||
val displayName = account.name.ifEmpty {
|
||||
account.username ?: account.publicKey.take(8)
|
||||
}
|
||||
Row(
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.height(48.dp)
|
||||
.clickable {
|
||||
if (!isCurrentAccount) {
|
||||
scope.launch {
|
||||
accountsSectionExpanded = false
|
||||
drawerState.close()
|
||||
kotlinx.coroutines.delay(150)
|
||||
onSwitchAccount(account.publicKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(start = 14.dp, end = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
// Account avatar
|
||||
Box(
|
||||
modifier = Modifier.size(36.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
AvatarImage(
|
||||
publicKey = account.publicKey,
|
||||
avatarRepository = avatarRepository,
|
||||
size = 36.dp,
|
||||
isDarkTheme = isDarkTheme,
|
||||
displayName = displayName
|
||||
)
|
||||
// Green checkmark for current account
|
||||
if (isCurrentAccount) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.size(14.dp)
|
||||
.background(
|
||||
color = drawerBackgroundColor,
|
||||
shape = CircleShape
|
||||
)
|
||||
.padding(1.5.dp)
|
||||
.background(
|
||||
color = Color(0xFF50A7EA),
|
||||
shape = CircleShape
|
||||
),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
imageVector = TablerIcons.Check,
|
||||
contentDescription = null,
|
||||
tint = Color.White,
|
||||
modifier = Modifier.size(8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.width(22.dp))
|
||||
|
||||
// Account name
|
||||
Text(
|
||||
text = displayName,
|
||||
fontSize = 15.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = if (isDarkTheme) Color(0xFFF4FFFFFF.toInt()) else Color(0xFF444444),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Add Account row
|
||||
Row(
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.height(48.dp)
|
||||
.clickable {
|
||||
scope.launch {
|
||||
drawerState.close()
|
||||
kotlinx.coroutines.delay(150)
|
||||
onAddAccount()
|
||||
}
|
||||
}
|
||||
.padding(start = 14.dp, end = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
// Plus icon in circle area
|
||||
Box(
|
||||
modifier = Modifier.size(36.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
imageVector = TablerIcons.Plus,
|
||||
contentDescription = "Add Account",
|
||||
tint = if (isDarkTheme) Color(0xFF828282) else Color(0xFF889198),
|
||||
modifier = Modifier.size(22.dp)
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.width(22.dp))
|
||||
|
||||
Text(
|
||||
text = "Add Account",
|
||||
fontSize = 15.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = if (isDarkTheme) Color(0xFFF4FFFFFF.toInt()) else Color(0xFF444444),
|
||||
maxLines = 1
|
||||
)
|
||||
}
|
||||
|
||||
// Divider after accounts section
|
||||
Divider(
|
||||
color = if (isDarkTheme) Color(0xFF2A2A2A) else Color(0xFFE8E8E8),
|
||||
thickness = 0.5.dp
|
||||
)
|
||||
}
|
||||
} // Close AnimatedVisibility
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// <20>📱 MENU ITEMS
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
Column(
|
||||
modifier =
|
||||
@@ -599,17 +773,21 @@ fun ChatsListScreen(
|
||||
.padding(vertical = 8.dp)
|
||||
) {
|
||||
val menuIconColor =
|
||||
if (isDarkTheme) Color(0xFF7A7F85)
|
||||
else textColor.copy(alpha = 0.6f)
|
||||
if (isDarkTheme) Color(0xFF828282)
|
||||
else Color(0xFF889198)
|
||||
|
||||
val menuTextColor =
|
||||
if (isDarkTheme) Color(0xFFF4FFFFFF)
|
||||
else Color(0xFF444444)
|
||||
|
||||
val accentColor = if (isDarkTheme) PrimaryBlueDark else PrimaryBlue
|
||||
|
||||
// 👤 Profile
|
||||
DrawerMenuItemEnhanced(
|
||||
icon = TablerIcons.User,
|
||||
painter = painterResource(id = R.drawable.left_status_profile),
|
||||
text = "My Profile",
|
||||
iconColor = menuIconColor,
|
||||
textColor = textColor,
|
||||
textColor = menuTextColor,
|
||||
onClick = {
|
||||
scope.launch {
|
||||
drawerState.close()
|
||||
@@ -620,12 +798,12 @@ fun ChatsListScreen(
|
||||
}
|
||||
)
|
||||
|
||||
// <EFBFBD> Requests
|
||||
// 📦 Requests
|
||||
DrawerMenuItemEnhanced(
|
||||
icon = TablerIcons.MessageCircle2,
|
||||
painter = painterResource(id = R.drawable.msg_archive),
|
||||
text = "Requests",
|
||||
iconColor = menuIconColor,
|
||||
textColor = textColor,
|
||||
textColor = menuTextColor,
|
||||
badge = if (topLevelRequestsCount > 0) topLevelRequestsCount.toString() else null,
|
||||
badgeColor = accentColor,
|
||||
onClick = {
|
||||
@@ -638,12 +816,12 @@ fun ChatsListScreen(
|
||||
}
|
||||
)
|
||||
|
||||
// <EFBFBD>📖 Saved Messages
|
||||
// 📖 Saved Messages
|
||||
DrawerMenuItemEnhanced(
|
||||
icon = TablerIcons.Bookmark,
|
||||
painter = painterResource(id = R.drawable.msg_saved),
|
||||
text = "Saved Messages",
|
||||
iconColor = menuIconColor,
|
||||
textColor = textColor,
|
||||
textColor = menuTextColor,
|
||||
onClick = {
|
||||
scope.launch {
|
||||
drawerState.close()
|
||||
@@ -658,10 +836,10 @@ fun ChatsListScreen(
|
||||
|
||||
// ⚙️ Settings
|
||||
DrawerMenuItemEnhanced(
|
||||
icon = TablerIcons.Settings,
|
||||
painter = painterResource(id = R.drawable.msg_settings_old),
|
||||
text = "Settings",
|
||||
iconColor = menuIconColor,
|
||||
textColor = textColor,
|
||||
textColor = menuTextColor,
|
||||
onClick = {
|
||||
scope.launch {
|
||||
drawerState.close()
|
||||
@@ -2479,7 +2657,7 @@ fun DialogItemContent(
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
TablerIcons.Bookmark,
|
||||
painter = painterResource(R.drawable.bookmark_outlined),
|
||||
contentDescription = null,
|
||||
tint = Color.White,
|
||||
modifier = Modifier.size(24.dp)
|
||||
@@ -3182,7 +3360,7 @@ fun RequestsScreen(
|
||||
}
|
||||
}
|
||||
|
||||
/** 🎨 Enhanced Drawer Menu Item - красивый пункт меню с hover эффектом */
|
||||
/** 🎨 Enhanced Drawer Menu Item - пункт меню в стиле Telegram */
|
||||
@Composable
|
||||
fun DrawerMenuItemEnhanced(
|
||||
icon: androidx.compose.ui.graphics.vector.ImageVector,
|
||||
@@ -3196,8 +3374,9 @@ fun DrawerMenuItemEnhanced(
|
||||
Row(
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.height(48.dp)
|
||||
.clickable(onClick = onClick)
|
||||
.padding(horizontal = 20.dp, vertical = 14.dp),
|
||||
.padding(start = 19.dp, end = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
@@ -3207,12 +3386,72 @@ fun DrawerMenuItemEnhanced(
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(20.dp))
|
||||
Spacer(modifier = Modifier.width(29.dp))
|
||||
|
||||
Text(
|
||||
text = text,
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 15.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = textColor,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
|
||||
badge?.let {
|
||||
Box(
|
||||
modifier =
|
||||
Modifier
|
||||
.defaultMinSize(minWidth = 22.dp, minHeight = 22.dp)
|
||||
.background(
|
||||
color = badgeColor,
|
||||
shape = CircleShape
|
||||
),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = it,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = Color.White,
|
||||
lineHeight = 12.sp,
|
||||
modifier = Modifier.padding(horizontal = 5.dp, vertical = 3.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 🎨 Enhanced Drawer Menu Item (Painter variant) - для XML drawable иконок */
|
||||
@Composable
|
||||
fun DrawerMenuItemEnhanced(
|
||||
painter: Painter,
|
||||
text: String,
|
||||
iconColor: Color,
|
||||
textColor: Color,
|
||||
badge: String? = null,
|
||||
badgeColor: Color = Color(0xFFE53935),
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
Row(
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.height(48.dp)
|
||||
.clickable(onClick = onClick)
|
||||
.padding(start = 19.dp, end = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
painter = painter,
|
||||
contentDescription = null,
|
||||
tint = iconColor,
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(29.dp))
|
||||
|
||||
Text(
|
||||
text = text,
|
||||
fontSize = 15.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = textColor,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user