feat(desktop): branded loading screen instead of login flash on startup

- Add AccountState.Loading as initial state (was LoggedOut)
- Show centered "Amethyst" + spinner while accounts load from storage
- After loadSavedAccount(): transition to LoggedIn or LoggedOut
- No more 0.5s flash of login screen when account exists

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
nrobi144
2026-05-29 07:02:52 +03:00
co-authored by Claude Opus 4.6
parent da0aaaa281
commit 0e431d674c
3 changed files with 38 additions and 6 deletions
@@ -32,6 +32,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState
@@ -864,6 +865,7 @@ fun App(
}
is AccountState.ConnectingRelays -> {}
is AccountState.Loading -> {}
}
}
@@ -924,8 +926,10 @@ fun App(
if (current != null) {
accountManager.loadNwcConnection(current.npub)
}
} else {
// No saved account found → show login screen
accountManager.setLoggedOut()
}
// If failure: state remains LoggedOut → login screen shows automatically
}
onDispose {
@@ -956,6 +960,28 @@ fun App(
com.vitorpamplona.amethyst.desktop.ui.deck.LocalLocalRelayStore provides localRelayStore,
) {
when (accountState) {
is AccountState.Loading -> {
// Branded loading screen while accounts load from storage
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center,
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
androidx.compose.material3.CircularProgressIndicator(
modifier = Modifier.size(32.dp),
color = MaterialTheme.colorScheme.primary,
strokeWidth = 3.dp,
)
Spacer(Modifier.height(16.dp))
Text(
"Amethyst",
style = MaterialTheme.typography.headlineMedium,
color = MaterialTheme.colorScheme.onBackground,
)
}
}
}
is AccountState.LoggedOut -> {
LoginScreen(
accountManager = accountManager,
@@ -69,6 +69,8 @@ import kotlinx.coroutines.withTimeout
import java.io.File
sealed class AccountState {
data object Loading : AccountState()
data object LoggedOut : AccountState()
data object ConnectingRelays : AccountState()
@@ -123,7 +125,7 @@ class AccountManager internal constructor(
private val _allAccounts = MutableStateFlow<ImmutableList<AccountInfo>>(persistentListOf())
val allAccounts: StateFlow<ImmutableList<AccountInfo>> = _allAccounts.asStateFlow()
private val _accountState = MutableStateFlow<AccountState>(AccountState.LoggedOut)
private val _accountState = MutableStateFlow<AccountState>(AccountState.Loading)
val accountState: StateFlow<AccountState> = _accountState.asStateFlow()
private val _nwcConnection = MutableStateFlow<Nip47WalletConnect.Nip47URINorm?>(null)
@@ -463,6 +465,10 @@ class AccountManager internal constructor(
accountStorage.setCurrentAccount(npub)
}
fun setLoggedOut() {
_accountState.value = AccountState.LoggedOut
}
fun setConnectingRelays() {
_accountState.value = AccountState.ConnectingRelays
}
@@ -94,12 +94,12 @@ class AccountManagerStateTransitionTest {
advanceUntilIdle()
// Should see: LoggedOut → ConnectingRelays → LoggedIn
// Should see: Loading → ConnectingRelays → LoggedIn
assertTrue(
states.size >= 3,
"Expected at least 3 state transitions, got ${states.size}: $states",
)
assertIs<AccountState.LoggedOut>(states[0])
assertIs<AccountState.Loading>(states[0])
assertIs<AccountState.ConnectingRelays>(states[1])
assertIs<AccountState.LoggedIn>(states[2])
@@ -125,12 +125,12 @@ class AccountManagerStateTransitionTest {
advanceUntilIdle()
// Should see: LoggedOut → ConnectingRelays → LoggedOut
// Should see: Loading → ConnectingRelays → LoggedOut
assertTrue(
states.size >= 3,
"Expected at least 3 state transitions, got ${states.size}: $states",
)
assertIs<AccountState.LoggedOut>(states[0])
assertIs<AccountState.Loading>(states[0])
assertIs<AccountState.ConnectingRelays>(states[1])
assertIs<AccountState.LoggedOut>(states[2])