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:
2026-02-13 17:37:03 +05:00
parent e17b03c1c5
commit 93ce53d3d5
30 changed files with 1269 additions and 147 deletions

View File

@@ -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)
)