Refactor code structure for improved readability and maintainability

This commit is contained in:
k1ngsterr1
2026-02-08 06:18:20 +05:00
parent 0d0e1e2c22
commit 162747ea35
3 changed files with 2640 additions and 2309 deletions

View File

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