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