From ebdf72e2c170b06af9e8e1d8fa735ba29bedd628 Mon Sep 17 00:00:00 2001 From: Ole-Morten Duesund Date: Wed, 18 Mar 2026 17:02:16 +0100 Subject: [PATCH] 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) --- .../java/no/naiv/implausibly/MainActivity.kt | 5 +-- .../implausibly/ui/navigation/AppNavHost.kt | 32 ++++--------------- 2 files changed, 8 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/no/naiv/implausibly/MainActivity.kt b/app/src/main/java/no/naiv/implausibly/MainActivity.kt index ca8b701..089b853 100644 --- a/app/src/main/java/no/naiv/implausibly/MainActivity.kt +++ b/app/src/main/java/no/naiv/implausibly/MainActivity.kt @@ -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 ) } } diff --git a/app/src/main/java/no/naiv/implausibly/ui/navigation/AppNavHost.kt b/app/src/main/java/no/naiv/implausibly/ui/navigation/AppNavHost.kt index 8a6bf69..1c17558 100644 --- a/app/src/main/java/no/naiv/implausibly/ui/navigation/AppNavHost.kt +++ b/app/src/main/java/no/naiv/implausibly/ui/navigation/AppNavHost.kt @@ -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)