From 42b8de79092a1d90758f6d1856db3d193af1455a Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 29 Oct 2024 13:12:57 +0300 Subject: [PATCH] Updated on 2026-08-14 --- features/onboarding-v2/impl/build.gradle.kts | 8 +- .../stepper/api/OnboardingStepperComponent.kt | 31 +++++++ .../impl/DefaultOnboardingStepperComponent.kt | 76 +++++++++++++++++ .../v2/stepper/impl/di/ComponentModule.kt | 20 +++++ .../v2/stepper/impl/ui/OnboardingStepper.kt | 83 +++++++++++++++++++ 5 files changed, 216 insertions(+), 2 deletions(-) create mode 100644 features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/stepper/api/OnboardingStepperComponent.kt create mode 100644 features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/stepper/impl/DefaultOnboardingStepperComponent.kt create mode 100644 features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/stepper/impl/di/ComponentModule.kt create mode 100644 features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/stepper/impl/ui/OnboardingStepper.kt diff --git a/features/onboarding-v2/impl/build.gradle.kts b/features/onboarding-v2/impl/build.gradle.kts index e0028b37e7..8cc0843478 100644 --- a/features/onboarding-v2/impl/build.gradle.kts +++ b/features/onboarding-v2/impl/build.gradle.kts @@ -22,6 +22,12 @@ dependencies { implementation(projects.core.utils) implementation(projects.core.ui) implementation(projects.core.res) + implementation(projects.core.decompose) + + /** Domain */ + implementation(projects.domain.models) + implementation(projects.domain.feedback) + implementation(projects.domain.core) /** Tangem libraries */ implementation(deps.tangem.card.core) @@ -31,8 +37,6 @@ dependencies { /** AndroidX libraries */ implementation(deps.androidx.core.ktx) - implementation(deps.androidx.appCompat) - implementation(deps.androidx.constraintLayout) implementation(deps.lifecycle.runtime.ktx) /** Compose libraries */ 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 new file mode 100644 index 0000000000..3a6cd92582 --- /dev/null +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/stepper/api/OnboardingStepperComponent.kt @@ -0,0 +1,31 @@ +package com.tangem.features.onboarding.v2.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 com.tangem.domain.models.scan.ScanResponse +import kotlinx.coroutines.flow.StateFlow + +interface OnboardingStepperComponent : ComposableContentComponent { + + data class StepperState( + @IntRange(from = 0) val currentStep: Int, + @IntRange(from = 0) val steps: Int, + val title: TextReference, + ) + + class Params( + val initState: StepperState, + val popBack: () -> Unit, + val scanResponse: ScanResponse, + ) + + val state: StateFlow + + fun changeStep(step: Int) + fun nextStep() + fun changeTitle(title: TextReference) + + interface Factory : ComponentFactory +} \ No newline at end of file diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/stepper/impl/DefaultOnboardingStepperComponent.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/stepper/impl/DefaultOnboardingStepperComponent.kt new file mode 100644 index 0000000000..8e0bfb77ac --- /dev/null +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/stepper/impl/DefaultOnboardingStepperComponent.kt @@ -0,0 +1,76 @@ +package com.tangem.features.onboarding.v2.stepper.impl + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.arkivanov.essenty.instancekeeper.getOrCreateSimple +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.ui.extensions.TextReference +import com.tangem.domain.feedback.GetCardInfoUseCase +import com.tangem.domain.feedback.SendFeedbackEmailUseCase +import com.tangem.domain.feedback.models.FeedbackEmailType +import com.tangem.features.onboarding.v2.stepper.api.OnboardingStepperComponent +import com.tangem.features.onboarding.v2.stepper.impl.ui.OnboardingStepper +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch + +internal class DefaultOnboardingStepperComponent @AssistedInject constructor( + @Assisted val context: AppComponentContext, + @Assisted val params: OnboardingStepperComponent.Params, + private val getCardInfoUseCase: GetCardInfoUseCase, + private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase, +) : OnboardingStepperComponent, AppComponentContext by context { + + override val state = instanceKeeper.getOrCreateSimple { MutableStateFlow(params.initState) } + + override fun changeStep(step: Int) { + state.update { it.copy(currentStep = step.coerceAtLeast(minimumValue = 0)) } + } + + override fun nextStep() { + state.update { state -> + if (state.currentStep < state.steps - 1) { + state.copy(currentStep = state.currentStep + 1) + } else { + state + } + } + } + + override fun changeTitle(title: TextReference) { + state.update { it.copy(title = title) } + } + + private fun openSupport() { + componentScope.launch { + val cardInfo = getCardInfoUseCase(params.scanResponse).getOrNull() ?: return@launch + sendFeedbackEmailUseCase(FeedbackEmailType.DirectUserRequest(cardInfo)) + } + } + + @Composable + override fun Content(modifier: Modifier) { + val state by state.collectAsStateWithLifecycle() + + OnboardingStepper( + state = state, + onBackClick = remember(this) { params.popBack }, + onSupportButtonClick = remember(this) { ::openSupport }, + modifier = modifier, + ) + } + + @AssistedFactory + interface Factory : OnboardingStepperComponent.Factory { + override fun create( + context: AppComponentContext, + params: OnboardingStepperComponent.Params, + ): DefaultOnboardingStepperComponent + } +} \ No newline at end of file diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/stepper/impl/di/ComponentModule.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/stepper/impl/di/ComponentModule.kt new file mode 100644 index 0000000000..e0401a486d --- /dev/null +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/stepper/impl/di/ComponentModule.kt @@ -0,0 +1,20 @@ +package com.tangem.features.onboarding.v2.stepper.impl.di + +import com.tangem.features.onboarding.v2.stepper.api.OnboardingStepperComponent +import com.tangem.features.onboarding.v2.stepper.impl.DefaultOnboardingStepperComponent +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 bindOnboardingStepperComponent( + factory: DefaultOnboardingStepperComponent.Factory, + ): OnboardingStepperComponent.Factory +} \ No newline at end of file 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 new file mode 100644 index 0000000000..a4d735e82b --- /dev/null +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/stepper/impl/ui/OnboardingStepper.kt @@ -0,0 +1,83 @@ +package com.tangem.features.onboarding.v2.stepper.impl.ui + +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.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.LinearProgressIndicator +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.onboarding.v2.impl.R +import com.tangem.features.onboarding.v2.stepper.api.OnboardingStepperComponent + +@Composable +internal fun OnboardingStepper( + state: OnboardingStepperComponent.StepperState, + onBackClick: () -> Unit, + onSupportButtonClick: () -> 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 = TopAppBarButtonUM.Back(onBackClick), + endButton = TopAppBarButtonUM(iconRes = R.drawable.ic_chat_24, onIconClicked = onSupportButtonClick) + .takeIf { state.steps != state.currentStep }, + title = if (state.steps == state.currentStep) { + resourceReference(R.string.common_done) + } else { + state.title + }, + containerColor = TangemTheme.colors.background.primary, + modifier = modifier, + ) + + LinearProgressIndicator( + modifier = Modifier + .padding(horizontal = 16.dp) + .height(4.dp) + .fillMaxWidth(), + progress = { animatedIndicatorFraction }, + color = TangemTheme.colors.icon.primary1, + backgroundColor = TangemTheme.colors.background.tertiary, + strokeCap = StrokeCap.Round, + ) + } +} + +@Preview +@Composable +private fun Preview() { + TangemThemePreview { + Box( + Modifier + .fillMaxSize() + .background(TangemTheme.colors.background.primary), + ) { + OnboardingStepper( + modifier = Modifier.align(Alignment.TopCenter), + state = OnboardingStepperComponent.StepperState( + currentStep = 2, + steps = 3, + title = resourceReference(R.string.common_done), + ), + onBackClick = {}, + onSupportButtonClick = {}, + ) + } + } +} \ No newline at end of file