fix: smart navigation waits for DataStore before routing

Use flow.first() instead of collectAsState(initial = null) to
determine start destination. The previous approach fired immediately
with the initial null value before DataStore loaded from disk, always
routing to Setup. Now the loading screen suspends until preferences
are actually read.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ole-Morten Duesund 2026-03-18 17:02:16 +01:00
commit ebdf72e2c1
2 changed files with 8 additions and 29 deletions

View file

@ -8,7 +8,6 @@ import androidx.activity.enableEdgeToEdge
import dagger.hilt.android.AndroidEntryPoint
import no.naiv.implausibly.data.AppPreferences
import no.naiv.implausibly.data.repository.InstanceRepository
import no.naiv.implausibly.data.repository.SiteRepository
import no.naiv.implausibly.ui.navigation.AppNavHost
import no.naiv.implausibly.ui.theme.ImplausiblyTheme
import javax.inject.Inject
@ -18,7 +17,6 @@ class MainActivity : ComponentActivity() {
@Inject lateinit var appPreferences: AppPreferences
@Inject lateinit var instanceRepository: InstanceRepository
@Inject lateinit var siteRepository: SiteRepository
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -27,8 +25,7 @@ class MainActivity : ComponentActivity() {
ImplausiblyTheme {
AppNavHost(
appPreferences = appPreferences,
instanceRepository = instanceRepository,
siteRepository = siteRepository
instanceRepository = instanceRepository
)
}
}

View file

@ -3,19 +3,14 @@ package no.naiv.implausibly.ui.navigation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import kotlinx.coroutines.flow.first
import no.naiv.implausibly.data.AppPreferences
import no.naiv.implausibly.data.repository.InstanceRepository
import no.naiv.implausibly.data.repository.SiteRepository
import no.naiv.implausibly.ui.common.LoadingIndicator
import no.naiv.implausibly.ui.dashboard.DashboardScreen
import no.naiv.implausibly.ui.setup.SetupScreen
@ -25,42 +20,29 @@ import no.naiv.implausibly.ui.sites.SiteListScreen
* App-level navigation host.
*
* On launch, checks DataStore for a previously selected instance/site.
* If found, navigates directly to the dashboard (or site list if no site
* is selected). Otherwise, shows the setup screen.
* If found, navigates directly to the dashboard. Otherwise, shows setup.
*/
@Composable
fun AppNavHost(
appPreferences: AppPreferences,
instanceRepository: InstanceRepository,
siteRepository: SiteRepository
instanceRepository: InstanceRepository
) {
val navController = rememberNavController()
// Collect saved preferences to determine start destination
val savedInstanceId by appPreferences.selectedInstanceId.collectAsState(initial = null)
val savedSiteId by appPreferences.selectedSiteId.collectAsState(initial = null)
var hasNavigated by remember { mutableStateOf(false) }
NavHost(
navController = navController,
startDestination = Routes.LOADING
) {
// Loading screen — resolves start destination from preferences
composable(Routes.LOADING) {
LoadingIndicator()
LaunchedEffect(savedInstanceId, savedSiteId) {
// Wait for preferences to load (initial null vs actual null)
// Once we have a value (even if null), navigate
if (hasNavigated) return@LaunchedEffect
hasNavigated = true
val instanceId = savedInstanceId
val siteId = savedSiteId
// Use flow.first() to suspend until DataStore actually loads
LaunchedEffect(Unit) {
val instanceId = appPreferences.selectedInstanceId.first()
val siteId = appPreferences.selectedSiteId.first()
val destination = when {
instanceId != null && siteId != null -> {
// Verify the instance still exists
val instance = instanceRepository.getById(instanceId)
if (instance != null) {
Routes.dashboard(instanceId, siteId)