From 54eca8e5140ef1807a0de57379417e35191ab740 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 30 Oct 2024 15:41:46 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../navigation/inner/InnerNavigation.kt | 16 ++++++ .../navigation/inner/InnerNavigationHolder.kt | 5 ++ .../impl/DefaultOnboardingEntryComponent.kt | 53 ++++++++++++++++++- .../entry/impl/model/OnboardingEntryModel.kt | 23 ++++++++ .../entry/impl/model/state/OnboardingState.kt | 7 +++ .../impl/routing/OnboardingChildFactory.kt | 21 ++++++++ .../v2/entry/impl/routing/OnboardingRoute.kt | 9 ++++ .../v2/entry/impl/ui/OnboardingEntry.kt | 38 +++++++------ .../stepper/api/OnboardingStepperComponent.kt | 1 + .../v2/stepper/impl/ui/OnboardingStepper.kt | 15 ++++++ .../api/OnboardingWallet12Component.kt | 13 +++++ .../DefaultOnboardingWallet12Component.kt | 46 ++++++++++++++++ .../v2/wallet12/impl/di/ComponentModule.kt | 20 +++++++ 13 files changed, 250 insertions(+), 17 deletions(-) create mode 100644 core/decompose/src/main/kotlin/com/tangem/core/decompose/navigation/inner/InnerNavigation.kt create mode 100644 core/decompose/src/main/kotlin/com/tangem/core/decompose/navigation/inner/InnerNavigationHolder.kt create mode 100644 features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/model/OnboardingEntryModel.kt create mode 100644 features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/model/state/OnboardingState.kt create mode 100644 features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/routing/OnboardingChildFactory.kt create mode 100644 features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/routing/OnboardingRoute.kt create mode 100644 features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/wallet12/api/OnboardingWallet12Component.kt create mode 100644 features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/wallet12/impl/DefaultOnboardingWallet12Component.kt create mode 100644 features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/wallet12/impl/di/ComponentModule.kt diff --git a/core/decompose/src/main/kotlin/com/tangem/core/decompose/navigation/inner/InnerNavigation.kt b/core/decompose/src/main/kotlin/com/tangem/core/decompose/navigation/inner/InnerNavigation.kt new file mode 100644 index 0000000000..eecc01d57f --- /dev/null +++ b/core/decompose/src/main/kotlin/com/tangem/core/decompose/navigation/inner/InnerNavigation.kt @@ -0,0 +1,16 @@ +package com.tangem.core.decompose.navigation.inner + +import kotlinx.coroutines.flow.StateFlow + +/** + * Interface to provide internal navigation access from child to parent + */ +interface InnerNavigation { + val state: StateFlow + fun pop(onComplete: (Boolean) -> Unit) +} + +interface InnerNavigationState { + val stackSize: Int + val stackMaxSize: Int? +} \ No newline at end of file diff --git a/core/decompose/src/main/kotlin/com/tangem/core/decompose/navigation/inner/InnerNavigationHolder.kt b/core/decompose/src/main/kotlin/com/tangem/core/decompose/navigation/inner/InnerNavigationHolder.kt new file mode 100644 index 0000000000..3c60bfb7b7 --- /dev/null +++ b/core/decompose/src/main/kotlin/com/tangem/core/decompose/navigation/inner/InnerNavigationHolder.kt @@ -0,0 +1,5 @@ +package com.tangem.core.decompose.navigation.inner + +interface InnerNavigationHolder { + val innerNavigation: InnerNavigation +} \ No newline at end of file diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/DefaultOnboardingEntryComponent.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/DefaultOnboardingEntryComponent.kt index df2a3fbde4..4766803dd8 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/DefaultOnboardingEntryComponent.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/DefaultOnboardingEntryComponent.kt @@ -2,10 +2,21 @@ package com.tangem.features.onboarding.v2.entry.impl import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +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.pop +import com.arkivanov.decompose.value.Value import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.child +import com.tangem.core.decompose.context.childByContext +import com.tangem.core.decompose.model.getOrCreateModel +import com.tangem.core.decompose.navigation.inner.InnerNavigationHolder import com.tangem.core.ui.extensions.stringReference import com.tangem.features.onboarding.v2.entry.OnboardingEntryComponent +import com.tangem.features.onboarding.v2.entry.impl.model.OnboardingEntryModel +import com.tangem.features.onboarding.v2.entry.impl.routing.OnboardingChildFactory +import com.tangem.features.onboarding.v2.entry.impl.routing.OnboardingRoute import com.tangem.features.onboarding.v2.entry.impl.ui.OnboardingEntry import com.tangem.features.onboarding.v2.stepper.api.OnboardingStepperComponent import dagger.assisted.Assisted @@ -16,21 +27,56 @@ internal class DefaultOnboardingEntryComponent @AssistedInject constructor( @Assisted val context: AppComponentContext, @Assisted val params: OnboardingEntryComponent.Params, stepperFactory: OnboardingStepperComponent.Factory, + onboardingChildFactory: OnboardingChildFactory, ) : OnboardingEntryComponent, AppComponentContext by context { + private val model: OnboardingEntryModel = getOrCreateModel(params) + private val stepperComponent = stepperFactory.create( context = child("stepper"), params = OnboardingStepperComponent.Params( scanResponse = params.scanResponse, initState = OnboardingStepperComponent.StepperState( currentStep = 0, - steps = 3, + steps = 0, title = stringReference(""), + showProgress = false, ), - popBack = { router.pop() }, + popBack = { + popInternal { success -> + if (success.not()) { + stackNavigation.pop() + } + } + }, ), ) + private val stackNavigation = StackNavigation() + + private val innerStack: Value> = childStack( + key = "innerStack", + source = stackNavigation, + serializer = null, + initialConfiguration = model.state.value.currentRoute, + handleBackButton = true, + childFactory = { configuration, factoryContext -> + onboardingChildFactory.createChild( + route = configuration, + childContext = childByContext(factoryContext), + ) + }, + ) + + private fun popInternal(onComplete: (Boolean) -> Unit) { + val activeChild = innerStack.value.active.instance + if (activeChild is InnerNavigationHolder) { + activeChild.innerNavigation.pop(onComplete) + } else { + onComplete(false) + } + } + @Composable override fun Content(modifier: Modifier) { OnboardingEntry( @@ -38,6 +84,9 @@ internal class DefaultOnboardingEntryComponent @AssistedInject constructor( stepperContent = { modifierParam -> stepperComponent.Content(modifierParam) }, + content = { modifierParam -> + // TODO + }, ) } diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/model/OnboardingEntryModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/model/OnboardingEntryModel.kt new file mode 100644 index 0000000000..8c4d8cb474 --- /dev/null +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/model/OnboardingEntryModel.kt @@ -0,0 +1,23 @@ +package com.tangem.features.onboarding.v2.entry.impl.model + +import com.tangem.core.decompose.di.ComponentScoped +import com.tangem.core.decompose.model.Model +import com.tangem.features.onboarding.v2.entry.OnboardingEntryComponent +import com.tangem.features.onboarding.v2.entry.impl.model.state.OnboardingState +import com.tangem.features.onboarding.v2.entry.impl.routing.OnboardingRoute +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.flow.MutableStateFlow +import javax.inject.Inject + +@ComponentScoped +internal class OnboardingEntryModel @Inject constructor( + override val dispatchers: CoroutineDispatcherProvider, + private val params: OnboardingEntryComponent.Params, +) : Model() { + + val state = MutableStateFlow( + OnboardingState( + currentRoute = OnboardingRoute.Wallet12(params.scanResponse), + ), + ) +} \ No newline at end of file diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/model/state/OnboardingState.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/model/state/OnboardingState.kt new file mode 100644 index 0000000000..66e5d7c4c4 --- /dev/null +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/model/state/OnboardingState.kt @@ -0,0 +1,7 @@ +package com.tangem.features.onboarding.v2.entry.impl.model.state + +import com.tangem.features.onboarding.v2.entry.impl.routing.OnboardingRoute + +internal data class OnboardingState( + val currentRoute: OnboardingRoute, +) \ No newline at end of file diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/routing/OnboardingChildFactory.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/routing/OnboardingChildFactory.kt new file mode 100644 index 0000000000..faf659c52e --- /dev/null +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/routing/OnboardingChildFactory.kt @@ -0,0 +1,21 @@ +package com.tangem.features.onboarding.v2.entry.impl.routing + +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.features.onboarding.v2.wallet12.api.OnboardingWallet12Component +import javax.inject.Inject + +internal class OnboardingChildFactory @Inject constructor( + private val wallet12ComponentFactory: OnboardingWallet12Component.Factory, +) { + + fun createChild(route: OnboardingRoute, childContext: AppComponentContext) { + when (route) { + is OnboardingRoute.Wallet12 -> wallet12ComponentFactory.create( + context = childContext, + params = OnboardingWallet12Component.Params( + scanResponse = route.scanResponse, + ), + ) + } + } +} \ No newline at end of file diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/routing/OnboardingRoute.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/routing/OnboardingRoute.kt new file mode 100644 index 0000000000..5b9847190a --- /dev/null +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/routing/OnboardingRoute.kt @@ -0,0 +1,9 @@ +package com.tangem.features.onboarding.v2.entry.impl.routing + +import com.tangem.core.decompose.navigation.Route +import com.tangem.domain.models.scan.ScanResponse + +sealed class OnboardingRoute : Route { + + data class Wallet12(val scanResponse: ScanResponse) : OnboardingRoute() +} \ No newline at end of file diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/ui/OnboardingEntry.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/ui/OnboardingEntry.kt index 5a17a8966e..3b8da8d889 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/ui/OnboardingEntry.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/ui/OnboardingEntry.kt @@ -1,12 +1,10 @@ package com.tangem.features.onboarding.v2.entry.impl.ui import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.foundation.layout.* import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview import com.tangem.core.ui.components.appbar.TangemTopAppBar import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM @@ -15,7 +13,11 @@ import com.tangem.core.ui.res.TangemThemePreview import com.tangem.features.onboarding.v2.impl.R @Composable -internal inline fun OnboardingEntry(modifier: Modifier = Modifier, stepperContent: @Composable (Modifier) -> Unit) { +internal inline fun OnboardingEntry( + modifier: Modifier = Modifier, + stepperContent: @Composable (Modifier) -> Unit, + content: @Composable (Modifier) -> Unit, +) { Column( modifier = modifier .fillMaxSize() @@ -23,6 +25,7 @@ internal inline fun OnboardingEntry(modifier: Modifier = Modifier, stepperConten .background(TangemTheme.colors.background.primary), ) { stepperContent(Modifier.fillMaxWidth()) + content(Modifier.fillMaxSize()) } } @@ -30,15 +33,20 @@ internal inline fun OnboardingEntry(modifier: Modifier = Modifier, stepperConten @Composable private fun Preview() { TangemThemePreview { - OnboardingEntry { - TangemTopAppBar( - title = "Onboarding", - startButton = TopAppBarButtonUM( - iconRes = R.drawable.ic_back_24, - enabled = true, - onIconClicked = {}, - ), - ) - } + OnboardingEntry( + stepperContent = { + TangemTopAppBar( + title = "Onboarding", + startButton = TopAppBarButtonUM( + iconRes = R.drawable.ic_back_24, + enabled = true, + onIconClicked = {}, + ), + ) + }, + content = { modifier -> + Box(modifier.background(Color.Cyan.copy(alpha = 0.3f))) + }, + ) } } \ No newline at end of file diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/stepper/api/OnboardingStepperComponent.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/stepper/api/OnboardingStepperComponent.kt index 235ab31fc8..328deab736 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/stepper/api/OnboardingStepperComponent.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/stepper/api/OnboardingStepperComponent.kt @@ -13,6 +13,7 @@ internal interface OnboardingStepperComponent : ComposableContentComponent { @IntRange(from = 0) val currentStep: Int, @IntRange(from = 0) val steps: Int, val title: TextReference, + val showProgress: Boolean, ) class Params( diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/stepper/impl/ui/OnboardingStepper.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/stepper/impl/ui/OnboardingStepper.kt index a4d735e82b..a22c50812e 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/stepper/impl/ui/OnboardingStepper.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/stepper/impl/ui/OnboardingStepper.kt @@ -1,11 +1,15 @@ package com.tangem.features.onboarding.v2.stepper.impl.ui +import androidx.compose.animation.core.LinearEasing +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.tween import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -29,6 +33,15 @@ internal fun OnboardingStepper( val fraction = state.currentStep.toFloat() / state.steps.coerceAtLeast(1) val animatedIndicatorFraction by TangemAnimations.horizontalIndicatorAsState(targetFraction = fraction) + val progressAlpha by animateFloatAsState( + targetValue = if (state.showProgress) 1f else 0f, + animationSpec = tween( + durationMillis = 300, + easing = LinearEasing, + ), + label = "progressAlpha", + ) + Column( modifier = modifier, verticalArrangement = Arrangement.spacedBy(8.dp), @@ -48,6 +61,7 @@ internal fun OnboardingStepper( LinearProgressIndicator( modifier = Modifier + .alpha(progressAlpha) .padding(horizontal = 16.dp) .height(4.dp) .fillMaxWidth(), @@ -74,6 +88,7 @@ private fun Preview() { currentStep = 2, steps = 3, title = resourceReference(R.string.common_done), + showProgress = true, ), onBackClick = {}, onSupportButtonClick = {}, diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/wallet12/api/OnboardingWallet12Component.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/wallet12/api/OnboardingWallet12Component.kt new file mode 100644 index 0000000000..c950955961 --- /dev/null +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/wallet12/api/OnboardingWallet12Component.kt @@ -0,0 +1,13 @@ +package com.tangem.features.onboarding.v2.wallet12.api + +import com.tangem.core.decompose.factory.ComponentFactory +import com.tangem.core.decompose.navigation.inner.InnerNavigationHolder +import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.domain.models.scan.ScanResponse + +interface OnboardingWallet12Component : ComposableContentComponent, InnerNavigationHolder { + + data class Params(val scanResponse: ScanResponse) + + interface Factory : ComponentFactory +} \ No newline at end of file diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/wallet12/impl/DefaultOnboardingWallet12Component.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/wallet12/impl/DefaultOnboardingWallet12Component.kt new file mode 100644 index 0000000000..edfbb70eed --- /dev/null +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/wallet12/impl/DefaultOnboardingWallet12Component.kt @@ -0,0 +1,46 @@ +package com.tangem.features.onboarding.v2.wallet12.impl + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.decompose.navigation.inner.InnerNavigation +import com.tangem.core.decompose.navigation.inner.InnerNavigationState +import com.tangem.features.onboarding.v2.wallet12.api.OnboardingWallet12Component +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import kotlinx.coroutines.flow.MutableStateFlow + +@Suppress("UnusedPrivateMember") +class DefaultOnboardingWallet12Component @AssistedInject constructor( + @Assisted private val context: AppComponentContext, + @Assisted private val params: OnboardingWallet12Component.Params, +) : OnboardingWallet12Component, AppComponentContext by context { + + override val innerNavigation: InnerNavigation = object : InnerNavigation { + override val state = MutableStateFlow( + Wallet12InnerNavigationState(0, null), // TODO + ) + + override fun pop(onComplete: (Boolean) -> Unit) { + // TODO + } + } + + @Composable + override fun Content(modifier: Modifier) { + } + + @AssistedFactory + interface Factory : OnboardingWallet12Component.Factory { + override fun create( + context: AppComponentContext, + params: OnboardingWallet12Component.Params, + ): DefaultOnboardingWallet12Component + } +} + +data class Wallet12InnerNavigationState( + override val stackSize: Int, + override val stackMaxSize: Int?, +) : InnerNavigationState \ No newline at end of file diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/wallet12/impl/di/ComponentModule.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/wallet12/impl/di/ComponentModule.kt new file mode 100644 index 0000000000..70322dc3b1 --- /dev/null +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/wallet12/impl/di/ComponentModule.kt @@ -0,0 +1,20 @@ +package com.tangem.features.onboarding.v2.wallet12.impl.di + +import com.tangem.features.onboarding.v2.wallet12.api.OnboardingWallet12Component +import com.tangem.features.onboarding.v2.wallet12.impl.DefaultOnboardingWallet12Component +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal interface ComponentModule { + + @Binds + @Singleton + fun bindOnboardingWallet12Component( + factory: DefaultOnboardingWallet12Component.Factory, + ): OnboardingWallet12Component.Factory +} \ No newline at end of file