refactor: Clean up OnboardingScreen code for improved readability and maintainability
This commit is contained in:
@@ -39,6 +39,7 @@ 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.SettingsScreen
|
||||
import com.rosetta.messenger.ui.splash.SplashScreen
|
||||
import com.rosetta.messenger.ui.theme.RosettaAndroidTheme
|
||||
import java.text.SimpleDateFormat
|
||||
@@ -437,15 +438,18 @@ fun MainScreen(
|
||||
// Навигация между экранами
|
||||
var selectedUser by remember { mutableStateOf<SearchUser?>(null) }
|
||||
var showSearchScreen by remember { mutableStateOf(false) }
|
||||
var showSettingsScreen by remember { mutableStateOf(false) }
|
||||
|
||||
// 🔥 TELEGRAM-STYLE анимация - чистый slide БЕЗ прозрачности
|
||||
AnimatedContent(
|
||||
targetState = Triple(selectedUser, showSearchScreen, Unit),
|
||||
targetState = Triple(selectedUser, showSearchScreen, showSettingsScreen),
|
||||
transitionSpec = {
|
||||
val isEnteringChat = targetState.first != null && initialState.first == null
|
||||
val isExitingChat = targetState.first == null && initialState.first != null
|
||||
val isEnteringSearch = targetState.second && !initialState.second
|
||||
val isExitingSearch = !targetState.second && initialState.second
|
||||
val isEnteringSettings = targetState.third && !initialState.third
|
||||
val isExitingSettings = !targetState.third && initialState.third
|
||||
|
||||
when {
|
||||
// 🚀 Вход в чат - плавный fade
|
||||
@@ -478,6 +482,18 @@ fun MainScreen(
|
||||
fadeOut(animationSpec = tween(150))
|
||||
}
|
||||
|
||||
// ⚙️ Вход в Settings - плавный fade
|
||||
isEnteringSettings -> {
|
||||
fadeIn(animationSpec = tween(200)) togetherWith
|
||||
fadeOut(animationSpec = tween(150))
|
||||
}
|
||||
|
||||
// 🔙 Выход из Settings - плавный fade
|
||||
isExitingSettings -> {
|
||||
fadeIn(animationSpec = tween(200)) togetherWith
|
||||
fadeOut(animationSpec = tween(150))
|
||||
}
|
||||
|
||||
// Default - мгновенный переход
|
||||
else -> {
|
||||
EnterTransition.None togetherWith ExitTransition.None
|
||||
@@ -485,7 +501,7 @@ fun MainScreen(
|
||||
}
|
||||
},
|
||||
label = "screenNavigation"
|
||||
) { (currentUser, isSearchOpen, _) ->
|
||||
) { (currentUser, isSearchOpen, isSettingsOpen) ->
|
||||
when {
|
||||
currentUser != null -> {
|
||||
// Экран чата
|
||||
@@ -525,6 +541,41 @@ fun MainScreen(
|
||||
}
|
||||
)
|
||||
}
|
||||
isSettingsOpen -> {
|
||||
// Экран настроек
|
||||
SettingsScreen(
|
||||
isDarkTheme = isDarkTheme,
|
||||
accountName = accountName,
|
||||
accountPhone = accountPhone,
|
||||
accountPublicKey = accountPublicKey,
|
||||
onBack = { showSettingsScreen = false },
|
||||
onToggleTheme = onToggleTheme,
|
||||
onProfileClick = {
|
||||
// TODO: Navigate to profile editor
|
||||
},
|
||||
onPrivacySecurityClick = {
|
||||
// TODO: Navigate to privacy settings
|
||||
},
|
||||
onNotificationsClick = {
|
||||
// TODO: Navigate to notifications settings
|
||||
},
|
||||
onDataStorageClick = {
|
||||
// TODO: Navigate to data storage settings
|
||||
},
|
||||
onChatSettingsClick = {
|
||||
// TODO: Navigate to chat settings
|
||||
},
|
||||
onLanguageClick = {
|
||||
// TODO: Navigate to language selection
|
||||
},
|
||||
onHelpClick = {
|
||||
// TODO: Navigate to help center
|
||||
},
|
||||
onAboutClick = {
|
||||
// TODO: Show about dialog
|
||||
}
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
// Список чатов
|
||||
ChatsListScreen(
|
||||
@@ -558,9 +609,7 @@ fun MainScreen(
|
||||
online = 1
|
||||
)
|
||||
},
|
||||
onSettingsClick = {
|
||||
// TODO: Navigate to settings
|
||||
},
|
||||
onSettingsClick = { showSettingsScreen = true },
|
||||
onInviteFriendsClick = {
|
||||
// TODO: Share invite link
|
||||
},
|
||||
|
||||
@@ -5,38 +5,178 @@ import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.intPreferencesKey
|
||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||
import androidx.datastore.preferences.preferencesDataStore
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "rosetta_preferences")
|
||||
private val Context.dataStore: DataStore<Preferences> by
|
||||
preferencesDataStore(name = "rosetta_preferences")
|
||||
|
||||
class PreferencesManager(private val context: Context) {
|
||||
|
||||
|
||||
companion object {
|
||||
// Onboarding & Theme
|
||||
val HAS_SEEN_ONBOARDING = booleanPreferencesKey("has_seen_onboarding")
|
||||
val IS_DARK_THEME = booleanPreferencesKey("is_dark_theme")
|
||||
|
||||
// Notifications
|
||||
val NOTIFICATIONS_ENABLED = booleanPreferencesKey("notifications_enabled")
|
||||
val NOTIFICATION_SOUND_ENABLED = booleanPreferencesKey("notification_sound_enabled")
|
||||
val NOTIFICATION_VIBRATE_ENABLED = booleanPreferencesKey("notification_vibrate_enabled")
|
||||
val NOTIFICATION_PREVIEW_ENABLED = booleanPreferencesKey("notification_preview_enabled")
|
||||
|
||||
// Chat Settings
|
||||
val MESSAGE_TEXT_SIZE = intPreferencesKey("message_text_size") // 0=small, 1=medium, 2=large
|
||||
val SEND_BY_ENTER = booleanPreferencesKey("send_by_enter")
|
||||
val AUTO_DOWNLOAD_PHOTOS = booleanPreferencesKey("auto_download_photos")
|
||||
val AUTO_DOWNLOAD_VIDEOS = booleanPreferencesKey("auto_download_videos")
|
||||
val AUTO_DOWNLOAD_FILES = booleanPreferencesKey("auto_download_files")
|
||||
|
||||
// Privacy
|
||||
val SHOW_ONLINE_STATUS = booleanPreferencesKey("show_online_status")
|
||||
val SHOW_READ_RECEIPTS = booleanPreferencesKey("show_read_receipts")
|
||||
val SHOW_TYPING_INDICATOR = booleanPreferencesKey("show_typing_indicator")
|
||||
|
||||
// Language
|
||||
val APP_LANGUAGE = stringPreferencesKey("app_language") // "en", "ru", etc.
|
||||
}
|
||||
|
||||
val hasSeenOnboarding: Flow<Boolean> = context.dataStore.data
|
||||
.map { preferences ->
|
||||
preferences[HAS_SEEN_ONBOARDING] ?: false
|
||||
}
|
||||
|
||||
val isDarkTheme: Flow<Boolean> = context.dataStore.data
|
||||
.map { preferences ->
|
||||
preferences[IS_DARK_THEME] ?: true // Default to dark theme like Telegram
|
||||
}
|
||||
|
||||
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
// 🎨 ONBOARDING & THEME
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
|
||||
val hasSeenOnboarding: Flow<Boolean> =
|
||||
context.dataStore.data.map { preferences -> preferences[HAS_SEEN_ONBOARDING] ?: false }
|
||||
|
||||
val isDarkTheme: Flow<Boolean> =
|
||||
context.dataStore.data.map { preferences ->
|
||||
preferences[IS_DARK_THEME] ?: true // Default to dark theme like Telegram
|
||||
}
|
||||
|
||||
suspend fun setHasSeenOnboarding(value: Boolean) {
|
||||
context.dataStore.edit { preferences ->
|
||||
preferences[HAS_SEEN_ONBOARDING] = value
|
||||
}
|
||||
context.dataStore.edit { preferences -> preferences[HAS_SEEN_ONBOARDING] = value }
|
||||
}
|
||||
|
||||
|
||||
suspend fun setDarkTheme(value: Boolean) {
|
||||
context.dataStore.edit { preferences ->
|
||||
preferences[IS_DARK_THEME] = value
|
||||
}
|
||||
context.dataStore.edit { preferences -> preferences[IS_DARK_THEME] = value }
|
||||
}
|
||||
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
// 🔔 NOTIFICATIONS
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
|
||||
val notificationsEnabled: Flow<Boolean> =
|
||||
context.dataStore.data.map { preferences -> preferences[NOTIFICATIONS_ENABLED] ?: true }
|
||||
|
||||
val notificationSoundEnabled: Flow<Boolean> =
|
||||
context.dataStore.data.map { preferences ->
|
||||
preferences[NOTIFICATION_SOUND_ENABLED] ?: true
|
||||
}
|
||||
|
||||
val notificationVibrateEnabled: Flow<Boolean> =
|
||||
context.dataStore.data.map { preferences ->
|
||||
preferences[NOTIFICATION_VIBRATE_ENABLED] ?: true
|
||||
}
|
||||
|
||||
val notificationPreviewEnabled: Flow<Boolean> =
|
||||
context.dataStore.data.map { preferences ->
|
||||
preferences[NOTIFICATION_PREVIEW_ENABLED] ?: true
|
||||
}
|
||||
|
||||
suspend fun setNotificationsEnabled(value: Boolean) {
|
||||
context.dataStore.edit { preferences -> preferences[NOTIFICATIONS_ENABLED] = value }
|
||||
}
|
||||
|
||||
suspend fun setNotificationSoundEnabled(value: Boolean) {
|
||||
context.dataStore.edit { preferences -> preferences[NOTIFICATION_SOUND_ENABLED] = value }
|
||||
}
|
||||
|
||||
suspend fun setNotificationVibrateEnabled(value: Boolean) {
|
||||
context.dataStore.edit { preferences -> preferences[NOTIFICATION_VIBRATE_ENABLED] = value }
|
||||
}
|
||||
|
||||
suspend fun setNotificationPreviewEnabled(value: Boolean) {
|
||||
context.dataStore.edit { preferences -> preferences[NOTIFICATION_PREVIEW_ENABLED] = value }
|
||||
}
|
||||
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
// 💬 CHAT SETTINGS
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
|
||||
val messageTextSize: Flow<Int> =
|
||||
context.dataStore.data.map { preferences ->
|
||||
preferences[MESSAGE_TEXT_SIZE] ?: 1 // Default medium
|
||||
}
|
||||
|
||||
val sendByEnter: Flow<Boolean> =
|
||||
context.dataStore.data.map { preferences -> preferences[SEND_BY_ENTER] ?: false }
|
||||
|
||||
val autoDownloadPhotos: Flow<Boolean> =
|
||||
context.dataStore.data.map { preferences -> preferences[AUTO_DOWNLOAD_PHOTOS] ?: true }
|
||||
|
||||
val autoDownloadVideos: Flow<Boolean> =
|
||||
context.dataStore.data.map { preferences -> preferences[AUTO_DOWNLOAD_VIDEOS] ?: false }
|
||||
|
||||
val autoDownloadFiles: Flow<Boolean> =
|
||||
context.dataStore.data.map { preferences -> preferences[AUTO_DOWNLOAD_FILES] ?: false }
|
||||
|
||||
suspend fun setMessageTextSize(value: Int) {
|
||||
context.dataStore.edit { preferences -> preferences[MESSAGE_TEXT_SIZE] = value }
|
||||
}
|
||||
|
||||
suspend fun setSendByEnter(value: Boolean) {
|
||||
context.dataStore.edit { preferences -> preferences[SEND_BY_ENTER] = value }
|
||||
}
|
||||
|
||||
suspend fun setAutoDownloadPhotos(value: Boolean) {
|
||||
context.dataStore.edit { preferences -> preferences[AUTO_DOWNLOAD_PHOTOS] = value }
|
||||
}
|
||||
|
||||
suspend fun setAutoDownloadVideos(value: Boolean) {
|
||||
context.dataStore.edit { preferences -> preferences[AUTO_DOWNLOAD_VIDEOS] = value }
|
||||
}
|
||||
|
||||
suspend fun setAutoDownloadFiles(value: Boolean) {
|
||||
context.dataStore.edit { preferences -> preferences[AUTO_DOWNLOAD_FILES] = value }
|
||||
}
|
||||
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
// 🔐 PRIVACY
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
|
||||
val showOnlineStatus: Flow<Boolean> =
|
||||
context.dataStore.data.map { preferences -> preferences[SHOW_ONLINE_STATUS] ?: true }
|
||||
|
||||
val showReadReceipts: Flow<Boolean> =
|
||||
context.dataStore.data.map { preferences -> preferences[SHOW_READ_RECEIPTS] ?: true }
|
||||
|
||||
val showTypingIndicator: Flow<Boolean> =
|
||||
context.dataStore.data.map { preferences -> preferences[SHOW_TYPING_INDICATOR] ?: true }
|
||||
|
||||
suspend fun setShowOnlineStatus(value: Boolean) {
|
||||
context.dataStore.edit { preferences -> preferences[SHOW_ONLINE_STATUS] = value }
|
||||
}
|
||||
|
||||
suspend fun setShowReadReceipts(value: Boolean) {
|
||||
context.dataStore.edit { preferences -> preferences[SHOW_READ_RECEIPTS] = value }
|
||||
}
|
||||
|
||||
suspend fun setShowTypingIndicator(value: Boolean) {
|
||||
context.dataStore.edit { preferences -> preferences[SHOW_TYPING_INDICATOR] = value }
|
||||
}
|
||||
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
// 🌐 LANGUAGE
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
|
||||
val appLanguage: Flow<String> =
|
||||
context.dataStore.data.map { preferences ->
|
||||
preferences[APP_LANGUAGE] ?: "en" // Default English
|
||||
}
|
||||
|
||||
suspend fun setAppLanguage(value: String) {
|
||||
context.dataStore.edit { preferences -> preferences[APP_LANGUAGE] = value }
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,505 @@
|
||||
package com.rosetta.messenger.ui.settings
|
||||
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.material.icons.outlined.*
|
||||
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
|
||||
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun SettingsScreen(
|
||||
isDarkTheme: Boolean,
|
||||
accountName: String,
|
||||
accountPhone: String,
|
||||
accountPublicKey: String,
|
||||
onBack: () -> Unit,
|
||||
onToggleTheme: () -> Unit,
|
||||
onProfileClick: () -> Unit = {},
|
||||
onPrivacySecurityClick: () -> Unit = {},
|
||||
onNotificationsClick: () -> Unit = {},
|
||||
onDataStorageClick: () -> Unit = {},
|
||||
onChatSettingsClick: () -> Unit = {},
|
||||
onLanguageClick: () -> Unit = {},
|
||||
onHelpClick: () -> Unit = {},
|
||||
onAboutClick: () -> Unit = {}
|
||||
) {
|
||||
// Цвета в зависимости от темы
|
||||
val backgroundColor = if (isDarkTheme) Color(0xFF0F0F0F) else Color.White
|
||||
val surfaceColor = if (isDarkTheme) Color(0xFF1A1A1A) else Color(0xFFF5F5F5)
|
||||
val textColor = if (isDarkTheme) Color.White else Color(0xFF1A1A1A)
|
||||
val secondaryTextColor = if (isDarkTheme) Color(0xFF999999) else Color(0xFF666666)
|
||||
val dividerColor = if (isDarkTheme) Color(0xFF2A2A2A) else Color(0xFFE8E8E8)
|
||||
val iconTintColor = if (isDarkTheme) Color(0xFF999999) else Color(0xFF666666)
|
||||
|
||||
Column(modifier = Modifier.fillMaxSize().background(backgroundColor)) {
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
// 🎨 TOP BAR
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
text = "Settings",
|
||||
color = textColor,
|
||||
fontSize = 20.sp,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onBack) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.ArrowBack,
|
||||
contentDescription = "Back",
|
||||
tint = textColor
|
||||
)
|
||||
}
|
||||
},
|
||||
colors = TopAppBarDefaults.topAppBarColors(containerColor = backgroundColor)
|
||||
)
|
||||
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
// 📱 CONTENT
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) {
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
// 👤 PROFILE SECTION
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
Surface(
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.clickable(onClick = onProfileClick),
|
||||
color = surfaceColor
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
// Avatar
|
||||
Box(
|
||||
modifier =
|
||||
Modifier.size(64.dp).clip(CircleShape).background(PrimaryBlue),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = getInitials(accountName),
|
||||
fontSize = 24.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = Color.White
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
|
||||
// Name and Phone
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = accountName,
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = textColor
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Text(text = accountPhone, fontSize = 14.sp, color = secondaryTextColor)
|
||||
Spacer(modifier = Modifier.height(2.dp))
|
||||
Text(
|
||||
text = accountPublicKey.take(12) + "...",
|
||||
fontSize = 12.sp,
|
||||
color = secondaryTextColor.copy(alpha = 0.7f)
|
||||
)
|
||||
}
|
||||
|
||||
// Arrow
|
||||
Icon(
|
||||
imageVector = Icons.Default.ChevronRight,
|
||||
contentDescription = null,
|
||||
tint = iconTintColor
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
// 🎨 APPEARANCE SECTION
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
SettingsSectionTitle(title = "Appearance", isDarkTheme = isDarkTheme)
|
||||
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
|
||||
color = surfaceColor,
|
||||
shape = RoundedCornerShape(16.dp)
|
||||
) {
|
||||
Column {
|
||||
SettingsToggleItem(
|
||||
icon =
|
||||
if (isDarkTheme) Icons.Outlined.DarkMode
|
||||
else Icons.Outlined.LightMode,
|
||||
title = "Dark Mode",
|
||||
subtitle = if (isDarkTheme) "Enabled" else "Disabled",
|
||||
isChecked = isDarkTheme,
|
||||
onToggle = onToggleTheme,
|
||||
isDarkTheme = isDarkTheme
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
// 🔐 PRIVACY & SECURITY SECTION
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
SettingsSectionTitle(title = "Privacy & Security", isDarkTheme = isDarkTheme)
|
||||
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
|
||||
color = surfaceColor,
|
||||
shape = RoundedCornerShape(16.dp)
|
||||
) {
|
||||
Column {
|
||||
SettingsNavigationItem(
|
||||
icon = Icons.Outlined.Lock,
|
||||
title = "Privacy Settings",
|
||||
subtitle = "Control who can see your info",
|
||||
onClick = onPrivacySecurityClick,
|
||||
isDarkTheme = isDarkTheme,
|
||||
showDivider = true
|
||||
)
|
||||
SettingsNavigationItem(
|
||||
icon = Icons.Outlined.Security,
|
||||
title = "Security",
|
||||
subtitle = "Passcode, 2FA, sessions",
|
||||
onClick = onPrivacySecurityClick,
|
||||
isDarkTheme = isDarkTheme,
|
||||
showDivider = true
|
||||
)
|
||||
SettingsNavigationItem(
|
||||
icon = Icons.Outlined.Block,
|
||||
title = "Blocked Users",
|
||||
subtitle = "Manage blocked contacts",
|
||||
onClick = onPrivacySecurityClick,
|
||||
isDarkTheme = isDarkTheme
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
// 🔔 NOTIFICATIONS SECTION
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
SettingsSectionTitle(title = "Notifications & Sounds", isDarkTheme = isDarkTheme)
|
||||
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
|
||||
color = surfaceColor,
|
||||
shape = RoundedCornerShape(16.dp)
|
||||
) {
|
||||
Column {
|
||||
SettingsNavigationItem(
|
||||
icon = Icons.Outlined.Notifications,
|
||||
title = "Notifications",
|
||||
subtitle = "Messages, groups, channels",
|
||||
onClick = onNotificationsClick,
|
||||
isDarkTheme = isDarkTheme,
|
||||
showDivider = true
|
||||
)
|
||||
SettingsNavigationItem(
|
||||
icon = Icons.Outlined.VolumeUp,
|
||||
title = "Sounds",
|
||||
subtitle = "Notification sounds",
|
||||
onClick = onNotificationsClick,
|
||||
isDarkTheme = isDarkTheme
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
// 💾 DATA & STORAGE SECTION
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
SettingsSectionTitle(title = "Data & Storage", isDarkTheme = isDarkTheme)
|
||||
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
|
||||
color = surfaceColor,
|
||||
shape = RoundedCornerShape(16.dp)
|
||||
) {
|
||||
Column {
|
||||
SettingsNavigationItem(
|
||||
icon = Icons.Outlined.Storage,
|
||||
title = "Storage Usage",
|
||||
subtitle = "Clear cache, manage data",
|
||||
onClick = onDataStorageClick,
|
||||
isDarkTheme = isDarkTheme,
|
||||
showDivider = true
|
||||
)
|
||||
SettingsNavigationItem(
|
||||
icon = Icons.Outlined.DataUsage,
|
||||
title = "Network Usage",
|
||||
subtitle = "Mobile and Wi-Fi",
|
||||
onClick = onDataStorageClick,
|
||||
isDarkTheme = isDarkTheme,
|
||||
showDivider = true
|
||||
)
|
||||
SettingsNavigationItem(
|
||||
icon = Icons.Outlined.DownloadForOffline,
|
||||
title = "Auto-Download",
|
||||
subtitle = "Photos, videos, files",
|
||||
onClick = onDataStorageClick,
|
||||
isDarkTheme = isDarkTheme
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
// 💬 CHAT SETTINGS SECTION
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
SettingsSectionTitle(title = "Chat Settings", isDarkTheme = isDarkTheme)
|
||||
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
|
||||
color = surfaceColor,
|
||||
shape = RoundedCornerShape(16.dp)
|
||||
) {
|
||||
Column {
|
||||
SettingsNavigationItem(
|
||||
icon = Icons.Outlined.ChatBubbleOutline,
|
||||
title = "Chat Background",
|
||||
subtitle = "Set wallpaper for chats",
|
||||
onClick = onChatSettingsClick,
|
||||
isDarkTheme = isDarkTheme,
|
||||
showDivider = true
|
||||
)
|
||||
SettingsNavigationItem(
|
||||
icon = Icons.Outlined.TextFields,
|
||||
title = "Message Text Size",
|
||||
subtitle = "Adjust text size in chats",
|
||||
onClick = onChatSettingsClick,
|
||||
isDarkTheme = isDarkTheme,
|
||||
showDivider = true
|
||||
)
|
||||
SettingsNavigationItem(
|
||||
icon = Icons.Outlined.EmojiEmotions,
|
||||
title = "Stickers & Emojis",
|
||||
subtitle = "Manage sticker packs",
|
||||
onClick = onChatSettingsClick,
|
||||
isDarkTheme = isDarkTheme
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
// 🌐 GENERAL SECTION
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
SettingsSectionTitle(title = "General", isDarkTheme = isDarkTheme)
|
||||
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
|
||||
color = surfaceColor,
|
||||
shape = RoundedCornerShape(16.dp)
|
||||
) {
|
||||
Column {
|
||||
SettingsNavigationItem(
|
||||
icon = Icons.Outlined.Language,
|
||||
title = "Language",
|
||||
subtitle = "English",
|
||||
onClick = onLanguageClick,
|
||||
isDarkTheme = isDarkTheme
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
// ℹ️ SUPPORT SECTION
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
SettingsSectionTitle(title = "Support", isDarkTheme = isDarkTheme)
|
||||
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
|
||||
color = surfaceColor,
|
||||
shape = RoundedCornerShape(16.dp)
|
||||
) {
|
||||
Column {
|
||||
SettingsNavigationItem(
|
||||
icon = Icons.Outlined.HelpOutline,
|
||||
title = "Help Center",
|
||||
subtitle = "FAQ and support",
|
||||
onClick = onHelpClick,
|
||||
isDarkTheme = isDarkTheme,
|
||||
showDivider = true
|
||||
)
|
||||
SettingsNavigationItem(
|
||||
icon = Icons.Outlined.Info,
|
||||
title = "About",
|
||||
subtitle = "Version 1.0.0",
|
||||
onClick = onAboutClick,
|
||||
isDarkTheme = isDarkTheme
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
// 📦 HELPER COMPONENTS
|
||||
// ═════════════════════════════════════════════════════════════
|
||||
|
||||
@Composable
|
||||
private fun SettingsSectionTitle(title: String, isDarkTheme: Boolean) {
|
||||
val textColor = if (isDarkTheme) Color(0xFF666666) else Color(0xFF999999)
|
||||
|
||||
Text(
|
||||
text = title.uppercase(),
|
||||
fontSize = 13.sp,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = textColor,
|
||||
modifier = Modifier.padding(horizontal = 32.dp, vertical = 8.dp)
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SettingsNavigationItem(
|
||||
icon: ImageVector,
|
||||
title: String,
|
||||
subtitle: String,
|
||||
onClick: () -> Unit,
|
||||
isDarkTheme: Boolean,
|
||||
showDivider: Boolean = false
|
||||
) {
|
||||
val textColor = if (isDarkTheme) Color.White else Color(0xFF1A1A1A)
|
||||
val secondaryTextColor = if (isDarkTheme) Color(0xFF999999) else Color(0xFF666666)
|
||||
val iconTintColor = if (isDarkTheme) Color(0xFF999999) else Color(0xFF666666)
|
||||
val dividerColor = if (isDarkTheme) Color(0xFF2A2A2A) else Color(0xFFE8E8E8)
|
||||
|
||||
Column {
|
||||
Row(
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.clickable(onClick = onClick)
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
// Icon
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
tint = iconTintColor,
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
|
||||
// Title and Subtitle
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(text = title, fontSize = 16.sp, color = textColor)
|
||||
Spacer(modifier = Modifier.height(2.dp))
|
||||
Text(text = subtitle, fontSize = 13.sp, color = secondaryTextColor)
|
||||
}
|
||||
|
||||
// Arrow
|
||||
Icon(
|
||||
imageVector = Icons.Default.ChevronRight,
|
||||
contentDescription = null,
|
||||
tint = iconTintColor.copy(alpha = 0.5f),
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
}
|
||||
|
||||
if (showDivider) {
|
||||
Divider(
|
||||
color = dividerColor,
|
||||
thickness = 0.5.dp,
|
||||
modifier = Modifier.padding(start = 56.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SettingsToggleItem(
|
||||
icon: ImageVector,
|
||||
title: String,
|
||||
subtitle: String,
|
||||
isChecked: Boolean,
|
||||
onToggle: () -> Unit,
|
||||
isDarkTheme: Boolean
|
||||
) {
|
||||
val textColor = if (isDarkTheme) Color.White else Color(0xFF1A1A1A)
|
||||
val secondaryTextColor = if (isDarkTheme) Color(0xFF999999) else Color(0xFF666666)
|
||||
val iconTintColor = if (isDarkTheme) Color(0xFF999999) else Color(0xFF666666)
|
||||
|
||||
Row(
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.clickable(onClick = onToggle)
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
// Icon
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
tint = iconTintColor,
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
|
||||
// Title and Subtitle
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(text = title, fontSize = 16.sp, color = textColor)
|
||||
Spacer(modifier = Modifier.height(2.dp))
|
||||
Text(text = subtitle, fontSize = 13.sp, color = secondaryTextColor)
|
||||
}
|
||||
|
||||
// Toggle Switch
|
||||
Switch(
|
||||
checked = isChecked,
|
||||
onCheckedChange = { onToggle() },
|
||||
colors =
|
||||
SwitchDefaults.colors(
|
||||
checkedThumbColor = Color.White,
|
||||
checkedTrackColor = PrimaryBlue,
|
||||
uncheckedThumbColor = Color.White,
|
||||
uncheckedTrackColor =
|
||||
if (isDarkTheme) Color(0xFF3A3A3A) else Color(0xFFCCCCCC)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getInitials(name: String): String {
|
||||
if (name.isBlank()) return "?"
|
||||
val parts = name.trim().split(" ").filter { it.isNotEmpty() }
|
||||
return when {
|
||||
parts.isEmpty() -> "?"
|
||||
parts.size == 1 -> parts[0].take(2).uppercase()
|
||||
else -> (parts[0].first().toString() + parts[1].first().toString()).uppercase()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user