Refactor code structure for improved readability and maintainability
This commit is contained in:
@@ -4,7 +4,6 @@ import android.Manifest
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
@@ -21,6 +20,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.google.firebase.FirebaseApp
|
||||
import com.google.firebase.messaging.FirebaseMessaging
|
||||
@@ -29,12 +29,12 @@ import com.rosetta.messenger.data.DecryptedAccount
|
||||
import com.rosetta.messenger.data.PreferencesManager
|
||||
import com.rosetta.messenger.data.RecentSearchesManager
|
||||
import com.rosetta.messenger.database.RosettaDatabase
|
||||
import com.rosetta.messenger.repository.AvatarRepository
|
||||
import com.rosetta.messenger.network.PacketPushNotification
|
||||
import com.rosetta.messenger.network.ProtocolManager
|
||||
import com.rosetta.messenger.network.ProtocolState
|
||||
import com.rosetta.messenger.network.PushNotificationAction
|
||||
import com.rosetta.messenger.network.SearchUser
|
||||
import com.rosetta.messenger.repository.AvatarRepository
|
||||
import com.rosetta.messenger.ui.auth.AccountInfo
|
||||
import com.rosetta.messenger.ui.auth.AuthFlow
|
||||
import com.rosetta.messenger.ui.chats.ChatDetailScreen
|
||||
@@ -42,6 +42,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.components.SwipeBackContainer
|
||||
import com.rosetta.messenger.ui.crashlogs.CrashLogsScreen
|
||||
import com.rosetta.messenger.ui.onboarding.OnboardingScreen
|
||||
import com.rosetta.messenger.ui.settings.BackupScreen
|
||||
import com.rosetta.messenger.ui.settings.BiometricEnableScreen
|
||||
@@ -50,7 +51,6 @@ 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.crashlogs.CrashLogsScreen
|
||||
import com.rosetta.messenger.ui.splash.SplashScreen
|
||||
import com.rosetta.messenger.ui.theme.RosettaAndroidTheme
|
||||
import java.text.SimpleDateFormat
|
||||
@@ -58,7 +58,6 @@ import java.util.Date
|
||||
import java.util.Locale
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
|
||||
class MainActivity : FragmentActivity() {
|
||||
private lateinit var preferencesManager: PreferencesManager
|
||||
@@ -134,12 +133,13 @@ class MainActivity : FragmentActivity() {
|
||||
val scope = rememberCoroutineScope()
|
||||
val themeMode by preferencesManager.themeMode.collectAsState(initial = "dark")
|
||||
val systemInDarkTheme = isSystemInDarkTheme()
|
||||
val isDarkTheme = when (themeMode) {
|
||||
"light" -> false
|
||||
"dark" -> true
|
||||
"auto" -> systemInDarkTheme
|
||||
else -> true
|
||||
}
|
||||
val isDarkTheme =
|
||||
when (themeMode) {
|
||||
"light" -> false
|
||||
"dark" -> true
|
||||
"auto" -> systemInDarkTheme
|
||||
else -> true
|
||||
}
|
||||
val isLoggedIn by accountManager.isLoggedIn.collectAsState(initial = null)
|
||||
var showSplash by remember { mutableStateOf(true) }
|
||||
var showOnboarding by remember { mutableStateOf(true) }
|
||||
@@ -315,9 +315,7 @@ class MainActivity : FragmentActivity() {
|
||||
}
|
||||
},
|
||||
onThemeModeChange = { mode ->
|
||||
scope.launch {
|
||||
preferencesManager.setThemeMode(mode)
|
||||
}
|
||||
scope.launch { preferencesManager.setThemeMode(mode) }
|
||||
},
|
||||
onLogout = {
|
||||
// Set currentAccount to null immediately to prevent UI
|
||||
@@ -343,10 +341,13 @@ class MainActivity : FragmentActivity() {
|
||||
.filter { it.isNotEmpty() }
|
||||
.let { words ->
|
||||
when {
|
||||
words.isEmpty() -> "??"
|
||||
words.isEmpty() ->
|
||||
"??"
|
||||
words.size == 1 ->
|
||||
words[0]
|
||||
.take(2)
|
||||
.take(
|
||||
2
|
||||
)
|
||||
.uppercase()
|
||||
else ->
|
||||
"${words[0].first()}${words[1].first()}".uppercase()
|
||||
@@ -475,6 +476,27 @@ class MainActivity : FragmentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigation sealed class — replaces ~15 boolean flags with a type-safe navigation stack. ChatsList
|
||||
* is always the base layer and is not part of the stack. Each SwipeBackContainer reads a
|
||||
* derivedStateOf, so pushing/popping an unrelated screen won't trigger recomposition of other
|
||||
* screens.
|
||||
*/
|
||||
sealed class Screen {
|
||||
data object Profile : Screen()
|
||||
data object Search : Screen()
|
||||
data class ChatDetail(val user: SearchUser) : Screen()
|
||||
data class OtherProfile(val user: SearchUser) : Screen()
|
||||
data object Updates : Screen()
|
||||
data object Theme : Screen()
|
||||
data object Safety : Screen()
|
||||
data object Backup : Screen()
|
||||
data object Logs : Screen()
|
||||
data object CrashLogs : Screen()
|
||||
data object Biometric : Screen()
|
||||
data object Appearance : Screen()
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MainScreen(
|
||||
account: DecryptedAccount? = null,
|
||||
@@ -495,12 +517,12 @@ fun MainScreen(
|
||||
val accountPublicKey = account?.publicKey ?: "04c266b98ae5"
|
||||
val accountPrivateKey = account?.privateKey ?: ""
|
||||
val privateKeyHash = account?.privateKeyHash ?: ""
|
||||
|
||||
|
||||
// Username state - загружается из EncryptedAccount
|
||||
// Following desktop version pattern: username is stored locally and loaded on app start
|
||||
var accountUsername by remember { mutableStateOf("") }
|
||||
var reloadTrigger by remember { mutableIntStateOf(0) }
|
||||
|
||||
|
||||
// Load username AND name from AccountManager (persisted in DataStore)
|
||||
val context = LocalContext.current
|
||||
LaunchedEffect(accountPublicKey, reloadTrigger) {
|
||||
@@ -514,11 +536,14 @@ fun MainScreen(
|
||||
|
||||
// Состояние протокола для передачи в SearchScreen
|
||||
val protocolState by ProtocolManager.state.collectAsState()
|
||||
|
||||
|
||||
// Перечитать username/name после получения own profile с сервера
|
||||
// Аналог Desktop: useUserInformation автоматически обновляет UI при PacketSearch ответе
|
||||
LaunchedEffect(protocolState) {
|
||||
if (protocolState == ProtocolState.AUTHENTICATED && accountPublicKey.isNotBlank() && accountPublicKey != "04c266b98ae5") {
|
||||
if (protocolState == ProtocolState.AUTHENTICATED &&
|
||||
accountPublicKey.isNotBlank() &&
|
||||
accountPublicKey != "04c266b98ae5"
|
||||
) {
|
||||
delay(2000) // Ждём fetchOwnProfile() → PacketSearch → AccountManager update
|
||||
val accountManager = AccountManager(context)
|
||||
val encryptedAccount = accountManager.getAccount(accountPublicKey)
|
||||
@@ -527,46 +552,92 @@ fun MainScreen(
|
||||
}
|
||||
}
|
||||
|
||||
// Навигация между экранами
|
||||
var selectedUser by remember { mutableStateOf<SearchUser?>(null) }
|
||||
var showSearchScreen by remember { mutableStateOf(false) }
|
||||
var showProfileScreen by remember { mutableStateOf(false) }
|
||||
var showOtherProfileScreen by remember { mutableStateOf(false) }
|
||||
var selectedOtherUser by remember { mutableStateOf<SearchUser?>(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) }
|
||||
var showLogsScreen by remember { mutableStateOf(false) }
|
||||
var showCrashLogsScreen by remember { mutableStateOf(false) }
|
||||
var showBiometricScreen by remember { mutableStateOf(false) }
|
||||
var showAppearanceScreen by remember { mutableStateOf(false) }
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// Navigation stack — sealed class instead of ~15 boolean flags.
|
||||
// ChatsList is always the base layer (not in stack).
|
||||
// Each derivedStateOf only recomposes its SwipeBackContainer
|
||||
// when that specific screen appears/disappears — not on every
|
||||
// navigation change. This eliminates the massive recomposition
|
||||
// that happened when ANY boolean flag changed.
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
var navStack by remember { mutableStateOf<List<Screen>>(emptyList()) }
|
||||
|
||||
// Derived visibility — only triggers recomposition when THIS screen changes
|
||||
val isProfileVisible by remember { derivedStateOf { navStack.any { it is Screen.Profile } } }
|
||||
val isSearchVisible by remember { derivedStateOf { navStack.any { it is Screen.Search } } }
|
||||
val chatDetailScreen by remember {
|
||||
derivedStateOf { navStack.filterIsInstance<Screen.ChatDetail>().lastOrNull() }
|
||||
}
|
||||
val selectedUser = chatDetailScreen?.user
|
||||
val otherProfileScreen by remember {
|
||||
derivedStateOf { navStack.filterIsInstance<Screen.OtherProfile>().lastOrNull() }
|
||||
}
|
||||
val selectedOtherUser = otherProfileScreen?.user
|
||||
val isUpdatesVisible by remember { derivedStateOf { navStack.any { it is Screen.Updates } } }
|
||||
val isThemeVisible by remember { derivedStateOf { navStack.any { it is Screen.Theme } } }
|
||||
val isSafetyVisible by remember { derivedStateOf { navStack.any { it is Screen.Safety } } }
|
||||
val isBackupVisible by remember { derivedStateOf { navStack.any { it is Screen.Backup } } }
|
||||
val isLogsVisible by remember { derivedStateOf { navStack.any { it is Screen.Logs } } }
|
||||
val isCrashLogsVisible by remember {
|
||||
derivedStateOf { navStack.any { it is Screen.CrashLogs } }
|
||||
}
|
||||
val isBiometricVisible by remember {
|
||||
derivedStateOf { navStack.any { it is Screen.Biometric } }
|
||||
}
|
||||
val isAppearanceVisible by remember {
|
||||
derivedStateOf { navStack.any { it is Screen.Appearance } }
|
||||
}
|
||||
|
||||
// Navigation helpers
|
||||
fun pushScreen(screen: Screen) {
|
||||
navStack = navStack + screen
|
||||
}
|
||||
fun popScreen() {
|
||||
navStack = navStack.dropLast(1)
|
||||
}
|
||||
fun popProfileAndChildren() {
|
||||
navStack =
|
||||
navStack.filterNot {
|
||||
it is Screen.Profile ||
|
||||
it is Screen.Theme ||
|
||||
it is Screen.Safety ||
|
||||
it is Screen.Backup ||
|
||||
it is Screen.Logs ||
|
||||
it is Screen.CrashLogs ||
|
||||
it is Screen.Biometric ||
|
||||
it is Screen.Appearance
|
||||
}
|
||||
}
|
||||
fun popChatAndChildren() {
|
||||
navStack = navStack.filterNot { it is Screen.ChatDetail || it is Screen.OtherProfile }
|
||||
}
|
||||
|
||||
// ProfileViewModel для логов
|
||||
val profileViewModel: com.rosetta.messenger.ui.settings.ProfileViewModel = androidx.lifecycle.viewmodel.compose.viewModel()
|
||||
val profileViewModel: com.rosetta.messenger.ui.settings.ProfileViewModel =
|
||||
androidx.lifecycle.viewmodel.compose.viewModel()
|
||||
val profileState by profileViewModel.state.collectAsState()
|
||||
|
||||
|
||||
// Appearance: background blur color preference
|
||||
val prefsManager = remember { com.rosetta.messenger.data.PreferencesManager(context) }
|
||||
val backgroundBlurColorId by prefsManager.backgroundBlurColorId.collectAsState(initial = "avatar")
|
||||
val backgroundBlurColorId by
|
||||
prefsManager.backgroundBlurColorId.collectAsState(initial = "avatar")
|
||||
val pinnedChats by prefsManager.pinnedChats.collectAsState(initial = emptySet())
|
||||
|
||||
|
||||
// AvatarRepository для работы с аватарами
|
||||
val avatarRepository = remember(accountPublicKey) {
|
||||
if (accountPublicKey.isNotBlank() && accountPublicKey != "04c266b98ae5") {
|
||||
val database = RosettaDatabase.getDatabase(context)
|
||||
AvatarRepository(
|
||||
context = context,
|
||||
avatarDao = database.avatarDao(),
|
||||
currentPublicKey = accountPublicKey
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
val avatarRepository =
|
||||
remember(accountPublicKey) {
|
||||
if (accountPublicKey.isNotBlank() && accountPublicKey != "04c266b98ae5") {
|
||||
val database = RosettaDatabase.getDatabase(context)
|
||||
AvatarRepository(
|
||||
context = context,
|
||||
avatarDao = database.avatarDao(),
|
||||
currentPublicKey = accountPublicKey
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
// Coroutine scope for profile updates
|
||||
val mainScreenScope = rememberCoroutineScope()
|
||||
|
||||
@@ -582,9 +653,7 @@ fun MainScreen(
|
||||
accountPrivateKey = accountPrivateKey,
|
||||
privateKeyHash = privateKeyHash,
|
||||
onToggleTheme = onToggleTheme,
|
||||
onProfileClick = {
|
||||
showProfileScreen = true
|
||||
},
|
||||
onProfileClick = { pushScreen(Screen.Profile) },
|
||||
onNewGroupClick = {
|
||||
// TODO: Navigate to new group
|
||||
},
|
||||
@@ -596,43 +665,46 @@ fun MainScreen(
|
||||
},
|
||||
onSavedMessagesClick = {
|
||||
// Открываем чат с самим собой (Saved Messages)
|
||||
selectedUser =
|
||||
SearchUser(
|
||||
title = "Saved Messages",
|
||||
username = "",
|
||||
publicKey = accountPublicKey,
|
||||
verified = 0,
|
||||
online = 1
|
||||
pushScreen(
|
||||
Screen.ChatDetail(
|
||||
SearchUser(
|
||||
title = "Saved Messages",
|
||||
username = "",
|
||||
publicKey = accountPublicKey,
|
||||
verified = 0,
|
||||
online = 1
|
||||
)
|
||||
)
|
||||
)
|
||||
},
|
||||
onSettingsClick = { showProfileScreen = true },
|
||||
onSettingsClick = { pushScreen(Screen.Profile) },
|
||||
onInviteFriendsClick = {
|
||||
// TODO: Share invite link
|
||||
},
|
||||
onSearchClick = { showSearchScreen = true },
|
||||
onSearchClick = { pushScreen(Screen.Search) },
|
||||
onNewChat = {
|
||||
// TODO: Show new chat screen
|
||||
},
|
||||
onUserSelect = { selectedChatUser -> selectedUser = selectedChatUser },
|
||||
onUserSelect = { selectedChatUser ->
|
||||
pushScreen(Screen.ChatDetail(selectedChatUser))
|
||||
},
|
||||
backgroundBlurColorId = backgroundBlurColorId,
|
||||
pinnedChats = pinnedChats,
|
||||
onTogglePin = { opponentKey ->
|
||||
mainScreenScope.launch {
|
||||
prefsManager.togglePinChat(opponentKey)
|
||||
}
|
||||
mainScreenScope.launch { prefsManager.togglePinChat(opponentKey) }
|
||||
},
|
||||
avatarRepository = avatarRepository,
|
||||
onLogout = onLogout
|
||||
)
|
||||
)
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// Profile Screen — MUST be before sub-screens so it stays
|
||||
// visible beneath them during swipe-back animation
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
SwipeBackContainer(
|
||||
isVisible = showProfileScreen,
|
||||
onBack = { showProfileScreen = false },
|
||||
isDarkTheme = isDarkTheme
|
||||
isVisible = isProfileVisible,
|
||||
onBack = { popProfileAndChildren() },
|
||||
isDarkTheme = isDarkTheme
|
||||
) {
|
||||
// Экран профиля
|
||||
ProfileScreen(
|
||||
@@ -641,33 +713,19 @@ fun MainScreen(
|
||||
accountUsername = accountUsername,
|
||||
accountPublicKey = accountPublicKey,
|
||||
accountPrivateKeyHash = privateKeyHash,
|
||||
onBack = { showProfileScreen = false },
|
||||
onBack = { popProfileAndChildren() },
|
||||
onSaveProfile = { name, username ->
|
||||
accountName = name
|
||||
accountUsername = username
|
||||
mainScreenScope.launch {
|
||||
onAccountInfoUpdated()
|
||||
}
|
||||
mainScreenScope.launch { onAccountInfoUpdated() }
|
||||
},
|
||||
onLogout = onLogout,
|
||||
onNavigateToTheme = {
|
||||
showThemeScreen = true
|
||||
},
|
||||
onNavigateToAppearance = {
|
||||
showAppearanceScreen = true
|
||||
},
|
||||
onNavigateToSafety = {
|
||||
showSafetyScreen = true
|
||||
},
|
||||
onNavigateToLogs = {
|
||||
showLogsScreen = true
|
||||
},
|
||||
onNavigateToCrashLogs = {
|
||||
showCrashLogsScreen = true
|
||||
},
|
||||
onNavigateToBiometric = {
|
||||
showBiometricScreen = true
|
||||
},
|
||||
onNavigateToTheme = { pushScreen(Screen.Theme) },
|
||||
onNavigateToAppearance = { pushScreen(Screen.Appearance) },
|
||||
onNavigateToSafety = { pushScreen(Screen.Safety) },
|
||||
onNavigateToLogs = { pushScreen(Screen.Logs) },
|
||||
onNavigateToCrashLogs = { pushScreen(Screen.CrashLogs) },
|
||||
onNavigateToBiometric = { pushScreen(Screen.Biometric) },
|
||||
viewModel = profileViewModel,
|
||||
avatarRepository = avatarRepository,
|
||||
dialogDao = RosettaDatabase.getDatabase(context).dialogDao(),
|
||||
@@ -677,132 +735,109 @@ fun MainScreen(
|
||||
|
||||
// Other screens with swipe back
|
||||
SwipeBackContainer(
|
||||
isVisible = showBackupScreen,
|
||||
onBack = {
|
||||
showBackupScreen = false
|
||||
showSafetyScreen = true
|
||||
},
|
||||
isDarkTheme = isDarkTheme
|
||||
isVisible = isBackupVisible,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.Backup } + Screen.Safety },
|
||||
isDarkTheme = isDarkTheme
|
||||
) {
|
||||
BackupScreen(
|
||||
isDarkTheme = isDarkTheme,
|
||||
onBack = {
|
||||
showBackupScreen = false
|
||||
showSafetyScreen = true
|
||||
},
|
||||
onVerifyPassword = { password ->
|
||||
// Verify password by trying to decrypt the private key
|
||||
try {
|
||||
val publicKey = account?.publicKey ?: return@BackupScreen null
|
||||
val accountManager = AccountManager(context)
|
||||
val encryptedAccount = accountManager.getAccount(publicKey)
|
||||
isDarkTheme = isDarkTheme,
|
||||
onBack = {
|
||||
navStack = navStack.filterNot { it is Screen.Backup } + Screen.Safety
|
||||
},
|
||||
onVerifyPassword = { password ->
|
||||
// Verify password by trying to decrypt the private key
|
||||
try {
|
||||
val publicKey = account?.publicKey ?: return@BackupScreen null
|
||||
val accountManager = AccountManager(context)
|
||||
val encryptedAccount = accountManager.getAccount(publicKey)
|
||||
|
||||
if (encryptedAccount != null) {
|
||||
// Try to decrypt private key with password
|
||||
val decryptedPrivateKey = com.rosetta.messenger.crypto.CryptoManager.decryptWithPassword(
|
||||
encryptedAccount.encryptedPrivateKey,
|
||||
password
|
||||
)
|
||||
if (encryptedAccount != null) {
|
||||
// Try to decrypt private key with password
|
||||
val decryptedPrivateKey =
|
||||
com.rosetta.messenger.crypto.CryptoManager
|
||||
.decryptWithPassword(
|
||||
encryptedAccount.encryptedPrivateKey,
|
||||
password
|
||||
)
|
||||
|
||||
if (decryptedPrivateKey != null) {
|
||||
// Password is correct, decrypt seed phrase
|
||||
com.rosetta.messenger.crypto.CryptoManager.decryptWithPassword(
|
||||
encryptedAccount.encryptedSeedPhrase,
|
||||
password
|
||||
)
|
||||
if (decryptedPrivateKey != null) {
|
||||
// Password is correct, decrypt seed phrase
|
||||
com.rosetta.messenger.crypto.CryptoManager.decryptWithPassword(
|
||||
encryptedAccount.encryptedSeedPhrase,
|
||||
password
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} else {
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
SwipeBackContainer(
|
||||
isVisible = showSafetyScreen,
|
||||
onBack = {
|
||||
showSafetyScreen = false
|
||||
showProfileScreen = true
|
||||
},
|
||||
isDarkTheme = isDarkTheme
|
||||
isVisible = isSafetyVisible,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.Safety } },
|
||||
isDarkTheme = isDarkTheme
|
||||
) {
|
||||
SafetyScreen(
|
||||
isDarkTheme = isDarkTheme,
|
||||
accountPublicKey = accountPublicKey,
|
||||
accountPrivateKey = accountPrivateKey,
|
||||
onBack = {
|
||||
showSafetyScreen = false
|
||||
showProfileScreen = true
|
||||
},
|
||||
onBackupClick = {
|
||||
showSafetyScreen = false
|
||||
showBackupScreen = true
|
||||
},
|
||||
onDeleteAccount = {
|
||||
// TODO: Implement account deletion
|
||||
}
|
||||
isDarkTheme = isDarkTheme,
|
||||
accountPublicKey = accountPublicKey,
|
||||
accountPrivateKey = accountPrivateKey,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.Safety } },
|
||||
onBackupClick = {
|
||||
navStack = navStack.filterNot { it is Screen.Safety } + Screen.Backup
|
||||
},
|
||||
onDeleteAccount = {
|
||||
// TODO: Implement account deletion
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
SwipeBackContainer(
|
||||
isVisible = showThemeScreen,
|
||||
onBack = {
|
||||
showThemeScreen = false
|
||||
showProfileScreen = true
|
||||
},
|
||||
isDarkTheme = isDarkTheme
|
||||
isVisible = isThemeVisible,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.Theme } },
|
||||
isDarkTheme = isDarkTheme
|
||||
) {
|
||||
ThemeScreen(
|
||||
isDarkTheme = isDarkTheme,
|
||||
currentThemeMode = themeMode,
|
||||
onBack = {
|
||||
showThemeScreen = false
|
||||
showProfileScreen = true
|
||||
},
|
||||
onThemeModeChange = onThemeModeChange
|
||||
isDarkTheme = isDarkTheme,
|
||||
currentThemeMode = themeMode,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.Theme } },
|
||||
onThemeModeChange = onThemeModeChange
|
||||
)
|
||||
}
|
||||
|
||||
SwipeBackContainer(
|
||||
isVisible = showAppearanceScreen,
|
||||
onBack = {
|
||||
showAppearanceScreen = false
|
||||
showProfileScreen = true
|
||||
},
|
||||
isDarkTheme = isDarkTheme
|
||||
isVisible = isAppearanceVisible,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.Appearance } },
|
||||
isDarkTheme = isDarkTheme
|
||||
) {
|
||||
com.rosetta.messenger.ui.settings.AppearanceScreen(
|
||||
isDarkTheme = isDarkTheme,
|
||||
currentBlurColorId = backgroundBlurColorId,
|
||||
onBack = {
|
||||
showAppearanceScreen = false
|
||||
showProfileScreen = true
|
||||
},
|
||||
onBlurColorChange = { newId ->
|
||||
mainScreenScope.launch {
|
||||
prefsManager.setBackgroundBlurColorId(newId)
|
||||
}
|
||||
},
|
||||
onToggleTheme = onToggleTheme,
|
||||
accountPublicKey = accountPublicKey,
|
||||
accountName = accountName,
|
||||
avatarRepository = avatarRepository
|
||||
isDarkTheme = isDarkTheme,
|
||||
currentBlurColorId = backgroundBlurColorId,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.Appearance } },
|
||||
onBlurColorChange = { newId ->
|
||||
mainScreenScope.launch { prefsManager.setBackgroundBlurColorId(newId) }
|
||||
},
|
||||
onToggleTheme = onToggleTheme,
|
||||
accountPublicKey = accountPublicKey,
|
||||
accountName = accountName,
|
||||
avatarRepository = avatarRepository
|
||||
)
|
||||
}
|
||||
|
||||
SwipeBackContainer(
|
||||
isVisible = showUpdatesScreen,
|
||||
onBack = { showUpdatesScreen = false },
|
||||
isDarkTheme = isDarkTheme
|
||||
isVisible = isUpdatesVisible,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.Updates } },
|
||||
isDarkTheme = isDarkTheme
|
||||
) {
|
||||
UpdatesScreen(
|
||||
isDarkTheme = isDarkTheme,
|
||||
onBack = { showUpdatesScreen = false }
|
||||
isDarkTheme = isDarkTheme,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.Updates } }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -810,40 +845,40 @@ fun MainScreen(
|
||||
var isImageViewerOpen by remember { mutableStateOf(false) }
|
||||
|
||||
SwipeBackContainer(
|
||||
isVisible = selectedUser != null,
|
||||
onBack = { selectedUser = null },
|
||||
isDarkTheme = isDarkTheme,
|
||||
swipeEnabled = !isImageViewerOpen
|
||||
isVisible = selectedUser != null,
|
||||
onBack = { popChatAndChildren() },
|
||||
isDarkTheme = isDarkTheme,
|
||||
swipeEnabled = !isImageViewerOpen
|
||||
) {
|
||||
if (selectedUser != null) {
|
||||
selectedUser?.let { currentChatUser ->
|
||||
// Экран чата
|
||||
ChatDetailScreen(
|
||||
user = selectedUser!!,
|
||||
user = currentChatUser,
|
||||
currentUserPublicKey = accountPublicKey,
|
||||
currentUserPrivateKey = accountPrivateKey,
|
||||
onBack = { selectedUser = null },
|
||||
onBack = { popChatAndChildren() },
|
||||
onUserProfileClick = { user ->
|
||||
// Открываем профиль другого пользователя
|
||||
selectedOtherUser = user
|
||||
showOtherProfileScreen = true
|
||||
pushScreen(Screen.OtherProfile(user))
|
||||
},
|
||||
onNavigateToChat = { forwardUser ->
|
||||
// 📨 Forward: переход в выбранный чат с полными данными
|
||||
selectedUser = forwardUser
|
||||
navStack =
|
||||
navStack.filterNot {
|
||||
it is Screen.ChatDetail || it is Screen.OtherProfile
|
||||
} + Screen.ChatDetail(forwardUser)
|
||||
},
|
||||
isDarkTheme = isDarkTheme,
|
||||
avatarRepository = avatarRepository,
|
||||
onImageViewerChanged = { isOpen ->
|
||||
isImageViewerOpen = isOpen
|
||||
}
|
||||
onImageViewerChanged = { isOpen -> isImageViewerOpen = isOpen }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
SwipeBackContainer(
|
||||
isVisible = showSearchScreen,
|
||||
onBack = { showSearchScreen = false },
|
||||
isDarkTheme = isDarkTheme
|
||||
isVisible = isSearchVisible,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.Search } },
|
||||
isDarkTheme = isDarkTheme
|
||||
) {
|
||||
// Экран поиска
|
||||
SearchScreen(
|
||||
@@ -851,114 +886,95 @@ fun MainScreen(
|
||||
currentUserPublicKey = accountPublicKey,
|
||||
isDarkTheme = isDarkTheme,
|
||||
protocolState = protocolState,
|
||||
onBackClick = { showSearchScreen = false },
|
||||
onBackClick = { navStack = navStack.filterNot { it is Screen.Search } },
|
||||
onUserSelect = { selectedSearchUser ->
|
||||
showSearchScreen = false
|
||||
selectedUser = selectedSearchUser
|
||||
navStack =
|
||||
navStack.filterNot { it is Screen.Search } +
|
||||
Screen.ChatDetail(selectedSearchUser)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
SwipeBackContainer(
|
||||
isVisible = showLogsScreen,
|
||||
onBack = {
|
||||
showLogsScreen = false
|
||||
showProfileScreen = true
|
||||
},
|
||||
isDarkTheme = isDarkTheme
|
||||
isVisible = isLogsVisible,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.Logs } },
|
||||
isDarkTheme = isDarkTheme
|
||||
) {
|
||||
com.rosetta.messenger.ui.settings.ProfileLogsScreen(
|
||||
isDarkTheme = isDarkTheme,
|
||||
logs = profileState.logs,
|
||||
onBack = {
|
||||
showLogsScreen = false
|
||||
showProfileScreen = true
|
||||
},
|
||||
onClearLogs = {
|
||||
profileViewModel.clearLogs()
|
||||
}
|
||||
isDarkTheme = isDarkTheme,
|
||||
logs = profileState.logs,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.Logs } },
|
||||
onClearLogs = { profileViewModel.clearLogs() }
|
||||
)
|
||||
}
|
||||
|
||||
SwipeBackContainer(
|
||||
isVisible = showCrashLogsScreen,
|
||||
onBack = {
|
||||
showCrashLogsScreen = false
|
||||
showProfileScreen = true
|
||||
},
|
||||
isDarkTheme = isDarkTheme
|
||||
isVisible = isCrashLogsVisible,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.CrashLogs } },
|
||||
isDarkTheme = isDarkTheme
|
||||
) {
|
||||
CrashLogsScreen(
|
||||
onBackClick = {
|
||||
showCrashLogsScreen = false
|
||||
showProfileScreen = true
|
||||
}
|
||||
onBackClick = { navStack = navStack.filterNot { it is Screen.CrashLogs } }
|
||||
)
|
||||
}
|
||||
|
||||
SwipeBackContainer(
|
||||
isVisible = showOtherProfileScreen,
|
||||
onBack = {
|
||||
showOtherProfileScreen = false
|
||||
selectedOtherUser = null
|
||||
},
|
||||
isDarkTheme = isDarkTheme
|
||||
isVisible = selectedOtherUser != null,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.OtherProfile } },
|
||||
isDarkTheme = isDarkTheme
|
||||
) {
|
||||
if (selectedOtherUser != null) {
|
||||
selectedOtherUser?.let { currentOtherUser ->
|
||||
OtherProfileScreen(
|
||||
user = selectedOtherUser!!,
|
||||
isDarkTheme = isDarkTheme,
|
||||
onBack = {
|
||||
showOtherProfileScreen = false
|
||||
selectedOtherUser = null
|
||||
},
|
||||
avatarRepository = avatarRepository
|
||||
user = currentOtherUser,
|
||||
isDarkTheme = isDarkTheme,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.OtherProfile } },
|
||||
avatarRepository = avatarRepository
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Biometric Enable Screen
|
||||
SwipeBackContainer(
|
||||
isVisible = showBiometricScreen,
|
||||
onBack = {
|
||||
showBiometricScreen = false
|
||||
showProfileScreen = true
|
||||
},
|
||||
isDarkTheme = isDarkTheme
|
||||
isVisible = isBiometricVisible,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.Biometric } },
|
||||
isDarkTheme = isDarkTheme
|
||||
) {
|
||||
val biometricManager = remember { com.rosetta.messenger.biometric.BiometricAuthManager(context) }
|
||||
val biometricPrefs = remember { com.rosetta.messenger.biometric.BiometricPreferences(context) }
|
||||
val biometricManager = remember {
|
||||
com.rosetta.messenger.biometric.BiometricAuthManager(context)
|
||||
}
|
||||
val biometricPrefs = remember {
|
||||
com.rosetta.messenger.biometric.BiometricPreferences(context)
|
||||
}
|
||||
val activity = context as? FragmentActivity
|
||||
|
||||
BiometricEnableScreen(
|
||||
isDarkTheme = isDarkTheme,
|
||||
onBack = {
|
||||
showBiometricScreen = false
|
||||
showProfileScreen = true
|
||||
},
|
||||
onEnable = { password, onSuccess, onError ->
|
||||
if (activity == null) {
|
||||
onError("Activity not available")
|
||||
return@BiometricEnableScreen
|
||||
}
|
||||
|
||||
biometricManager.encryptPassword(
|
||||
activity = activity,
|
||||
password = password,
|
||||
onSuccess = { encryptedPassword ->
|
||||
mainScreenScope.launch {
|
||||
biometricPrefs.saveEncryptedPassword(accountPublicKey, encryptedPassword)
|
||||
biometricPrefs.enableBiometric()
|
||||
onSuccess()
|
||||
}
|
||||
},
|
||||
onError = { error -> onError(error) },
|
||||
onCancel = {
|
||||
showBiometricScreen = false
|
||||
showProfileScreen = true
|
||||
isDarkTheme = isDarkTheme,
|
||||
onBack = { navStack = navStack.filterNot { it is Screen.Biometric } },
|
||||
onEnable = { password, onSuccess, onError ->
|
||||
if (activity == null) {
|
||||
onError("Activity not available")
|
||||
return@BiometricEnableScreen
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
biometricManager.encryptPassword(
|
||||
activity = activity,
|
||||
password = password,
|
||||
onSuccess = { encryptedPassword ->
|
||||
mainScreenScope.launch {
|
||||
biometricPrefs.saveEncryptedPassword(
|
||||
accountPublicKey,
|
||||
encryptedPassword
|
||||
)
|
||||
biometricPrefs.enableBiometric()
|
||||
onSuccess()
|
||||
}
|
||||
},
|
||||
onError = { error -> onError(error) },
|
||||
onCancel = {
|
||||
navStack = navStack.filterNot { it is Screen.Biometric }
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user