From c761f40891d238608da19ca9e69e4a94c286326e Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 28 Jul 2025 13:57:47 +0500 Subject: [PATCH] Updated on 2026-08-14 --- .../entry/AddExistingWalletModel.kt | 37 +++--- .../AddExistingWalletStepperStateManager.kt | 79 +++++++++++++ .../DefaultAddExistingWalletComponent.kt | 43 ++++++- .../entry/di/AddExistingWalletModule.kt | 32 +++++- .../entry/entity/AddExistingWalletUM.kt | 2 +- .../routing/AddExistingWalletChildFactory.kt | 4 +- .../entry/routing/AddExistingWalletRoute.kt | 2 +- .../entry/ui/AddExistingWalletContent.kt | 25 +++-- .../stepper/api/HotWalletStepperComponent.kt | 33 ++++++ .../stepper/di/HotWalletStepperModule.kt | 18 +++ .../impl/DefaultHotWalletStepperComponent.kt | 48 ++++++++ .../stepper/impl/HotWalletStepperModel.kt | 42 +++++++ .../stepper/impl/ui/HotWalletStepper.kt | 106 ++++++++++++++++++ 13 files changed, 437 insertions(+), 34 deletions(-) create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletStepperStateManager.kt create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/api/HotWalletStepperComponent.kt create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/di/HotWalletStepperModule.kt create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/DefaultHotWalletStepperComponent.kt create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/HotWalletStepperModel.kt create mode 100644 features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/ui/HotWalletStepper.kt diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletModel.kt index c021c4b032..ad499a7aa4 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletModel.kt @@ -1,8 +1,9 @@ -package com.tangem.features.hotwallet.addexistingwallet.root +package com.tangem.features.hotwallet.addexistingwallet.entry import com.arkivanov.decompose.router.stack.StackNavigation import com.arkivanov.decompose.router.stack.pop import com.arkivanov.decompose.router.stack.push +import com.arkivanov.decompose.router.stack.replaceAll import com.arkivanov.decompose.router.stack.replaceCurrent import com.tangem.common.routing.AppRoute import com.tangem.core.decompose.di.ModelScoped @@ -10,7 +11,7 @@ import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.navigation.Router import com.tangem.domain.settings.ShouldAskPermissionUseCase import com.tangem.features.hotwallet.addexistingwallet.im.port.AddExistingWalletImportComponent -import com.tangem.features.hotwallet.addexistingwallet.root.routing.AddExistingWalletRoute +import com.tangem.features.hotwallet.addexistingwallet.entry.routing.AddExistingWalletRoute import com.tangem.features.hotwallet.addexistingwallet.start.AddExistingWalletStartComponent import com.tangem.features.hotwallet.manualbackup.completed.ManualBackupCompletedComponent import com.tangem.features.hotwallet.accesscode.confirm.ConfirmAccessCodeComponent @@ -43,14 +44,31 @@ internal class AddExistingWalletModel @Inject constructor( when (currentRoute) { is AddExistingWalletRoute.Import -> stackNavigation.pop() is AddExistingWalletRoute.BackupCompleted -> Unit + is AddExistingWalletRoute.SetAccessCode -> Unit is AddExistingWalletRoute.ConfirmAccessCode -> stackNavigation.pop() - is AddExistingWalletRoute.SetAccessCode -> stackNavigation.pop() is AddExistingWalletRoute.PushNotifications -> Unit is AddExistingWalletRoute.SetupFinished -> Unit is AddExistingWalletRoute.Start -> Unit } } + fun onSkipAccessCode() { + navigateToPushNotificationsOrNext() + } + + private fun navigateToPushNotificationsOrNext() { + modelScope.launch { + val shouldRequestPush = shouldAskPermissionUseCase(PUSH_PERMISSION) + if (shouldRequestPush) { + // is yet blocked by [REDACTED_TASK_KEY] + // stackNavigation.replaceAll(AddExistingWalletRoute.PushNotifications) + stackNavigation.replaceAll(AddExistingWalletRoute.SetupFinished) + } else { + stackNavigation.replaceAll(AddExistingWalletRoute.SetupFinished) + } + } + } + inner class AddExistingWalletStartModelCallbacks : AddExistingWalletStartComponent.ModelCallbacks { override fun onBackClick() { router.pop() @@ -69,7 +87,7 @@ internal class AddExistingWalletModel @Inject constructor( inner class ManualBackupCompletedComponentModelCallbacks : ManualBackupCompletedComponent.ModelCallbacks { override fun onContinueClick() { - stackNavigation.push(AddExistingWalletRoute.SetAccessCode) + stackNavigation.replaceCurrent(AddExistingWalletRoute.SetAccessCode) } } @@ -81,16 +99,7 @@ internal class AddExistingWalletModel @Inject constructor( inner class ConfirmAccessCodeModelCallbacks : ConfirmAccessCodeComponent.ModelCallbacks { override fun onAccessCodeConfirmed() { - modelScope.launch { - val shouldRequestPush = shouldAskPermissionUseCase(PUSH_PERMISSION) - if (shouldRequestPush) { - // is yet blocked by [REDACTED_TASK_KEY] - // stackNavigation.replaceCurrent(AddExistingWalletRoute.PushNotifications) - stackNavigation.replaceCurrent(AddExistingWalletRoute.SetupFinished) - } else { - stackNavigation.replaceCurrent(AddExistingWalletRoute.SetupFinished) - } - } + navigateToPushNotificationsOrNext() } } diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletStepperStateManager.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletStepperStateManager.kt new file mode 100644 index 0000000000..14eabf9ebc --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletStepperStateManager.kt @@ -0,0 +1,79 @@ +package com.tangem.features.hotwallet.addexistingwallet.entry + +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.features.hotwallet.addexistingwallet.entry.routing.AddExistingWalletRoute +import com.tangem.features.hotwallet.impl.R +import com.tangem.features.hotwallet.stepper.api.HotWalletStepperComponent + +internal class AddExistingWalletStepperStateManager { + + fun getStepperState(route: AddExistingWalletRoute): HotWalletStepperComponent.StepperUM? { + return when (route) { + is AddExistingWalletRoute.Start -> null + + is AddExistingWalletRoute.Import -> HotWalletStepperComponent.StepperUM( + currentStep = STEP_IMPORT, + steps = STEPS_COUNT, + title = resourceReference(R.string.wallet_import_seed_navtitle), + showBackButton = true, + showSkipButton = false, + showFeedbackButton = true, + ) + + is AddExistingWalletRoute.BackupCompleted -> HotWalletStepperComponent.StepperUM( + currentStep = STEP_BACKUP, + steps = STEPS_COUNT, + title = resourceReference(R.string.common_backup), + showBackButton = false, + showSkipButton = false, + showFeedbackButton = false, + ) + + is AddExistingWalletRoute.SetAccessCode -> HotWalletStepperComponent.StepperUM( + currentStep = STEP_ACCESS_CODE, + steps = STEPS_COUNT, + title = resourceReference(R.string.access_code_navtitle), + showBackButton = false, + showSkipButton = true, + showFeedbackButton = false, + ) + + is AddExistingWalletRoute.ConfirmAccessCode -> HotWalletStepperComponent.StepperUM( + currentStep = STEP_ACCESS_CODE, + steps = STEPS_COUNT, + title = resourceReference(R.string.access_code_navtitle), + showBackButton = true, + showSkipButton = true, + showFeedbackButton = false, + ) + + is AddExistingWalletRoute.PushNotifications -> HotWalletStepperComponent.StepperUM( + currentStep = STEP_NOTIFICATIONS, + steps = STEPS_COUNT, + title = resourceReference(R.string.onboarding_title_notifications), + showBackButton = false, + showSkipButton = false, + showFeedbackButton = false, + ) + + is AddExistingWalletRoute.SetupFinished -> HotWalletStepperComponent.StepperUM( + currentStep = STEP_DONE, + steps = STEPS_COUNT, + title = resourceReference(R.string.common_done), + showBackButton = false, + showSkipButton = false, + showFeedbackButton = false, + ) + } + } + + companion object { + private const val STEPS_COUNT = 5 + + private const val STEP_IMPORT = 1 + private const val STEP_BACKUP = 2 + private const val STEP_ACCESS_CODE = 3 + private const val STEP_NOTIFICATIONS = 4 + private const val STEP_DONE = 5 + } +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/DefaultAddExistingWalletComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/DefaultAddExistingWalletComponent.kt index 1792942faf..53e611282b 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/DefaultAddExistingWalletComponent.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/DefaultAddExistingWalletComponent.kt @@ -1,4 +1,4 @@ -package com.tangem.features.hotwallet.addexistingwallet.root +package com.tangem.features.hotwallet.addexistingwallet.entry import androidx.activity.compose.BackHandler import androidx.compose.runtime.Composable @@ -6,14 +6,16 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import com.arkivanov.decompose.extensions.compose.subscribeAsState import com.arkivanov.decompose.router.stack.childStack -import com.arkivanov.decompose.router.stack.pop import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.childByContext import com.tangem.core.decompose.model.getOrCreateModel +import com.tangem.core.ui.extensions.TextReference import com.tangem.features.hotwallet.AddExistingWalletComponent -import com.tangem.features.hotwallet.addexistingwallet.root.routing.AddExistingWalletChildFactory -import com.tangem.features.hotwallet.addexistingwallet.root.routing.AddExistingWalletRoute -import com.tangem.features.hotwallet.addexistingwallet.root.ui.AddExistingWalletContent +import com.tangem.features.hotwallet.addexistingwallet.entry.routing.AddExistingWalletChildFactory +import com.tangem.features.hotwallet.addexistingwallet.entry.routing.AddExistingWalletRoute +import com.tangem.features.hotwallet.addexistingwallet.entry.ui.AddExistingWalletContent +import com.tangem.features.hotwallet.stepper.api.HotWalletStepperComponent +import com.tangem.features.hotwallet.stepper.impl.DefaultHotWalletStepperComponent import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -21,7 +23,9 @@ import dagger.assisted.AssistedInject internal class DefaultAddExistingWalletComponent @AssistedInject constructor( @Assisted appComponentContext: AppComponentContext, @Assisted private val params: Unit, + private val stepperStateManager: AddExistingWalletStepperStateManager, addExistingWalletChildFactory: AddExistingWalletChildFactory, + stepperComponentFactory: DefaultHotWalletStepperComponent.Factory, ) : AddExistingWalletComponent, AppComponentContext by appComponentContext { private val model: AddExistingWalletModel = getOrCreateModel(params) @@ -43,13 +47,42 @@ internal class DefaultAddExistingWalletComponent @AssistedInject constructor( }, ) + private val stepperComponent = stepperComponentFactory.create( + context = this, + params = HotWalletStepperComponent.Params( + initState = HotWalletStepperComponent.StepperUM( + currentStep = 0, + steps = 0, + title = TextReference.EMPTY, + showBackButton = false, + showSkipButton = false, + showFeedbackButton = false, + ), + callback = object : HotWalletStepperComponent.ModelCallback { + override fun onBackClick() { + onChildBack() + } + + override fun onSkipClick() { + model.onSkipAccessCode() + } + }, + ), + ) + @Composable override fun Content(modifier: Modifier) { val stackState by innerStack.subscribeAsState() + val currentRoute = stackState.active.configuration BackHandler(onBack = ::onChildBack) + + val stepperState = stepperStateManager.getStepperState(currentRoute) + stepperState?.let { stepperComponent.updateState(it) } + AddExistingWalletContent( stackState = stackState, + stepperComponent = stepperComponent.takeIf { stepperState != null }, ) } diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/di/AddExistingWalletModule.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/di/AddExistingWalletModule.kt index 0c6a950236..a8af84bae5 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/di/AddExistingWalletModule.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/di/AddExistingWalletModule.kt @@ -1,11 +1,16 @@ -package com.tangem.features.hotwallet.addexistingwallet.root.di +package com.tangem.features.hotwallet.addexistingwallet.entry.di import com.tangem.core.decompose.model.Model import com.tangem.features.hotwallet.AddExistingWalletComponent -import com.tangem.features.hotwallet.addexistingwallet.root.AddExistingWalletModel -import com.tangem.features.hotwallet.addexistingwallet.root.DefaultAddExistingWalletComponent +import com.tangem.features.hotwallet.addexistingwallet.entry.AddExistingWalletModel +import com.tangem.features.hotwallet.addexistingwallet.entry.AddExistingWalletStepperStateManager +import com.tangem.features.hotwallet.addexistingwallet.entry.DefaultAddExistingWalletComponent +import com.tangem.features.hotwallet.stepper.api.HotWalletStepperComponent +import com.tangem.features.hotwallet.stepper.impl.DefaultHotWalletStepperComponent +import com.tangem.features.hotwallet.stepper.impl.HotWalletStepperModel import dagger.Binds import dagger.Module +import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import dagger.multibindings.ClassKey @@ -14,7 +19,7 @@ import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) -internal interface AddExistingWalletModule { +internal interface AddExistingWalletModuleBinds { @Binds @Singleton @@ -26,4 +31,23 @@ internal interface AddExistingWalletModule { @IntoMap @ClassKey(AddExistingWalletModel::class) fun bindAddExistingWalletModel(model: AddExistingWalletModel): Model + + @Binds + fun bindFactory(impl: DefaultHotWalletStepperComponent.Factory): HotWalletStepperComponent.Factory + + @Binds + @IntoMap + @ClassKey(HotWalletStepperModel::class) + fun bindHotWalletStepperModel(model: HotWalletStepperModel): Model +} + +@Module +@InstallIn(SingletonComponent::class) +internal object AddExistingWalletModule { + + @Provides + @Singleton + fun provideAddExistingWalletStepperStateManager(): AddExistingWalletStepperStateManager { + return AddExistingWalletStepperStateManager() + } } \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/entity/AddExistingWalletUM.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/entity/AddExistingWalletUM.kt index 8030308588..fb538425f2 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/entity/AddExistingWalletUM.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/entity/AddExistingWalletUM.kt @@ -1,4 +1,4 @@ -package com.tangem.features.hotwallet.addexistingwallet.root.entity +package com.tangem.features.hotwallet.addexistingwallet.entry.entity internal data class AddExistingWalletUM( val onBackClick: () -> Unit, diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletChildFactory.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletChildFactory.kt index 38642f07bf..10bdd316a8 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletChildFactory.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletChildFactory.kt @@ -1,10 +1,10 @@ -package com.tangem.features.hotwallet.addexistingwallet.root.routing +package com.tangem.features.hotwallet.addexistingwallet.entry.routing import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.features.hotwallet.accesscode.set.SetAccessCodeComponent import com.tangem.features.hotwallet.accesscode.confirm.ConfirmAccessCodeComponent -import com.tangem.features.hotwallet.addexistingwallet.root.AddExistingWalletModel +import com.tangem.features.hotwallet.addexistingwallet.entry.AddExistingWalletModel import com.tangem.features.hotwallet.addexistingwallet.start.AddExistingWalletStartComponent import com.tangem.features.hotwallet.addexistingwallet.im.port.AddExistingWalletImportComponent import com.tangem.features.hotwallet.manualbackup.completed.ManualBackupCompletedComponent diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletRoute.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletRoute.kt index f4f83e4193..f1102b80eb 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletRoute.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/routing/AddExistingWalletRoute.kt @@ -1,4 +1,4 @@ -package com.tangem.features.hotwallet.addexistingwallet.root.routing +package com.tangem.features.hotwallet.addexistingwallet.entry.routing import com.tangem.core.decompose.navigation.Route import kotlinx.serialization.Serializable diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/ui/AddExistingWalletContent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/ui/AddExistingWalletContent.kt index bc43867900..f0fd3f4adf 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/ui/AddExistingWalletContent.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/ui/AddExistingWalletContent.kt @@ -1,6 +1,7 @@ -package com.tangem.features.hotwallet.addexistingwallet.root.ui +package com.tangem.features.hotwallet.addexistingwallet.entry.ui import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.systemBarsPadding @@ -12,19 +13,29 @@ import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation import com.arkivanov.decompose.router.stack.ChildStack import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.core.ui.res.TangemTheme -import com.tangem.features.hotwallet.addexistingwallet.root.routing.AddExistingWalletRoute +import com.tangem.features.hotwallet.addexistingwallet.entry.routing.AddExistingWalletRoute +import com.tangem.features.hotwallet.stepper.api.HotWalletStepperComponent @Composable -internal fun AddExistingWalletContent(stackState: ChildStack) { - Children( - stack = stackState, - animation = stackAnimation(slide()), +internal fun AddExistingWalletContent( + stackState: ChildStack, + stepperComponent: HotWalletStepperComponent?, +) { + Column( modifier = Modifier .background(color = TangemTheme.colors.background.primary) .fillMaxSize() .imePadding() .systemBarsPadding(), ) { - it.instance.Content(Modifier.fillMaxSize()) + stepperComponent?.Content(Modifier) + + Children( + stack = stackState, + animation = stackAnimation(slide()), + modifier = Modifier.fillMaxSize(), + ) { + it.instance.Content(Modifier.fillMaxSize()) + } } } \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/api/HotWalletStepperComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/api/HotWalletStepperComponent.kt new file mode 100644 index 0000000000..c14de91c72 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/api/HotWalletStepperComponent.kt @@ -0,0 +1,33 @@ +package com.tangem.features.hotwallet.stepper.api + +import androidx.annotation.IntRange +import com.tangem.core.decompose.factory.ComponentFactory +import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.core.ui.extensions.TextReference +import kotlinx.coroutines.flow.StateFlow + +interface HotWalletStepperComponent : ComposableContentComponent { + + data class StepperUM( + @IntRange(from = 0) val currentStep: Int, + @IntRange(from = 0) val steps: Int, + val title: TextReference, + val showBackButton: Boolean, + val showSkipButton: Boolean, + val showFeedbackButton: Boolean, + ) + + interface ModelCallback { + fun onBackClick() + fun onSkipClick() + } + + class Params( + val initState: StepperUM, + val callback: ModelCallback, + ) + + val state: StateFlow + + interface Factory : ComponentFactory +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/di/HotWalletStepperModule.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/di/HotWalletStepperModule.kt new file mode 100644 index 0000000000..211c9265f3 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/di/HotWalletStepperModule.kt @@ -0,0 +1,18 @@ +package com.tangem.features.hotwallet.stepper.di + +import com.tangem.features.hotwallet.stepper.api.HotWalletStepperComponent +import com.tangem.features.hotwallet.stepper.impl.DefaultHotWalletStepperComponent +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent + +@Module +@InstallIn(SingletonComponent::class) +internal interface HotWalletStepperModule { + + @Binds + fun bindHotWalletStepperComponentFactory( + impl: DefaultHotWalletStepperComponent.Factory, + ): HotWalletStepperComponent.Factory +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/DefaultHotWalletStepperComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/DefaultHotWalletStepperComponent.kt new file mode 100644 index 0000000000..320920105b --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/DefaultHotWalletStepperComponent.kt @@ -0,0 +1,48 @@ +package com.tangem.features.hotwallet.stepper.impl + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.decompose.model.getOrCreateModel +import com.tangem.features.hotwallet.stepper.api.HotWalletStepperComponent +import com.tangem.features.hotwallet.stepper.impl.ui.HotWalletStepper +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject + +internal class DefaultHotWalletStepperComponent @AssistedInject constructor( + @Assisted val context: AppComponentContext, + @Assisted val params: HotWalletStepperComponent.Params, +) : HotWalletStepperComponent, AppComponentContext by context { + + private val model: HotWalletStepperModel = getOrCreateModel(params) + + override val state = model.uiState + + fun updateState(newState: HotWalletStepperComponent.StepperUM) { + model.updateState(newState) + } + + @Composable + override fun Content(modifier: Modifier) { + val uiState by state.collectAsStateWithLifecycle() + + HotWalletStepper( + state = uiState, + modifier = modifier, + onBackClick = model::onBackClick, + onSkipClick = model::onSkipClick, + onFeedbackClick = model::onFeedbackClick, + ) + } + + @AssistedFactory + interface Factory : HotWalletStepperComponent.Factory { + override fun create( + context: AppComponentContext, + params: HotWalletStepperComponent.Params, + ): DefaultHotWalletStepperComponent + } +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/HotWalletStepperModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/HotWalletStepperModel.kt new file mode 100644 index 0000000000..ed64c2fb93 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/HotWalletStepperModel.kt @@ -0,0 +1,42 @@ +package com.tangem.features.hotwallet.stepper.impl + +import androidx.compose.runtime.Stable +import com.tangem.core.decompose.di.ModelScoped +import com.tangem.core.decompose.model.Model +import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.features.hotwallet.stepper.api.HotWalletStepperComponent +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import javax.inject.Inject + +@Stable +@ModelScoped +internal class HotWalletStepperModel @Inject constructor( + paramsContainer: ParamsContainer, + override val dispatchers: CoroutineDispatcherProvider, +) : Model() { + + val params = paramsContainer.require() + + val uiState: StateFlow + field = MutableStateFlow(params.initState) + + fun updateState(newState: HotWalletStepperComponent.StepperUM) { + uiState.value = newState + } + + fun onBackClick() { + params.callback.onBackClick() + } + + fun onSkipClick() { + // TODO send analytics + params.callback.onSkipClick() + } + + fun onFeedbackClick() { + // TODO send analytics + // openFeedback() + } +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/ui/HotWalletStepper.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/ui/HotWalletStepper.kt new file mode 100644 index 0000000000..f9af5a29b8 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/ui/HotWalletStepper.kt @@ -0,0 +1,106 @@ +package com.tangem.features.hotwallet.stepper.impl.ui + +import android.content.res.Configuration +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.tangem.core.ui.components.appbar.TangemTopAppBar +import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM +import com.tangem.core.ui.components.progressbar.TangemLinearProgressIndicator +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.res.TangemAnimations +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.features.hotwallet.impl.R +import com.tangem.features.hotwallet.stepper.api.HotWalletStepperComponent + +@Composable +internal fun HotWalletStepper( + state: HotWalletStepperComponent.StepperUM, + onBackClick: () -> Unit, + onSkipClick: () -> Unit, + onFeedbackClick: () -> Unit, + modifier: Modifier = Modifier, +) { + val fraction = state.currentStep.toFloat() / state.steps.coerceAtLeast(1) + val animatedIndicatorFraction by TangemAnimations.horizontalIndicatorAsState(targetFraction = fraction) + + Column( + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + TangemTopAppBar( + startButton = if (state.showBackButton) { + TopAppBarButtonUM.Back { onBackClick() } + } else { + null + }, + endButton = when { + state.showSkipButton -> TopAppBarButtonUM.Text( + text = resourceReference(R.string.common_skip), + onClicked = { onSkipClick() }, + ) + state.showFeedbackButton -> TopAppBarButtonUM.Icon( + iconRes = R.drawable.ic_chat_24, + onClicked = { onFeedbackClick() }, + ) + else -> null + }, + title = state.title, + containerColor = TangemTheme.colors.background.primary, + modifier = modifier, + titleAlignment = Alignment.CenterHorizontally, + ) + + TangemLinearProgressIndicator( + modifier = Modifier + .padding(horizontal = 16.dp) + .height(4.dp) + .fillMaxWidth(), + progress = { animatedIndicatorFraction }, + color = TangemTheme.colors.icon.primary1, + backgroundColor = TangemTheme.colors.icon.primary1.copy(alpha = 0.4f), + strokeCap = StrokeCap.Round, + ) + } +} + +@Preview +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun HotWalletStepper_Preview() { + TangemThemePreview { + Box( + Modifier + .fillMaxSize() + .background(TangemTheme.colors.background.primary), + ) { + HotWalletStepper( + modifier = Modifier.align(Alignment.TopCenter), + state = HotWalletStepperComponent.StepperUM( + currentStep = 2, + steps = 3, + title = resourceReference(R.string.common_done), + showBackButton = true, + showSkipButton = false, + showFeedbackButton = true, + ), + onBackClick = {}, + onSkipClick = {}, + onFeedbackClick = {}, + ) + } + } +} \ No newline at end of file