250 lines
11 KiB
Kotlin
250 lines
11 KiB
Kotlin
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.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.ui.auth.AccountInfo
|
|
import com.rosetta.messenger.ui.auth.AuthFlow
|
|
import com.rosetta.messenger.ui.chats.ChatsListScreen
|
|
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)
|
|
|
|
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<Boolean?>(null) }
|
|
var currentAccount by remember { mutableStateOf<DecryptedAccount?>(null) }
|
|
var accountInfoList by remember { mutableStateOf<List<AccountInfo>>(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 = {
|
|
scope.launch {
|
|
com.rosetta.messenger.network.ProtocolManager.disconnect()
|
|
accountManager.logout()
|
|
currentAccount = null
|
|
}
|
|
}
|
|
)
|
|
}
|
|
"main" -> {
|
|
MainScreen(
|
|
account = currentAccount,
|
|
isDarkTheme = isDarkTheme,
|
|
onToggleTheme = {
|
|
scope.launch {
|
|
preferencesManager.setDarkTheme(!isDarkTheme)
|
|
}
|
|
},
|
|
onLogout = {
|
|
scope.launch {
|
|
com.rosetta.messenger.network.ProtocolManager.disconnect()
|
|
accountManager.logout()
|
|
currentAccount = null
|
|
}
|
|
}
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@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"
|
|
|
|
ChatsListScreen(
|
|
isDarkTheme = isDarkTheme,
|
|
accountName = accountName,
|
|
accountPhone = accountPhone,
|
|
accountPublicKey = accountPublicKey,
|
|
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 = {
|
|
// TODO: Show search
|
|
},
|
|
onNewChat = {
|
|
// TODO: Show new chat screen
|
|
},
|
|
onLogout = onLogout
|
|
)
|
|
}
|