Updated on 2026-08-14
This commit is contained in:
parent
ecb494f839
commit
42b8de7909
5 changed files with 216 additions and 2 deletions
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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<StepperState>
|
||||
|
||||
fun changeStep(step: Int)
|
||||
fun nextStep()
|
||||
fun changeTitle(title: TextReference)
|
||||
|
||||
interface Factory : ComponentFactory<Params, OnboardingStepperComponent>
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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 = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue