fix: clone navigation uses single setup route with optional params

Previous approach had two separate composable entries for setup and
setup-with-clone-params, which Jetpack Navigation couldn't distinguish.
Now uses one route with nullable query param arguments and defaultValue
null, so navigating to "setup" opens fresh and "setup?cloneInstanceId=
...&cloneSiteId=..." opens pre-filled.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ole-Morten Duesund 2026-03-18 17:23:56 +01:00
commit 3e0ad6c350
2 changed files with 20 additions and 31 deletions

View file

@ -16,12 +16,6 @@ import no.naiv.implausibly.ui.dashboard.DashboardScreen
import no.naiv.implausibly.ui.setup.SetupScreen import no.naiv.implausibly.ui.setup.SetupScreen
import no.naiv.implausibly.ui.sites.SiteListScreen 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. Otherwise, shows setup.
*/
@Composable @Composable
fun AppNavHost( fun AppNavHost(
appPreferences: AppPreferences, appPreferences: AppPreferences,
@ -36,7 +30,6 @@ fun AppNavHost(
composable(Routes.LOADING) { composable(Routes.LOADING) {
LoadingIndicator() LoadingIndicator()
// Use flow.first() to suspend until DataStore actually loads
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
val instanceId = appPreferences.selectedInstanceId.first() val instanceId = appPreferences.selectedInstanceId.first()
val siteId = appPreferences.selectedSiteId.first() val siteId = appPreferences.selectedSiteId.first()
@ -47,11 +40,11 @@ fun AppNavHost(
if (instance != null) { if (instance != null) {
Routes.dashboard(instanceId, siteId) Routes.dashboard(instanceId, siteId)
} else { } else {
Routes.SETUP Routes.setup()
} }
} }
instanceId != null -> Routes.siteList(instanceId) instanceId != null -> Routes.siteList(instanceId)
else -> Routes.SETUP else -> Routes.setup()
} }
navController.navigate(destination) { navController.navigate(destination) {
@ -60,27 +53,26 @@ fun AppNavHost(
} }
} }
composable(Routes.SETUP) { // Single setup route with optional clone params
SetupScreen(
onInstanceAdded = { instanceId ->
navController.navigate(Routes.siteList(instanceId)) {
popUpTo(Routes.SETUP) { inclusive = true }
}
}
)
}
composable( composable(
route = Routes.SETUP_CLONE, route = Routes.SETUP,
arguments = listOf( arguments = listOf(
navArgument("cloneInstanceId") { type = NavType.StringType }, navArgument("cloneInstanceId") {
navArgument("cloneSiteId") { type = NavType.StringType } type = NavType.StringType
nullable = true
defaultValue = null
},
navArgument("cloneSiteId") {
type = NavType.StringType
nullable = true
defaultValue = null
}
) )
) { ) {
SetupScreen( SetupScreen(
onInstanceAdded = { instanceId -> onInstanceAdded = { instanceId ->
navController.navigate(Routes.siteList(instanceId)) { navController.navigate(Routes.siteList(instanceId)) {
popUpTo(0) { inclusive = true } popUpTo(Routes.SETUP) { inclusive = true }
} }
} }
) )
@ -113,7 +105,7 @@ fun AppNavHost(
DashboardScreen( DashboardScreen(
onBack = { navController.popBackStack() }, onBack = { navController.popBackStack() },
onNavigateToSetup = { onNavigateToSetup = {
navController.navigate(Routes.SETUP) { navController.navigate(Routes.setup()) {
popUpTo(0) { inclusive = true } popUpTo(0) { inclusive = true }
} }
}, },

View file

@ -1,18 +1,15 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
package no.naiv.implausibly.ui.navigation package no.naiv.implausibly.ui.navigation
/**
* Navigation route constants.
* Arguments are passed as path parameters (strings only no complex objects).
*/
object Routes { object Routes {
const val LOADING = "loading" const val LOADING = "loading"
const val SETUP = "setup" const val SETUP = "setup?cloneInstanceId={cloneInstanceId}&cloneSiteId={cloneSiteId}"
const val SETUP_CLONE = "setup?cloneInstanceId={cloneInstanceId}&cloneSiteId={cloneSiteId}"
const val SITE_LIST = "site_list/{instanceId}" const val SITE_LIST = "site_list/{instanceId}"
const val DASHBOARD = "dashboard/{instanceId}/{siteId}" const val DASHBOARD = "dashboard/{instanceId}/{siteId}"
fun setup() = "setup"
fun setupClone(instanceId: String, siteId: String) =
"setup?cloneInstanceId=$instanceId&cloneSiteId=$siteId"
fun siteList(instanceId: String) = "site_list/$instanceId" fun siteList(instanceId: String) = "site_list/$instanceId"
fun dashboard(instanceId: String, siteId: String) = "dashboard/$instanceId/$siteId" fun dashboard(instanceId: String, siteId: String) = "dashboard/$instanceId/$siteId"
fun setupClone(instanceId: String, siteId: String) = "setup?cloneInstanceId=$instanceId&cloneSiteId=$siteId"
} }