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

View file

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