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/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() + } } 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 5521d39..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 @@ -9,15 +9,12 @@ 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 @@ -28,15 +25,10 @@ internal class HomeNavHostComponent( @InjectedParam private val initialStack: List, ) : 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() - } + private val homeNavigator: HomeNavigation = HomeNavigator() override val stack: StateFlow> = childStack( - source = homeNavigator, + source = homeNavigator.navigator, serializer = HomeConfig.serializer(), initialStack = { initialStack }, key = "HomeStack", @@ -44,34 +36,34 @@ internal class HomeNavHostComponent( childFactory = { config, childCtx -> when (config) { HomeConfig.First -> HomeChild.First( - AppComponentFactory.createComponent( + AppComponentFactory.createScreenComponent( childContext = childCtx, - navigation = FirstScreenNavigation( - toSecond = { homeNavigator.pushNew(HomeConfig.Second) }, - ), + navigation = homeNavigator, ), ) HomeConfig.Second -> HomeChild.Second( - AppComponentFactory.createComponent( + AppComponentFactory.createScreenComponent( childContext = childCtx, - navigation = SecondScreenNavigation( - pop = { homeNavigator.pop() }, - toThird = { id -> homeNavigator.pushNew(HomeConfig.Third(ThirdScreenArgs(id))) }, - ), + navigation = homeNavigator, ), ) is HomeConfig.Third -> HomeChild.Third( - AppComponentFactory.createComponent( + AppComponentFactory.createScreenComponent( childContext = childCtx, - navigation = ThirdScreenNavigation( - pop = { homeNavigator.pop() }, - ), + navigation = homeNavigator, config.args, ), ) } }, ).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/home/HomeNavigation.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavigation.kt new file mode 100644 index 0000000..76cc81e --- /dev/null +++ b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavigation.kt @@ -0,0 +1,34 @@ +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 +} + +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..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,8 @@ package app.futured.kmptemplate.feature.navigation.profile 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,8 +25,12 @@ sealed interface ProfileConfig { @Serializable data object Profile : ProfileConfig + + @Serializable + data class Third(val args: ThirdScreenArgs) : ProfileConfig } sealed interface ProfileChild { data class Profile(val screen: ProfileScreen) : 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 f5d760c..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 @@ -6,9 +6,10 @@ 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 import com.arkivanov.decompose.router.stack.childStack import com.arkivanov.decompose.router.stack.navigate import com.arkivanov.decompose.router.stack.pop @@ -19,24 +20,31 @@ 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: ProfileNavHostNavigation = 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.createScreenComponent( + childContext = childCtx, + navigation = navigator, + ), + ) + + is ProfileConfig.Third -> ProfileChild.Third( + AppComponentFactory.createScreenComponent( childContext = childCtx, - navigation = ProfileScreenNavigation( - toLogin = navigationActions.toLogin, - ), + navigation = navigator, + config.args, ), ) } @@ -44,9 +52,9 @@ internal class ProfileNavHostComponent( ).asStateFlow() override val actions: ProfileNavHost.Actions = object : ProfileNavHost.Actions { - override fun pop() = stackNavigator.pop() - override fun navigate(newStack: List>) = stackNavigator.navigate { - newStack.map { it.configuration } - } + override fun navigate(newStack: List>) = + navigator.stackNavigator.navigate { newStack.map { it.configuration } } + + override fun pop() = navigator.stackNavigator.pop() } } 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..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 @@ -1,7 +1,30 @@ 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 { + val stackNavigator: StackNavigation +} + +internal class ProfileNavHostNavigator( + private val navigateToLogin: () -> Unit, +) : ProfileNavHostNavigation { + override 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 861e7d9..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 fd0a98e..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 @@ -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 @@ -23,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 { @@ -37,18 +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, - ProfileNavHostNavigation( - toLogin = 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 09e4a68..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,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> createScreenComponent( childContext: AppComponentContext, - navigation: NavigationActions, + navigation: N, vararg parameters: Any?, ): C = get( qualifier = null, @@ -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/firstScreen/FirstComponent.kt b/shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstComponent.kt index 8f30a4e..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 { +), FirstScreen, FirstScreenNavigation by navigation { companion object { private const val COUNTER_ALERT = 10L @@ -36,7 +35,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/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/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) +} 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 970d067..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 @@ -22,10 +22,10 @@ internal class SecondComponent( @InjectedParam componentContext: AppComponentContext, @InjectedParam override val navigation: SecondScreenNavigation, ) : ScreenComponent( - componentContext = componentContext, - defaultState = SecondViewState, -), - SecondScreen { + componentContext = componentContext, + defaultState = SecondViewState, + ), + 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) } }, ) @@ -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, ) @@ -58,7 +58,7 @@ internal class SecondComponent( ).asStateFlow() 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() +}