Repository: arkivanov/Decompose
Pull request #229 (2022-10-05 10:21:14)

add `replaceAll` extension function for `StackNavigator`
This adds an extension function to `StackNavigator` called `replaceAll`. It's main purpose is to serve as a convenient alternative to the `navigate` pair of functions. By taking in the Configurations as varargs some boilerplate is eliminated (if the user does not intend to build upon the current stack, that is)

Thanks @arkivanov for the constructive discussion :)


Patch:
diff --git a/decompose/api/android/decompose.api b/decompose/api/android/decompose.api
index 585ea94d..7bdc249e 100644
--- a/decompose/api/android/decompose.api
+++ b/decompose/api/android/decompose.api
@@ -177,6 +177,8 @@ public final class com/arkivanov/decompose/router/stack/StackNavigatorExtKt {
 	public static final fun popWhile (Lcom/arkivanov/decompose/router/stack/StackNavigator;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V
 	public static final fun push (Lcom/arkivanov/decompose/router/stack/StackNavigator;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)V
 	public static synthetic fun push$default (Lcom/arkivanov/decompose/router/stack/StackNavigator;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V
+	public static final fun replaceAll (Lcom/arkivanov/decompose/router/stack/StackNavigator;[Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)V
+	public static synthetic fun replaceAll$default (Lcom/arkivanov/decompose/router/stack/StackNavigator;[Ljava/lang/Object;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V
 	public static final fun replaceCurrent (Lcom/arkivanov/decompose/router/stack/StackNavigator;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)V
 	public static synthetic fun replaceCurrent$default (Lcom/arkivanov/decompose/router/stack/StackNavigator;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V
 }
diff --git a/decompose/api/jvm/decompose.api b/decompose/api/jvm/decompose.api
index 7647fc31..55cd07ca 100644
--- a/decompose/api/jvm/decompose.api
+++ b/decompose/api/jvm/decompose.api
@@ -164,6 +164,8 @@ public final class com/arkivanov/decompose/router/stack/StackNavigatorExtKt {
 	public static final fun popWhile (Lcom/arkivanov/decompose/router/stack/StackNavigator;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V
 	public static final fun push (Lcom/arkivanov/decompose/router/stack/StackNavigator;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)V
 	public static synthetic fun push$default (Lcom/arkivanov/decompose/router/stack/StackNavigator;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V
+	public static final fun replaceAll (Lcom/arkivanov/decompose/router/stack/StackNavigator;[Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)V
+	public static synthetic fun replaceAll$default (Lcom/arkivanov/decompose/router/stack/StackNavigator;[Ljava/lang/Object;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V
 	public static final fun replaceCurrent (Lcom/arkivanov/decompose/router/stack/StackNavigator;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)V
 	public static synthetic fun replaceCurrent$default (Lcom/arkivanov/decompose/router/stack/StackNavigator;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V
 }
diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigatorExt.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigatorExt.kt
index 4602ca42..06ec9ddc 100644
--- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigatorExt.kt
+++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/stack/StackNavigatorExt.kt
@@ -65,6 +65,15 @@ fun <C : Any> StackNavigator<C>.replaceCurrent(configuration: C, onComplete: ()
     )
 }
 
+/**
+ * Replaces the whole stack with the provided [configurations].
+ *
+ * @param onComplete called when the navigation is finished (either synchronously or asynchronously).
+ */
+fun <C : Any> StackNavigator<C>.replaceAll(vararg configurations: C, onComplete: () -> Unit = { }) {
+    navigate(transformer = { configurations.toList() }, onComplete = { _, _ -> onComplete() })
+}
+
 /**
  * Removes all components with configurations of [configuration]'s class, and adds the provided [configuration] to the top of the stack.
  * The operation is performed as one transaction. If there is already a component with the same configuration, it will not be recreated.
diff --git a/docs/navigation/stack/navigation.md b/docs/navigation/stack/navigation.md
index 33f80484..f919be43 100644
--- a/docs/navigation/stack/navigation.md
+++ b/docs/navigation/stack/navigation.md
@@ -41,7 +41,7 @@ sealed class Configuration {
 val navigation = StackNavigation<Configuration>()
 ```
 
-### push
+### Push
 
 Pushes the provided `Configuration` at the top of the stack.
 
@@ -50,7 +50,7 @@ navigation.push(Configuration.B)
 navigation.push(Configuration.C)
 ```
 
-![](../../media/RouterPush.png)
+![](../media/RouterPush.png)
 
 ### pop
 
@@ -69,7 +69,7 @@ navigation.pop { isSuccess ->
 }
 ```
 
-![](../../media/RouterPop.png)
+![](../media/RouterPop.png)
 
 ### popWhile
 
@@ -79,7 +79,7 @@ Drops the configurations at the top of the stack while the provided predicate re
 navigation.popWhile { topOfStack: Configuration -> topOfStack !is B }
 ```
 
-![](Decompose/media/RouterPopWhile.png)
+![](../media/RouterPopWhile.png)
 
 ### replaceCurrent
 
@@ -89,7 +89,7 @@ Replaces the current configuration at the top of the stack with the provided `Co
 navigation.replaceCurrent(Configuration.D)
 ```
 
-![](../../media/RouterReplaceCurrent.png)
+![](../media/RouterReplaceCurrent.png)
 
 ### bringToFront
 
@@ -102,4 +102,4 @@ Removes all components with configurations of the provided `Configuration`'s cla
 navigation.bringToFront(Configuration.B)
 ```
 
-![](../../media/RouterBringToFront.png)
+![](../media/RouterBringToFront.png)


Test patch:
diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/RouterReplaceAllTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/RouterReplaceAllTest.kt
new file mode 100644
index 00000000..fb89ff58
--- /dev/null
+++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/stack/RouterReplaceAllTest.kt
@@ -0,0 +1,28 @@
+package com.arkivanov.decompose.router.stack
+
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+
+@Suppress("TestFunctionName")
+class RouterReplaceAllTest {
+
+    @Test
+    fun WHEN_replaceAll_THEN_configurations_correctly_applied() {
+        val navigator = TestStackNavigator(listOf(1, 2, 3))
+
+        navigator.replaceAll(3, 4, 5)
+
+        assertEquals(listOf(3, 4, 5), navigator.configurations)
+    }
+
+    @Test
+    fun WHEN_replace_all_THEN_onComplete_called() {
+        val navigator = TestStackNavigator(listOf(1, 2, 3))
+        var isCalled = false
+
+        navigator.replaceAll(4) { isCalled = true }
+
+        assertTrue(isCalled)
+    }
+}