diff --git a/app/src/main/java/com/rosetta/messenger/MainActivity.kt b/app/src/main/java/com/rosetta/messenger/MainActivity.kt index 13247f9..68fec53 100644 --- a/app/src/main/java/com/rosetta/messenger/MainActivity.kt +++ b/app/src/main/java/com/rosetta/messenger/MainActivity.kt @@ -39,7 +39,12 @@ import com.rosetta.messenger.ui.chats.ChatsListScreen import com.rosetta.messenger.ui.chats.SearchScreen import com.rosetta.messenger.ui.components.OptimizedEmojiCache import com.rosetta.messenger.ui.onboarding.OnboardingScreen +import com.rosetta.messenger.ui.settings.BackupScreen +import com.rosetta.messenger.ui.settings.OtherProfileScreen import com.rosetta.messenger.ui.settings.ProfileScreen +import com.rosetta.messenger.ui.settings.SafetyScreen +import com.rosetta.messenger.ui.settings.ThemeScreen +import com.rosetta.messenger.ui.settings.UpdatesScreen import com.rosetta.messenger.ui.splash.SplashScreen import com.rosetta.messenger.ui.theme.RosettaAndroidTheme import java.text.SimpleDateFormat @@ -439,79 +444,152 @@ fun MainScreen( var selectedUser by remember { mutableStateOf(null) } var showSearchScreen by remember { mutableStateOf(false) } var showProfileScreen by remember { mutableStateOf(false) } + var showOtherProfileScreen by remember { mutableStateOf(false) } + var selectedOtherUser by remember { mutableStateOf(null) } + + // Дополнительные экраны настроек + var showUpdatesScreen by remember { mutableStateOf(false) } + var showThemeScreen by remember { mutableStateOf(false) } + var showSafetyScreen by remember { mutableStateOf(false) } + var showBackupScreen by remember { mutableStateOf(false) } - // 🔥 TELEGRAM-STYLE анимация - чистый slide БЕЗ прозрачности - AnimatedContent( - targetState = Pair(Pair(selectedUser, showSearchScreen), showProfileScreen), - transitionSpec = { - val isEnteringChat = targetState.first.first != null && initialState.first.first == null - val isExitingChat = targetState.first.first == null && initialState.first.first != null - val isEnteringSearch = targetState.first.second && !initialState.first.second - val isExitingSearch = !targetState.first.second && initialState.first.second - val isEnteringProfile = targetState.second && !initialState.second - val isExitingProfile = !targetState.second && initialState.second + // 🔥 Простая навигация с fade-in анимацией + Box(modifier = Modifier.fillMaxSize()) { + // Base layer - chats list + androidx.compose.animation.AnimatedVisibility( + visible = !showBackupScreen && !showSafetyScreen && !showThemeScreen && + !showUpdatesScreen && selectedUser == null && !showSearchScreen && + !showProfileScreen && !showOtherProfileScreen, + enter = fadeIn(animationSpec = tween(300)), + exit = fadeOut(animationSpec = tween(200)) + ) { + ChatsListScreen( + isDarkTheme = isDarkTheme, + accountName = accountName, + accountPhone = accountPhone, + accountPublicKey = accountPublicKey, + accountPrivateKey = accountPrivateKey, + privateKeyHash = privateKeyHash, + onToggleTheme = onToggleTheme, + onProfileClick = { + showProfileScreen = true + }, + onNewGroupClick = { + // TODO: Navigate to new group + }, + onCallsClick = { + // TODO: Navigate to calls + }, + onSavedMessagesClick = { + // Открываем чат с самим собой (Saved Messages) + selectedUser = + SearchUser( + title = "Saved Messages", + username = "", + publicKey = accountPublicKey, + verified = 0, + online = 1 + ) + }, + onSettingsClick = { showProfileScreen = true }, + onInviteFriendsClick = { + // TODO: Share invite link + }, + onSearchClick = { showSearchScreen = true }, + onNewChat = { + // TODO: Show new chat screen + }, + onUserSelect = { selectedChatUser -> selectedUser = selectedChatUser }, + onLogout = onLogout + ) + } - when { - // 🚀 Вход в чат - плавный fade - isEnteringChat -> { - fadeIn(animationSpec = tween(200)) togetherWith - fadeOut(animationSpec = tween(150)) + // Other screens with fade animation + androidx.compose.animation.AnimatedVisibility( + visible = showBackupScreen, + enter = fadeIn(animationSpec = tween(300)), + exit = fadeOut(animationSpec = tween(200)) + ) { + if (showBackupScreen) { + BackupScreen( + isDarkTheme = isDarkTheme, + onBack = { showBackupScreen = false }, + onVerifyPassword = { password -> + // TODO: Implement password verification + if (password == "test") { + "word1 word2 word3 word4 word5 word6 word7 word8 word9 word10 word11 word12" + } else null } + ) + } + } - // 🔙 Выход из чата - плавный fade - isExitingChat -> { - fadeIn(animationSpec = tween(200)) togetherWith - fadeOut(animationSpec = tween(150)) + androidx.compose.animation.AnimatedVisibility( + visible = showSafetyScreen, + enter = fadeIn(animationSpec = tween(300)), + exit = fadeOut(animationSpec = tween(200)) + ) { + if (showSafetyScreen) { + SafetyScreen( + isDarkTheme = isDarkTheme, + accountPublicKey = accountPublicKey, + onBack = { showSafetyScreen = false }, + onBackupClick = { showBackupScreen = true }, + onDeleteAccount = { + // TODO: Implement account deletion + Log.d("MainActivity", "Delete account requested") } + ) + } + } - // 🔍 Вход в Search - плавный fade - isEnteringSearch -> { - fadeIn(animationSpec = tween(200)) togetherWith - fadeOut(animationSpec = tween(150)) + androidx.compose.animation.AnimatedVisibility( + visible = showThemeScreen, + enter = fadeIn(animationSpec = tween(300)), + exit = fadeOut(animationSpec = tween(200)) + ) { + if (showThemeScreen) { + ThemeScreen( + isDarkTheme = isDarkTheme, + onBack = { showThemeScreen = false }, + onThemeChange = { isDark -> + onToggleTheme() } + ) + } + } - // 🔙 Выход из Search - плавный fade - isEnteringSearch -> { - fadeIn(animationSpec = tween(200)) togetherWith - fadeOut(animationSpec = tween(150)) - } + androidx.compose.animation.AnimatedVisibility( + visible = showUpdatesScreen, + enter = fadeIn(animationSpec = tween(300)), + exit = fadeOut(animationSpec = tween(200)) + ) { + if (showUpdatesScreen) { + UpdatesScreen( + isDarkTheme = isDarkTheme, + onBack = { showUpdatesScreen = false } + ) + } + } - // 🔙 Выход из Search - плавный fade - isExitingSearch -> { - fadeIn(animationSpec = tween(200)) togetherWith - fadeOut(animationSpec = tween(150)) - } - - // 👤 Вход в Profile - плавный fade - isEnteringProfile -> { - fadeIn(animationSpec = tween(200)) togetherWith - fadeOut(animationSpec = tween(150)) - } - - // 🔙 Выход из Profile - плавный fade - isExitingProfile -> { - fadeIn(animationSpec = tween(200)) togetherWith - fadeOut(animationSpec = tween(150)) - } - - // Default - мгновенный переход - else -> { - EnterTransition.None togetherWith ExitTransition.None - } - } - }, - label = "screenNavigation" - ) { (chatAndSearchState, isProfileOpen) -> - val (currentUser, isSearchOpen) = chatAndSearchState - when { - currentUser != null -> { + androidx.compose.animation.AnimatedVisibility( + visible = selectedUser != null, + enter = fadeIn(animationSpec = tween(300)), + exit = fadeOut(animationSpec = tween(200)) + ) { + if (selectedUser != null) { // Экран чата ChatDetailScreen( - user = currentUser, + user = selectedUser!!, currentUserPublicKey = accountPublicKey, currentUserPrivateKey = accountPrivateKey, isDarkTheme = isDarkTheme, onBack = { selectedUser = null }, + onUserProfileClick = { user -> + // Открываем профиль другого пользователя + selectedOtherUser = user + showOtherProfileScreen = true + }, onNavigateToChat = { publicKey -> // 📨 Forward: переход в выбранный чат // Нужно получить SearchUser из публичного ключа @@ -528,7 +606,14 @@ fun MainScreen( } ) } - isSearchOpen -> { + } + + androidx.compose.animation.AnimatedVisibility( + visible = showSearchScreen, + enter = fadeIn(animationSpec = tween(300)), + exit = fadeOut(animationSpec = tween(200)) + ) { + if (showSearchScreen) { // Экран поиска SearchScreen( privateKeyHash = privateKeyHash, @@ -542,7 +627,14 @@ fun MainScreen( } ) } - isProfileOpen -> { + } + + androidx.compose.animation.AnimatedVisibility( + visible = showProfileScreen, + enter = fadeIn(animationSpec = tween(300)), + exit = fadeOut(animationSpec = tween(200)) + ) { + if (showProfileScreen) { // Экран профиля ProfileScreen( isDarkTheme = isDarkTheme, @@ -554,67 +646,30 @@ fun MainScreen( // TODO: Save profile changes Log.d("MainActivity", "Saving profile: name=$name, username=$username") }, - onLogout = { - // TODO: Implement logout - Log.d("MainActivity", "Logout requested") - }, + onLogout = onLogout, onNavigateToTheme = { - // Toggle theme for now - onToggleTheme() + showThemeScreen = true }, onNavigateToSafety = { - // TODO: Navigate to safety screen - Log.d("MainActivity", "Navigate to safety") - }, - onNavigateToUpdates = { - // TODO: Navigate to updates screen - Log.d("MainActivity", "Navigate to updates") + showSafetyScreen = true } ) } - else -> { - // Список чатов - ChatsListScreen( - isDarkTheme = isDarkTheme, - accountName = accountName, - accountPhone = accountPhone, - accountPublicKey = accountPublicKey, - accountPrivateKey = accountPrivateKey, - privateKeyHash = privateKeyHash, - onToggleTheme = onToggleTheme, - onProfileClick = { - showProfileScreen = true - }, - onNewGroupClick = { - // TODO: Navigate to new group - }, - onContactsClick = { - // TODO: Navigate to contacts - }, - onCallsClick = { - // TODO: Navigate to calls - }, - onSavedMessagesClick = { - // Открываем чат с самим собой (Saved Messages) - selectedUser = - SearchUser( - title = "Saved Messages", - username = "", - publicKey = accountPublicKey, - verified = 0, - online = 1 - ) - }, - onSettingsClick = { showProfileScreen = true }, - onInviteFriendsClick = { - // TODO: Share invite link - }, - onSearchClick = { showSearchScreen = true }, - onNewChat = { - // TODO: Show new chat screen - }, - onUserSelect = { selectedChatUser -> selectedUser = selectedChatUser }, - onLogout = onLogout + } + + androidx.compose.animation.AnimatedVisibility( + visible = showOtherProfileScreen, + enter = fadeIn(animationSpec = tween(300)), + exit = fadeOut(animationSpec = tween(200)) + ) { + if (showOtherProfileScreen && selectedOtherUser != null) { + OtherProfileScreen( + user = selectedOtherUser!!, + isDarkTheme = isDarkTheme, + onBack = { + showOtherProfileScreen = false + selectedOtherUser = null + } ) } } diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatDetailScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatDetailScreen.kt index 8a51372..4988bdb 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/ChatDetailScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/ChatDetailScreen.kt @@ -237,7 +237,7 @@ fun ChatDetailScreen( currentUserPrivateKey: String, isDarkTheme: Boolean, onBack: () -> Unit, - onUserProfileClick: () -> Unit = {}, + onUserProfileClick: (SearchUser) -> Unit = {}, onNavigateToChat: (String) -> Unit = {}, // 📨 Callback для навигации в другой чат (Forward) viewModel: ChatViewModel = viewModel() ) { @@ -964,7 +964,7 @@ fun ChatDetailScreen( ?.hide() focusManager .clearFocus() - onUserProfileClick() + onUserProfileClick(user) }, contentAlignment = Alignment.Center @@ -1032,7 +1032,7 @@ fun ChatDetailScreen( ?.hide() focusManager .clearFocus() - onUserProfileClick() + onUserProfileClick(user) } ) { Row( diff --git a/app/src/main/java/com/rosetta/messenger/ui/settings/BackupScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/settings/BackupScreen.kt new file mode 100644 index 0000000..e83f1cd --- /dev/null +++ b/app/src/main/java/com/rosetta/messenger/ui/settings/BackupScreen.kt @@ -0,0 +1,254 @@ +package com.rosetta.messenger.ui.settings + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.BasicTextField +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.Visibility +import androidx.compose.material.icons.filled.VisibilityOff +import androidx.compose.material.icons.filled.Warning +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.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +@Composable +fun BackupScreen( + isDarkTheme: Boolean, + onBack: () -> Unit, + onVerifyPassword: (String) -> String? // Returns seed phrase if password is correct +) { + val backgroundColor = if (isDarkTheme) Color(0xFF1A1A1A) else Color(0xFFFFFFFF) + 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) + + var password by remember { mutableStateOf("") } + var seedPhrase by remember { mutableStateOf(null) } + var passwordVisible by remember { mutableStateOf(false) } + + Column( + modifier = Modifier + .fillMaxSize() + .background(backgroundColor) + .statusBarsPadding() + ) { + // Top Bar + Surface( + modifier = Modifier.fillMaxWidth(), + color = backgroundColor + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 4.dp, vertical = 8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + IconButton(onClick = onBack) { + Icon( + imageVector = Icons.Filled.ArrowBack, + contentDescription = "Back", + tint = if (isDarkTheme) Color.White else Color.Black + ) + } + Text( + text = "Backup", + fontSize = 20.sp, + fontWeight = FontWeight.SemiBold, + color = textColor, + modifier = Modifier.padding(start = 8.dp) + ) + } + } + + // Content + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .padding(16.dp) + ) { + // Warning Card + Surface( + modifier = Modifier.fillMaxWidth(), + color = Color(0xFFEF4444).copy(alpha = 0.15f), + shape = RoundedCornerShape(12.dp) + ) { + Row( + modifier = Modifier.padding(16.dp), + verticalAlignment = Alignment.Top + ) { + Icon( + imageVector = Icons.Filled.Warning, + contentDescription = null, + tint = Color(0xFFEF4444), + modifier = Modifier + .size(24.dp) + .padding(top = 2.dp) + ) + Spacer(modifier = Modifier.width(12.dp)) + Text( + text = "If you want to give your recovery phrase to someone, please stop! This may lead to the compromise of your conversations.", + fontSize = 13.sp, + color = textColor, + lineHeight = 18.sp + ) + } + } + + Spacer(modifier = Modifier.height(24.dp)) + + if (seedPhrase == null) { + // Password Input + Text( + text = "Confirm Password", + fontSize = 14.sp, + fontWeight = FontWeight.SemiBold, + color = textColor, + modifier = Modifier.padding(bottom = 8.dp) + ) + + Surface( + modifier = Modifier.fillMaxWidth(), + color = surfaceColor, + shape = RoundedCornerShape(12.dp) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + BasicTextField( + value = password, + onValueChange = { newValue -> + password = newValue + // Try to verify password + val result = onVerifyPassword(newValue) + if (result != null) { + seedPhrase = result + } + }, + modifier = Modifier.weight(1f), + textStyle = TextStyle( + color = textColor, + fontSize = 16.sp + ), + visualTransformation = if (passwordVisible) + VisualTransformation.None + else + PasswordVisualTransformation(), + decorationBox = { innerTextField -> + if (password.isEmpty()) { + Text( + text = "Enter your password", + color = secondaryTextColor, + fontSize = 16.sp + ) + } + innerTextField() + } + ) + + IconButton( + onClick = { passwordVisible = !passwordVisible } + ) { + Icon( + imageVector = if (passwordVisible) + Icons.Filled.Visibility + else + Icons.Filled.VisibilityOff, + contentDescription = if (passwordVisible) "Hide password" else "Show password", + tint = secondaryTextColor + ) + } + } + } + + Text( + text = "To view your recovery phrase, enter the password you specified when creating your account.", + fontSize = 12.sp, + color = secondaryTextColor, + modifier = Modifier.padding(horizontal = 8.dp, vertical = 8.dp), + lineHeight = 16.sp + ) + } else { + // Seed Phrase Display + Text( + text = "Your Recovery Phrase", + fontSize = 14.sp, + fontWeight = FontWeight.SemiBold, + color = textColor, + modifier = Modifier.padding(bottom = 8.dp) + ) + + Surface( + modifier = Modifier.fillMaxWidth(), + color = Color(0xFF2E7D32).copy(alpha = 0.1f), + shape = RoundedCornerShape(12.dp) + ) { + Column(modifier = Modifier.padding(20.dp)) { + val words = seedPhrase!!.split(" ") + words.chunked(3).forEach { rowWords -> + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + rowWords.forEachIndexed { index, word -> + Text( + text = "${words.indexOf(word) + 1}. $word", + fontSize = 14.sp, + fontWeight = FontWeight.Medium, + color = textColor, + modifier = Modifier.padding(vertical = 4.dp) + ) + } + } + } + } + } + + Text( + text = "Please don't share your seed phrase! The administration will never ask you for it. Write it down and store it in a safe place.", + fontSize = 12.sp, + color = Color(0xFFEF4444), + fontWeight = FontWeight.Medium, + modifier = Modifier.padding(horizontal = 8.dp, vertical = 12.dp), + lineHeight = 16.sp + ) + + Spacer(modifier = Modifier.height(16.dp)) + + Button( + onClick = { + // TODO: Copy to clipboard + }, + modifier = Modifier + .fillMaxWidth() + .height(48.dp), + colors = ButtonDefaults.buttonColors( + containerColor = Color(0xFF007AFF) + ), + shape = RoundedCornerShape(12.dp) + ) { + Text( + text = "Copy to Clipboard", + fontSize = 16.sp, + fontWeight = FontWeight.Medium + ) + } + } + } + } +} diff --git a/app/src/main/java/com/rosetta/messenger/ui/settings/OtherProfileScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/settings/OtherProfileScreen.kt new file mode 100644 index 0000000..6b883c8 --- /dev/null +++ b/app/src/main/java/com/rosetta/messenger/ui/settings/OtherProfileScreen.kt @@ -0,0 +1,205 @@ +package com.rosetta.messenger.ui.settings + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.outlined.Block +import androidx.compose.material.icons.outlined.ContentCopy +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.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +@Composable +fun OtherProfileScreen( + isDarkTheme: Boolean, + userName: String, + userUsername: String, + userPublicKey: String, + isBlocked: Boolean, + onBack: () -> Unit, + onBlockUser: () -> Unit, + onUnblockUser: () -> Unit +) { + val backgroundColor = if (isDarkTheme) Color(0xFF1A1A1A) else Color(0xFFFFFFFF) + 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) + + Column( + modifier = Modifier + .fillMaxSize() + .background(backgroundColor) + .verticalScroll(rememberScrollState()) + ) { + // Profile Card with avatar + ProfileCard( + name = userName, + username = userUsername, + publicKey = userPublicKey, + isDarkTheme = isDarkTheme, + onBack = onBack, + hasChanges = false, + onSave = {} + ) + + Spacer(modifier = Modifier.height(16.dp)) + + Column(modifier = Modifier.padding(horizontal = 16.dp)) { + // Username Section (if available) + if (userUsername.isNotBlank()) { + Text( + text = "Username", + fontSize = 14.sp, + fontWeight = FontWeight.SemiBold, + color = textColor, + modifier = Modifier.padding(bottom = 8.dp) + ) + + Surface( + modifier = Modifier.fillMaxWidth(), + color = surfaceColor, + shape = RoundedCornerShape(12.dp) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = "@$userUsername", + fontSize = 16.sp, + color = textColor, + modifier = Modifier.weight(1f) + ) + IconButton( + onClick = { /* TODO: Copy to clipboard */ } + ) { + Icon( + imageVector = Icons.Outlined.ContentCopy, + contentDescription = "Copy", + tint = secondaryTextColor, + modifier = Modifier.size(20.dp) + ) + } + } + } + + Text( + text = "Username for search user or send message.", + fontSize = 12.sp, + color = secondaryTextColor, + modifier = Modifier.padding(horizontal = 8.dp, vertical = 8.dp), + lineHeight = 16.sp + ) + + Spacer(modifier = Modifier.height(16.dp)) + } + + // Public Key Section + Text( + text = "Public Key", + fontSize = 14.sp, + fontWeight = FontWeight.SemiBold, + color = textColor, + modifier = Modifier.padding(bottom = 8.dp) + ) + + Surface( + modifier = Modifier.fillMaxWidth(), + color = surfaceColor, + shape = RoundedCornerShape(12.dp) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = userPublicKey.take(20) + "..." + userPublicKey.takeLast(20), + fontSize = 12.sp, + color = textColor, + modifier = Modifier.weight(1f) + ) + IconButton( + onClick = { /* TODO: Copy to clipboard */ } + ) { + Icon( + imageVector = Icons.Outlined.ContentCopy, + contentDescription = "Copy", + tint = secondaryTextColor, + modifier = Modifier.size(20.dp) + ) + } + } + } + + Text( + text = "This is user public key. If user haven't set a @username yet, you can send message using public key.", + fontSize = 12.sp, + color = secondaryTextColor, + modifier = Modifier.padding(horizontal = 8.dp, vertical = 8.dp), + lineHeight = 16.sp + ) + + Spacer(modifier = Modifier.height(24.dp)) + + // Block/Unblock Section + Surface( + modifier = Modifier.fillMaxWidth(), + color = surfaceColor, + shape = RoundedCornerShape(16.dp) + ) { + Column { + if (isBlocked) { + ProfileNavigationItem( + icon = Icons.Outlined.Block, + iconBackground = Color(0xFF2E7D32), // green + title = "Unblock User", + subtitle = "Allow this user to message you", + onClick = onUnblockUser, + isDarkTheme = isDarkTheme, + hideChevron = true, + textColor = Color(0xFF2E7D32) + ) + } else { + ProfileNavigationItem( + icon = Icons.Outlined.Block, + iconBackground = Color(0xFFEF4444), // red + title = "Block this user", + subtitle = "Prevent this user from messaging you", + onClick = onBlockUser, + isDarkTheme = isDarkTheme, + hideChevron = true, + textColor = Color(0xFFEF4444) + ) + } + } + } + + Text( + text = if (isBlocked) { + "If you want the user to be able to send you messages again, you can unblock them. You can block them later." + } else { + "The person will no longer be able to message you if you block them. You can unblock them later." + }, + fontSize = 12.sp, + color = secondaryTextColor, + modifier = Modifier.padding(horizontal = 8.dp, vertical = 8.dp), + lineHeight = 16.sp + ) + + Spacer(modifier = Modifier.height(32.dp)) + } + } +} diff --git a/app/src/main/java/com/rosetta/messenger/ui/settings/ProfileScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/settings/ProfileScreen.kt index e7d30c9..f2cebdd 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/settings/ProfileScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/settings/ProfileScreen.kt @@ -101,8 +101,7 @@ fun ProfileScreen( onSaveProfile: (name: String, username: String) -> Unit, onLogout: () -> Unit, onNavigateToTheme: () -> Unit = {}, - onNavigateToSafety: () -> Unit = {}, - onNavigateToUpdates: () -> Unit = {} + onNavigateToSafety: () -> Unit = {} ) { // Цвета в зависимости от темы - такие же как в ChatsListScreen val backgroundColor = if (isDarkTheme) Color(0xFF1A1A1A) else Color(0xFFFFFFFF) @@ -210,15 +209,6 @@ fun ProfileScreen( shape = RoundedCornerShape(16.dp) ) { Column { - ProfileNavigationItem( - icon = Icons.Outlined.Refresh, - iconBackground = Color(0xFF84CC16), // lime-600 - title = "Updates", - subtitle = "Check for new versions", - onClick = onNavigateToUpdates, - isDarkTheme = isDarkTheme, - showDivider = true - ) ProfileNavigationItem( icon = Icons.Outlined.Brush, iconBackground = Color(0xFF6366F1), // indigo @@ -558,7 +548,7 @@ private fun ProfileCopyField( } @Composable -private fun ProfileNavigationItem( +fun ProfileNavigationItem( icon: ImageVector, iconBackground: Color, title: String, diff --git a/app/src/main/java/com/rosetta/messenger/ui/settings/SafetyScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/settings/SafetyScreen.kt new file mode 100644 index 0000000..26e3d1b --- /dev/null +++ b/app/src/main/java/com/rosetta/messenger/ui/settings/SafetyScreen.kt @@ -0,0 +1,228 @@ +package com.rosetta.messenger.ui.settings + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.Warning +import androidx.compose.material.icons.outlined.ContentCopy +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +@Composable +fun SafetyScreen( + isDarkTheme: Boolean, + accountPublicKey: String, + onBack: () -> Unit, + onBackupClick: () -> Unit, + onDeleteAccount: () -> Unit +) { + val backgroundColor = if (isDarkTheme) Color(0xFF1A1A1A) else Color(0xFFFFFFFF) + 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) + + Column( + modifier = Modifier + .fillMaxSize() + .background(backgroundColor) + .statusBarsPadding() + ) { + // Top Bar + Surface( + modifier = Modifier.fillMaxWidth(), + color = backgroundColor + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 4.dp, vertical = 8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + IconButton(onClick = onBack) { + Icon( + imageVector = Icons.Filled.ArrowBack, + contentDescription = "Back", + tint = if (isDarkTheme) Color.White else Color.Black + ) + } + Text( + text = "Safety & Security", + fontSize = 20.sp, + fontWeight = FontWeight.SemiBold, + color = textColor, + modifier = Modifier.padding(start = 8.dp) + ) + } + } + + // Content + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .padding(16.dp) + ) { + // Warning Card + Surface( + modifier = Modifier.fillMaxWidth(), + color = Color(0xFFFF9800).copy(alpha = 0.15f), + shape = RoundedCornerShape(12.dp) + ) { + Row( + modifier = Modifier.padding(16.dp), + verticalAlignment = Alignment.Top + ) { + Icon( + imageVector = Icons.Filled.Warning, + contentDescription = null, + tint = Color(0xFFFF9800), + modifier = Modifier + .size(24.dp) + .padding(top = 2.dp) + ) + Spacer(modifier = Modifier.width(12.dp)) + Text( + text = "Never share your private key or seed phrase with anyone. Keep your backup secure and private.", + fontSize = 13.sp, + color = textColor, + lineHeight = 18.sp + ) + } + } + + Spacer(modifier = Modifier.height(24.dp)) + + // Public Key Section + Text( + text = "Public Key", + fontSize = 14.sp, + fontWeight = FontWeight.SemiBold, + color = textColor, + modifier = Modifier.padding(bottom = 8.dp) + ) + + Surface( + modifier = Modifier.fillMaxWidth(), + color = surfaceColor, + shape = RoundedCornerShape(12.dp) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = accountPublicKey.take(20) + "..." + accountPublicKey.takeLast(20), + fontSize = 12.sp, + color = textColor, + modifier = Modifier.weight(1f) + ) + IconButton( + onClick = { /* TODO: Copy to clipboard */ } + ) { + Icon( + imageVector = Icons.Outlined.ContentCopy, + contentDescription = "Copy", + tint = secondaryTextColor, + modifier = Modifier.size(20.dp) + ) + } + } + } + + Text( + text = "This is your public key. You can safely share this with others so they can message you.", + fontSize = 12.sp, + color = secondaryTextColor, + modifier = Modifier.padding(horizontal = 8.dp, vertical = 8.dp), + lineHeight = 16.sp + ) + + Spacer(modifier = Modifier.height(24.dp)) + + // Backup Section + Text( + text = "Backup & Recovery", + fontSize = 14.sp, + fontWeight = FontWeight.SemiBold, + color = textColor, + modifier = Modifier.padding(bottom = 8.dp) + ) + + Surface( + modifier = Modifier.fillMaxWidth(), + color = surfaceColor, + shape = RoundedCornerShape(16.dp) + ) { + Column { + ProfileNavigationItem( + icon = Icons.Outlined.ContentCopy, + iconBackground = Color(0xFFFF9800), + title = "Backup Seed Phrase", + subtitle = "View and save your recovery phrase", + onClick = onBackupClick, + isDarkTheme = isDarkTheme, + textColor = Color(0xFFFF9800) + ) + } + } + + Text( + text = "Please save your seed phrase, it is necessary for future access to your conversations. Do not share this seed phrase with anyone.", + fontSize = 12.sp, + color = secondaryTextColor, + modifier = Modifier.padding(horizontal = 8.dp, vertical = 8.dp), + lineHeight = 16.sp + ) + + Spacer(modifier = Modifier.height(24.dp)) + + // Danger Zone + Text( + text = "Danger Zone", + fontSize = 14.sp, + fontWeight = FontWeight.SemiBold, + color = Color(0xFFEF4444), + modifier = Modifier.padding(bottom = 8.dp) + ) + + Surface( + modifier = Modifier.fillMaxWidth(), + color = surfaceColor, + shape = RoundedCornerShape(16.dp) + ) { + Column { + ProfileNavigationItem( + icon = Icons.Outlined.ContentCopy, + iconBackground = Color(0xFFEF4444), + title = "Delete Account", + subtitle = "Permanently delete your account", + onClick = onDeleteAccount, + isDarkTheme = isDarkTheme, + hideChevron = true, + textColor = Color(0xFFEF4444) + ) + } + } + + Text( + text = "This action cannot be undone. It will result in the complete deletion of account data from your device and server.", + fontSize = 12.sp, + color = secondaryTextColor, + modifier = Modifier.padding(horizontal = 8.dp, vertical = 8.dp), + lineHeight = 16.sp + ) + } + } +} diff --git a/app/src/main/java/com/rosetta/messenger/ui/settings/ThemeScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/settings/ThemeScreen.kt new file mode 100644 index 0000000..34e111c --- /dev/null +++ b/app/src/main/java/com/rosetta/messenger/ui/settings/ThemeScreen.kt @@ -0,0 +1,501 @@ +package com.rosetta.messenger.ui.settings + +import androidx.compose.animation.animateColorAsState +import androidx.compose.animation.core.tween +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.filled.DarkMode +import androidx.compose.material.icons.filled.LightMode +import androidx.compose.material.icons.filled.Smartphone +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +@Composable +fun ThemeScreen( + isDarkTheme: Boolean, + onBack: () -> Unit, + onThemeChange: (Boolean) -> Unit +) { + val backgroundColor by animateColorAsState( + targetValue = if (isDarkTheme) Color(0xFF1A1A1A) else Color(0xFFFFFFFF), + animationSpec = tween(300), + label = "backgroundColor" + ) + val surfaceColor by animateColorAsState( + targetValue = if (isDarkTheme) Color(0xFF2C2C2E) else Color(0xFFF2F2F7), + animationSpec = tween(300), + label = "surfaceColor" + ) + val textColor by animateColorAsState( + targetValue = if (isDarkTheme) Color.White else Color.Black, + animationSpec = tween(300), + label = "textColor" + ) + val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666) + + // Theme mode: "light", "dark", "auto" + var themeMode by remember { mutableStateOf(if (isDarkTheme) "dark" else "light") } + + Column( + modifier = Modifier + .fillMaxSize() + .background(backgroundColor) + .statusBarsPadding() + ) { + // Top Bar + Surface( + modifier = Modifier.fillMaxWidth(), + color = backgroundColor + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 4.dp, vertical = 8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + IconButton(onClick = onBack) { + Icon( + imageVector = Icons.Filled.ArrowBack, + contentDescription = "Back", + tint = textColor + ) + } + Text( + text = "Theme", + fontSize = 20.sp, + fontWeight = FontWeight.SemiBold, + color = textColor, + modifier = Modifier.padding(start = 8.dp) + ) + } + } + + // Content + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .padding(16.dp) + ) { + // ═══════════════════════════════════════════════════════ + // 🎨 THEME PREVIEW CARDS - Like Desktop Version + // ═══════════════════════════════════════════════════════ + Surface( + modifier = Modifier.fillMaxWidth(), + color = surfaceColor, + shape = RoundedCornerShape(16.dp) + ) { + Column(modifier = Modifier.padding(16.dp)) { + Text( + text = "Theme", + fontSize = 14.sp, + color = secondaryTextColor, + modifier = Modifier.padding(bottom = 12.dp) + ) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceEvenly + ) { + // Light Theme Preview + ThemePreviewCard( + isSelected = themeMode == "light", + isDark = false, + label = "Light", + onClick = { + themeMode = "light" + onThemeChange(false) + } + ) + + Spacer(modifier = Modifier.width(12.dp)) + + // Dark Theme Preview + ThemePreviewCard( + isSelected = themeMode == "dark", + isDark = true, + label = "Dark", + onClick = { + themeMode = "dark" + onThemeChange(true) + } + ) + } + } + } + + Spacer(modifier = Modifier.height(16.dp)) + + // ═══════════════════════════════════════════════════════ + // 🔘 THEME MODE SELECTOR + // ═══════════════════════════════════════════════════════ + Surface( + modifier = Modifier.fillMaxWidth(), + color = surfaceColor, + shape = RoundedCornerShape(16.dp) + ) { + Column { + ThemeModeOption( + icon = Icons.Filled.LightMode, + title = "Light", + subtitle = "Always use light theme", + isSelected = themeMode == "light", + onClick = { + themeMode = "light" + onThemeChange(false) + }, + textColor = textColor, + secondaryTextColor = secondaryTextColor, + showDivider = true, + isDarkTheme = isDarkTheme + ) + + ThemeModeOption( + icon = Icons.Filled.DarkMode, + title = "Dark", + subtitle = "Always use dark theme", + isSelected = themeMode == "dark", + onClick = { + themeMode = "dark" + onThemeChange(true) + }, + textColor = textColor, + secondaryTextColor = secondaryTextColor, + showDivider = true, + isDarkTheme = isDarkTheme + ) + + ThemeModeOption( + icon = Icons.Filled.Smartphone, + title = "System", + subtitle = "Match system settings", + isSelected = themeMode == "auto", + onClick = { + themeMode = "auto" + // For now, we'll just keep the current theme + }, + textColor = textColor, + secondaryTextColor = secondaryTextColor, + showDivider = false, + isDarkTheme = isDarkTheme + ) + } + } + + Text( + text = "If you choose the automatic mode, the theme will change depending on your system settings. We recommend using automatic mode.", + fontSize = 12.sp, + color = secondaryTextColor, + modifier = Modifier.padding(horizontal = 8.dp, vertical = 12.dp), + lineHeight = 16.sp + ) + + Spacer(modifier = Modifier.height(24.dp)) + + // ═══════════════════════════════════════════════════════ + // 💬 MESSAGE PREVIEW + // ═══════════════════════════════════════════════════════ + Text( + text = "Preview", + fontSize = 16.sp, + fontWeight = FontWeight.SemiBold, + color = textColor, + modifier = Modifier.padding(bottom = 12.dp, start = 4.dp) + ) + + Surface( + modifier = Modifier + .fillMaxWidth() + .height(220.dp), + color = surfaceColor, + shape = RoundedCornerShape(16.dp) + ) { + Box( + modifier = Modifier + .fillMaxSize() + .padding(16.dp) + ) { + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.Center + ) { + // Received message + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Start + ) { + MessageBubble( + text = "Hey! How are you? 👋", + isFromMe = false, + isDarkTheme = isDarkTheme + ) + } + + Spacer(modifier = Modifier.height(8.dp)) + + // Sent message + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.End + ) { + MessageBubble( + text = "I'm great, thanks! 🎉", + isFromMe = true, + isDarkTheme = isDarkTheme + ) + } + + Spacer(modifier = Modifier.height(8.dp)) + + // Another received message + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Start + ) { + MessageBubble( + text = "Love the new theme! 🌙", + isFromMe = false, + isDarkTheme = isDarkTheme + ) + } + } + } + } + } + } +} + +@Composable +private fun ThemePreviewCard( + isSelected: Boolean, + isDark: Boolean, + label: String, + onClick: () -> Unit +) { + val cardBg = if (isDark) Color(0xFF1C1C1E) else Color(0xFFF5F5F5) + val messageBg = if (isDark) Color(0xFF3A3A3C) else Color(0xFFE5E5EA) + val myMessageBg = Color(0xFF007AFF) + val borderColor = if (isSelected) Color(0xFF007AFF) else Color.Transparent + + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier + .clip(RoundedCornerShape(12.dp)) + .clickable(onClick = onClick) + ) { + // Preview Card + Box( + modifier = Modifier + .size(width = 130.dp, height = 90.dp) + .clip(RoundedCornerShape(12.dp)) + .border( + width = if (isSelected) 2.dp else 1.dp, + color = if (isSelected) borderColor else Color(0xFF3A3A3C).copy(alpha = 0.3f), + shape = RoundedCornerShape(12.dp) + ) + .background(cardBg) + .padding(8.dp) + ) { + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.SpaceEvenly + ) { + // Fake message bubbles + Box( + modifier = Modifier + .fillMaxWidth(0.7f) + .height(14.dp) + .clip(RoundedCornerShape(7.dp)) + .background(messageBg) + ) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.End + ) { + Box( + modifier = Modifier + .fillMaxWidth(0.6f) + .height(14.dp) + .clip(RoundedCornerShape(7.dp)) + .background(myMessageBg) + ) + } + + Box( + modifier = Modifier + .fillMaxWidth(0.5f) + .height(14.dp) + .clip(RoundedCornerShape(7.dp)) + .background(messageBg) + ) + } + } + + Spacer(modifier = Modifier.height(8.dp)) + + // Label with checkmark + Row( + verticalAlignment = Alignment.CenterVertically + ) { + if (isSelected) { + Icon( + imageVector = Icons.Filled.Check, + contentDescription = null, + tint = Color(0xFF007AFF), + modifier = Modifier.size(16.dp) + ) + Spacer(modifier = Modifier.width(4.dp)) + } + Text( + text = label, + fontSize = 13.sp, + fontWeight = if (isSelected) FontWeight.SemiBold else FontWeight.Normal, + color = if (isSelected) Color(0xFF007AFF) else Color(0xFF8E8E93) + ) + } + } +} + +@Composable +private fun ThemeModeOption( + icon: ImageVector, + title: String, + subtitle: String, + isSelected: Boolean, + onClick: () -> Unit, + textColor: Color, + secondaryTextColor: Color, + showDivider: Boolean, + isDarkTheme: Boolean +) { + val dividerColor = if (isDarkTheme) Color(0xFF3A3A3A) else Color(0xFFE8E8E8) + + Column( + modifier = Modifier + .fillMaxWidth() + .clickable(onClick = onClick) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + // Icon with background + Box( + modifier = Modifier + .size(36.dp) + .clip(RoundedCornerShape(8.dp)) + .background( + when (title) { + "Light" -> Color(0xFFFFA500) + "Dark" -> Color(0xFF6366F1) + else -> Color(0xFF8E8E93) + } + ), + contentAlignment = Alignment.Center + ) { + Icon( + imageVector = icon, + contentDescription = null, + tint = Color.White, + modifier = Modifier.size(20.dp) + ) + } + + Spacer(modifier = Modifier.width(12.dp)) + + Column(modifier = Modifier.weight(1f)) { + Text( + text = title, + fontSize = 16.sp, + fontWeight = FontWeight.Medium, + color = textColor + ) + Text( + text = subtitle, + fontSize = 12.sp, + color = secondaryTextColor + ) + } + + // Radio button + Box( + modifier = Modifier + .size(24.dp) + .border( + width = 2.dp, + color = if (isSelected) Color(0xFF007AFF) else secondaryTextColor.copy(alpha = 0.5f), + shape = CircleShape + ) + .padding(4.dp) + .background( + color = if (isSelected) Color(0xFF007AFF) else Color.Transparent, + shape = CircleShape + ) + ) + } + + if (showDivider) { + Divider( + color = dividerColor, + thickness = 0.5.dp, + modifier = Modifier.padding(start = 64.dp) + ) + } + } +} + +@Composable +private fun MessageBubble( + text: String, + isFromMe: Boolean, + isDarkTheme: Boolean +) { + val bubbleColor = if (isFromMe) { + Color(0xFF007AFF) + } else { + if (isDarkTheme) Color(0xFF3A3A3C) else Color(0xFFE5E5EA) + } + + val textColorMsg = if (isFromMe) { + Color.White + } else { + if (isDarkTheme) Color.White else Color.Black + } + + Surface( + color = bubbleColor, + shape = RoundedCornerShape( + topStart = 16.dp, + topEnd = 16.dp, + bottomStart = if (isFromMe) 16.dp else 4.dp, + bottomEnd = if (isFromMe) 4.dp else 16.dp + ) + ) { + Text( + text = text, + color = textColorMsg, + fontSize = 14.sp, + modifier = Modifier.padding(horizontal = 12.dp, vertical = 8.dp) + ) + } +} diff --git a/app/src/main/java/com/rosetta/messenger/ui/settings/UpdatesScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/settings/UpdatesScreen.kt new file mode 100644 index 0000000..368f9a6 --- /dev/null +++ b/app/src/main/java/com/rosetta/messenger/ui/settings/UpdatesScreen.kt @@ -0,0 +1,170 @@ +package com.rosetta.messenger.ui.settings + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +@Composable +fun UpdatesScreen( + isDarkTheme: Boolean, + onBack: () -> Unit +) { + val backgroundColor = if (isDarkTheme) Color(0xFF1A1A1A) else Color(0xFFFFFFFF) + 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) + + Column( + modifier = Modifier + .fillMaxSize() + .background(backgroundColor) + .statusBarsPadding() + ) { + // Top Bar + Surface( + modifier = Modifier.fillMaxWidth(), + color = backgroundColor + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 4.dp, vertical = 8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + IconButton(onClick = onBack) { + Icon( + imageVector = Icons.Filled.ArrowBack, + contentDescription = "Back", + tint = if (isDarkTheme) Color.White else Color.Black + ) + } + Text( + text = "Updates", + fontSize = 20.sp, + fontWeight = FontWeight.SemiBold, + color = textColor, + modifier = Modifier.padding(start = 8.dp) + ) + } + } + + // Content + Column( + modifier = Modifier + .fillMaxSize() + .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 + ) + } + } + + Spacer(modifier = Modifier.height(24.dp)) + + // Version Info + Surface( + modifier = Modifier.fillMaxWidth(), + color = surfaceColor, + shape = RoundedCornerShape(12.dp) + ) { + Column(modifier = Modifier.padding(16.dp)) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = "Application Version", + fontSize = 14.sp, + color = textColor + ) + Text( + text = "1.0.0", + fontSize = 14.sp, + fontWeight = FontWeight.Medium, + color = secondaryTextColor + ) + } + Spacer(modifier = Modifier.height(12.dp)) + Divider(color = if (isDarkTheme) Color(0xFF3A3A3A) else Color(0xFFE8E8E8)) + Spacer(modifier = Modifier.height(12.dp)) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = "Build Number", + fontSize = 14.sp, + color = textColor + ) + Text( + text = "100", + fontSize = 14.sp, + fontWeight = FontWeight.Medium, + color = secondaryTextColor + ) + } + } + } + + Text( + text = "We recommend always keeping the app up to date to improve visual effects and have the latest features.", + fontSize = 12.sp, + color = secondaryTextColor, + modifier = Modifier.padding(horizontal = 8.dp, vertical = 8.dp), + lineHeight = 16.sp + ) + + 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 + ) + } + } + } +}