package com.rosetta.messenger import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.compose.animation.* import androidx.compose.animation.core.FastOutSlowInEasing import androidx.compose.animation.core.tween import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.rosetta.messenger.data.AccountManager import com.rosetta.messenger.data.DecryptedAccount import com.rosetta.messenger.data.PreferencesManager import com.rosetta.messenger.data.RecentSearchesManager import com.rosetta.messenger.network.ProtocolManager import com.rosetta.messenger.ui.auth.AccountInfo import com.rosetta.messenger.ui.auth.AuthFlow import com.rosetta.messenger.ui.chats.ChatsListScreen import com.rosetta.messenger.ui.chats.ChatDetailScreen import com.rosetta.messenger.ui.chats.SearchScreen import com.rosetta.messenger.network.SearchUser import com.rosetta.messenger.ui.onboarding.OnboardingScreen import com.rosetta.messenger.ui.splash.SplashScreen import com.rosetta.messenger.ui.theme.RosettaAndroidTheme import kotlinx.coroutines.launch class MainActivity : ComponentActivity() { private lateinit var preferencesManager: PreferencesManager private lateinit var accountManager: AccountManager override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() preferencesManager = PreferencesManager(this) accountManager = AccountManager(this) RecentSearchesManager.init(this) setContent { val scope = rememberCoroutineScope() val isDarkTheme by preferencesManager.isDarkTheme.collectAsState(initial = true) val isLoggedIn by accountManager.isLoggedIn.collectAsState(initial = null) var showSplash by remember { mutableStateOf(true) } var showOnboarding by remember { mutableStateOf(true) } var hasExistingAccount by remember { mutableStateOf(null) } var currentAccount by remember { mutableStateOf(null) } var accountInfoList by remember { mutableStateOf>(emptyList()) } // Check for existing accounts and build AccountInfo list // Also force logout so user always sees unlock screen on app restart LaunchedEffect(Unit) { accountManager.logout() // Always start logged out val accounts = accountManager.getAllAccounts() hasExistingAccount = accounts.isNotEmpty() accountInfoList = accounts.map { account -> val shortKey = account.publicKey.take(7) val displayName = account.name ?: shortKey val initials = displayName.trim().split(Regex("\\s+")) .filter { it.isNotEmpty() } .let { words -> when { words.isEmpty() -> "??" words.size == 1 -> words[0].take(2).uppercase() else -> "${words[0].first()}${words[1].first()}".uppercase() } } AccountInfo( id = account.publicKey, name = displayName, initials = initials, publicKey = account.publicKey ) } } // Wait for initial load if (hasExistingAccount == null) { Box( modifier = Modifier .fillMaxSize() .background(if (isDarkTheme) Color(0xFF1B1B1B) else Color.White) ) return@setContent } RosettaAndroidTheme( darkTheme = isDarkTheme, animated = true ) { Surface( modifier = Modifier.fillMaxSize(), color = if (isDarkTheme) Color(0xFF1B1B1B) else Color.White ) { AnimatedContent( targetState = when { showSplash -> "splash" showOnboarding && hasExistingAccount == false -> "onboarding" isLoggedIn != true && hasExistingAccount == false -> "auth_new" isLoggedIn != true && hasExistingAccount == true -> "auth_unlock" else -> "main" }, transitionSpec = { fadeIn(animationSpec = tween(600)) togetherWith fadeOut(animationSpec = tween(600)) }, label = "screenTransition" ) { screen -> when (screen) { "splash" -> { SplashScreen( isDarkTheme = isDarkTheme, onSplashComplete = { showSplash = false } ) } "onboarding" -> { OnboardingScreen( isDarkTheme = isDarkTheme, onThemeToggle = { scope.launch { preferencesManager.setDarkTheme(!isDarkTheme) } }, onStartMessaging = { showOnboarding = false } ) } "auth_new", "auth_new", "auth_unlock" -> { AuthFlow( isDarkTheme = isDarkTheme, hasExistingAccount = screen == "auth_unlock", accounts = accountInfoList, onAuthComplete = { account -> currentAccount = account hasExistingAccount = true // Reload accounts list scope.launch { val accounts = accountManager.getAllAccounts() accountInfoList = accounts.map { acc -> val shortKey = acc.publicKey.take(7) val displayName = acc.name ?: shortKey val initials = displayName.trim().split(Regex("\\s+")) .filter { it.isNotEmpty() } .let { words -> when { words.isEmpty() -> "??" words.size == 1 -> words[0].take(2).uppercase() else -> "${words[0].first()}${words[1].first()}".uppercase() } } AccountInfo( id = acc.publicKey, name = displayName, initials = initials, publicKey = acc.publicKey ) } } }, onLogout = { // Set currentAccount to null immediately to prevent UI lag currentAccount = null scope.launch { com.rosetta.messenger.network.ProtocolManager.disconnect() accountManager.logout() } } ) } "main" -> { MainScreen( account = currentAccount, isDarkTheme = isDarkTheme, onToggleTheme = { scope.launch { preferencesManager.setDarkTheme(!isDarkTheme) } }, onLogout = { // Set currentAccount to null immediately to prevent UI lag currentAccount = null scope.launch { com.rosetta.messenger.network.ProtocolManager.disconnect() accountManager.logout() } } ) } } } } } } } } @Composable fun MainScreen( account: DecryptedAccount? = null, isDarkTheme: Boolean = true, onToggleTheme: () -> Unit = {}, onLogout: () -> Unit = {} ) { val accountName = account?.publicKey ?: "04c266b98ae5" val accountPhone = account?.publicKey?.take(16)?.let { "+${it.take(1)} ${it.substring(1, 4)} ${it.substring(4, 7)}${it.substring(7)}" } ?: "+7 775 9932587" val accountPublicKey = account?.publicKey ?: "04c266b98ae5" val accountPrivateKey = account?.privateKey ?: "" val privateKeyHash = account?.privateKeyHash ?: "" // Состояние протокола для передачи в SearchScreen val protocolState by ProtocolManager.state.collectAsState() // Навигация между экранами var selectedUser by remember { mutableStateOf(null) } var showSearchScreen by remember { mutableStateOf(false) } // Анимированный переход между экранами AnimatedContent( targetState = Triple(selectedUser, showSearchScreen, Unit), transitionSpec = { // Только плавный fade без смещения - чтобы header не прыгал fadeIn( animationSpec = tween( durationMillis = 200, easing = FastOutSlowInEasing ) ) togetherWith fadeOut( animationSpec = tween( durationMillis = 150, easing = FastOutSlowInEasing ) ) using SizeTransform(clip = false) }, label = "screenNavigation" ) { (user, isSearchOpen, _) -> when { user != null -> { // Экран чата ChatDetailScreen( user = user, currentUserPublicKey = accountPublicKey, currentUserPrivateKey = accountPrivateKey, isDarkTheme = isDarkTheme, onBack = { selectedUser = null } ) } isSearchOpen -> { // Экран поиска SearchScreen( privateKeyHash = privateKeyHash, currentUserPublicKey = accountPublicKey, isDarkTheme = isDarkTheme, protocolState = protocolState, onBackClick = { showSearchScreen = false }, onUserSelect = { user -> showSearchScreen = false selectedUser = user } ) } else -> { // Список чатов ChatsListScreen( isDarkTheme = isDarkTheme, accountName = accountName, accountPhone = accountPhone, accountPublicKey = accountPublicKey, privateKeyHash = privateKeyHash, onToggleTheme = onToggleTheme, onProfileClick = { // TODO: Navigate to profile }, onNewGroupClick = { // TODO: Navigate to new group }, onContactsClick = { // TODO: Navigate to contacts }, onCallsClick = { // TODO: Navigate to calls }, onSavedMessagesClick = { // TODO: Navigate to saved messages }, onSettingsClick = { // TODO: Navigate to settings }, onInviteFriendsClick = { // TODO: Share invite link }, onSearchClick = { showSearchScreen = true }, onNewChat = { // TODO: Show new chat screen }, onUserSelect = { user -> selectedUser = user }, onLogout = onLogout ) } } } }