fix(android settings): remove open delay — seed theme instead of a loading gate

The previous flicker fix gated the whole settings form behind a 'loaded' spinner,
which read as a small lag when opening Settings (it waited for DataStore's first
emit even though it's already warm). Instead, expose the applied theme id
(NsColors.themeId, set in applyNsTheme) and seed the ViewModel's initial state with
it, so the theme picker no longer jumps from the default — no gate, no spinner, no
delay. Remaining text fields fill within a frame from the warm DataStore.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
HoneyandClaude Opus 4.8 committed 2026-08-12 00:40:38 +02:00
1 parent 711a084686
commit d195518dfb
3 files changed
+5 -12

No files matched your search

@@ -67,15 +67,6 @@ fun SettingsScreen(onOpenStats: () -> Unit = {}) {
) )
} }
// Wait for the persisted settings before drawing the form — otherwise the default state
// (e.g. theme = Dark Ocean) flashes for a frame and then jumps to the real values.
if (!state.loaded) {
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
CircularProgressIndicator(color = NsAccent)
}
return
}
Column( Column(
verticalArrangement = Arrangement.spacedBy(12.dp), verticalArrangement = Arrangement.spacedBy(12.dp),
modifier = Modifier modifier = Modifier
@@ -7,6 +7,7 @@ import androidx.compose.ui.graphics.Color
// ── Dynamic theme state — Compose tracks reads automatically ───────────────── // ── Dynamic theme state — Compose tracks reads automatically ─────────────────
object NsColors { object NsColors {
var themeId by mutableStateOf("dark-ocean") // currently applied theme id (readable)
var accent by mutableStateOf(Color(0xFF4F8EF7)) var accent by mutableStateOf(Color(0xFF4F8EF7))
var accentDim by mutableStateOf(Color(0xFF2A6CD6)) var accentDim by mutableStateOf(Color(0xFF2A6CD6))
var bg by mutableStateOf(Color(0xFF070D14)) var bg by mutableStateOf(Color(0xFF070D14))
@@ -88,6 +89,7 @@ val NS_THEMES = listOf(
fun applyNsTheme(themeId: String) { fun applyNsTheme(themeId: String) {
val t = NS_THEMES.find { it.id == themeId } ?: NS_THEMES.first() val t = NS_THEMES.find { it.id == themeId } ?: NS_THEMES.first()
NsColors.themeId = t.id
NsColors.accent = t.accent NsColors.accent = t.accent
NsColors.accentDim = t.accentDim NsColors.accentDim = t.accentDim
NsColors.bg = t.bg NsColors.bg = t.bg
@@ -6,6 +6,7 @@ import androidx.lifecycle.viewModelScope
import com.novasonic.app.api.AbsClient import com.novasonic.app.api.AbsClient
import com.novasonic.app.app import com.novasonic.app.app
import com.novasonic.app.data.PrefsStore import com.novasonic.app.data.PrefsStore
import com.novasonic.app.ui.theme.NsColors
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import io.ktor.client.engine.android.Android import io.ktor.client.engine.android.Android
import io.ktor.client.plugins.HttpTimeout import io.ktor.client.plugins.HttpTimeout
@@ -35,7 +36,6 @@ data class SettingsUiState(
val absUrl: String = "", val absUrl: String = "",
val absApiKey: String = "", val absApiKey: String = "",
val absChapterTrackMode: Boolean = false, val absChapterTrackMode: Boolean = false,
val loaded: Boolean = false,
val isSaving: Boolean = false, val isSaving: Boolean = false,
val savedOk: Boolean = false, val savedOk: Boolean = false,
val error: String? = null, val error: String? = null,
@@ -48,7 +48,8 @@ class SettingsViewModel(app: Application) : AndroidViewModel(app) {
private val repo = app.app.repository private val repo = app.app.repository
private val _state = MutableStateFlow(SettingsUiState()) // Seed the theme with the already-applied one so the picker doesn't jump before DataStore emits.
private val _state = MutableStateFlow(SettingsUiState(theme = NsColors.themeId))
val state: StateFlow<SettingsUiState> = _state val state: StateFlow<SettingsUiState> = _state
init { init {
@@ -67,7 +68,6 @@ class SettingsViewModel(app: Application) : AndroidViewModel(app) {
absUrl = s.absUrl, absUrl = s.absUrl,
absApiKey = s.absApiKey, absApiKey = s.absApiKey,
absChapterTrackMode = s.absChapterTrackMode, absChapterTrackMode = s.absChapterTrackMode,
loaded = true,
) )
} }
} }