From 71d6b91f0da79e55f4a5478c8b9f3748b22f3797 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov Date: Tue, 10 Aug 2021 01:09:22 +0100 Subject: [PATCH] Added Navigator.bringToFront extension function --- .../com/arkivanov/decompose/NavigatorExt.kt | 10 +++ .../router/NavigatorBringToFrontTest.kt | 87 +++++++++++++++++++ .../decompose/router/TestNavigator.kt | 12 +++ 3 files changed, 109 insertions(+) create mode 100644 decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/NavigatorBringToFrontTest.kt create mode 100644 decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/TestNavigator.kt diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/NavigatorExt.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/NavigatorExt.kt index d19845ad..f6d41d12 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/NavigatorExt.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/NavigatorExt.kt @@ -15,3 +15,13 @@ inline fun Navigator.popWhile(crossinline predicate: (C) -> Boolean fun Navigator.replaceCurrent(configuration: C) { navigate { it.dropLast(1) + configuration } } + +/** + * 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. + */ +fun Navigator.bringToFront(configuration: C) { + navigate { stack -> + stack.filterNot { it::class == configuration::class } + configuration + } +} diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/NavigatorBringToFrontTest.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/NavigatorBringToFrontTest.kt new file mode 100644 index 00000000..090b3468 --- /dev/null +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/NavigatorBringToFrontTest.kt @@ -0,0 +1,87 @@ +package com.arkivanov.decompose.router + +import com.arkivanov.decompose.bringToFront +import kotlin.test.Test +import kotlin.test.assertEquals + +@Suppress("TestFunctionName") +class NavigatorBringToFrontTest { + + @Test + fun WHEN_does_not_contain_THEN_new_configuration_pushed() { + val navigator = TestNavigator(listOf(Config.A, Config.B)) + + navigator.bringToFront(Config.C(0)) + + assertEquals(listOf(Config.A, Config.B, Config.C(0)), navigator.stack) + } + + @Test + fun WHEN_exists_with_same_value_at_start_THEN_existing_configuration_moved_to_end() { + val navigator = TestNavigator(listOf(Config.C(0), Config.A, Config.B)) + + navigator.bringToFront(Config.C(0)) + + assertEquals(listOf(Config.A, Config.B, Config.C(0)), navigator.stack) + } + + @Test + fun WHEN_exists_with_same_value_in_the_middle_THEN_existing_configuration_moved_to_end() { + val navigator = TestNavigator(listOf(Config.A, Config.C(0), Config.B)) + + navigator.bringToFront(Config.C(0)) + + assertEquals(listOf(Config.A, Config.B, Config.C(0)), navigator.stack) + } + + @Test + fun WHEN_exists_with_same_value_at_the_end_THEN_existing_configuration_moved_to_end() { + val navigator = TestNavigator(listOf(Config.A, Config.B, Config.C(0))) + + navigator.bringToFront(Config.C(0)) + + assertEquals(listOf(Config.A, Config.B, Config.C(0)), navigator.stack) + } + + @Test + fun WHEN_exists_with_different_value_at_start_THEN_removed_and_new_added_to_end() { + val navigator = TestNavigator(listOf(Config.C(0), Config.A, Config.B)) + + navigator.bringToFront(Config.C(1)) + + assertEquals(listOf(Config.A, Config.B, Config.C(1)), navigator.stack) + } + + @Test + fun WHEN_exists_with_different_value_in_the_middle_THEN_removed_and_new_added_to_end() { + val navigator = TestNavigator(listOf(Config.A, Config.C(0), Config.B)) + + navigator.bringToFront(Config.C(1)) + + assertEquals(listOf(Config.A, Config.B, Config.C(1)), navigator.stack) + } + + @Test + fun WHEN_exists_with_different_value_at_the_end_THEN_removed_and_new_added_to_end() { + val navigator = TestNavigator(listOf(Config.A, Config.B, Config.C(0))) + + navigator.bringToFront(Config.C(1)) + + assertEquals(listOf(Config.A, Config.B, Config.C(1)), navigator.stack) + } + + @Test + fun WHEN_multiple_exist_THEN_all_removed_and_new_added_to_end() { + val navigator = TestNavigator(listOf(Config.C(0), Config.A, Config.C(1), Config.B)) + + navigator.bringToFront(Config.C(1)) + + assertEquals(listOf(Config.A, Config.B, Config.C(1)), navigator.stack) + } + + private sealed class Config { + object A : Config() + object B : Config() + data class C(val value: Int) : Config() + } +} diff --git a/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/TestNavigator.kt b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/TestNavigator.kt new file mode 100644 index 00000000..17307872 --- /dev/null +++ b/decompose/src/commonTest/kotlin/com/arkivanov/decompose/router/TestNavigator.kt @@ -0,0 +1,12 @@ +package com.arkivanov.decompose.router + +import com.arkivanov.decompose.Navigator + +class TestNavigator( + var stack: List = emptyList() +) : Navigator { + + override fun navigate(transformer: (stack: List) -> List) { + stack = stack.let(transformer) + } +}