diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 32f01dcf..83c76b90 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -12,7 +12,7 @@ androidGradlePlugin = "8.6.1" junit = "4.13.2" junitJupiterEngine = "5.11.0" junitJupiterApi = "5.11.0" -kotlin = "2.0.20" +kotlin = "2.1.0" lifecycleRuntimeKtx = "2.8.7" material = "1.7.5" kotlinxCoroutinesCore = "1.9.0" diff --git a/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/BackStackManager.kt b/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/BackStackManager.kt index da9d994d..441e4162 100644 --- a/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/BackStackManager.kt +++ b/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/BackStackManager.kt @@ -16,7 +16,6 @@ import moe.tlaster.precompose.navigation.route.isSceneRoute import kotlin.coroutines.Continuation import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine -import kotlin.math.max @Stable internal class BackStackManager : LifecycleEventObserver { @@ -137,27 +136,7 @@ internal class BackStackManager : LifecycleEventObserver { } else { currentBackStacks } - - val popUpTo = options.popUpTo - val index = when (popUpTo) { - PopUpTo.None -> -1 - PopUpTo.Prev -> backStack.lastIndex - 1 - is PopUpTo.Route -> if (popUpTo.route.isNotEmpty()) { - backStack.indexOfLast { it.hasRoute(popUpTo.route, path, options.includePath) } - } else { - 0 - } - } - if (index != -1) { - val stacksToDrop = backStack.subList( - if (popUpTo.inclusive) index else index + 1, - backStack.size, - ) - backStacks.value -= stacksToDrop - stacksToDrop.forEach { - it.destroy() - } - } + popWithOptions(options.popUpTo, backStack) } } @@ -175,6 +154,13 @@ internal class BackStackManager : LifecycleEventObserver { popUpTo: PopUpTo, ) { val currentBackStacks = backStacks.value + popWithOptions(popUpTo, currentBackStacks) + } + + private fun popWithOptions( + popUpTo: PopUpTo, + currentBackStacks: List, + ) { if (currentBackStacks.size <= 1) { return } @@ -182,18 +168,23 @@ internal class BackStackManager : LifecycleEventObserver { PopUpTo.None -> -1 PopUpTo.Prev -> currentBackStacks.lastIndex - 1 is PopUpTo.Route -> if (popUpTo.route.isNotEmpty()) { - currentBackStacks.indexOfLast { it.hasRoute(popUpTo.route, "", false) } + currentBackStacks.indexOfLast { it.hasRoute(popUpTo.route, "", false) }.let { + if (it == -1) { + // route not found + 0 + } else { + it + } + } } else { 0 } }.let { if (popUpTo.inclusive) it else it + 1 - }.let { - max(it, 0) } if (index != -1) { val stacksToDrop = currentBackStacks.subList( - index, + index.coerceAtLeast(1), // make sure not remove the initial route currentBackStacks.size, ) backStacks.value -= stacksToDrop diff --git a/precompose/src/jvmTest/kotlin/moe/tlaster/precompose/navigation/BackStackManagerTest.kt b/precompose/src/jvmTest/kotlin/moe/tlaster/precompose/navigation/BackStackManagerTest.kt index 1529ffe3..73fe20d9 100644 --- a/precompose/src/jvmTest/kotlin/moe/tlaster/precompose/navigation/BackStackManagerTest.kt +++ b/precompose/src/jvmTest/kotlin/moe/tlaster/precompose/navigation/BackStackManagerTest.kt @@ -712,4 +712,30 @@ class BackStackManagerTest { manager.push("screen3") assertNotEquals(stateId, manager.backStacks.value.last().stateId) } + + @Test + fun testPopUpToWithInvalidPath() = runMainTest { + val manager = BackStackManager() + manager.init( + lifecycleOwner = TestLifecycleOwner(), + viewModelStoreOwner = TestViewModelStoreOwner(), + ) + manager.setRouteGraph( + routeGraph = RouteGraph( + "screen1", + listOf( + TestRoute("screen1", "screen1"), + TestRoute("screen2", "screen2"), + TestRoute("screen3", "screen3"), + ), + ), + ) + manager.push("screen2") + manager.push("screen3") + manager.popWithOptions(PopUpTo("screen4", inclusive = true)) + assertEquals( + listOf("screen1"), + manager.backStacks.value.map { it.path }, + ) + } } diff --git a/settings.gradle.kts b/settings.gradle.kts index e3815c63..04e21434 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -27,7 +27,7 @@ include(":precompose-molecule") include(":sample:todo:android") include(":sample:todo:desktop") include(":sample:todo:common") -include(":sample:todo:ios") +// include(":sample:todo:ios") // include(":sample:todo:macos") include(":sample:todo:js") include(":sample:molecule:composeApp")