Updated on 2026-08-14
This commit is contained in:
parent
4594602447
commit
b98bc113fc
54 changed files with 667 additions and 198 deletions
|
|
@ -30,6 +30,7 @@ dependencies {
|
|||
implementation(projects.domain.core)
|
||||
|
||||
/** Tangem libraries */
|
||||
implementation(projects.libs.tangemSdkApi)
|
||||
implementation(deps.tangem.card.core)
|
||||
implementation(deps.tangem.card.android) {
|
||||
exclude(module = "joda-time")
|
||||
|
|
@ -40,12 +41,14 @@ dependencies {
|
|||
implementation(deps.lifecycle.runtime.ktx)
|
||||
|
||||
/** Compose libraries */
|
||||
implementation(deps.compose.material) // to use buttons
|
||||
implementation(deps.compose.material3)
|
||||
implementation(deps.compose.animation)
|
||||
implementation(deps.compose.foundation)
|
||||
implementation(deps.compose.ui)
|
||||
implementation(deps.compose.ui.tooling)
|
||||
implementation(deps.compose.coil)
|
||||
implementation(deps.decompose.ext.compose)
|
||||
|
||||
/** Other libraries */
|
||||
implementation(deps.kotlin.immutable.collections)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
package com.tangem.features.onboarding.v2
|
||||
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
interface TitleProvider {
|
||||
val currentTitle: StateFlow<TextReference>
|
||||
fun changeTitle(text: TextReference)
|
||||
}
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
package com.tangem.features.onboarding.v2.entry.impl
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.arkivanov.decompose.extensions.compose.jetpack.subscribeAsState
|
||||
import com.arkivanov.decompose.router.stack.ChildStack
|
||||
import com.arkivanov.decompose.router.stack.StackNavigation
|
||||
import com.arkivanov.decompose.router.stack.childStack
|
||||
|
|
@ -12,16 +14,19 @@ 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 com.tangem.utils.coroutines.JobHolder
|
||||
import com.tangem.utils.coroutines.saveIn
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
internal class DefaultOnboardingEntryComponent @AssistedInject constructor(
|
||||
@Assisted val context: AppComponentContext,
|
||||
|
|
@ -30,6 +35,7 @@ internal class DefaultOnboardingEntryComponent @AssistedInject constructor(
|
|||
onboardingChildFactory: OnboardingChildFactory,
|
||||
) : OnboardingEntryComponent, AppComponentContext by context {
|
||||
|
||||
private val innerNavigationLinkJobHolder = JobHolder()
|
||||
private val model: OnboardingEntryModel = getOrCreateModel(params)
|
||||
|
||||
private val stepperComponent = stepperFactory.create(
|
||||
|
|
@ -39,7 +45,7 @@ internal class DefaultOnboardingEntryComponent @AssistedInject constructor(
|
|||
initState = OnboardingStepperComponent.StepperState(
|
||||
currentStep = 0,
|
||||
steps = 0,
|
||||
title = stringReference(""),
|
||||
title = model.titleProvider.currentTitle.value,
|
||||
showProgress = false,
|
||||
),
|
||||
popBack = {
|
||||
|
|
@ -68,6 +74,10 @@ internal class DefaultOnboardingEntryComponent @AssistedInject constructor(
|
|||
},
|
||||
)
|
||||
|
||||
init {
|
||||
linkToInnerNavigation()
|
||||
}
|
||||
|
||||
private fun popInternal(onComplete: (Boolean) -> Unit) {
|
||||
val activeChild = innerStack.value.active.instance
|
||||
if (activeChild is InnerNavigationHolder) {
|
||||
|
|
@ -77,16 +87,36 @@ internal class DefaultOnboardingEntryComponent @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun linkToInnerNavigation() {
|
||||
innerStack.observe { stack ->
|
||||
val activeChild = stack.active.instance
|
||||
if (activeChild is InnerNavigationHolder) {
|
||||
componentScope.launch {
|
||||
activeChild.innerNavigation.state.collect { state ->
|
||||
stepperComponent.state.update {
|
||||
it.copy(
|
||||
currentStep = state.stackSize,
|
||||
steps = state.stackMaxSize ?: 0,
|
||||
title = model.titleProvider.currentTitle.value,
|
||||
showProgress = state.stackMaxSize != null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}.saveIn(innerNavigationLinkJobHolder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val innerStackState by innerStack.subscribeAsState()
|
||||
|
||||
OnboardingEntry(
|
||||
modifier = modifier,
|
||||
stepperContent = { modifierParam ->
|
||||
stepperComponent.Content(modifierParam)
|
||||
},
|
||||
content = { modifierParam ->
|
||||
// TODO
|
||||
},
|
||||
childStack = innerStackState,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,16 @@
|
|||
package com.tangem.features.onboarding.v2.entry.impl.di
|
||||
|
||||
import com.tangem.core.decompose.di.DecomposeComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.onboarding.v2.entry.OnboardingEntryComponent
|
||||
import com.tangem.features.onboarding.v2.entry.impl.DefaultOnboardingEntryComponent
|
||||
import com.tangem.features.onboarding.v2.entry.impl.model.OnboardingEntryModel
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import dagger.multibindings.ClassKey
|
||||
import dagger.multibindings.IntoMap
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
|
|
@ -17,4 +22,13 @@ internal interface ComponentModule {
|
|||
fun bindOnboardingEntryComponent(
|
||||
factory: DefaultOnboardingEntryComponent.Factory,
|
||||
): OnboardingEntryComponent.Factory
|
||||
}
|
||||
|
||||
@Module
|
||||
@InstallIn(DecomposeComponent::class)
|
||||
internal interface ModelModule {
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(OnboardingEntryModel::class)
|
||||
fun provideModel(model: OnboardingEntryModel): Model
|
||||
}
|
||||
|
|
@ -2,6 +2,12 @@ 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.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.domain.models.scan.ProductType
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.features.onboarding.v2.TitleProvider
|
||||
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
|
||||
|
|
@ -11,13 +17,42 @@ import javax.inject.Inject
|
|||
|
||||
@ComponentScoped
|
||||
internal class OnboardingEntryModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val params: OnboardingEntryComponent.Params,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<OnboardingEntryComponent.Params>()
|
||||
|
||||
val titleProvider = object : TitleProvider {
|
||||
override val currentTitle = MutableStateFlow(stringReference(""))
|
||||
override fun changeTitle(text: TextReference) {
|
||||
currentTitle.value = text
|
||||
}
|
||||
}
|
||||
|
||||
val state = MutableStateFlow(
|
||||
OnboardingState(
|
||||
currentRoute = OnboardingRoute.Wallet12(params.scanResponse),
|
||||
currentRoute = routeByProductType(params.scanResponse),
|
||||
),
|
||||
)
|
||||
|
||||
private fun routeByProductType(scanResponse: ScanResponse): OnboardingRoute {
|
||||
return when (scanResponse.productType) {
|
||||
ProductType.Note -> TODO()
|
||||
ProductType.Twins -> TODO()
|
||||
ProductType.Wallet -> OnboardingRoute.Wallet12(
|
||||
scanResponse = scanResponse,
|
||||
withSeedPhraseFlow = false,
|
||||
titleProvider = titleProvider,
|
||||
)
|
||||
ProductType.Wallet2 -> OnboardingRoute.Wallet12(
|
||||
scanResponse = scanResponse,
|
||||
withSeedPhraseFlow = true,
|
||||
titleProvider = titleProvider,
|
||||
)
|
||||
ProductType.Start2Coin -> TODO()
|
||||
ProductType.Ring -> TODO()
|
||||
ProductType.Visa -> TODO()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +1,24 @@
|
|||
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 com.tangem.features.onboarding.v2.multiwallet.api.OnboardingMultiWalletComponent
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class OnboardingChildFactory @Inject constructor(
|
||||
private val wallet12ComponentFactory: OnboardingWallet12Component.Factory,
|
||||
private val wallet12ComponentFactory: OnboardingMultiWalletComponent.Factory,
|
||||
) {
|
||||
|
||||
fun createChild(route: OnboardingRoute, childContext: AppComponentContext) {
|
||||
when (route) {
|
||||
fun createChild(route: OnboardingRoute, childContext: AppComponentContext): Any {
|
||||
return when (route) {
|
||||
is OnboardingRoute.Wallet12 -> wallet12ComponentFactory.create(
|
||||
context = childContext,
|
||||
params = OnboardingWallet12Component.Params(
|
||||
params = OnboardingMultiWalletComponent.Params(
|
||||
scanResponse = route.scanResponse,
|
||||
withSeedPhraseFlow = route.withSeedPhraseFlow,
|
||||
titleProvider = route.titleProvider,
|
||||
),
|
||||
)
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,8 +2,15 @@ package com.tangem.features.onboarding.v2.entry.impl.routing
|
|||
|
||||
import com.tangem.core.decompose.navigation.Route
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.features.onboarding.v2.TitleProvider
|
||||
|
||||
sealed class OnboardingRoute : Route {
|
||||
|
||||
data class Wallet12(val scanResponse: ScanResponse) : OnboardingRoute()
|
||||
data object None : OnboardingRoute()
|
||||
|
||||
data class Wallet12(
|
||||
val titleProvider: TitleProvider,
|
||||
val scanResponse: ScanResponse,
|
||||
val withSeedPhraseFlow: Boolean,
|
||||
) : OnboardingRoute()
|
||||
}
|
||||
|
|
@ -4,19 +4,19 @@ import androidx.compose.foundation.background
|
|||
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
|
||||
import com.arkivanov.decompose.extensions.compose.jetpack.stack.Children
|
||||
import com.arkivanov.decompose.extensions.compose.jetpack.stack.animation.slide
|
||||
import com.arkivanov.decompose.extensions.compose.jetpack.stack.animation.stackAnimation
|
||||
import com.arkivanov.decompose.router.stack.ChildStack
|
||||
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.entry.impl.routing.OnboardingRoute
|
||||
import com.tangem.features.onboarding.v2.multiwallet.api.OnboardingMultiWalletComponent
|
||||
|
||||
@Composable
|
||||
internal inline fun OnboardingEntry(
|
||||
modifier: Modifier = Modifier,
|
||||
childStack: ChildStack<OnboardingRoute, Any>,
|
||||
stepperContent: @Composable (Modifier) -> Unit,
|
||||
content: @Composable (Modifier) -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
|
|
@ -25,28 +25,19 @@ internal inline fun OnboardingEntry(
|
|||
.background(TangemTheme.colors.background.primary),
|
||||
) {
|
||||
stepperContent(Modifier.fillMaxWidth())
|
||||
content(Modifier.fillMaxSize())
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun Preview() {
|
||||
TangemThemePreview {
|
||||
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)))
|
||||
},
|
||||
)
|
||||
Children(
|
||||
stack = childStack,
|
||||
animation = stackAnimation(slide()),
|
||||
) {
|
||||
when (it.configuration) {
|
||||
is OnboardingRoute.Wallet12 -> {
|
||||
(it.instance as OnboardingMultiWalletComponent).Content(
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
OnboardingRoute.None -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.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
|
||||
import com.tangem.features.onboarding.v2.TitleProvider
|
||||
|
||||
interface OnboardingMultiWalletComponent : ComposableContentComponent, InnerNavigationHolder {
|
||||
|
||||
data class Params(
|
||||
val titleProvider: TitleProvider,
|
||||
val scanResponse: ScanResponse,
|
||||
val withSeedPhraseFlow: Boolean,
|
||||
)
|
||||
|
||||
interface Factory : ComponentFactory<Params, OnboardingMultiWalletComponent>
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.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.core.decompose.navigation.inner.InnerNavigation
|
||||
import com.tangem.core.decompose.navigation.inner.InnerNavigationState
|
||||
import com.tangem.features.onboarding.v2.multiwallet.api.OnboardingMultiWalletComponent
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.model.OnboardingMultiWalletModel
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.ui.OnboardingMultiWallet
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
internal class DefaultOnboardingMultiWalletComponent @AssistedInject constructor(
|
||||
@Assisted private val context: AppComponentContext,
|
||||
@Assisted private val params: OnboardingMultiWalletComponent.Params,
|
||||
) : OnboardingMultiWalletComponent, AppComponentContext by context {
|
||||
|
||||
private val model: OnboardingMultiWalletModel = getOrCreateModel(params)
|
||||
|
||||
override val innerNavigation: InnerNavigation = object : InnerNavigation {
|
||||
override val state = MutableStateFlow(
|
||||
Wallet12InnerNavigationState(1, 5), // TODO
|
||||
)
|
||||
|
||||
override fun pop(onComplete: (Boolean) -> Unit) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val uiState by model.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
OnboardingMultiWallet(
|
||||
modifier = modifier,
|
||||
state = uiState,
|
||||
)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : OnboardingMultiWalletComponent.Factory {
|
||||
override fun create(
|
||||
context: AppComponentContext,
|
||||
params: OnboardingMultiWalletComponent.Params,
|
||||
): DefaultOnboardingMultiWalletComponent
|
||||
}
|
||||
}
|
||||
|
||||
data class Wallet12InnerNavigationState(
|
||||
override val stackSize: Int,
|
||||
override val stackMaxSize: Int?,
|
||||
) : InnerNavigationState
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.impl.di
|
||||
|
||||
import com.tangem.core.decompose.di.DecomposeComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.onboarding.v2.multiwallet.api.OnboardingMultiWalletComponent
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.DefaultOnboardingMultiWalletComponent
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.model.OnboardingMultiWalletModel
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import dagger.multibindings.ClassKey
|
||||
import dagger.multibindings.IntoMap
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal interface ComponentModule {
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindComponent(factory: DefaultOnboardingMultiWalletComponent.Factory): OnboardingMultiWalletComponent.Factory
|
||||
}
|
||||
|
||||
@Module
|
||||
@InstallIn(DecomposeComponent::class)
|
||||
internal interface ModelModule {
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(OnboardingMultiWalletModel::class)
|
||||
fun provideModel(model: OnboardingMultiWalletModel): Model
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.impl.model
|
||||
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.features.onboarding.v2.impl.R
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.ui.state.OnboardingMultiWalletUM
|
||||
|
||||
internal fun resetCardDialog(onConfirm: () -> Unit, onDismiss: () -> Unit, onDismissButtonClick: () -> Unit) =
|
||||
OnboardingMultiWalletUM.Dialog(
|
||||
title = resourceReference(R.string.onboarding_activation_error_title),
|
||||
description = resourceReference(R.string.onboarding_activation_error_message),
|
||||
confirmButtonText = resourceReference(R.string.common_ok),
|
||||
dismissButtonText = resourceReference(R.string.common_support),
|
||||
onDismiss = onDismiss,
|
||||
onConfirm = onConfirm,
|
||||
onDismissButtonClick = onDismissButtonClick,
|
||||
)
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.impl.model
|
||||
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.domain.feedback.GetCardInfoUseCase
|
||||
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
||||
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.features.onboarding.v2.multiwallet.api.OnboardingMultiWalletComponent
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.ui.state.OnboardingMultiWalletUM
|
||||
import com.tangem.sdk.api.TangemSdkManager
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@ComponentScoped
|
||||
internal class OnboardingMultiWalletModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val tangemSdkManager: TangemSdkManager,
|
||||
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
|
||||
private val getCardInfoUseCase: GetCardInfoUseCase,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<OnboardingMultiWalletComponent.Params>()
|
||||
private val currentScanResponse = MutableStateFlow(params.scanResponse)
|
||||
|
||||
val state = MutableStateFlow(
|
||||
OnboardingMultiWalletState(
|
||||
currentStep = getInitialStep(),
|
||||
),
|
||||
)
|
||||
|
||||
val uiState = MutableStateFlow(
|
||||
OnboardingMultiWalletUM(
|
||||
onCreateWalletClick = { createWallet(false) },
|
||||
showSeedPhraseOption = params.withSeedPhraseFlow,
|
||||
onOtherOptionsClick = { /* navigate */ },
|
||||
onBack = { },
|
||||
dialog = null,
|
||||
),
|
||||
)
|
||||
|
||||
init {
|
||||
initScreenTitleSub()
|
||||
}
|
||||
|
||||
private fun getInitialStep(): OnboardingMultiWalletState.Step {
|
||||
val card = currentScanResponse.value.card
|
||||
|
||||
return when {
|
||||
card.wallets.isNotEmpty() && card.backupStatus == CardDTO.BackupStatus.NoBackup ->
|
||||
OnboardingMultiWalletState.Step.AddBackupDevice
|
||||
card.wallets.isNotEmpty() && card.backupStatus?.isActive == true ->
|
||||
OnboardingMultiWalletState.Step.FinishBackup
|
||||
else ->
|
||||
OnboardingMultiWalletState.Step.CreateWallet
|
||||
}
|
||||
}
|
||||
|
||||
private fun initScreenTitleSub() {
|
||||
val title = screenTitleByStep(getInitialStep())
|
||||
params.titleProvider.changeTitle(title)
|
||||
|
||||
modelScope.launch {
|
||||
state
|
||||
.map { it.currentStep }
|
||||
.collectLatest { step ->
|
||||
val title = screenTitleByStep(step)
|
||||
params.titleProvider.changeTitle(title)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createWallet(shouldReset: Boolean) {
|
||||
modelScope.launch {
|
||||
val result = tangemSdkManager.createProductWallet(
|
||||
scanResponse = currentScanResponse.value,
|
||||
shouldReset = shouldReset,
|
||||
)
|
||||
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
currentScanResponse.update {
|
||||
it.copy(
|
||||
card = result.data.card,
|
||||
derivedKeys = result.data.derivedKeys,
|
||||
primaryCard = result.data.primaryCard,
|
||||
)
|
||||
}
|
||||
|
||||
// TODO
|
||||
// Analytics.send(Onboarding.CreateWallet.WalletCreatedSuccessfully())
|
||||
}
|
||||
|
||||
is CompletionResult.Failure -> {
|
||||
if (result.error is TangemSdkError.WalletAlreadyCreated) {
|
||||
// show should reset dialog
|
||||
handleActivationError()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleActivationError() {
|
||||
uiState.update {
|
||||
it.copy(
|
||||
dialog = resetCardDialog(
|
||||
onConfirm = {
|
||||
uiState.update { it.copy(dialog = null) }
|
||||
resetCard()
|
||||
},
|
||||
onDismiss = {
|
||||
uiState.update { it.copy(dialog = null) }
|
||||
},
|
||||
onDismissButtonClick = ::navigateToSupportScreen,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun resetCard() {
|
||||
createWallet(true)
|
||||
}
|
||||
|
||||
fun navigateToSupportScreen() {
|
||||
modelScope.launch {
|
||||
val cardInfo = getCardInfoUseCase(currentScanResponse.value).getOrNull() ?: return@launch
|
||||
sendFeedbackEmailUseCase(FeedbackEmailType.DirectUserRequest(cardInfo))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.impl.model
|
||||
|
||||
data class OnboardingMultiWalletState(
|
||||
val currentStep: Step,
|
||||
) {
|
||||
|
||||
enum class Step {
|
||||
GeneratePrivateKeys, CreateWallet, AddBackupDevice, FinishBackup, Done
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.impl.model
|
||||
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.features.onboarding.v2.impl.R
|
||||
|
||||
fun screenTitleByStep(step: OnboardingMultiWalletState.Step): TextReference = when (step) {
|
||||
OnboardingMultiWalletState.Step.GeneratePrivateKeys -> TODO()
|
||||
OnboardingMultiWalletState.Step.CreateWallet -> resourceReference(R.string.onboarding_create_wallet_header)
|
||||
OnboardingMultiWalletState.Step.AddBackupDevice -> TODO()
|
||||
OnboardingMultiWalletState.Step.FinishBackup -> TODO()
|
||||
OnboardingMultiWalletState.Step.Done -> TODO()
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.impl.ui
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.BasicDialog
|
||||
import com.tangem.core.ui.components.DialogButtonUM
|
||||
import com.tangem.core.ui.components.PrimaryButtonIconEnd
|
||||
import com.tangem.core.ui.components.SecondaryButton
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.onboarding.v2.impl.R
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.ui.state.OnboardingMultiWalletUM
|
||||
|
||||
@Composable
|
||||
internal fun OnboardingMultiWallet(state: OnboardingMultiWalletUM, modifier: Modifier = Modifier) {
|
||||
if (state.dialog != null) {
|
||||
BasicDialog(
|
||||
title = state.dialog.title.resolveReference(),
|
||||
message = state.dialog.description.resolveReference(),
|
||||
confirmButton = DialogButtonUM(
|
||||
title = state.dialog.confirmButtonText.resolveReference(),
|
||||
onClick = state.dialog.onConfirm,
|
||||
),
|
||||
dismissButton = DialogButtonUM(
|
||||
title = state.dialog.dismissButtonText.resolveReference(),
|
||||
onClick = state.dialog.onDismissButtonClick,
|
||||
),
|
||||
onDismissDialog = state.dialog.onDismiss,
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.navigationBarsPadding()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Box(Modifier.weight(1f))
|
||||
|
||||
PrimaryButtonIconEnd(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp, end = 16.dp, bottom = 12.dp)
|
||||
.fillMaxWidth(),
|
||||
iconResId = R.drawable.ic_tangem_24,
|
||||
text = stringResource(R.string.onboarding_create_wallet_button_create_wallet),
|
||||
onClick = state.onCreateWalletClick,
|
||||
)
|
||||
|
||||
SecondaryButton(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
text = stringResource(R.string.onboarding_create_wallet_options_button_options),
|
||||
onClick = state.onOtherOptionsClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun Preview() {
|
||||
TangemThemePreview {
|
||||
OnboardingMultiWallet(
|
||||
state = OnboardingMultiWalletUM(
|
||||
onCreateWalletClick = {},
|
||||
showSeedPhraseOption = true,
|
||||
onBack = {},
|
||||
onOtherOptionsClick = {},
|
||||
dialog = null,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.impl.ui.state
|
||||
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
||||
internal data class OnboardingMultiWalletUM(
|
||||
val onCreateWalletClick: () -> Unit,
|
||||
val showSeedPhraseOption: Boolean,
|
||||
val onOtherOptionsClick: () -> Unit,
|
||||
val onBack: () -> Unit,
|
||||
val dialog: Dialog?,
|
||||
) {
|
||||
data class Dialog(
|
||||
val title: TextReference,
|
||||
val description: TextReference,
|
||||
val dismissButtonText: TextReference,
|
||||
val confirmButtonText: TextReference,
|
||||
val onConfirm: () -> Unit,
|
||||
val onDismissButtonClick: () -> Unit,
|
||||
val onDismiss: () -> Unit,
|
||||
)
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ 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
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
internal interface OnboardingStepperComponent : ComposableContentComponent {
|
||||
|
||||
|
|
@ -22,11 +22,7 @@ internal interface OnboardingStepperComponent : ComposableContentComponent {
|
|||
val scanResponse: ScanResponse,
|
||||
)
|
||||
|
||||
val state: StateFlow<StepperState>
|
||||
|
||||
fun changeStep(step: Int)
|
||||
fun nextStep()
|
||||
fun changeTitle(title: TextReference)
|
||||
val state: MutableStateFlow<StepperState>
|
||||
|
||||
interface Factory : ComponentFactory<Params, OnboardingStepperComponent>
|
||||
}
|
||||
|
|
@ -7,7 +7,6 @@ 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
|
||||
|
|
@ -17,7 +16,6 @@ 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(
|
||||
|
|
@ -29,24 +27,6 @@ internal class DefaultOnboardingStepperComponent @AssistedInject constructor(
|
|||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
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<Params, OnboardingWallet12Component>
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
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
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
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
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue