From ec53de34bcd8eda374b6e880f4e237b1c4a1186f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rudolf=20Hlad=C3=ADk?= Date: Fri, 6 Dec 2024 15:08:38 +0100 Subject: [PATCH 1/4] Preview new navigation --- .../navigation/home/HomeNavHostComponent.kt | 29 +++++----------- .../feature/navigation/home/HomeNavigation.kt | 33 +++++++++++++++++++ .../navigation/profile/ProfileNavHost.kt | 2 ++ .../feature/ui/firstScreen/FirstComponent.kt | 4 +-- .../ui/firstScreen/FirstScreenNavigation.kt | 6 ++-- .../ui/secondScreen/SecondComponent.kt | 6 ++-- .../ui/secondScreen/SecondScreenNavigation.kt | 8 ++--- .../feature/ui/thirdScreen/ThirdComponent.kt | 4 +-- .../ui/thirdScreen/ThirdScreenNavigation.kt | 6 ++-- 9 files changed, 61 insertions(+), 37 deletions(-) create mode 100644 shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavigation.kt diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavHostComponent.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavHostComponent.kt index 3c90d58..b6de106 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavHostComponent.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavHostComponent.kt @@ -5,19 +5,13 @@ import app.futured.kmptemplate.feature.ui.base.AppComponent import app.futured.kmptemplate.feature.ui.base.AppComponentContext import app.futured.kmptemplate.feature.ui.base.AppComponentFactory import app.futured.kmptemplate.feature.ui.firstScreen.FirstComponent -import app.futured.kmptemplate.feature.ui.firstScreen.FirstScreenNavigation import app.futured.kmptemplate.feature.ui.secondScreen.SecondComponent -import app.futured.kmptemplate.feature.ui.secondScreen.SecondScreenNavigation import app.futured.kmptemplate.feature.ui.thirdScreen.ThirdComponent -import app.futured.kmptemplate.feature.ui.thirdScreen.ThirdScreenArgs -import app.futured.kmptemplate.feature.ui.thirdScreen.ThirdScreenNavigation import com.arkivanov.decompose.Child import com.arkivanov.decompose.router.stack.ChildStack -import com.arkivanov.decompose.router.stack.StackNavigation import com.arkivanov.decompose.router.stack.childStack import com.arkivanov.decompose.router.stack.navigate import com.arkivanov.decompose.router.stack.pop -import com.arkivanov.decompose.router.stack.pushNew import kotlinx.coroutines.flow.StateFlow import org.koin.core.annotation.Factory import org.koin.core.annotation.InjectedParam @@ -26,17 +20,19 @@ import org.koin.core.annotation.InjectedParam internal class HomeNavHostComponent( @InjectedParam componentContext: AppComponentContext, @InjectedParam private val initialStack: List, + private val homeNavigator: HomeNavigation, ) : AppComponent(componentContext, Unit), HomeNavHost { - private val homeNavigator = StackNavigation() override val actions: HomeNavHost.Actions = object : HomeNavHost.Actions { - override fun navigate(newStack: List>) = homeNavigator.navigate { newStack.map { it.configuration } } - override fun pop() = homeNavigator.pop() + override fun navigate(newStack: List>) = + homeNavigator.navigator.navigate { newStack.map { it.configuration } } + + override fun pop() = homeNavigator.navigator.pop() } override val stack: StateFlow> = childStack( - source = homeNavigator, + source = homeNavigator.navigator, serializer = HomeConfig.serializer(), initialStack = { initialStack }, key = "HomeStack", @@ -46,28 +42,21 @@ internal class HomeNavHostComponent( HomeConfig.First -> HomeChild.First( AppComponentFactory.createComponent( childContext = childCtx, - navigation = FirstScreenNavigation( - toSecond = { homeNavigator.pushNew(HomeConfig.Second) }, - ), + navigation = homeNavigator, ), ) HomeConfig.Second -> HomeChild.Second( AppComponentFactory.createComponent( childContext = childCtx, - navigation = SecondScreenNavigation( - pop = { homeNavigator.pop() }, - toThird = { id -> homeNavigator.pushNew(HomeConfig.Third(ThirdScreenArgs(id))) }, - ), + navigation = homeNavigator, ), ) is HomeConfig.Third -> HomeChild.Third( AppComponentFactory.createComponent( childContext = childCtx, - navigation = ThirdScreenNavigation( - pop = { homeNavigator.pop() }, - ), + navigation = homeNavigator, config.args, ), ) diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavigation.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavigation.kt new file mode 100644 index 0000000..50070de --- /dev/null +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavigation.kt @@ -0,0 +1,33 @@ +package app.futured.kmptemplate.feature.navigation.home + +import app.futured.kmptemplate.feature.ui.firstScreen.FirstComponent +import app.futured.kmptemplate.feature.ui.firstScreen.FirstScreenNavigation +import app.futured.kmptemplate.feature.ui.secondScreen.SecondComponent +import app.futured.kmptemplate.feature.ui.secondScreen.SecondScreenNavigation +import app.futured.kmptemplate.feature.ui.thirdScreen.ThirdComponent +import app.futured.kmptemplate.feature.ui.thirdScreen.ThirdScreenArgs +import app.futured.kmptemplate.feature.ui.thirdScreen.ThirdScreenNavigation +import com.arkivanov.decompose.router.stack.StackNavigation +import com.arkivanov.decompose.router.stack.pop +import com.arkivanov.decompose.router.stack.pushNew +import org.koin.core.annotation.Single + +internal interface HomeNavigation : FirstScreenNavigation, SecondScreenNavigation, ThirdScreenNavigation { + val navigator: StackNavigation +} + +@Single +internal class HomeNavigator : HomeNavigation { + override val navigator = StackNavigation() + + override fun FirstComponent.navigateToSecond() = + navigator.pushNew(HomeConfig.Second) + + override fun SecondComponent.pop() = navigator.pop() + + + override fun SecondComponent.navigateToThird(id: String) = + navigator.pushNew(HomeConfig.Third(ThirdScreenArgs(id))) + + override fun ThirdComponent.pop() = navigator.pop() +} diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHost.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHost.kt index 17bbfcf..6c64636 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHost.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHost.kt @@ -23,8 +23,10 @@ sealed interface ProfileConfig { @Serializable data object Profile : ProfileConfig +// data object Third : ProfileConfig } sealed interface ProfileChild { data class Profile(val screen: ProfileScreen) : ProfileChild +// data class Third(val screen: DeepLinkDestination.ThirdScreen) : ProfileChild } diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstComponent.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstComponent.kt index 8f30a4e..1ca5d21 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstComponent.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstComponent.kt @@ -25,7 +25,7 @@ internal class FirstComponent( componentContext = componentContext, defaultState = FirstViewState(), ), - FirstScreen { + FirstScreen, FirstScreenNavigation by navigation{ companion object { private const val COUNTER_ALERT = 10L @@ -36,7 +36,7 @@ internal class FirstComponent( override val viewState: StateFlow = componentState.asStateFlow() override val actions: FirstScreen.Actions = object : FirstScreen.Actions { - override fun onNext() = navigation.toSecond() + override fun onNext() = navigateToSecond() } init { diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstScreenNavigation.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstScreenNavigation.kt index e601e79..a8f6c19 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstScreenNavigation.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstScreenNavigation.kt @@ -2,6 +2,6 @@ package app.futured.kmptemplate.feature.ui.firstScreen import app.futured.arkitekt.decompose.navigation.NavigationActions -data class FirstScreenNavigation( - val toSecond: () -> Unit, -) : NavigationActions +internal interface FirstScreenNavigation : NavigationActions { + fun FirstComponent.navigateToSecond() +} diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondComponent.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondComponent.kt index 1d8ba0f..fc96c22 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondComponent.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondComponent.kt @@ -25,7 +25,7 @@ internal class SecondComponent( componentContext = componentContext, defaultState = SecondViewState, ), - SecondScreen { + SecondScreen, SecondScreenNavigation by navigation { override val viewState: StateFlow = componentState @@ -34,7 +34,7 @@ internal class SecondComponent( dismiss = { result -> pickerNavigator.dismiss() if (result != null) { - navigation.toThird(result) + navigateToThird(result) } }, ) @@ -58,7 +58,7 @@ internal class SecondComponent( ).asStateFlow(componentCoroutineScope) override val actions: SecondScreen.Actions = object : SecondScreen.Actions { - override fun onBack() = navigation.pop() + override fun onBack() = pop() override fun onPickVeggie() = pickerNavigator.activate(SecondScreen.PickerType.Vegetable) override fun onPickFruit() = pickerNavigator.activate(SecondScreen.PickerType.Fruit) override fun onPickerDismissed() = pickerNavigator.dismiss() diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondScreenNavigation.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondScreenNavigation.kt index c86994f..7308f74 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondScreenNavigation.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondScreenNavigation.kt @@ -2,7 +2,7 @@ package app.futured.kmptemplate.feature.ui.secondScreen import app.futured.arkitekt.decompose.navigation.NavigationActions -data class SecondScreenNavigation( - val pop: () -> Unit, - val toThird: (id: String) -> Unit, -) : NavigationActions +internal interface SecondScreenNavigation : NavigationActions { + fun SecondComponent.pop() + fun SecondComponent.navigateToThird(id: String) +} diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdComponent.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdComponent.kt index ff46a5d..03b0239 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdComponent.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdComponent.kt @@ -17,11 +17,11 @@ internal class ThirdComponent( componentContext = componentContext, defaultState = ThirdViewState(text = MR.strings.third_screen_text.format(args.id)), ), - ThirdScreen { + ThirdScreen, ThirdScreenNavigation by navigation { override val viewState: StateFlow = componentState override val actions: ThirdScreen.Actions = object : ThirdScreen.Actions { - override fun onBack() = navigation.pop() + override fun onBack() = pop() } } diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdScreenNavigation.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdScreenNavigation.kt index 5d54359..49153d0 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdScreenNavigation.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdScreenNavigation.kt @@ -2,6 +2,6 @@ package app.futured.kmptemplate.feature.ui.thirdScreen import app.futured.arkitekt.decompose.navigation.NavigationActions -data class ThirdScreenNavigation( - val pop: () -> Unit, -) : NavigationActions +internal interface ThirdScreenNavigation : NavigationActions { + fun ThirdComponent.pop() +} From 7c9402ab0b13cb5553b133bbcd0bf4856e98a88d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rudolf=20Hlad=C3=ADk?= Date: Fri, 6 Dec 2024 15:45:30 +0100 Subject: [PATCH 2/4] Use Third screen with navigation on different stacks --- .../android/ui/navigation/ProfileNavHostUi.kt | 2 ++ .../android/ui/screen/ProfileScreenUi.kt | 3 ++ .../navigation/home/HomeNavHostComponent.kt | 11 ++++--- .../feature/navigation/home/HomeNavigation.kt | 9 +++--- .../navigation/profile/ProfileNavHost.kt | 9 ++++-- .../profile/ProfileNavHostComponent.kt | 27 +++++++++++------ .../profile/ProfileNavHostNavigation.kt | 29 ++++++++++++++++--- .../navigation/root/RootNavHostComponent.kt | 2 +- .../signedIn/SignedInNavHostComponent.kt | 5 +--- .../feature/ui/base/AppComponentFactory.kt | 4 +-- .../feature/ui/firstScreen/FirstComponent.kt | 3 +- .../ui/profileScreen/ProfileComponent.kt | 10 +++---- .../feature/ui/profileScreen/ProfileScreen.kt | 1 + .../profileScreen/ProfileScreenNavigation.kt | 7 +++-- 14 files changed, 82 insertions(+), 40 deletions(-) diff --git a/androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/navigation/ProfileNavHostUi.kt b/androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/navigation/ProfileNavHostUi.kt index 2a4ecec..aa29310 100644 --- a/androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/navigation/ProfileNavHostUi.kt +++ b/androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/navigation/ProfileNavHostUi.kt @@ -10,6 +10,7 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.lifecycle.compose.collectAsStateWithLifecycle import app.futured.kmptemplate.android.ui.screen.ProfileScreenUi +import app.futured.kmptemplate.android.ui.screen.ThirdScreenUi import app.futured.kmptemplate.feature.navigation.profile.ProfileChild import app.futured.kmptemplate.feature.navigation.profile.ProfileConfig import app.futured.kmptemplate.feature.navigation.profile.ProfileNavHost @@ -43,6 +44,7 @@ fun ProfileNavHostUi( ) { child -> when (val childInstance = child.instance) { is ProfileChild.Profile -> ProfileScreenUi(screen = childInstance.screen, modifier = Modifier.fillMaxSize()) + is ProfileChild.Third -> ThirdScreenUi(screen = childInstance.screen, modifier = Modifier.fillMaxSize()) } } }, diff --git a/androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/screen/ProfileScreenUi.kt b/androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/screen/ProfileScreenUi.kt index f9e1e2c..8c281b3 100644 --- a/androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/screen/ProfileScreenUi.kt +++ b/androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/screen/ProfileScreenUi.kt @@ -56,6 +56,9 @@ private fun Content( Button(onClick = actions::onLogout) { Text(kmpStringResource(MR.strings.generic_sign_out)) } + Button(onClick = actions::onThird) { + Text("Nav to third") + } } } } diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavHostComponent.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavHostComponent.kt index b6de106..e8f780d 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavHostComponent.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavHostComponent.kt @@ -5,8 +5,11 @@ import app.futured.kmptemplate.feature.ui.base.AppComponent import app.futured.kmptemplate.feature.ui.base.AppComponentContext import app.futured.kmptemplate.feature.ui.base.AppComponentFactory import app.futured.kmptemplate.feature.ui.firstScreen.FirstComponent +import app.futured.kmptemplate.feature.ui.firstScreen.FirstScreenNavigation import app.futured.kmptemplate.feature.ui.secondScreen.SecondComponent +import app.futured.kmptemplate.feature.ui.secondScreen.SecondScreenNavigation import app.futured.kmptemplate.feature.ui.thirdScreen.ThirdComponent +import app.futured.kmptemplate.feature.ui.thirdScreen.ThirdScreenNavigation import com.arkivanov.decompose.Child import com.arkivanov.decompose.router.stack.ChildStack import com.arkivanov.decompose.router.stack.childStack @@ -20,9 +23,9 @@ import org.koin.core.annotation.InjectedParam internal class HomeNavHostComponent( @InjectedParam componentContext: AppComponentContext, @InjectedParam private val initialStack: List, - private val homeNavigator: HomeNavigation, ) : AppComponent(componentContext, Unit), HomeNavHost { + private val homeNavigator: HomeNavigation = HomeNavigator() override val actions: HomeNavHost.Actions = object : HomeNavHost.Actions { override fun navigate(newStack: List>) = @@ -40,21 +43,21 @@ internal class HomeNavHostComponent( childFactory = { config, childCtx -> when (config) { HomeConfig.First -> HomeChild.First( - AppComponentFactory.createComponent( + AppComponentFactory.createComponent( childContext = childCtx, navigation = homeNavigator, ), ) HomeConfig.Second -> HomeChild.Second( - AppComponentFactory.createComponent( + AppComponentFactory.createComponent( childContext = childCtx, navigation = homeNavigator, ), ) is HomeConfig.Third -> HomeChild.Third( - AppComponentFactory.createComponent( + AppComponentFactory.createComponent( childContext = childCtx, navigation = homeNavigator, config.args, diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavigation.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavigation.kt index 50070de..76cc81e 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavigation.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavigation.kt @@ -16,18 +16,19 @@ internal interface HomeNavigation : FirstScreenNavigation, SecondScreenNavigatio val navigator: StackNavigation } -@Single internal class HomeNavigator : HomeNavigation { override val navigator = StackNavigation() override fun FirstComponent.navigateToSecond() = navigator.pushNew(HomeConfig.Second) - override fun SecondComponent.pop() = navigator.pop() - + override fun SecondComponent.pop() = + navigator.pop() override fun SecondComponent.navigateToThird(id: String) = navigator.pushNew(HomeConfig.Third(ThirdScreenArgs(id))) - override fun ThirdComponent.pop() = navigator.pop() + override fun ThirdComponent.pop() { + navigator.pop() + } } diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHost.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHost.kt index 6c64636..95d3c72 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHost.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHost.kt @@ -1,6 +1,9 @@ package app.futured.kmptemplate.feature.navigation.profile +import app.futured.kmptemplate.feature.navigation.deepLink.DeepLinkDestination import app.futured.kmptemplate.feature.ui.profileScreen.ProfileScreen +import app.futured.kmptemplate.feature.ui.thirdScreen.ThirdScreen +import app.futured.kmptemplate.feature.ui.thirdScreen.ThirdScreenArgs import com.arkivanov.decompose.Child import com.arkivanov.decompose.router.stack.ChildStack import com.arkivanov.essenty.backhandler.BackHandlerOwner @@ -23,10 +26,12 @@ sealed interface ProfileConfig { @Serializable data object Profile : ProfileConfig -// data object Third : ProfileConfig + + @Serializable + data class Third(val args: ThirdScreenArgs) : ProfileConfig } sealed interface ProfileChild { data class Profile(val screen: ProfileScreen) : ProfileChild -// data class Third(val screen: DeepLinkDestination.ThirdScreen) : ProfileChild + data class Third(val screen: ThirdScreen) : ProfileChild } diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHostComponent.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHostComponent.kt index 38ebf53..a4b881c 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHostComponent.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHostComponent.kt @@ -6,6 +6,8 @@ import app.futured.kmptemplate.feature.ui.base.AppComponentContext import app.futured.kmptemplate.feature.ui.base.AppComponentFactory import app.futured.kmptemplate.feature.ui.profileScreen.ProfileComponent import app.futured.kmptemplate.feature.ui.profileScreen.ProfileScreenNavigation +import app.futured.kmptemplate.feature.ui.thirdScreen.ThirdComponent +import app.futured.kmptemplate.feature.ui.thirdScreen.ThirdScreenNavigation import com.arkivanov.decompose.Child import com.arkivanov.decompose.router.stack.ChildStack import com.arkivanov.decompose.router.stack.StackNavigation @@ -19,33 +21,40 @@ import org.koin.core.annotation.InjectedParam @Factory internal class ProfileNavHostComponent( @InjectedParam componentContext: AppComponentContext, - @InjectedParam navigationActions: ProfileNavHostNavigation, + @InjectedParam toLogin: () -> Unit, @InjectedParam private val initialStack: List, ) : AppComponent(componentContext, Unit), ProfileNavHost { - private val stackNavigator = StackNavigation() + private val navigator = ProfileNavHostNavigator(toLogin) + override val stack: StateFlow> = childStack( - source = stackNavigator, + source = navigator.stackNavigator, serializer = ProfileConfig.serializer(), initialStack = { initialStack }, handleBackButton = true, childFactory = { config, childCtx -> when (config) { ProfileConfig.Profile -> ProfileChild.Profile( - AppComponentFactory.createComponent( + AppComponentFactory.createComponent( childContext = childCtx, - navigation = ProfileScreenNavigation( - toLogin = navigationActions.toLogin, - ), + navigation = navigator, ), ) + + is ProfileConfig.Third -> ProfileChild.Third( + AppComponentFactory.createComponent( + childContext = childCtx, + navigation = navigator, + config.args + ) + ) } }, ).asStateFlow(componentCoroutineScope) override val actions: ProfileNavHost.Actions = object : ProfileNavHost.Actions { - override fun pop() = stackNavigator.pop() - override fun navigate(newStack: List>) = stackNavigator.navigate { + override fun pop() = navigator.stackNavigator.pop() + override fun navigate(newStack: List>) = navigator.stackNavigator.navigate { newStack.map { it.configuration } } } diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHostNavigation.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHostNavigation.kt index d8ad663..be8162b 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHostNavigation.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHostNavigation.kt @@ -1,7 +1,28 @@ package app.futured.kmptemplate.feature.navigation.profile -import app.futured.arkitekt.decompose.navigation.NavigationActions +import app.futured.kmptemplate.feature.ui.profileScreen.ProfileScreen +import app.futured.kmptemplate.feature.ui.profileScreen.ProfileScreenNavigation +import app.futured.kmptemplate.feature.ui.thirdScreen.ThirdComponent +import app.futured.kmptemplate.feature.ui.thirdScreen.ThirdScreenArgs +import app.futured.kmptemplate.feature.ui.thirdScreen.ThirdScreenNavigation +import com.arkivanov.decompose.router.stack.StackNavigation +import com.arkivanov.decompose.router.stack.pop +import com.arkivanov.decompose.router.stack.push -data class ProfileNavHostNavigation( - val toLogin: () -> Unit, -) : NavigationActions +internal interface ProfileNavHostNavigation : ProfileScreenNavigation, ThirdScreenNavigation + +internal class ProfileNavHostNavigator( + private val navigateToLogin: () -> Unit, +) : ProfileNavHostNavigation { + val stackNavigator = StackNavigation() + + override fun ProfileScreen.toLogin() = navigateToLogin() + + override fun ProfileScreen.navigateToThird(id: String) { + stackNavigator.push(ProfileConfig.Third(ThirdScreenArgs(id))) + } + + override fun ThirdComponent.pop() { + stackNavigator.pop() + } +} diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostComponent.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostComponent.kt index 4a2fcff..7385fbe 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostComponent.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostComponent.kt @@ -40,7 +40,7 @@ internal class RootNavHostComponent( childFactory = { config, childCtx -> when (config) { RootConfig.Login -> RootChild.Login( - screen = AppComponentFactory.createComponent( + screen = AppComponentFactory.createComponent( childContext = childCtx, navigation = LoginScreenNavigation( toSignedIn = { diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostComponent.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostComponent.kt index 8e14e83..9329927 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostComponent.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostComponent.kt @@ -4,7 +4,6 @@ import app.futured.arkitekt.decompose.ext.asStateFlow import app.futured.arkitekt.decompose.ext.switchTab import app.futured.kmptemplate.feature.navigation.home.HomeNavHostComponent import app.futured.kmptemplate.feature.navigation.profile.ProfileNavHostComponent -import app.futured.kmptemplate.feature.navigation.profile.ProfileNavHostNavigation import app.futured.kmptemplate.feature.ui.base.AppComponent import app.futured.kmptemplate.feature.ui.base.AppComponentContext import app.futured.kmptemplate.feature.ui.base.AppComponentFactory @@ -46,9 +45,7 @@ internal class SignedInNavHostComponent( is SignedInConfig.Profile -> SignedInChild.Profile( AppComponentFactory.createComponent( childContext = childCtx, - ProfileNavHostNavigation( - toLogin = navigationActions.toLogin, - ), + navigationActions.toLogin, config.initialStack, ), ) diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/base/AppComponentFactory.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/base/AppComponentFactory.kt index 09e4a68..e5a0fce 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/base/AppComponentFactory.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/base/AppComponentFactory.kt @@ -19,9 +19,9 @@ internal object AppComponentFactory : KoinComponent { * @param parameters Additional parameters for the component. * @return The created screen component. */ - inline fun > createComponent( + inline fun , reified N: NavigationActions> createComponent( childContext: AppComponentContext, - navigation: NavigationActions, + navigation: N, vararg parameters: Any?, ): C = get( qualifier = null, diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstComponent.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstComponent.kt index 1ca5d21..c6c7454 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstComponent.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstComponent.kt @@ -24,8 +24,7 @@ internal class FirstComponent( ) : ScreenComponent( componentContext = componentContext, defaultState = FirstViewState(), -), - FirstScreen, FirstScreenNavigation by navigation{ +), FirstScreen, FirstScreenNavigation by navigation { companion object { private const val COUNTER_ALERT = 10L diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileComponent.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileComponent.kt index 03587f5..b15055f 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileComponent.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileComponent.kt @@ -11,13 +11,13 @@ internal class ProfileComponent( @InjectedParam componentContext: AppComponentContext, @InjectedParam override val navigation: ProfileScreenNavigation, ) : ScreenComponent( - componentContext, - ProfileViewState, - ), - ProfileScreen { + componentContext, + ProfileViewState, +), ProfileScreen, ProfileScreenNavigation by navigation { override val actions: ProfileScreen.Actions = object : ProfileScreen.Actions { - override fun onLogout() = navigation.toLogin() + override fun onLogout() = toLogin() + override fun onThird() = navigateToThird("hello third from profile") } override val viewState: StateFlow = componentState diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileScreen.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileScreen.kt index 177b2c6..94e0020 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileScreen.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileScreen.kt @@ -8,5 +8,6 @@ interface ProfileScreen { interface Actions { fun onLogout() + fun onThird() } } diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileScreenNavigation.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileScreenNavigation.kt index 02258fc..5411846 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileScreenNavigation.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileScreenNavigation.kt @@ -2,6 +2,7 @@ package app.futured.kmptemplate.feature.ui.profileScreen import app.futured.arkitekt.decompose.navigation.NavigationActions -internal data class ProfileScreenNavigation( - val toLogin: () -> Unit, -) : NavigationActions +internal interface ProfileScreenNavigation : NavigationActions { + fun ProfileScreen.toLogin() + fun ProfileScreen.navigateToThird(id: String) +} From a483502cc4484217455a91b556f8b7c6b9cd51ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rudolf=20Hlad=C3=ADk?= Date: Fri, 7 Feb 2025 10:55:52 +0100 Subject: [PATCH 3/4] Update navigation for all trees --- .../navigation/home/HomeNavHostComponent.kt | 20 ++++----- .../navigation/profile/ProfileNavHost.kt | 1 - .../profile/ProfileNavHostComponent.kt | 17 ++++---- .../profile/ProfileNavHostNavigation.kt | 6 ++- .../navigation/root/RootNavHostComponent.kt | 41 ++++++++----------- .../navigation/root/RootNavHostFactory.kt | 2 +- .../navigation/root/RootNavHostNavigation.kt | 24 +++++++++++ .../signedIn/SignedInNavHostComponent.kt | 8 ++-- .../signedIn/SignedInNavHostNavigation.kt | 6 +-- .../feature/ui/base/AppComponentFactory.kt | 4 +- .../feature/ui/loginScreen/LoginComponent.kt | 9 ++-- .../ui/loginScreen/LoginScreenNavigation.kt | 6 +-- .../ui/secondScreen/SecondComponent.kt | 4 +- 13 files changed, 83 insertions(+), 65 deletions(-) create mode 100644 shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostNavigation.kt diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavHostComponent.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavHostComponent.kt index 9eebd1f..8193927 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavHostComponent.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavHostComponent.kt @@ -27,13 +27,6 @@ internal class HomeNavHostComponent( private val homeNavigator: HomeNavigation = HomeNavigator() - override val actions: HomeNavHost.Actions = object : HomeNavHost.Actions { - override fun navigate(newStack: List>) = - homeNavigator.navigator.navigate { newStack.map { it.configuration } } - - override fun pop() = homeNavigator.navigator.pop() - } - override val stack: StateFlow> = childStack( source = homeNavigator.navigator, serializer = HomeConfig.serializer(), @@ -43,21 +36,21 @@ internal class HomeNavHostComponent( childFactory = { config, childCtx -> when (config) { HomeConfig.First -> HomeChild.First( - AppComponentFactory.createComponent( + AppComponentFactory.createScreenComponent( childContext = childCtx, navigation = homeNavigator, ), ) HomeConfig.Second -> HomeChild.Second( - AppComponentFactory.createComponent( + AppComponentFactory.createScreenComponent( childContext = childCtx, navigation = homeNavigator, ), ) is HomeConfig.Third -> HomeChild.Third( - AppComponentFactory.createComponent( + AppComponentFactory.createScreenComponent( childContext = childCtx, navigation = homeNavigator, config.args, @@ -66,4 +59,11 @@ internal class HomeNavHostComponent( } }, ).asStateFlow() + + override val actions: HomeNavHost.Actions = object : HomeNavHost.Actions { + override fun navigate(newStack: List>) = + homeNavigator.navigator.navigate { newStack.map { it.configuration } } + + override fun pop() = homeNavigator.navigator.pop() + } } diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHost.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHost.kt index 95d3c72..156c843 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHost.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHost.kt @@ -1,6 +1,5 @@ package app.futured.kmptemplate.feature.navigation.profile -import app.futured.kmptemplate.feature.navigation.deepLink.DeepLinkDestination import app.futured.kmptemplate.feature.ui.profileScreen.ProfileScreen import app.futured.kmptemplate.feature.ui.thirdScreen.ThirdScreen import app.futured.kmptemplate.feature.ui.thirdScreen.ThirdScreenArgs diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHostComponent.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHostComponent.kt index a63155d..14476ce 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHostComponent.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHostComponent.kt @@ -10,7 +10,6 @@ import app.futured.kmptemplate.feature.ui.thirdScreen.ThirdComponent import app.futured.kmptemplate.feature.ui.thirdScreen.ThirdScreenNavigation import com.arkivanov.decompose.Child import com.arkivanov.decompose.router.stack.ChildStack -import com.arkivanov.decompose.router.stack.StackNavigation import com.arkivanov.decompose.router.stack.childStack import com.arkivanov.decompose.router.stack.navigate import com.arkivanov.decompose.router.stack.pop @@ -25,7 +24,7 @@ internal class ProfileNavHostComponent( @InjectedParam private val initialStack: List, ) : AppComponent(componentContext, Unit), ProfileNavHost { - private val navigator = ProfileNavHostNavigator(toLogin) + private val navigator: ProfileNavHostNavigation = ProfileNavHostNavigator(toLogin) override val stack: StateFlow> = childStack( source = navigator.stackNavigator, @@ -35,27 +34,27 @@ internal class ProfileNavHostComponent( childFactory = { config, childCtx -> when (config) { ProfileConfig.Profile -> ProfileChild.Profile( - AppComponentFactory.createComponent( + AppComponentFactory.createScreenComponent( childContext = childCtx, navigation = navigator, ), ) is ProfileConfig.Third -> ProfileChild.Third( - AppComponentFactory.createComponent( + AppComponentFactory.createScreenComponent( childContext = childCtx, navigation = navigator, - config.args - ) + config.args, + ), ) } }, ).asStateFlow() override val actions: ProfileNavHost.Actions = object : ProfileNavHost.Actions { + override fun navigate(newStack: List>) = + navigator.stackNavigator.navigate { newStack.map { it.configuration } } + override fun pop() = navigator.stackNavigator.pop() - override fun navigate(newStack: List>) = navigator.stackNavigator.navigate { - newStack.map { it.configuration } - } } } diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHostNavigation.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHostNavigation.kt index be8162b..cbede68 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHostNavigation.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHostNavigation.kt @@ -9,12 +9,14 @@ import com.arkivanov.decompose.router.stack.StackNavigation import com.arkivanov.decompose.router.stack.pop import com.arkivanov.decompose.router.stack.push -internal interface ProfileNavHostNavigation : ProfileScreenNavigation, ThirdScreenNavigation +internal interface ProfileNavHostNavigation : ProfileScreenNavigation, ThirdScreenNavigation { + val stackNavigator: StackNavigation +} internal class ProfileNavHostNavigator( private val navigateToLogin: () -> Unit, ) : ProfileNavHostNavigation { - val stackNavigator = StackNavigation() + override val stackNavigator = StackNavigation() override fun ProfileScreen.toLogin() = navigateToLogin() diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostComponent.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostComponent.kt index 6eccd33..7da034d 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostComponent.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostComponent.kt @@ -4,16 +4,15 @@ import app.futured.arkitekt.decompose.ext.asStateFlow import app.futured.kmptemplate.feature.navigation.deepLink.DeepLinkDestination import app.futured.kmptemplate.feature.navigation.deepLink.DeepLinkResolver import app.futured.kmptemplate.feature.navigation.signedIn.SignedInNavHostComponent -import app.futured.kmptemplate.feature.navigation.signedIn.SignedInNavHostNavigation import app.futured.kmptemplate.feature.ui.base.AppComponent import app.futured.kmptemplate.feature.ui.base.AppComponentContext import app.futured.kmptemplate.feature.ui.base.AppComponentFactory import app.futured.kmptemplate.feature.ui.loginScreen.LoginComponent +import app.futured.kmptemplate.feature.ui.loginScreen.LoginScreen import app.futured.kmptemplate.feature.ui.loginScreen.LoginScreenNavigation import app.futured.kmptemplate.feature.ui.thirdScreen.ThirdScreenArgs import co.touchlab.kermit.Logger import com.arkivanov.decompose.router.slot.ChildSlot -import com.arkivanov.decompose.router.slot.SlotNavigation import com.arkivanov.decompose.router.slot.activate import com.arkivanov.decompose.router.slot.childSlot import com.arkivanov.essenty.lifecycle.doOnCreate @@ -27,36 +26,31 @@ internal class RootNavHostComponent( private val deepLinkResolver: DeepLinkResolver, ) : AppComponent(componentContext, RootNavHostViewState), RootNavHost { - private val slotNavigator = SlotNavigation() + private val rootNavigator: RootNavHostNavigation = RootNavHostNavigator() private var pendingDeepLink: DeepLinkDestination? = null private val logger = Logger.withTag("RootNavHostComponent") override val slot: StateFlow> = childSlot( - source = slotNavigator, + source = rootNavigator.slotNavigator, serializer = RootConfig.serializer(), initialConfiguration = { null }, handleBackButton = false, childFactory = { config, childCtx -> when (config) { - RootConfig.Login -> RootChild.Login( - screen = AppComponentFactory.createComponent( + RootConfig.Login -> { + val screen: LoginScreen = AppComponentFactory.createScreenComponent( childContext = childCtx, - navigation = LoginScreenNavigation( - toSignedIn = { - slotNavigator.activate(RootConfig.SignedIn()) - }, - ), - ), - ) + navigation = rootNavigator, + ) + RootChild.Login(screen = screen) + } is RootConfig.SignedIn -> RootChild.SignedIn( - navHost = AppComponentFactory.createComponent( + navHost = AppComponentFactory.createAppComponent( childContext = childCtx, + { rootNavigator.slotNavigator.activate(RootConfig.Login) }, config.initialConfig, - SignedInNavHostNavigation( - toLogin = { slotNavigator.activate(RootConfig.Login) }, - ), ), ) } @@ -66,7 +60,7 @@ internal class RootNavHostComponent( init { doOnCreate { if (!consumeDeepLink()) { - slotNavigator.activate(RootConfig.Login) + rootNavigator.slotNavigator.activate(RootConfig.Login) } } } @@ -86,12 +80,13 @@ internal class RootNavHostComponent( */ private fun consumeDeepLink(): Boolean { val deepLink = pendingDeepLink ?: return false - when (deepLink) { - DeepLinkDestination.HomeTab -> slotNavigator.activate(RootConfig.deepLinkHome()) - DeepLinkDestination.ProfileTab -> slotNavigator.activate(RootConfig.deepLinkProfile()) - DeepLinkDestination.SecondScreen -> slotNavigator.activate(RootConfig.deepLinkSecondScreen()) - is DeepLinkDestination.ThirdScreen -> slotNavigator.activate(RootConfig.deepLinkThirdScreen(ThirdScreenArgs(deepLink.argument))) + val deepLinkConfig = when (deepLink) { + DeepLinkDestination.HomeTab -> RootConfig.deepLinkHome() + DeepLinkDestination.ProfileTab -> RootConfig.deepLinkProfile() + DeepLinkDestination.SecondScreen -> RootConfig.deepLinkSecondScreen() + is DeepLinkDestination.ThirdScreen -> RootConfig.deepLinkThirdScreen(ThirdScreenArgs(deepLink.argument)) } + rootNavigator.slotNavigator.activate(deepLinkConfig) return true } } diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostFactory.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostFactory.kt index 5c37af8..bc7abc1 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostFactory.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostFactory.kt @@ -7,5 +7,5 @@ import org.koin.core.component.KoinComponent object RootNavHostFactory : KoinComponent { fun create( componentContext: AppComponentContext, - ): RootNavHost = AppComponentFactory.createComponent(componentContext) + ): RootNavHost = AppComponentFactory.createAppComponent(componentContext) } diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostNavigation.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostNavigation.kt new file mode 100644 index 0000000..06e8b3e --- /dev/null +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostNavigation.kt @@ -0,0 +1,24 @@ +package app.futured.kmptemplate.feature.navigation.root + +import app.futured.kmptemplate.feature.navigation.signedIn.SignedInNavHostComponent +import app.futured.kmptemplate.feature.navigation.signedIn.SignedInNavHostNavigation +import app.futured.kmptemplate.feature.ui.loginScreen.LoginComponent +import app.futured.kmptemplate.feature.ui.loginScreen.LoginScreenNavigation +import com.arkivanov.decompose.router.slot.SlotNavigation +import com.arkivanov.decompose.router.slot.activate + +internal interface RootNavHostNavigation : LoginScreenNavigation, SignedInNavHostNavigation { + val slotNavigator: SlotNavigation +} + +internal class RootNavHostNavigator: RootNavHostNavigation { + override val slotNavigator: SlotNavigation = SlotNavigation() + + override fun LoginComponent.toSignedIn() { + slotNavigator.activate(RootConfig.SignedIn()) + } + + override fun SignedInNavHostComponent.toLogin() { + slotNavigator.activate(RootConfig.Login) + } +} diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostComponent.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostComponent.kt index 7f7e7d0..e143695 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostComponent.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostComponent.kt @@ -22,7 +22,7 @@ import org.koin.core.annotation.InjectedParam @Factory internal class SignedInNavHostComponent( @InjectedParam componentContext: AppComponentContext, - @InjectedParam navigationActions: SignedInNavHostNavigation, + @InjectedParam navigationToLogin: () -> Unit, @InjectedParam initialConfig: SignedInConfig, ) : AppComponent(componentContext, SignedInNavHostViewState()), SignedInNavHost { @@ -36,16 +36,16 @@ internal class SignedInNavHostComponent( childFactory = { config, childCtx -> when (config) { is SignedInConfig.Home -> SignedInChild.Home( - AppComponentFactory.createComponent( + AppComponentFactory.createAppComponent( childContext = childCtx, config.initialStack, ), ) is SignedInConfig.Profile -> SignedInChild.Profile( - AppComponentFactory.createComponent( + navHost = AppComponentFactory.createAppComponent( childContext = childCtx, - navigationActions.toLogin, + navigationToLogin, config.initialStack, ), ) diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostNavigation.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostNavigation.kt index 8792b5b..0f38081 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostNavigation.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostNavigation.kt @@ -2,6 +2,6 @@ package app.futured.kmptemplate.feature.navigation.signedIn import app.futured.arkitekt.decompose.navigation.NavigationActions -data class SignedInNavHostNavigation( - val toLogin: () -> Unit, -) : NavigationActions +internal interface SignedInNavHostNavigation: NavigationActions { + fun SignedInNavHostComponent.toLogin() +} diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/base/AppComponentFactory.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/base/AppComponentFactory.kt index e5a0fce..63a3086 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/base/AppComponentFactory.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/base/AppComponentFactory.kt @@ -19,7 +19,7 @@ internal object AppComponentFactory : KoinComponent { * @param parameters Additional parameters for the component. * @return The created screen component. */ - inline fun , reified N: NavigationActions> createComponent( + inline fun , reified N: NavigationActions> createScreenComponent( childContext: AppComponentContext, navigation: N, vararg parameters: Any?, @@ -38,7 +38,7 @@ internal object AppComponentFactory : KoinComponent { * @param parameters Additional parameters for the component. * @return The created application component. */ - inline fun > createComponent( + inline fun > createAppComponent( childContext: AppComponentContext, vararg parameters: Any?, ): C = get( diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/loginScreen/LoginComponent.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/loginScreen/LoginComponent.kt index 2f19ea1..18e398a 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/loginScreen/LoginComponent.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/loginScreen/LoginComponent.kt @@ -11,13 +11,12 @@ internal class LoginComponent( @InjectedParam componentContext: AppComponentContext, @InjectedParam override val navigation: LoginScreenNavigation, ) : ScreenComponent( - componentContext, - LoginViewState, - ), - LoginScreen { + componentContext = componentContext, + defaultState = LoginViewState, +), LoginScreen, LoginScreenNavigation by navigation { override val actions: LoginScreen.Actions = object : LoginScreen.Actions { - override fun onLoginClick() = navigation.toSignedIn() + override fun onLoginClick() = toSignedIn() } override val viewState: StateFlow = componentState.asStateFlow() diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/loginScreen/LoginScreenNavigation.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/loginScreen/LoginScreenNavigation.kt index 3cebdd9..f4dcee4 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/loginScreen/LoginScreenNavigation.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/loginScreen/LoginScreenNavigation.kt @@ -2,6 +2,6 @@ package app.futured.kmptemplate.feature.ui.loginScreen import app.futured.arkitekt.decompose.navigation.NavigationActions -internal data class LoginScreenNavigation( - val toSignedIn: () -> Unit, -) : NavigationActions +internal interface LoginScreenNavigation : NavigationActions { + fun LoginComponent.toSignedIn() +} diff --git a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondComponent.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondComponent.kt index 3adbdc4..14720c7 100644 --- a/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondComponent.kt +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondComponent.kt @@ -44,12 +44,12 @@ internal class SecondComponent( serializer = SecondScreen.PickerType.serializer(), childFactory = { type, childContext -> when (type) { - SecondScreen.PickerType.Fruit -> AppComponentFactory.createComponent( + SecondScreen.PickerType.Fruit -> AppComponentFactory.createAppComponent( childContext, pickerNavigation, ) - SecondScreen.PickerType.Vegetable -> AppComponentFactory.createComponent( + SecondScreen.PickerType.Vegetable -> AppComponentFactory.createAppComponent( childContext, pickerNavigation, ) From fcfebebfbc29c9e39c89d34100dea16e2b06c2f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rudolf=20Hlad=C3=ADk?= Date: Fri, 7 Feb 2025 13:15:15 +0100 Subject: [PATCH 4/4] Add Third navigation to iOS app --- iosApp/iosApp/Views/Navigation/ProfileTabNavigationView.swift | 2 ++ iosApp/iosApp/Views/Screen/Profile/ProfileView.swift | 1 + iosApp/iosApp/Views/Screen/Profile/ProfileViewModel.swift | 4 ++++ 3 files changed, 7 insertions(+) diff --git a/iosApp/iosApp/Views/Navigation/ProfileTabNavigationView.swift b/iosApp/iosApp/Views/Navigation/ProfileTabNavigationView.swift index 800f917..4a1aafb 100644 --- a/iosApp/iosApp/Views/Navigation/ProfileTabNavigationView.swift +++ b/iosApp/iosApp/Views/Navigation/ProfileTabNavigationView.swift @@ -19,6 +19,8 @@ struct ProfileTabNavigationView: View { switch onEnum(of: child) { case .profile(let entry): ProfileView(ProfileViewModel(entry.screen)) + case .third(let entry): + ThirdView(ThirdViewModel(entry.screen)) } } } diff --git a/iosApp/iosApp/Views/Screen/Profile/ProfileView.swift b/iosApp/iosApp/Views/Screen/Profile/ProfileView.swift index 33758f6..70dfecc 100644 --- a/iosApp/iosApp/Views/Screen/Profile/ProfileView.swift +++ b/iosApp/iosApp/Views/Screen/Profile/ProfileView.swift @@ -12,6 +12,7 @@ struct ProfileView: View { VStack(spacing: 10) { Text(Localizable.login_screen_title.localized) Button(Localizable.generic_sign_out.localized, action: viewModel.onLogoutClick) + Button("Navigate to third", action: viewModel.onThirdClick) } } } diff --git a/iosApp/iosApp/Views/Screen/Profile/ProfileViewModel.swift b/iosApp/iosApp/Views/Screen/Profile/ProfileViewModel.swift index 7e0cdb7..c07df71 100644 --- a/iosApp/iosApp/Views/Screen/Profile/ProfileViewModel.swift +++ b/iosApp/iosApp/Views/Screen/Profile/ProfileViewModel.swift @@ -3,6 +3,7 @@ import SwiftUI protocol ProfileViewModelProtocol: DynamicProperty { func onLogoutClick() + func onThirdClick() } struct ProfileViewModel { @@ -17,4 +18,7 @@ extension ProfileViewModel: ProfileViewModelProtocol { func onLogoutClick() { actions.onLogout() } + func onThirdClick() { + actions.onThird() + } }