Updated on 2026-08-14
This commit is contained in:
commit
3c46ddfcf9
711 changed files with 12970 additions and 6062 deletions
1
features/biometry/api/.gitignore
vendored
Normal file
1
features/biometry/api/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
15
features/biometry/api/build.gradle.kts
Normal file
15
features/biometry/api/build.gradle.kts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
plugins {
|
||||
alias(deps.plugins.android.library)
|
||||
alias(deps.plugins.kotlin.android)
|
||||
id("configuration")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.tangem.features.biometry"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
/* Project - Core */
|
||||
implementation(projects.core.decompose)
|
||||
implementation(projects.core.ui)
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.features.biometry
|
||||
|
||||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
|
||||
interface AskBiometryComponent : ComposableContentComponent, ComposableBottomSheetComponent {
|
||||
|
||||
data class Params(
|
||||
val bottomSheetVariant: Boolean,
|
||||
val modelCallbacks: ModelCallbacks,
|
||||
)
|
||||
|
||||
interface ModelCallbacks {
|
||||
fun onAllowed()
|
||||
fun onDenied()
|
||||
}
|
||||
|
||||
interface Factory : ComponentFactory<Params, AskBiometryComponent>
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.tangem.features.biometry
|
||||
|
||||
interface BiometryFeatureToggles {
|
||||
val isAskForBiometryEnabled: Boolean
|
||||
}
|
||||
1
features/biometry/impl/.gitignore
vendored
Normal file
1
features/biometry/impl/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
52
features/biometry/impl/build.gradle.kts
Normal file
52
features/biometry/impl/build.gradle.kts
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
plugins {
|
||||
alias(deps.plugins.android.library)
|
||||
alias(deps.plugins.kotlin.android)
|
||||
alias(deps.plugins.kotlin.kapt)
|
||||
alias(deps.plugins.kotlin.serialization)
|
||||
alias(deps.plugins.hilt.android)
|
||||
id("configuration")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.tangem.features.biometry.impl"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.features.biometry.api)
|
||||
|
||||
/** Core modules */
|
||||
implementation(projects.core.ui)
|
||||
implementation(projects.core.res)
|
||||
implementation(projects.core.decompose)
|
||||
implementation(projects.core.analytics)
|
||||
implementation(projects.core.analytics.models)
|
||||
implementation(projects.core.configToggles)
|
||||
implementation(projects.core.navigation)
|
||||
|
||||
/** Domain */
|
||||
implementation(projects.domain.wallets)
|
||||
implementation(projects.domain.wallets.models)
|
||||
implementation(projects.domain.settings)
|
||||
implementation(projects.domain.card)
|
||||
|
||||
/** Compose libraries */
|
||||
implementation(deps.compose.material3)
|
||||
implementation(deps.compose.animation)
|
||||
implementation(deps.compose.foundation)
|
||||
implementation(deps.compose.ui)
|
||||
implementation(deps.compose.ui.tooling)
|
||||
|
||||
/** Tangem libraries */
|
||||
implementation(projects.libs.tangemSdkApi)
|
||||
implementation(tangemDeps.card.core)
|
||||
implementation(tangemDeps.card.android) {
|
||||
exclude(module = "joda-time")
|
||||
}
|
||||
|
||||
/** Other */
|
||||
implementation(deps.timber)
|
||||
|
||||
/** DI */
|
||||
implementation(deps.hilt.android)
|
||||
kapt(deps.hilt.kapt)
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
package com.tangem.features.biometry.impl
|
||||
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
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.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.features.biometry.AskBiometryComponent
|
||||
import com.tangem.features.biometry.impl.model.AskBiometryModel
|
||||
import com.tangem.features.biometry.impl.ui.AskBiometry
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
||||
internal class DefaultAskBiometryComponent @AssistedInject constructor(
|
||||
@Assisted appComponentContext: AppComponentContext,
|
||||
@Assisted params: AskBiometryComponent.Params,
|
||||
) : AskBiometryComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: AskBiometryModel = getOrCreateModel(params)
|
||||
private val bsShown = instanceKeeper.getOrCreateSimple { MutableStateFlow(true) }
|
||||
|
||||
init {
|
||||
model.dismissBSFlow
|
||||
.onEach { bsShown.value = false }
|
||||
.launchIn(componentScope)
|
||||
}
|
||||
|
||||
override fun dismiss() = model.dismiss()
|
||||
|
||||
@Composable
|
||||
override fun BottomSheet() {
|
||||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
val bsShown by bsShown.collectAsStateWithLifecycle()
|
||||
val bsConfig = remember(this, bsShown) {
|
||||
TangemBottomSheetConfig(
|
||||
isShown = bsShown,
|
||||
onDismissRequest = ::dismiss,
|
||||
content = TangemBottomSheetConfigContent.Empty,
|
||||
)
|
||||
}
|
||||
|
||||
TangemBottomSheet<TangemBottomSheetConfigContent.Empty>(
|
||||
config = bsConfig,
|
||||
content = { AskBiometry(state) },
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
AskBiometry(
|
||||
modifier = modifier.navigationBarsPadding(),
|
||||
state = state,
|
||||
)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : AskBiometryComponent.Factory {
|
||||
override fun create(
|
||||
context: AppComponentContext,
|
||||
params: AskBiometryComponent.Params,
|
||||
): DefaultAskBiometryComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.features.biometry.impl
|
||||
|
||||
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
|
||||
import com.tangem.features.biometry.BiometryFeatureToggles
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class DefaultBiometryFeatureToggles @Inject constructor(
|
||||
private val featureTogglesManager: FeatureTogglesManager,
|
||||
) : BiometryFeatureToggles {
|
||||
override val isAskForBiometryEnabled: Boolean
|
||||
get() = featureTogglesManager.isFeatureEnabled("ASK_BIOMETRY_REFACTORING_ENABLED")
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package com.tangem.features.biometry.impl.di
|
||||
|
||||
import com.tangem.core.decompose.di.ModelComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.biometry.AskBiometryComponent
|
||||
import com.tangem.features.biometry.BiometryFeatureToggles
|
||||
import com.tangem.features.biometry.impl.DefaultAskBiometryComponent
|
||||
import com.tangem.features.biometry.impl.DefaultBiometryFeatureToggles
|
||||
import com.tangem.features.biometry.impl.model.AskBiometryModel
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import dagger.multibindings.ClassKey
|
||||
import dagger.multibindings.IntoMap
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal interface FeatureModule {
|
||||
|
||||
@Binds
|
||||
fun bindFeatureToggles(impl: DefaultBiometryFeatureToggles): BiometryFeatureToggles
|
||||
|
||||
@Binds
|
||||
fun bindComponentFactory(impl: DefaultAskBiometryComponent.Factory): AskBiometryComponent.Factory
|
||||
}
|
||||
|
||||
@Module
|
||||
@InstallIn(ModelComponent::class)
|
||||
internal interface ModelModule {
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(AskBiometryModel::class)
|
||||
fun provideModel(model: AskBiometryModel): Model
|
||||
}
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
package com.tangem.features.biometry.impl.model
|
||||
|
||||
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.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.navigation.settings.SettingsManager
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.message.DialogMessage
|
||||
import com.tangem.core.ui.message.EventMessageAction
|
||||
import com.tangem.core.ui.message.SnackbarMessage
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.domain.settings.SetSaveWalletScreenShownUseCase
|
||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
||||
import com.tangem.features.biometry.AskBiometryComponent
|
||||
import com.tangem.features.biometry.impl.ui.state.AskBiometryUM
|
||||
import com.tangem.sdk.api.TangemSdkManager
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Stable
|
||||
@ModelScoped
|
||||
internal class AskBiometryModel @Inject constructor(
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
paramsContainer: ParamsContainer,
|
||||
private val setSaveWalletScreenShownUseCase: SetSaveWalletScreenShownUseCase,
|
||||
private val settingsRepository: SettingsRepository,
|
||||
private val tangemSdkManager: TangemSdkManager,
|
||||
private val userWalletsListManager: UserWalletsListManager,
|
||||
private val walletsRepository: WalletsRepository,
|
||||
private val cardSdkConfigRepository: CardSdkConfigRepository,
|
||||
private val settingsManager: SettingsManager,
|
||||
private val uiMessageSender: UiMessageSender,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<AskBiometryComponent.Params>()
|
||||
|
||||
private val _uiState = MutableStateFlow(
|
||||
AskBiometryUM(
|
||||
bottomSheetVariant = params.bottomSheetVariant,
|
||||
onAllowClick = ::onAllowClick,
|
||||
onDontAllowClick = ::dontAllow,
|
||||
onDismiss = ::dismiss,
|
||||
),
|
||||
)
|
||||
val uiState = _uiState.asStateFlow()
|
||||
val dismissBSFlow = MutableSharedFlow<Unit>()
|
||||
|
||||
init {
|
||||
modelScope.launch {
|
||||
setSaveWalletScreenShownUseCase()
|
||||
}
|
||||
}
|
||||
|
||||
private fun onAllowClick() {
|
||||
_uiState.update { it.copy(showProgress = true) }
|
||||
allowToUseBiometrics()
|
||||
}
|
||||
|
||||
private fun dontAllow() {
|
||||
params.modelCallbacks.onDenied()
|
||||
}
|
||||
|
||||
fun dismiss() {
|
||||
modelScope.launch {
|
||||
dismissBSFlow.emit(Unit)
|
||||
delay(timeMillis = 500)
|
||||
dontAllow()
|
||||
}
|
||||
}
|
||||
|
||||
private fun allowToUseBiometrics() {
|
||||
modelScope.launch {
|
||||
if (tangemSdkManager.checkNeedEnrollBiometrics()) {
|
||||
showEnrollBiometricsDialog()
|
||||
return@launch
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
* because it will be automatically saved on UserWalletsListManager switch
|
||||
*/
|
||||
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync ?: run {
|
||||
Timber.e("Unable to save user wallet")
|
||||
uiMessageSender.send(
|
||||
SnackbarMessage(stringReference("No selected user wallet")),
|
||||
)
|
||||
|
||||
return@launch
|
||||
}
|
||||
|
||||
handleSuccessAllowing(selectedUserWallet)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun handleSuccessAllowing(userWallet: UserWallet) {
|
||||
walletsRepository.saveShouldSaveUserWallets(item = true)
|
||||
settingsRepository.setShouldSaveAccessCodes(value = true)
|
||||
|
||||
cardSdkConfigRepository.setAccessCodeRequestPolicy(
|
||||
isBiometricsRequestPolicy = userWallet.hasAccessCode,
|
||||
)
|
||||
|
||||
if (_uiState.value.bottomSheetVariant) {
|
||||
dismissBSFlow.emit(Unit)
|
||||
delay(timeMillis = 500)
|
||||
}
|
||||
|
||||
params.modelCallbacks.onAllowed()
|
||||
}
|
||||
|
||||
private fun showEnrollBiometricsDialog() {
|
||||
uiMessageSender.send(
|
||||
DialogMessage(
|
||||
message = resourceReference(R.string.save_user_wallet_agreement_enroll_biometrics_description),
|
||||
title = resourceReference(R.string.save_user_wallet_agreement_enroll_biometrics_title),
|
||||
firstAction = EventMessageAction(
|
||||
title = resourceReference(R.string.common_enable),
|
||||
onClick = { settingsManager.openBiometricSettings() },
|
||||
),
|
||||
secondAction = EventMessageAction(
|
||||
title = resourceReference(R.string.common_cancel),
|
||||
onClick = {},
|
||||
),
|
||||
dismissOnFirstAction = true,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
package com.tangem.features.biometry.impl.ui
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.components.*
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.biometry.impl.ui.state.AskBiometryUM
|
||||
import com.tangem.features.biometry.R
|
||||
|
||||
@Composable
|
||||
internal fun AskBiometry(state: AskBiometryUM, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
if (state.bottomSheetVariant) {
|
||||
Header(onCloseClick = state.onDismiss)
|
||||
}
|
||||
|
||||
SpacerH32()
|
||||
SpacerHHalf()
|
||||
|
||||
Column(
|
||||
modifier = Modifier.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Title(modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing56))
|
||||
SpacerH32()
|
||||
Description(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens.spacing34)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
|
||||
SpacerHHalf()
|
||||
}
|
||||
Footer(state = state)
|
||||
SpacerH16()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Header(onCloseClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End,
|
||||
) {
|
||||
IconButton(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size32),
|
||||
onClick = onCloseClick,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size24),
|
||||
painter = painterResource(id = R.drawable.ic_close_24),
|
||||
tint = TangemTheme.colors.icon.secondary,
|
||||
contentDescription = stringResourceSafe(id = R.string.common_cancel),
|
||||
)
|
||||
}
|
||||
SpacerW8()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Title(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing32),
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size56),
|
||||
painter = painterResource(id = R.drawable.ic_fingerprint_24),
|
||||
tint = TangemTheme.colors.icon.primary1,
|
||||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
text = stringResourceSafe(id = R.string.save_user_wallet_agreement_header_biometrics),
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Description(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing24),
|
||||
horizontalAlignment = Alignment.Start,
|
||||
) {
|
||||
DescriptionItem(
|
||||
iconPainter = painterResource(id = R.drawable.ic_face_recognition_24),
|
||||
title = stringResourceSafe(id = R.string.save_user_wallet_agreement_access_title),
|
||||
description = stringResourceSafe(id = R.string.save_user_wallet_agreement_access_description),
|
||||
)
|
||||
DescriptionItem(
|
||||
iconPainter = painterResource(id = R.drawable.ic_lock_24),
|
||||
title = stringResourceSafe(id = R.string.save_user_wallet_agreement_code_title),
|
||||
description = stringResourceSafe(id = R.string.save_user_wallet_agreement_code_description_biometrics),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Footer(state: AskBiometryUM, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16)
|
||||
.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
PrimaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
showProgress = state.showProgress,
|
||||
text = stringResourceSafe(id = R.string.save_user_wallet_agreement_allow_biometrics),
|
||||
onClick = state.onAllowClick,
|
||||
)
|
||||
|
||||
if (state.bottomSheetVariant.not()) {
|
||||
SpacerH12()
|
||||
|
||||
SecondaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResourceSafe(id = R.string.save_user_wallet_agreement_dont_allow),
|
||||
onClick = state.onDontAllowClick,
|
||||
)
|
||||
}
|
||||
|
||||
SpacerH16()
|
||||
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(fraction = .7f),
|
||||
text = stringResourceSafe(R.string.save_user_wallet_agreement_notice),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DescriptionItem(iconPainter: Painter, title: String, description: String) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.Top,
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size24),
|
||||
painter = iconPainter,
|
||||
tint = TangemTheme.colors.icon.primary1,
|
||||
contentDescription = null,
|
||||
)
|
||||
SpacerW24()
|
||||
Column {
|
||||
Text(
|
||||
text = title,
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
SpacerH4()
|
||||
Text(
|
||||
text = description,
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun Preview() {
|
||||
TangemThemePreview {
|
||||
AskBiometry(
|
||||
state = AskBiometryUM(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun PreviewBS() {
|
||||
TangemThemePreview {
|
||||
AskBiometry(
|
||||
state = AskBiometryUM(bottomSheetVariant = true),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.features.biometry.impl.ui.state
|
||||
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
||||
internal data class AskBiometryUM(
|
||||
val bottomSheetVariant: Boolean = false,
|
||||
val showProgress: Boolean = false,
|
||||
val error: TextReference? = null,
|
||||
val onAllowClick: () -> Unit = {},
|
||||
val onDontAllowClick: () -> Unit = {},
|
||||
val onDismiss: () -> Unit = {},
|
||||
)
|
||||
|
|
@ -70,4 +70,6 @@ dependencies {
|
|||
implementation(deps.kotlin.immutable.collections)
|
||||
implementation(deps.reKotlin)
|
||||
implementation(deps.timber)
|
||||
implementation(deps.arrow.core)
|
||||
implementation(deps.arrow.fx)
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.features.details.di
|
||||
|
||||
import com.tangem.core.decompose.di.DecomposeComponent
|
||||
import com.tangem.core.decompose.di.ModelComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.details.model.DetailsModel
|
||||
import com.tangem.features.details.model.UserWalletListModel
|
||||
|
|
@ -11,7 +11,7 @@ import dagger.multibindings.ClassKey
|
|||
import dagger.multibindings.IntoMap
|
||||
|
||||
@Module
|
||||
@InstallIn(DecomposeComponent::class)
|
||||
@InstallIn(ModelComponent::class)
|
||||
internal interface ModelModule {
|
||||
|
||||
@Binds
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.tangem.features.details.model
|
|||
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.core.analytics.AppInstanceIdProvider
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
|
|
@ -32,7 +32,7 @@ import kotlinx.coroutines.launch
|
|||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@ComponentScoped
|
||||
@ModelScoped
|
||||
@Suppress("LongParameterList")
|
||||
internal class DetailsModel @Inject constructor(
|
||||
socialsBuilder: SocialsBuilder,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.features.details.model
|
||||
|
||||
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
|
|
@ -19,7 +19,7 @@ import kotlinx.coroutines.flow.launchIn
|
|||
import kotlinx.coroutines.flow.update
|
||||
import javax.inject.Inject
|
||||
|
||||
@ComponentScoped
|
||||
@ModelScoped
|
||||
internal class UserWalletListModel @Inject constructor(
|
||||
userWalletsFetcher: UserWalletsFetcher,
|
||||
shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.features.details.utils
|
||||
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.ui.components.block.model.BlockUM
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
|
|
@ -14,7 +14,7 @@ import kotlinx.collections.immutable.persistentListOf
|
|||
import kotlinx.collections.immutable.toImmutableList
|
||||
import javax.inject.Inject
|
||||
|
||||
@ComponentScoped
|
||||
@ModelScoped
|
||||
internal class ItemsBuilder @Inject constructor(private val router: Router) {
|
||||
|
||||
fun buildAll(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.features.details.utils
|
||||
|
||||
import androidx.compose.ui.text.intl.Locale
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.features.details.entity.DetailsFooterUM
|
||||
import com.tangem.features.details.impl.R
|
||||
|
|
@ -9,7 +9,7 @@ import kotlinx.collections.immutable.ImmutableList
|
|||
import kotlinx.collections.immutable.toImmutableList
|
||||
import javax.inject.Inject
|
||||
|
||||
@ComponentScoped
|
||||
@ModelScoped
|
||||
internal class SocialsBuilder @Inject constructor(
|
||||
private val urlOpener: UrlOpener,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import arrow.core.raise.fold
|
|||
import arrow.core.raise.recover
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.decompose.navigation.popTo
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
|
|
@ -33,7 +33,7 @@ import kotlinx.coroutines.suspendCancellableCoroutine
|
|||
import javax.inject.Inject
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
@ComponentScoped
|
||||
@ModelScoped
|
||||
@Suppress("LongParameterList")
|
||||
internal class UserWalletSaver @Inject constructor(
|
||||
private val scanCardProcessor: ScanCardProcessor,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import arrow.core.Either
|
|||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.ui.userwallet.converter.UserWalletItemUMConverter
|
||||
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
|
|
@ -31,7 +31,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|||
import kotlinx.coroutines.flow.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@ComponentScoped
|
||||
@ModelScoped
|
||||
internal class UserWalletsFetcher @Inject constructor(
|
||||
getWalletsUseCase: GetWalletsUseCase,
|
||||
private val getWalletTotalBalanceUseCase: GetWalletTotalBalanceUseCase,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.features.disclaimer.impl.di
|
||||
|
||||
import com.tangem.core.decompose.di.DecomposeComponent
|
||||
import com.tangem.core.decompose.di.ModelComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.disclaimer.impl.model.DisclaimerModel
|
||||
import dagger.Binds
|
||||
|
|
@ -10,7 +10,7 @@ import dagger.multibindings.ClassKey
|
|||
import dagger.multibindings.IntoMap
|
||||
|
||||
@Module
|
||||
@InstallIn(DecomposeComponent::class)
|
||||
@InstallIn(ModelComponent::class)
|
||||
internal interface ModelModule {
|
||||
|
||||
@Binds
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.features.disclaimer.impl.model
|
||||
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
|
|
@ -17,7 +17,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@ComponentScoped
|
||||
@ModelScoped
|
||||
@Suppress("LongParameterList")
|
||||
internal class DisclaimerModel @Inject constructor(
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
import com.arkivanov.decompose.extensions.compose.jetpack.stack.Children
|
||||
import com.arkivanov.decompose.extensions.compose.jetpack.stack.animation.stackAnimation
|
||||
import com.arkivanov.decompose.extensions.compose.jetpack.subscribeAsState
|
||||
import com.arkivanov.decompose.extensions.compose.stack.Children
|
||||
import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation
|
||||
import com.arkivanov.decompose.extensions.compose.subscribeAsState
|
||||
import com.arkivanov.decompose.router.stack.*
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import androidx.compose.runtime.getValue
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
import com.arkivanov.decompose.extensions.compose.jetpack.subscribeAsState
|
||||
import com.arkivanov.decompose.extensions.compose.subscribeAsState
|
||||
import com.arkivanov.decompose.router.slot.childSlot
|
||||
import com.arkivanov.decompose.router.slot.dismiss
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import androidx.compose.runtime.getValue
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
import com.arkivanov.decompose.extensions.compose.jetpack.subscribeAsState
|
||||
import com.arkivanov.decompose.extensions.compose.subscribeAsState
|
||||
import com.arkivanov.decompose.router.slot.childSlot
|
||||
import com.arkivanov.decompose.router.slot.dismiss
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.features.managetokens.di
|
||||
|
||||
import com.tangem.core.decompose.di.DecomposeComponent
|
||||
import com.tangem.core.decompose.di.ModelComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.managetokens.model.CustomTokenFormModel
|
||||
import com.tangem.features.managetokens.model.CustomTokenSelectorModel
|
||||
|
|
@ -13,7 +13,7 @@ import dagger.multibindings.ClassKey
|
|||
import dagger.multibindings.IntoMap
|
||||
|
||||
@Module
|
||||
@InstallIn(DecomposeComponent::class)
|
||||
@InstallIn(ModelComponent::class)
|
||||
internal interface ModelModule {
|
||||
|
||||
@Binds
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.tangem.features.managetokens.model
|
|||
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
|
|
@ -37,7 +37,7 @@ import javax.inject.Inject
|
|||
|
||||
// TODO: Divide to sub-components: [REDACTED_JIRA]
|
||||
@Suppress("LongParameterList", "LargeClass")
|
||||
@ComponentScoped
|
||||
@ModelScoped
|
||||
internal class CustomTokenFormModel @Inject constructor(
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val customCurrencyValidator: CustomCurrencyValidator,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.features.managetokens.model
|
|||
import arrow.core.getOrElse
|
||||
import com.arkivanov.decompose.router.slot.SlotNavigation
|
||||
import com.arkivanov.decompose.router.slot.activate
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
|
|
@ -33,7 +33,7 @@ import kotlinx.coroutines.flow.update
|
|||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@ComponentScoped
|
||||
@ModelScoped
|
||||
internal class CustomTokenSelectorModel @Inject constructor(
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val getSupportedNetworksUseCase: GetSupportedNetworksUseCase,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import arrow.core.getOrElse
|
|||
import com.arkivanov.decompose.router.slot.SlotNavigation
|
||||
import com.arkivanov.decompose.router.slot.activate
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
|
|
@ -42,7 +42,7 @@ import timber.log.Timber
|
|||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@ComponentScoped
|
||||
@ModelScoped
|
||||
internal class ManageTokensModel @Inject constructor(
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val router: Router,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.features.managetokens.model
|
|||
import arrow.core.flatten
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
|
|
@ -37,7 +37,7 @@ import timber.log.Timber
|
|||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@ComponentScoped
|
||||
@ModelScoped
|
||||
internal class OnboardingManageTokensModel @Inject constructor(
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val manageTokensListManager: ManageTokensListManager,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.features.managetokens.utils
|
|||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
|
|
@ -15,7 +15,7 @@ import com.tangem.features.managetokens.impl.R
|
|||
import kotlinx.collections.immutable.persistentMapOf
|
||||
import javax.inject.Inject
|
||||
|
||||
@ComponentScoped
|
||||
@ModelScoped
|
||||
internal class CustomCurrencyFormBuilder @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.features.managetokens.utils
|
||||
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.domain.managetokens.CheckIsCurrencyNotAddedUseCase
|
||||
import com.tangem.domain.managetokens.CreateCurrencyUseCase
|
||||
import com.tangem.domain.managetokens.FindTokenUseCase
|
||||
|
|
@ -20,7 +20,7 @@ import kotlinx.coroutines.launch
|
|||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@ComponentScoped
|
||||
@ModelScoped
|
||||
internal class CustomCurrencyValidator @Inject constructor(
|
||||
private val validateTokenFormUseCase: ValidateTokenFormUseCase,
|
||||
private val createCustomCurrencyUseCase: CreateCurrencyUseCase,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import androidx.compose.ui.util.fastForEachIndexed
|
|||
import androidx.compose.ui.util.fastMap
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.ui.clipboard.ClipboardManager
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
|
|
@ -40,7 +40,7 @@ import timber.log.Timber
|
|||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@ComponentScoped
|
||||
@ModelScoped
|
||||
internal class ManageTokensListManager @Inject constructor(
|
||||
private val getManagedTokensUseCase: GetManagedTokensUseCase,
|
||||
private val checkHasLinkedTokensUseCase: CheckHasLinkedTokensUseCase,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.features.markets.details.impl.di
|
||||
|
||||
import com.tangem.core.decompose.di.DecomposeComponent
|
||||
import com.tangem.core.decompose.di.ModelComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.markets.details.impl.model.MarketsTokenDetailsModel
|
||||
import dagger.Binds
|
||||
|
|
@ -10,7 +10,7 @@ import dagger.multibindings.ClassKey
|
|||
import dagger.multibindings.IntoMap
|
||||
|
||||
@Module
|
||||
@InstallIn(DecomposeComponent::class)
|
||||
@InstallIn(ModelComponent::class)
|
||||
internal interface ModelModule {
|
||||
|
||||
@Binds
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import com.tangem.common.ui.charts.state.MarketChartData
|
|||
import com.tangem.common.ui.charts.state.MarketChartDataProducer
|
||||
import com.tangem.common.ui.charts.state.sorted
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
|
|
@ -53,7 +53,7 @@ import javax.inject.Inject
|
|||
|
||||
@Suppress("LargeClass", "LongParameterList")
|
||||
@Stable
|
||||
@ComponentScoped
|
||||
@ModelScoped
|
||||
internal class MarketsTokenDetailsModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import androidx.compose.runtime.State
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import com.arkivanov.decompose.ExperimentalDecomposeApi
|
||||
import com.arkivanov.decompose.extensions.compose.jetpack.subscribeAsState
|
||||
import com.arkivanov.decompose.extensions.compose.subscribeAsState
|
||||
import com.arkivanov.decompose.router.stack.ChildStack
|
||||
import com.arkivanov.decompose.router.stack.StackNavigation
|
||||
import com.arkivanov.decompose.router.stack.childStack
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.Dp
|
||||
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.extensions.compose.stack.Children
|
||||
import com.arkivanov.decompose.extensions.compose.stack.animation.slide
|
||||
import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation
|
||||
import com.arkivanov.decompose.router.stack.ChildStack
|
||||
import com.tangem.core.ui.res.LocalMainBottomSheetColor
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.features.markets.portfolio.impl.di
|
||||
|
||||
import com.tangem.core.decompose.di.DecomposeComponent
|
||||
import com.tangem.core.decompose.di.ModelComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.markets.portfolio.impl.model.MarketsPortfolioModel
|
||||
import dagger.Binds
|
||||
|
|
@ -10,7 +10,7 @@ import dagger.multibindings.ClassKey
|
|||
import dagger.multibindings.IntoMap
|
||||
|
||||
@Module
|
||||
@InstallIn(DecomposeComponent::class)
|
||||
@InstallIn(ModelComponent::class)
|
||||
internal interface ModelModule {
|
||||
|
||||
@Binds
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.features.markets.portfolio.impl.model
|
|||
import androidx.compose.runtime.Stable
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
|
|
@ -38,7 +38,7 @@ import javax.inject.Inject
|
|||
|
||||
@Suppress("LongParameterList")
|
||||
@Stable
|
||||
@ComponentScoped
|
||||
@ModelScoped
|
||||
internal class MarketsPortfolioModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.tangem.features.markets.portfolio.impl.model
|
|||
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.ui.clipboard.ClipboardManager
|
||||
|
|
@ -31,7 +30,6 @@ import dagger.assisted.AssistedInject
|
|||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@ComponentScoped
|
||||
internal class TokenActionsHandler @AssistedInject constructor(
|
||||
private val router: Router,
|
||||
private val clipboardManager: ClipboardManager,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.features.markets.token.block.impl.di
|
||||
|
||||
import com.tangem.core.decompose.di.DecomposeComponent
|
||||
import com.tangem.core.decompose.di.ModelComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.markets.token.block.impl.model.TokenMarketBlockModel
|
||||
import dagger.Binds
|
||||
|
|
@ -10,7 +10,7 @@ import dagger.multibindings.ClassKey
|
|||
import dagger.multibindings.IntoMap
|
||||
|
||||
@Module
|
||||
@InstallIn(DecomposeComponent::class)
|
||||
@InstallIn(ModelComponent::class)
|
||||
internal interface ModelModule {
|
||||
|
||||
@Binds
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import com.tangem.common.routing.AppRoute
|
|||
import com.tangem.common.ui.charts.state.MarketChartData
|
||||
import com.tangem.common.ui.charts.state.converter.PriceAndTimePointValuesConverter
|
||||
import com.tangem.common.ui.charts.state.sorted
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
|
|
@ -31,7 +31,7 @@ import kotlinx.coroutines.launch
|
|||
import javax.inject.Inject
|
||||
|
||||
@Stable
|
||||
@ComponentScoped
|
||||
@ModelScoped
|
||||
internal class TokenMarketBlockModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.features.markets.tokenlist.impl.di
|
||||
|
||||
import com.tangem.core.decompose.di.DecomposeComponent
|
||||
import com.tangem.core.decompose.di.ModelComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.markets.tokenlist.impl.model.MarketsListModel
|
||||
import dagger.Binds
|
||||
|
|
@ -10,7 +10,7 @@ import dagger.multibindings.ClassKey
|
|||
import dagger.multibindings.IntoMap
|
||||
|
||||
@Module
|
||||
@InstallIn(DecomposeComponent::class)
|
||||
@InstallIn(ModelComponent::class)
|
||||
internal interface ModelModule {
|
||||
|
||||
@Binds
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.features.markets.tokenlist.impl.model
|
|||
import androidx.compose.runtime.Stable
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
|
|
@ -29,7 +29,7 @@ private const val UPDATE_QUOTES_TIMER_MILLIS = 60000L
|
|||
private const val SEARCH_QUERY_DEBOUNCE_MILLIS = 800L
|
||||
|
||||
@OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class)
|
||||
@ComponentScoped
|
||||
@ModelScoped
|
||||
@Stable
|
||||
internal class MarketsListModel @Inject constructor(
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
|
|
|
|||
22
features/nft/api/build.gradle.kts
Normal file
22
features/nft/api/build.gradle.kts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
plugins {
|
||||
alias(deps.plugins.android.library)
|
||||
alias(deps.plugins.kotlin.android)
|
||||
id("configuration")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.tangem.features.nft.api"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
/* Project - Domain */
|
||||
implementation(projects.domain.models)
|
||||
|
||||
/* Project - Core */
|
||||
implementation(projects.core.decompose)
|
||||
implementation(projects.core.ui)
|
||||
|
||||
/* Compose */
|
||||
implementation(deps.compose.runtime)
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.tangem.features.nft
|
||||
|
||||
interface NFTFeatureToggles {
|
||||
val isNFTEnabled: Boolean
|
||||
}
|
||||
1
features/nft/impl/.gitignore
vendored
Normal file
1
features/nft/impl/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
66
features/nft/impl/build.gradle.kts
Normal file
66
features/nft/impl/build.gradle.kts
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
plugins {
|
||||
alias(deps.plugins.android.library)
|
||||
alias(deps.plugins.kotlin.android)
|
||||
alias(deps.plugins.kotlin.kapt)
|
||||
alias(deps.plugins.kotlin.serialization)
|
||||
alias(deps.plugins.hilt.android)
|
||||
id("configuration")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.tangem.features.nft.impl"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
/** Api */
|
||||
implementation(projects.features.nft.api)
|
||||
|
||||
/** Core modules */
|
||||
implementation(projects.core.configToggles)
|
||||
implementation(projects.core.analytics)
|
||||
implementation(projects.core.analytics.models)
|
||||
implementation(projects.core.utils)
|
||||
implementation(projects.core.ui)
|
||||
implementation(projects.core.res)
|
||||
implementation(projects.core.decompose)
|
||||
implementation(projects.core.navigation)
|
||||
implementation(projects.core.datasource)
|
||||
|
||||
/** Common */
|
||||
implementation(projects.common.ui)
|
||||
implementation(projects.common.routing)
|
||||
|
||||
/** Tangem libraries */
|
||||
implementation(projects.libs.tangemSdkApi)
|
||||
implementation(tangemDeps.card.core)
|
||||
implementation(tangemDeps.card.android) {
|
||||
exclude(module = "joda-time")
|
||||
}
|
||||
|
||||
/** AndroidX libraries */
|
||||
implementation(deps.androidx.core.ktx)
|
||||
implementation(deps.lifecycle.runtime.ktx)
|
||||
|
||||
/** Compose libraries */
|
||||
implementation(deps.compose.material) // to use buttons and text field in MultiWalletSeedPhraseImport.kt
|
||||
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.lottie.compose)
|
||||
implementation(deps.decompose.ext.compose)
|
||||
implementation(deps.androidx.activity.compose)
|
||||
implementation(deps.androidx.datastore)
|
||||
|
||||
/** Other libraries */
|
||||
implementation(deps.kotlin.immutable.collections)
|
||||
implementation(deps.kotlin.serialization)
|
||||
implementation(deps.timber)
|
||||
implementation(deps.firebase.crashlytics)
|
||||
|
||||
/** DI */
|
||||
implementation(deps.hilt.android)
|
||||
kapt(deps.hilt.kapt)
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.features.nft
|
||||
|
||||
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
|
||||
|
||||
internal class DefaultNFTFeatureToggles(
|
||||
private val featureTogglesManager: FeatureTogglesManager,
|
||||
) : NFTFeatureToggles {
|
||||
override val isNFTEnabled: Boolean
|
||||
get() = featureTogglesManager.isFeatureEnabled(name = "NFT_ENABLED")
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.tangem.features.nft
|
||||
|
||||
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal object NFTFeatureModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideFeatureToggles(featureTogglesManager: FeatureTogglesManager): NFTFeatureToggles {
|
||||
return DefaultNFTFeatureToggles(featureTogglesManager)
|
||||
}
|
||||
}
|
||||
|
|
@ -3,4 +3,5 @@ package com.tangem.features.onboarding.v2
|
|||
interface OnboardingV2FeatureToggles {
|
||||
val isOnboardingV2Enabled: Boolean
|
||||
val isVisaOnboardingEnabled: Boolean
|
||||
val isNoteRefactoringEnabled: Boolean
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ dependencies {
|
|||
/** Api */
|
||||
implementation(projects.features.onboardingV2.api)
|
||||
implementation(projects.features.manageTokens.api)
|
||||
implementation(projects.features.biometry.api)
|
||||
|
||||
/** Core modules */
|
||||
implementation(projects.core.configToggles)
|
||||
|
|
|
|||
|
|
@ -10,4 +10,6 @@ internal class DefaultOnboardingV2FeatureToggles @Inject constructor(
|
|||
get() = featureTogglesManager.isFeatureEnabled("ONBOARDING_CODE_REFACTORING_ENABLED")
|
||||
override val isVisaOnboardingEnabled: Boolean
|
||||
get() = featureTogglesManager.isFeatureEnabled("VISA_ONBOARDING_ENABLED")
|
||||
override val isNoteRefactoringEnabled: Boolean
|
||||
get() = featureTogglesManager.isFeatureEnabled("NOTE_REFACTORING_ENABLED")
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.features.onboarding.v2.common.ui
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import coil.compose.AsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.tangem.features.onboarding.v2.impl.R
|
||||
|
||||
@Composable
|
||||
internal fun WalletCard(url: String?, modifier: Modifier = Modifier) {
|
||||
AsyncImage(
|
||||
modifier = modifier,
|
||||
model = ImageRequest.Builder(LocalContext.current)
|
||||
.data(url)
|
||||
.crossfade(true)
|
||||
.build(),
|
||||
placeholder = painterResource(R.drawable.card_placeholder_black),
|
||||
error = painterResource(R.drawable.card_placeholder_black),
|
||||
fallback = painterResource(R.drawable.card_placeholder_black),
|
||||
contentScale = ContentScale.Fit,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
|
|
@ -4,19 +4,23 @@ import androidx.activity.compose.BackHandler
|
|||
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.extensions.compose.subscribeAsState
|
||||
import com.arkivanov.decompose.router.stack.*
|
||||
import com.arkivanov.decompose.value.ObserveLifecycleMode
|
||||
import com.arkivanov.decompose.value.Value
|
||||
import com.arkivanov.decompose.value.subscribe
|
||||
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.resourceReference
|
||||
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.impl.R
|
||||
import com.tangem.features.onboarding.v2.stepper.api.OnboardingStepperComponent
|
||||
import com.tangem.utils.coroutines.JobHolder
|
||||
import com.tangem.utils.coroutines.saveIn
|
||||
|
|
@ -87,7 +91,10 @@ internal class DefaultOnboardingEntryComponent @AssistedInject constructor(
|
|||
@Suppress("MagicNumber")
|
||||
private fun linkToInnerNavigation() {
|
||||
// stepper linking
|
||||
innerStack.observe { stack ->
|
||||
innerStack.subscribe(
|
||||
lifecycle = lifecycle,
|
||||
mode = ObserveLifecycleMode.CREATE_DESTROY,
|
||||
) { stack ->
|
||||
val activeChild = stack.active.instance
|
||||
if (activeChild is InnerNavigationHolder) {
|
||||
componentScope.launch {
|
||||
|
|
@ -105,9 +112,19 @@ internal class DefaultOnboardingEntryComponent @AssistedInject constructor(
|
|||
} else {
|
||||
stepperComponent.state.update {
|
||||
it.copy(
|
||||
currentStep = if (stack.active.configuration is OnboardingRoute.ManageTokens) 7 else 8,
|
||||
steps = 8,
|
||||
title = model.titleProvider.currentTitle.value,
|
||||
currentStep = when (stack.active.configuration) {
|
||||
is OnboardingRoute.AskBiometry -> 7
|
||||
is OnboardingRoute.ManageTokens -> 8
|
||||
is OnboardingRoute.Done -> 9
|
||||
else -> error("Unsupported route")
|
||||
},
|
||||
steps = 9,
|
||||
title = when (stack.active.configuration) {
|
||||
is OnboardingRoute.AskBiometry -> resourceReference(R.string.onboarding_navbar_save_wallet)
|
||||
is OnboardingRoute.ManageTokens -> resourceReference(R.string.main_manage_tokens)
|
||||
is OnboardingRoute.Done -> resourceReference(R.string.onboarding_done_header)
|
||||
else -> error("Unsupported route")
|
||||
},
|
||||
showProgress = true,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
package com.tangem.features.onboarding.v2.entry.impl.analytics
|
||||
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
|
||||
internal sealed class OnboardingEntryEvent(
|
||||
category: String,
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
) : AnalyticsEvent(category, event, params) {
|
||||
|
||||
data class Biometric(
|
||||
val state: State,
|
||||
) : OnboardingEntryEvent(
|
||||
category = "Onboarding / Biometric",
|
||||
event = "Biometry",
|
||||
params = mapOf("State" to state.name),
|
||||
) {
|
||||
enum class State {
|
||||
On, Off
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.features.onboarding.v2.entry.impl.di
|
||||
|
||||
import com.tangem.core.decompose.di.DecomposeComponent
|
||||
import com.tangem.core.decompose.di.ModelComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.onboarding.v2.entry.OnboardingEntryComponent
|
||||
import com.tangem.features.onboarding.v2.entry.impl.DefaultOnboardingEntryComponent
|
||||
|
|
@ -25,7 +25,7 @@ internal interface ComponentModule {
|
|||
}
|
||||
|
||||
@Module
|
||||
@InstallIn(DecomposeComponent::class)
|
||||
@InstallIn(ModelComponent::class)
|
||||
internal interface ModelModule {
|
||||
@Binds
|
||||
@IntoMap
|
||||
|
|
|
|||
|
|
@ -1,20 +1,25 @@
|
|||
package com.tangem.features.onboarding.v2.entry.impl.model
|
||||
|
||||
import com.arkivanov.decompose.router.stack.StackNavigation
|
||||
import com.arkivanov.decompose.router.stack.navigate
|
||||
import com.arkivanov.decompose.router.stack.replaceAll
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.models.scan.ProductType
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.features.biometry.AskBiometryComponent
|
||||
import com.tangem.features.biometry.BiometryFeatureToggles
|
||||
import com.tangem.features.onboarding.v2.TitleProvider
|
||||
import com.tangem.features.onboarding.v2.entry.OnboardingEntryComponent
|
||||
import com.tangem.features.onboarding.v2.entry.impl.analytics.OnboardingEntryEvent
|
||||
import com.tangem.features.onboarding.v2.entry.impl.routing.OnboardingRoute
|
||||
import com.tangem.features.onboarding.v2.multiwallet.api.OnboardingMultiWalletComponent
|
||||
import com.tangem.sdk.api.TangemSdkManager
|
||||
|
|
@ -25,13 +30,16 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@ComponentScoped
|
||||
@Suppress("LongParameterList")
|
||||
@ModelScoped
|
||||
internal class OnboardingEntryModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val router: Router,
|
||||
private val tangemSdkManager: TangemSdkManager,
|
||||
private val settingsRepository: SettingsRepository,
|
||||
private val askBiometryFeatureToggles: BiometryFeatureToggles,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<OnboardingEntryComponent.Params>()
|
||||
|
|
@ -74,34 +82,69 @@ internal class OnboardingEntryModel @Inject constructor(
|
|||
titleProvider = titleProvider,
|
||||
onDone = ::onVisaOnboardingDone,
|
||||
)
|
||||
ProductType.Note -> OnboardingRoute.Note(
|
||||
scanResponse = scanResponse,
|
||||
titleProvider = titleProvider,
|
||||
onDone = ::navigateToFinalScreenFlow,
|
||||
)
|
||||
else -> error("Unsupported")
|
||||
}
|
||||
}
|
||||
|
||||
private fun onMultiWalletOnboardingDone(userWallet: UserWallet) {
|
||||
stackNavigation.navigate {
|
||||
listOf(OnboardingRoute.ManageTokens(userWallet))
|
||||
if (userWallet.scanResponse.cardTypesResolver.isMultiwalletAllowed()) {
|
||||
stackNavigation.replaceAll(OnboardingRoute.ManageTokens(userWallet))
|
||||
} else {
|
||||
navigateToFinalScreenFlow()
|
||||
}
|
||||
}
|
||||
|
||||
fun onManageTokensDone() {
|
||||
stackNavigation.navigate {
|
||||
listOf(OnboardingRoute.Done(onDone = ::navigateToWalletScreen))
|
||||
}
|
||||
navigateToFinalScreenFlow()
|
||||
}
|
||||
|
||||
private fun onVisaOnboardingDone() {
|
||||
stackNavigation.navigate {
|
||||
listOf(OnboardingRoute.Done(onDone = ::navigateToWalletScreen))
|
||||
navigateToFinalScreenFlow()
|
||||
}
|
||||
|
||||
private fun navigateToFinalScreenFlow() {
|
||||
if (askBiometryFeatureToggles.isAskForBiometryEnabled) {
|
||||
modelScope.launch {
|
||||
if (tangemSdkManager.checkCanUseBiometry() && settingsRepository.shouldShowSaveUserWalletScreen()) {
|
||||
stackNavigation.replaceAll(
|
||||
OnboardingRoute.AskBiometry(modelCallbacks = AskBiometryModelCallbacks()),
|
||||
)
|
||||
} else {
|
||||
stackNavigation.replaceAll(OnboardingRoute.Done(onDone = ::navigateToWalletScreen))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
stackNavigation.replaceAll(OnboardingRoute.Done(onDone = ::navigateToWalletScreen))
|
||||
}
|
||||
}
|
||||
|
||||
inner class AskBiometryModelCallbacks : AskBiometryComponent.ModelCallbacks {
|
||||
override fun onAllowed() {
|
||||
analyticsEventHandler.send(OnboardingEntryEvent.Biometric(OnboardingEntryEvent.Biometric.State.On))
|
||||
stackNavigation.replaceAll(OnboardingRoute.Done(onDone = ::navigateToWalletScreen))
|
||||
}
|
||||
|
||||
override fun onDenied() {
|
||||
analyticsEventHandler.send(OnboardingEntryEvent.Biometric(OnboardingEntryEvent.Biometric.State.Off))
|
||||
stackNavigation.replaceAll(OnboardingRoute.Done(onDone = ::navigateToWalletScreen))
|
||||
}
|
||||
}
|
||||
|
||||
private fun navigateToWalletScreen() {
|
||||
modelScope.launch(NonCancellable) {
|
||||
if (askBiometryFeatureToggles.isAskForBiometryEnabled) {
|
||||
router.replaceAll(AppRoute.Wallet)
|
||||
if (tangemSdkManager.checkCanUseBiometry() && settingsRepository.shouldShowSaveUserWalletScreen()) {
|
||||
delay(timeMillis = 1_800)
|
||||
router.push(AppRoute.SaveWallet)
|
||||
} else {
|
||||
modelScope.launch(NonCancellable) {
|
||||
router.replaceAll(AppRoute.Wallet)
|
||||
if (tangemSdkManager.checkCanUseBiometry() && settingsRepository.shouldShowSaveUserWalletScreen()) {
|
||||
delay(timeMillis = 1_800)
|
||||
router.push(AppRoute.SaveWallet)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
package com.tangem.features.onboarding.v2.entry.impl.routing
|
||||
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.features.biometry.AskBiometryComponent
|
||||
import com.tangem.features.managetokens.component.OnboardingManageTokensComponent
|
||||
import com.tangem.features.onboarding.v2.done.api.OnboardingDoneComponent
|
||||
import com.tangem.features.onboarding.v2.multiwallet.api.OnboardingMultiWalletComponent
|
||||
import com.tangem.features.onboarding.v2.note.api.OnboardingNoteComponent
|
||||
import com.tangem.features.onboarding.v2.visa.api.OnboardingVisaComponent
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -12,6 +14,8 @@ internal class OnboardingChildFactory @Inject constructor(
|
|||
private val onboardingManageTokensComponentFactory: OnboardingManageTokensComponent.Factory,
|
||||
private val onboardingDoneComponentFactory: OnboardingDoneComponent.Factory,
|
||||
private val onBoardingVisaComponentFactory: OnboardingVisaComponent.Factory,
|
||||
private val askBiometryComponentFactory: AskBiometryComponent.Factory,
|
||||
private val onboardingNoteComponentFactory: OnboardingNoteComponent.Factory,
|
||||
) {
|
||||
|
||||
fun createChild(route: OnboardingRoute, childContext: AppComponentContext, onManageTokensDone: () -> Unit): Any {
|
||||
|
|
@ -47,7 +51,22 @@ internal class OnboardingChildFactory @Inject constructor(
|
|||
onDone = route.onDone,
|
||||
),
|
||||
)
|
||||
else -> Unit
|
||||
is OnboardingRoute.Note -> onboardingNoteComponentFactory.create(
|
||||
context = childContext,
|
||||
params = OnboardingNoteComponent.Params(
|
||||
scanResponse = route.scanResponse,
|
||||
titleProvider = route.titleProvider,
|
||||
onDone = route.onDone,
|
||||
),
|
||||
)
|
||||
is OnboardingRoute.AskBiometry -> askBiometryComponentFactory.create(
|
||||
context = childContext,
|
||||
params = AskBiometryComponent.Params(
|
||||
bottomSheetVariant = false,
|
||||
modelCallbacks = route.modelCallbacks,
|
||||
),
|
||||
)
|
||||
is OnboardingRoute.None -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ 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.domain.wallets.models.UserWallet
|
||||
import com.tangem.features.biometry.AskBiometryComponent
|
||||
import com.tangem.features.onboarding.v2.TitleProvider
|
||||
import com.tangem.features.onboarding.v2.multiwallet.api.OnboardingMultiWalletComponent
|
||||
|
||||
|
|
@ -10,6 +11,12 @@ sealed class OnboardingRoute : Route {
|
|||
|
||||
data object None : OnboardingRoute()
|
||||
|
||||
data class Note(
|
||||
val titleProvider: TitleProvider,
|
||||
val scanResponse: ScanResponse,
|
||||
val onDone: () -> Unit,
|
||||
) : OnboardingRoute()
|
||||
|
||||
data class MultiWallet(
|
||||
val titleProvider: TitleProvider,
|
||||
val scanResponse: ScanResponse,
|
||||
|
|
@ -28,6 +35,10 @@ sealed class OnboardingRoute : Route {
|
|||
val userWallet: UserWallet,
|
||||
) : OnboardingRoute()
|
||||
|
||||
data class AskBiometry(
|
||||
val modelCallbacks: AskBiometryComponent.ModelCallbacks,
|
||||
) : OnboardingRoute()
|
||||
|
||||
data class Done(
|
||||
val onDone: () -> Unit,
|
||||
) : OnboardingRoute()
|
||||
|
|
|
|||
|
|
@ -4,14 +4,13 @@ import androidx.compose.foundation.background
|
|||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
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.extensions.compose.stack.Children
|
||||
import com.arkivanov.decompose.extensions.compose.stack.animation.slide
|
||||
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.onboarding.v2.entry.impl.routing.OnboardingRoute
|
||||
import com.tangem.features.onboarding.v2.multiwallet.api.OnboardingMultiWalletComponent
|
||||
|
||||
@Composable
|
||||
internal inline fun OnboardingEntry(
|
||||
|
|
@ -31,29 +30,7 @@ internal inline fun OnboardingEntry(
|
|||
stack = childStack,
|
||||
animation = stackAnimation(slide()),
|
||||
) {
|
||||
when (it.configuration) {
|
||||
is OnboardingRoute.MultiWallet -> {
|
||||
(it.instance as OnboardingMultiWalletComponent).Content(
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
is OnboardingRoute.ManageTokens -> {
|
||||
(it.instance as ComposableContentComponent).Content(
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
is OnboardingRoute.Done -> {
|
||||
(it.instance as ComposableContentComponent).Content(
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
is OnboardingRoute.Visa -> {
|
||||
(it.instance as ComposableContentComponent).Content(
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
OnboardingRoute.None -> {}
|
||||
}
|
||||
(it.instance as? ComposableContentComponent)?.Content(modifier = modifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,11 +6,11 @@ import androidx.compose.runtime.getValue
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.arkivanov.decompose.FaultyDecomposeApi
|
||||
import com.arkivanov.decompose.extensions.compose.jetpack.stack.Children
|
||||
import com.arkivanov.decompose.extensions.compose.jetpack.stack.animation.fade
|
||||
import com.arkivanov.decompose.extensions.compose.jetpack.stack.animation.slide
|
||||
import com.arkivanov.decompose.extensions.compose.jetpack.stack.animation.stackAnimation
|
||||
import com.arkivanov.decompose.extensions.compose.jetpack.subscribeAsState
|
||||
import com.arkivanov.decompose.extensions.compose.stack.Children
|
||||
import com.arkivanov.decompose.extensions.compose.stack.animation.fade
|
||||
import com.arkivanov.decompose.extensions.compose.stack.animation.slide
|
||||
import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation
|
||||
import com.arkivanov.decompose.extensions.compose.subscribeAsState
|
||||
import com.arkivanov.decompose.router.slot.SlotNavigation
|
||||
import com.arkivanov.decompose.router.slot.childSlot
|
||||
import com.arkivanov.decompose.router.slot.dismiss
|
||||
|
|
@ -70,7 +70,7 @@ internal class DefaultOnboardingMultiWalletComponent @AssistedInject constructor
|
|||
|
||||
private val innerNavigationStateFlow = instanceKeeper.getOrCreateSimple(key = "innerNavigationState") {
|
||||
MutableStateFlow(
|
||||
MultiWalletInnerNavigationState(1, 5),
|
||||
MultiWalletInnerNavigationState(1, 6),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -81,6 +81,8 @@ internal class DefaultOnboardingMultiWalletComponent @AssistedInject constructor
|
|||
backups = model.backups,
|
||||
)
|
||||
|
||||
val backButtonClickFlow = MutableSharedFlow<Unit>()
|
||||
|
||||
private val childStack: Value<ChildStack<OnboardingMultiWalletState.Step, ComposableContentComponent>> =
|
||||
childStack(
|
||||
key = "innerStack",
|
||||
|
|
@ -96,8 +98,6 @@ internal class DefaultOnboardingMultiWalletComponent @AssistedInject constructor
|
|||
},
|
||||
)
|
||||
|
||||
val backButtonClickFlow = MutableSharedFlow<Unit>()
|
||||
|
||||
override val innerNavigation: InnerNavigation = object : InnerNavigation {
|
||||
override val state = innerNavigationStateFlow
|
||||
|
||||
|
|
@ -117,7 +117,7 @@ internal class DefaultOnboardingMultiWalletComponent @AssistedInject constructor
|
|||
source = bottomSheetNavigation,
|
||||
serializer = null,
|
||||
handleBackButton = false,
|
||||
childFactory = { configuration, componentContext ->
|
||||
childFactory = { _, componentContext ->
|
||||
MultiWalletAccessCodeComponent(
|
||||
context = childByContext(componentContext),
|
||||
params = childParams,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.features.onboarding.v2.multiwallet.impl.child.accesscode.mode
|
|||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
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.onboarding.v2.multiwallet.impl.analytics.OnboardingEvent
|
||||
|
|
@ -22,7 +22,7 @@ import javax.inject.Inject
|
|||
private const val MINIMUM_ACCESS_CODE_LENGTH = 4
|
||||
|
||||
@Stable
|
||||
@ComponentScoped
|
||||
@ModelScoped
|
||||
internal class MultiWalletAccessCodeModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class MultiWalletBackupComponent(
|
|||
params.innerNavigation.update {
|
||||
it.copy(
|
||||
stackSize = 5,
|
||||
stackMaxSize = 8,
|
||||
stackMaxSize = 9,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import com.tangem.common.core.TangemSdkError
|
|||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.event.OnboardingAnalyticsEvent
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
|
|
@ -38,7 +38,7 @@ import kotlinx.coroutines.launch
|
|||
import javax.inject.Inject
|
||||
|
||||
@Stable
|
||||
@ComponentScoped
|
||||
@ModelScoped
|
||||
@Suppress("LongParameterList")
|
||||
class MultiWalletBackupModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class Wallet1ChooseOptionComponent(
|
|||
params.innerNavigation.update {
|
||||
it.copy(
|
||||
stackSize = 3,
|
||||
stackMaxSize = 8,
|
||||
stackMaxSize = 9,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.tangem.features.onboarding.v2.multiwallet.impl.child.chooseoption.mo
|
|||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.domain.card.repository.CardRepository
|
||||
|
|
@ -21,7 +21,7 @@ import kotlinx.coroutines.launch
|
|||
import javax.inject.Inject
|
||||
|
||||
@Stable
|
||||
@ComponentScoped
|
||||
@ModelScoped
|
||||
internal class Wallet1ChooseOptionModel @Inject constructor(
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
paramsContainer: ParamsContainer,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.impl.child.chooseoption.ui
|
||||
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.features.onboarding.v2.impl.R
|
||||
|
||||
internal val CarouselItems = listOf(
|
||||
CarouselItem(
|
||||
title = resourceReference(R.string.onboarding_wallet_info_title_first),
|
||||
subtitle = resourceReference(R.string.onboarding_wallet_info_subtitle_first),
|
||||
),
|
||||
CarouselItem(
|
||||
title = resourceReference(R.string.onboarding_wallet_info_title_second),
|
||||
subtitle = resourceReference(R.string.onboarding_wallet_info_subtitle_second),
|
||||
),
|
||||
CarouselItem(
|
||||
title = resourceReference(R.string.onboarding_wallet_info_title_third),
|
||||
subtitle = resourceReference(R.string.onboarding_wallet_info_subtitle_third),
|
||||
),
|
||||
CarouselItem(
|
||||
title = resourceReference(R.string.onboarding_wallet_info_title_fourth),
|
||||
subtitle = resourceReference(R.string.onboarding_wallet_info_subtitle_fourth),
|
||||
),
|
||||
)
|
||||
|
||||
internal data class CarouselItem(
|
||||
val title: TextReference,
|
||||
val subtitle: TextReference,
|
||||
)
|
||||
|
|
@ -1,10 +1,15 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.impl.child.chooseoption.ui
|
||||
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
|
|
@ -12,6 +17,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
import com.tangem.core.ui.components.SecondaryButton
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
|
@ -30,30 +36,44 @@ fun Wallet1ChooseOption(
|
|||
.navigationBarsPadding(),
|
||||
verticalArrangement = Arrangement.Bottom,
|
||||
) {
|
||||
// val pagerState = rememberPagerState(pageCount = { 10 }) // TODO [REDACTED_TASK_KEY]
|
||||
|
||||
Column(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(start = 32.dp, end = 32.dp, bottom = 16.dp)
|
||||
.weight(1f),
|
||||
.weight(1f)
|
||||
.padding(top = 32.dp, bottom = 16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.onboarding_wallet_info_title_first),
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(top = 16.dp),
|
||||
)
|
||||
val pagerState = rememberPagerState { CarouselItems.size }
|
||||
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.onboarding_wallet_info_subtitle_first),
|
||||
style = TangemTheme.typography.body1,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(top = 12.dp),
|
||||
HorizontalPager(pagerState) { selectedIndex ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(start = 32.dp, end = 32.dp, bottom = 16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
val item = CarouselItems[selectedIndex]
|
||||
Text(
|
||||
text = item.title.resolveReference(),
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(top = 16.dp),
|
||||
)
|
||||
Text(
|
||||
text = item.subtitle.resolveReference(),
|
||||
style = TangemTheme.typography.body1,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(top = 12.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Dots(
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||
count = CarouselItems.size,
|
||||
selectedIndex = pagerState.currentPage,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -77,6 +97,28 @@ fun Wallet1ChooseOption(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Dots(count: Int, selectedIndex: Int, modifier: Modifier = Modifier) {
|
||||
val generalColor = TangemTheme.colors.icon.informative
|
||||
val selectedColor = TangemTheme.colors.icon.primary1
|
||||
|
||||
Row(
|
||||
modifier = modifier,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
repeat(count) { index ->
|
||||
val color by animateColorAsState(if (index == selectedIndex) selectedColor else generalColor)
|
||||
|
||||
Canvas(Modifier.size(7.dp)) {
|
||||
drawCircle(
|
||||
color = color,
|
||||
radius = size.width / 2f,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun Preview() {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ internal class MultiWalletCreateWalletComponent(
|
|||
params.innerNavigation.update {
|
||||
it.copy(
|
||||
stackSize = 2,
|
||||
stackMaxSize = 8,
|
||||
stackMaxSize = 9,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import androidx.compose.runtime.Stable
|
|||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
|
|
@ -34,7 +34,7 @@ import javax.inject.Inject
|
|||
|
||||
@Suppress("LongParameterList")
|
||||
@Stable
|
||||
@ComponentScoped
|
||||
@ModelScoped
|
||||
internal class MultiWalletCreateWalletModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ internal class MultiWalletFinalizeComponent(
|
|||
params.innerNavigation.update {
|
||||
it.copy(
|
||||
stackSize = 7,
|
||||
stackMaxSize = 8,
|
||||
stackMaxSize = 9,
|
||||
)
|
||||
}
|
||||
params.parentParams.titleProvider.changeTitle(
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.features.onboarding.v2.multiwallet.impl.child.finalize.model
|
|||
import androidx.compose.runtime.Stable
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.domain.card.repository.CardRepository
|
||||
|
|
@ -39,7 +39,7 @@ import javax.inject.Inject
|
|||
|
||||
@Suppress("LongParameterList")
|
||||
@Stable
|
||||
@ComponentScoped
|
||||
@ModelScoped
|
||||
internal class MultiWalletFinalizeModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
|
|
@ -61,6 +61,7 @@ internal class MultiWalletFinalizeModel @Inject constructor(
|
|||
private val backupCardIds = backupServiceHolder.backupService.get()?.backupCardIds.orEmpty()
|
||||
|
||||
private var walletHasBackupError = false
|
||||
private var hasRing = false
|
||||
val uiState = _uiState.asStateFlow()
|
||||
val onBackFlow = MutableSharedFlow<Unit>()
|
||||
|
||||
|
|
@ -152,6 +153,7 @@ internal class MultiWalletFinalizeModel @Inject constructor(
|
|||
val primaryCardBatchId = backupService.primaryCardBatchId ?: return
|
||||
val isRing = isRing(primaryCardBatchId)
|
||||
val iconScanRes = if (isRing) R.drawable.img_hand_scan_ring else null
|
||||
if (isRing) hasRing = true
|
||||
|
||||
tangemSdkManager.changeProductType(isRing)
|
||||
backupService.proceedBackup(iconScanRes = iconScanRes) { result ->
|
||||
|
|
@ -178,6 +180,7 @@ internal class MultiWalletFinalizeModel @Inject constructor(
|
|||
val backupCardBatchId = backupService.backupCardsBatchIds.getOrNull(cardIndex) ?: return
|
||||
val isRing = isRing(backupCardBatchId)
|
||||
val iconScanRes = if (isRing) R.drawable.img_hand_scan_ring else null
|
||||
if (isRing) hasRing = true
|
||||
|
||||
tangemSdkManager.changeProductType(isRing)
|
||||
backupService.proceedBackup(iconScanRes = iconScanRes) { result ->
|
||||
|
|
@ -218,7 +221,6 @@ internal class MultiWalletFinalizeModel @Inject constructor(
|
|||
private fun finishBackup() {
|
||||
modelScope.launch {
|
||||
val scanResponse = params.multiWalletState.value.currentScanResponse
|
||||
|
||||
val userWalletCreated = createUserWallet(scanResponse)
|
||||
|
||||
val userWallet = when (params.parentParams.mode) {
|
||||
|
|
@ -249,6 +251,10 @@ internal class MultiWalletFinalizeModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
if (hasRing) {
|
||||
walletsRepository.setHasWalletsWithRing(userWallet.walletId)
|
||||
}
|
||||
|
||||
// save user wallet for manage tokens screen
|
||||
params.multiWalletState.update {
|
||||
it.copy(resultUserWallet = userWallet)
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ internal class Wallet1ScanPrimaryComponent(
|
|||
params.innerNavigation.update {
|
||||
it.copy(
|
||||
stackSize = 4,
|
||||
stackMaxSize = 8,
|
||||
stackMaxSize = 9,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.tangem.features.onboarding.v2.multiwallet.impl.child.scanprimary.mod
|
|||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
|
|
@ -15,7 +15,7 @@ import kotlinx.coroutines.launch
|
|||
import javax.inject.Inject
|
||||
|
||||
@Stable
|
||||
@ComponentScoped
|
||||
@ModelScoped
|
||||
internal class Wallet1ScanPrimaryModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ internal class MultiWalletSeedPhraseComponent(
|
|||
params.innerNavigation.update { st ->
|
||||
st.copy(
|
||||
stackSize = 3 + it.order,
|
||||
stackMaxSize = 10,
|
||||
stackMaxSize = 11,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import androidx.compose.runtime.Stable
|
|||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
|
|
@ -35,7 +35,7 @@ import javax.inject.Inject
|
|||
// =============================================
|
||||
@Suppress("LongParameterList")
|
||||
@Stable
|
||||
@ComponentScoped
|
||||
@ModelScoped
|
||||
internal class MultiWalletSeedPhraseModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import androidx.compose.foundation.lazy.LazyRow
|
|||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.OutlinedTextField
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -137,7 +137,6 @@ private fun PhraseBlock(state: MultiWalletSeedPhraseUM.Import, modifier: Modifie
|
|||
Column(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
) {
|
||||
// TODO migrate to material3
|
||||
OutlinedTextField(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
|
|
|||
|
|
@ -84,8 +84,8 @@ private fun SegmentSeedBlock(state: MultiWalletSeedPhraseUM.GenerateSeedPhrase,
|
|||
Text(
|
||||
text = pluralStringResourceSafe(
|
||||
id = R.plurals.onboarding_seed_generate_words_count,
|
||||
count = state.option.length,
|
||||
state.option.length,
|
||||
count = it.length,
|
||||
it.length,
|
||||
),
|
||||
modifier = Modifier
|
||||
.padding(vertical = TangemTheme.dimens.spacing10)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.impl.di
|
||||
|
||||
import com.tangem.core.decompose.di.DecomposeComponent
|
||||
import com.tangem.core.decompose.di.ModelComponent
|
||||
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
|
||||
|
|
@ -30,7 +30,7 @@ internal interface ComponentModule {
|
|||
}
|
||||
|
||||
@Module
|
||||
@InstallIn(DecomposeComponent::class)
|
||||
@InstallIn(ModelComponent::class)
|
||||
internal interface ModelModule {
|
||||
|
||||
@Binds
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.impl.model
|
||||
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ProductType
|
||||
import com.tangem.domain.wallets.usecase.GetCardImageUseCase
|
||||
import com.tangem.features.onboarding.v2.multiwallet.api.OnboardingMultiWalletComponent
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.analytics.OnboardingEvent
|
||||
|
|
@ -22,7 +23,7 @@ import kotlinx.coroutines.flow.*
|
|||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@ComponentScoped
|
||||
@ModelScoped
|
||||
internal class OnboardingMultiWalletModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
|
|
@ -116,8 +117,13 @@ internal class OnboardingMultiWalletModel @Inject constructor(
|
|||
card.wallets.isNotEmpty() && card.backupStatus == CardDTO.BackupStatus.NoBackup &&
|
||||
scanResponse.primaryCard == null -> OnboardingMultiWalletState.Step.ScanPrimary
|
||||
|
||||
card.wallets.isNotEmpty() && card.backupStatus == CardDTO.BackupStatus.NoBackup ->
|
||||
OnboardingMultiWalletState.Step.AddBackupDevice
|
||||
card.wallets.isNotEmpty() && card.backupStatus == CardDTO.BackupStatus.NoBackup -> {
|
||||
if (scanResponse.productType == ProductType.Wallet) {
|
||||
OnboardingMultiWalletState.Step.ChooseBackupOption
|
||||
} else {
|
||||
OnboardingMultiWalletState.Step.AddBackupDevice
|
||||
}
|
||||
}
|
||||
card.wallets.isNotEmpty() && card.backupStatus?.isActive == true ->
|
||||
OnboardingMultiWalletState.Step.Finalize
|
||||
else ->
|
||||
|
|
|
|||
|
|
@ -5,13 +5,13 @@ 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.CreateWallet ->
|
||||
resourceReference(R.string.onboarding_create_wallet_header)
|
||||
OnboardingMultiWalletState.Step.SeedPhrase,
|
||||
OnboardingMultiWalletState.Step.CreateWallet,
|
||||
-> resourceReference(R.string.onboarding_create_wallet_header)
|
||||
OnboardingMultiWalletState.Step.ScanPrimary,
|
||||
OnboardingMultiWalletState.Step.AddBackupDevice,
|
||||
->
|
||||
resourceReference(R.string.onboarding_navbar_title_creating_backup)
|
||||
OnboardingMultiWalletState.Step.Finalize ->
|
||||
resourceReference(R.string.onboarding_button_finalize_backup)
|
||||
else -> error("No title for the step")
|
||||
-> resourceReference(R.string.onboarding_navbar_title_creating_backup)
|
||||
OnboardingMultiWalletState.Step.ChooseBackupOption -> resourceReference(R.string.onboarding_getting_started)
|
||||
OnboardingMultiWalletState.Step.Finalize -> resourceReference(R.string.onboarding_button_finalize_backup)
|
||||
OnboardingMultiWalletState.Step.Done -> resourceReference(R.string.common_done)
|
||||
}
|
||||
|
|
@ -51,13 +51,13 @@ internal fun OnboardingMultiWallet(
|
|||
modifier = Modifier
|
||||
.padding(horizontal = 34.dp)
|
||||
.padding(top = 20.dp)
|
||||
.weight(.4f)
|
||||
.weight(.48f)
|
||||
.fillMaxWidth(),
|
||||
state = artworksState,
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier.weight(.44f),
|
||||
modifier = Modifier.weight(.52f),
|
||||
contentAlignment = Alignment.BottomStart,
|
||||
) {
|
||||
childContent(Modifier)
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@
|
|||
|
||||
package com.tangem.features.onboarding.v2.multiwallet.impl.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.core.Transition
|
||||
import androidx.compose.animation.core.animateFloat
|
||||
import androidx.compose.animation.core.updateTransition
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Text
|
||||
|
|
@ -13,18 +15,14 @@ import androidx.compose.runtime.*
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import androidx.compose.ui.zIndex
|
||||
import coil.compose.AsyncImage
|
||||
import coil.request.ImageRequest
|
||||
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.common.ui.WalletCard
|
||||
import kotlinx.coroutines.delay
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
|
|
@ -106,7 +104,7 @@ fun WalletArtworks(
|
|||
}
|
||||
}
|
||||
|
||||
val circleColor = TangemTheme.colors.background.secondary
|
||||
val circleColor = TangemTheme.colors.button.secondary
|
||||
|
||||
Canvas(
|
||||
modifier = Modifier
|
||||
|
|
@ -241,36 +239,21 @@ private fun AnimatedWalletCards(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WalletCard(url: String?, modifier: Modifier = Modifier) {
|
||||
AsyncImage(
|
||||
modifier = modifier,
|
||||
model = ImageRequest.Builder(LocalContext.current)
|
||||
.data(url)
|
||||
.crossfade(true)
|
||||
.build(),
|
||||
placeholder = painterResource(R.drawable.card_placeholder_black),
|
||||
error = painterResource(R.drawable.card_placeholder_black),
|
||||
fallback = painterResource(R.drawable.card_placeholder_black),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
|
||||
private fun WalletArtworksState.toTransitionSetState(maxWidthDp: Float, maxHeightDp: Float, density: Float) =
|
||||
when (this) {
|
||||
WalletArtworksState.Hidden -> listOf()
|
||||
is WalletArtworksState.Folded -> listOf(
|
||||
CardsTransitionState(
|
||||
WalletCardTransitionState(),
|
||||
WalletCardTransitionState(),
|
||||
WalletCardTransitionState(),
|
||||
WalletCardTransitionState(alpha = 1f),
|
||||
WalletCardTransitionState(alpha = 0f),
|
||||
WalletCardTransitionState(alpha = 0f),
|
||||
),
|
||||
)
|
||||
is WalletArtworksState.Stack -> listOf(
|
||||
CardsTransitionState(
|
||||
walletCard1 = WalletCardTransitionState(),
|
||||
walletCard2 = WalletCardTransitionState(),
|
||||
walletCard3 = WalletCardTransitionState(),
|
||||
WalletCardTransitionState(alpha = 1f),
|
||||
WalletCardTransitionState(alpha = 0f),
|
||||
WalletCardTransitionState(alpha = 0f),
|
||||
),
|
||||
CardsTransitionState(
|
||||
walletCard1 = WalletCardTransitionState().copy(
|
||||
|
|
@ -451,11 +434,14 @@ private fun WalletArtworksState.toTransitionSetState(maxWidthDp: Float, maxHeigh
|
|||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360, heightDp = 640)
|
||||
@Preview(showBackground = true, widthDp = 360, heightDp = 640, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun Preview() {
|
||||
TangemThemePreview {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
modifier = Modifier
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
var state: WalletArtworksState by remember { mutableStateOf(WalletArtworksState.Folded) }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.features.onboarding.v2.note.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 OnboardingNoteComponent : ComposableContentComponent, InnerNavigationHolder {
|
||||
|
||||
data class Params(
|
||||
val titleProvider: TitleProvider,
|
||||
val scanResponse: ScanResponse,
|
||||
val onDone: () -> Unit,
|
||||
)
|
||||
|
||||
interface Factory : ComponentFactory<Params, OnboardingNoteComponent>
|
||||
}
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.arkivanov.decompose.extensions.compose.stack.Children
|
||||
import com.arkivanov.decompose.extensions.compose.stack.animation.slide
|
||||
import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation
|
||||
import com.arkivanov.decompose.extensions.compose.subscribeAsState
|
||||
import com.arkivanov.decompose.router.stack.ChildStack
|
||||
import com.arkivanov.decompose.router.stack.childStack
|
||||
import com.arkivanov.decompose.router.stack.push
|
||||
import com.arkivanov.decompose.value.Value
|
||||
import com.arkivanov.decompose.value.subscribe
|
||||
import com.arkivanov.essenty.instancekeeper.getOrCreateSimple
|
||||
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.decompose.navigation.inner.InnerNavigation
|
||||
import com.tangem.core.decompose.navigation.inner.InnerNavigationState
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.features.onboarding.v2.impl.R
|
||||
import com.tangem.features.onboarding.v2.note.api.OnboardingNoteComponent
|
||||
import com.tangem.features.onboarding.v2.note.impl.child.create.OnboardingNoteCreateWalletComponent
|
||||
import com.tangem.features.onboarding.v2.note.impl.child.topup.OnboardingNoteTopUpComponent
|
||||
import com.tangem.features.onboarding.v2.note.impl.model.OnboardingNoteModel
|
||||
import com.tangem.features.onboarding.v2.note.impl.route.ONBOARDING_NOTE_STEPS_COUNT
|
||||
import com.tangem.features.onboarding.v2.note.impl.route.OnboardingNoteRoute
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
internal class DefaultOnboardingNoteComponent @AssistedInject constructor(
|
||||
@Assisted context: AppComponentContext,
|
||||
@Assisted val params: OnboardingNoteComponent.Params,
|
||||
) : OnboardingNoteComponent, AppComponentContext by context {
|
||||
|
||||
private val model: OnboardingNoteModel = getOrCreateModel(params)
|
||||
|
||||
private val currentChildBackEventHandle = instanceKeeper.getOrCreateSimple(key = "currentChildBackEventHandle") {
|
||||
MutableSharedFlow<Unit>()
|
||||
}
|
||||
|
||||
private val childParams = ChildParams(
|
||||
onBack = ::onChildBack,
|
||||
parentBackEvent = currentChildBackEventHandle,
|
||||
)
|
||||
|
||||
private val childStack: Value<ChildStack<OnboardingNoteRoute, ComposableContentComponent>> =
|
||||
childStack(
|
||||
key = "innerStack",
|
||||
source = model.stackNavigation,
|
||||
serializer = null,
|
||||
initialConfiguration = model.initialRoute,
|
||||
handleBackButton = true,
|
||||
childFactory = { configuration, factoryContext ->
|
||||
createChild(
|
||||
configuration,
|
||||
childByContext(factoryContext),
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
override val innerNavigation: InnerNavigation = object : InnerNavigation {
|
||||
override val state: StateFlow<InnerNavigationState> = model.innerNavigationState
|
||||
|
||||
override fun pop(onComplete: (Boolean) -> Unit) {
|
||||
// TODO [REDACTED_TASK_KEY]
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
componentScope.launch {
|
||||
model.onDone.collect { params.onDone() }
|
||||
}
|
||||
|
||||
// sets title and stepper value
|
||||
childStack.subscribe(lifecycle) { stack ->
|
||||
val currentRoute = stack.active.configuration
|
||||
params.titleProvider.changeTitle(TextReference.Res(R.string.onboarding_title))
|
||||
model.updateStepForNewRoute(currentRoute)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createChild(
|
||||
route: OnboardingNoteRoute,
|
||||
factoryContext: AppComponentContext,
|
||||
): ComposableContentComponent {
|
||||
return when (route) {
|
||||
OnboardingNoteRoute.CreateWallet -> OnboardingNoteCreateWalletComponent(
|
||||
appComponentContext = factoryContext,
|
||||
params = OnboardingNoteCreateWalletComponent.Params(
|
||||
childParams = childParams,
|
||||
onDone = { model.stackNavigation.push(OnboardingNoteRoute.TopUp) },
|
||||
),
|
||||
)
|
||||
OnboardingNoteRoute.TopUp -> OnboardingNoteTopUpComponent(
|
||||
appComponentContext = factoryContext,
|
||||
params = OnboardingNoteTopUpComponent.Params(
|
||||
childParams = childParams,
|
||||
onDone = { model.onDone.tryEmit(Unit) },
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onChildBack() = model.onChildBack(
|
||||
currentRoute = childStack.value.active.configuration,
|
||||
)
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val stackState by childStack.subscribeAsState()
|
||||
|
||||
Children(
|
||||
stack = stackState,
|
||||
animation = stackAnimation(slide()),
|
||||
) {
|
||||
it.instance.Content(modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : OnboardingNoteComponent.Factory {
|
||||
override fun create(
|
||||
context: AppComponentContext,
|
||||
params: OnboardingNoteComponent.Params,
|
||||
): DefaultOnboardingNoteComponent
|
||||
}
|
||||
|
||||
data class ChildParams(
|
||||
val onBack: () -> Unit,
|
||||
val parentBackEvent: SharedFlow<Unit>,
|
||||
)
|
||||
}
|
||||
|
||||
internal data class OnboardingNoteInnerNavigationState(
|
||||
override val stackSize: Int,
|
||||
) : InnerNavigationState {
|
||||
override val stackMaxSize: Int = ONBOARDING_NOTE_STEPS_COUNT
|
||||
}
|
||||
|
||||
internal const val ALL_STEPS_TOP_CONTAINER_WEIGHT = 0.48f
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.child.create
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
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.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.features.onboarding.v2.note.impl.DefaultOnboardingNoteComponent
|
||||
import com.tangem.features.onboarding.v2.note.impl.child.create.model.OnboardingNoteCreateWalletModel
|
||||
import com.tangem.features.onboarding.v2.note.impl.child.create.ui.OnboardingNoteCreateWallet
|
||||
|
||||
internal class OnboardingNoteCreateWalletComponent(
|
||||
appComponentContext: AppComponentContext,
|
||||
private val params: Params,
|
||||
) : ComposableContentComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: OnboardingNoteCreateWalletModel = getOrCreateModel(params)
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
BackHandler(onBack = remember(this) { { params.childParams.onBack() } })
|
||||
|
||||
OnboardingNoteCreateWallet(
|
||||
modifier = modifier,
|
||||
state = state,
|
||||
)
|
||||
}
|
||||
|
||||
data class Params(
|
||||
val childParams: DefaultOnboardingNoteComponent.ChildParams,
|
||||
val onDone: () -> Unit,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.child.create.model
|
||||
|
||||
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.onboarding.v2.note.impl.child.create.ui.state.OnboardingNoteCreateWalletUM
|
||||
import com.tangem.sdk.api.TangemSdkManager
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
@ModelScoped
|
||||
internal class OnboardingNoteCreateWalletModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val tangemSdkManager: TangemSdkManager,
|
||||
) : Model() {
|
||||
|
||||
private val _uiState = MutableStateFlow(
|
||||
OnboardingNoteCreateWalletUM(),
|
||||
)
|
||||
|
||||
val uiState: StateFlow<OnboardingNoteCreateWalletUM> = _uiState
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.child.create.ui
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.PrimaryButtonIconEnd
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
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.multiwallet.impl.ui.WalletArtworks
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.ui.WalletArtworksState
|
||||
import com.tangem.features.onboarding.v2.note.impl.ALL_STEPS_TOP_CONTAINER_WEIGHT
|
||||
import com.tangem.features.onboarding.v2.note.impl.child.create.ui.state.OnboardingNoteCreateWalletUM
|
||||
|
||||
@Composable
|
||||
internal fun OnboardingNoteCreateWallet(state: OnboardingNoteCreateWalletUM, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.navigationBarsPadding(),
|
||||
verticalArrangement = Arrangement.Bottom,
|
||||
) {
|
||||
WalletArtworks(
|
||||
url1 = state.artworkUrl,
|
||||
url2 = null,
|
||||
url3 = null,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 34.dp)
|
||||
.padding(top = 56.dp)
|
||||
.weight(ALL_STEPS_TOP_CONTAINER_WEIGHT)
|
||||
.fillMaxWidth(),
|
||||
state = WalletArtworksState.Folded,
|
||||
)
|
||||
Column(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(32.dp)
|
||||
.weight(1.0f),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.onboarding_create_wallet_header),
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(top = 16.dp),
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.onboarding_create_wallet_body),
|
||||
style = TangemTheme.typography.body1,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(top = 12.dp),
|
||||
)
|
||||
}
|
||||
|
||||
PrimaryButtonIconEnd(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
iconResId = R.drawable.ic_tangem_24,
|
||||
text = stringResourceSafe(R.string.onboarding_create_wallet_button_create_wallet),
|
||||
onClick = state.onCreateClick,
|
||||
showProgress = state.createWalletInProgress,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun OnboardingNoteCreatePreview() {
|
||||
TangemThemePreview {
|
||||
OnboardingNoteCreateWallet(
|
||||
state = OnboardingNoteCreateWalletUM(
|
||||
artworkUrl = null,
|
||||
createWalletInProgress = false,
|
||||
onCreateClick = {},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.child.create.ui.state
|
||||
|
||||
data class OnboardingNoteCreateWalletUM(
|
||||
val artworkUrl: String? = null,
|
||||
val createWalletInProgress: Boolean = false,
|
||||
val onCreateClick: () -> Unit = {},
|
||||
)
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.child.topup
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
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.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.features.onboarding.v2.note.impl.DefaultOnboardingNoteComponent
|
||||
import com.tangem.features.onboarding.v2.note.impl.child.topup.model.OnboardingNoteTopUpModel
|
||||
import com.tangem.features.onboarding.v2.note.impl.child.topup.ui.OnboardingNoteTopUp
|
||||
|
||||
internal class OnboardingNoteTopUpComponent(
|
||||
appComponentContext: AppComponentContext,
|
||||
private val params: Params,
|
||||
) : ComposableContentComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: OnboardingNoteTopUpModel = getOrCreateModel(params)
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
BackHandler(onBack = remember(this) { { params.childParams.onBack() } })
|
||||
|
||||
OnboardingNoteTopUp(
|
||||
modifier = modifier,
|
||||
state = state,
|
||||
)
|
||||
}
|
||||
|
||||
data class Params(
|
||||
val childParams: DefaultOnboardingNoteComponent.ChildParams,
|
||||
val onDone: () -> Unit,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.child.topup.model
|
||||
|
||||
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.onboarding.v2.note.impl.child.topup.ui.state.OnboardingNoteTopUpUM
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
@ModelScoped
|
||||
internal class OnboardingNoteTopUpModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
) : Model() {
|
||||
|
||||
private val _uiState = MutableStateFlow(
|
||||
OnboardingNoteTopUpUM(),
|
||||
)
|
||||
|
||||
val uiState: StateFlow<OnboardingNoteTopUpUM> = _uiState
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.child.topup.ui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.SpacerH8
|
||||
import com.tangem.core.ui.components.SpacerHMax
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.onboarding.v2.common.ui.WalletCard
|
||||
import com.tangem.features.onboarding.v2.impl.R
|
||||
|
||||
@Composable
|
||||
fun OnboardinNoteTopUpHeader(
|
||||
balance: String?,
|
||||
cardArtworkUrl: String?,
|
||||
isRefreshing: Boolean,
|
||||
onRefreshBalanceClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(vertical = 24.dp, horizontal = 32.dp)
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
TangemTheme.colors.button.secondary,
|
||||
shape = TangemTheme.shapes.roundedCornersMedium,
|
||||
),
|
||||
)
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.padding(horizontal = 32.dp),
|
||||
) {
|
||||
WalletCard(
|
||||
modifier = Modifier.width(120.dp),
|
||||
url = cardArtworkUrl,
|
||||
)
|
||||
SpacerHMax()
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.common_balance_title),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
SpacerH8()
|
||||
Text(
|
||||
text = balance.orEmpty(),
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
SpacerHMax()
|
||||
RefreshButton(
|
||||
isRefreshing = isRefreshing,
|
||||
onRefreshBalanceClick = onRefreshBalanceClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RefreshButton(isRefreshing: Boolean, onRefreshBalanceClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.size(TangemTheme.dimens.size48)
|
||||
.shadow(elevation = 2.dp, shape = CircleShape)
|
||||
.background(TangemTheme.colors.background.action)
|
||||
.clickable(
|
||||
enabled = !isRefreshing,
|
||||
onClick = onRefreshBalanceClick,
|
||||
indication = ripple(),
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
),
|
||||
) {
|
||||
if (isRefreshing) {
|
||||
Box {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.padding(TangemTheme.dimens.spacing12),
|
||||
color = TangemTheme.colors.icon.primary1,
|
||||
strokeWidth = TangemTheme.dimens.size2,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_refresh_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.text.disabled,
|
||||
modifier = Modifier.padding(TangemTheme.dimens.spacing12),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun OnboardinNoteTopUpHeaderPreview() {
|
||||
TangemThemePreview {
|
||||
OnboardinNoteTopUpHeader(
|
||||
balance = "0.00000001 BTC",
|
||||
cardArtworkUrl = "",
|
||||
onRefreshBalanceClick = {},
|
||||
isRefreshing = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.child.topup.ui
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.PrimaryButtonIconEnd
|
||||
import com.tangem.core.ui.components.SecondaryButton
|
||||
import com.tangem.core.ui.components.SpacerH8
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
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.note.impl.ALL_STEPS_TOP_CONTAINER_WEIGHT
|
||||
import com.tangem.features.onboarding.v2.note.impl.child.topup.ui.state.OnboardingNoteTopUpUM
|
||||
|
||||
@Composable
|
||||
fun OnboardingNoteTopUp(state: OnboardingNoteTopUpUM, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.navigationBarsPadding(),
|
||||
verticalArrangement = Arrangement.Bottom,
|
||||
) {
|
||||
OnboardinNoteTopUpHeader(
|
||||
balance = state.balance,
|
||||
cardArtworkUrl = state.cardArtworkUrl,
|
||||
onRefreshBalanceClick = state.onRefreshBalanceClick,
|
||||
isRefreshing = state.isRefreshing,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 34.dp)
|
||||
.padding(top = 20.dp)
|
||||
.weight(ALL_STEPS_TOP_CONTAINER_WEIGHT)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(32.dp)
|
||||
.weight(1.0f),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.onboarding_topup_title),
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(top = 16.dp),
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.onboarding_top_up_body),
|
||||
style = TangemTheme.typography.body1,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(top = 12.dp),
|
||||
)
|
||||
}
|
||||
|
||||
BottomButtons(state)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BottomButtons(state: OnboardingNoteTopUpUM) {
|
||||
if (state.availableForBuy) {
|
||||
PrimaryButtonIconEnd(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
iconResId = R.drawable.ic_tangem_24,
|
||||
text = stringResourceSafe(R.string.onboarding_top_up_button_but_crypto),
|
||||
onClick = state.onBuyCryptoClick,
|
||||
)
|
||||
SpacerH8()
|
||||
SecondaryButton(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
text = stringResourceSafe(R.string.onboarding_top_up_button_show_wallet_address),
|
||||
onClick = state.onShowWalletAddressClick,
|
||||
)
|
||||
} else {
|
||||
PrimaryButtonIconEnd(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
iconResId = R.drawable.ic_tangem_24,
|
||||
text = stringResourceSafe(R.string.onboarding_button_receive_crypto),
|
||||
onClick = state.onShowWalletAddressClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun OnboardingNoteTopUpPreview() {
|
||||
TangemThemePreview {
|
||||
OnboardingNoteTopUp(
|
||||
state = OnboardingNoteTopUpUM(
|
||||
availableForBuy = true,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.child.topup.ui.state
|
||||
|
||||
data class OnboardingNoteTopUpUM(
|
||||
val cardArtworkUrl: String? = null,
|
||||
val availableForBuy: Boolean = true,
|
||||
val balance: String? = null,
|
||||
val isRefreshing: Boolean = false,
|
||||
val amountToCreateAccount: String? = null,
|
||||
val onBuyCryptoClick: () -> Unit = {},
|
||||
val onShowWalletAddressClick: () -> Unit = {},
|
||||
val onRefreshBalanceClick: () -> Unit = {},
|
||||
)
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.di
|
||||
|
||||
import com.tangem.core.decompose.di.ModelComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.onboarding.v2.note.api.OnboardingNoteComponent
|
||||
import com.tangem.features.onboarding.v2.note.impl.DefaultOnboardingNoteComponent
|
||||
import com.tangem.features.onboarding.v2.note.impl.child.create.model.OnboardingNoteCreateWalletModel
|
||||
import com.tangem.features.onboarding.v2.note.impl.child.topup.model.OnboardingNoteTopUpModel
|
||||
import com.tangem.features.onboarding.v2.note.impl.model.OnboardingNoteModel
|
||||
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: DefaultOnboardingNoteComponent.Factory): OnboardingNoteComponent.Factory
|
||||
}
|
||||
|
||||
@Module
|
||||
@InstallIn(ModelComponent::class)
|
||||
internal interface ModelModule {
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(OnboardingNoteModel::class)
|
||||
fun provideNoteModel(model: OnboardingNoteModel): Model
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(OnboardingNoteCreateWalletModel::class)
|
||||
fun provideNoteCreateWalletModel(model: OnboardingNoteCreateWalletModel): Model
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(OnboardingNoteTopUpModel::class)
|
||||
fun provideNoteTopUpModel(model: OnboardingNoteTopUpModel): Model
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.model
|
||||
|
||||
import com.arkivanov.decompose.router.stack.StackNavigation
|
||||
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.onboarding.v2.note.api.OnboardingNoteComponent
|
||||
import com.tangem.features.onboarding.v2.note.impl.OnboardingNoteInnerNavigationState
|
||||
import com.tangem.features.onboarding.v2.note.impl.route.OnboardingNoteRoute
|
||||
import com.tangem.features.onboarding.v2.note.impl.route.stepNum
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import javax.inject.Inject
|
||||
|
||||
@ModelScoped
|
||||
internal class OnboardingNoteModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
) : Model() {
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
private val params = paramsContainer.require<OnboardingNoteComponent.Params>()
|
||||
|
||||
private val _currentScanResponse = MutableStateFlow(params.scanResponse)
|
||||
|
||||
val initialRoute = OnboardingNoteRoute.CreateWallet // TODO [REDACTED_TASK_KEY]
|
||||
|
||||
private val _innerNavigationState =
|
||||
MutableStateFlow(OnboardingNoteInnerNavigationState(stackSize = initialRoute.stepNum()))
|
||||
|
||||
val currentScanResponse = _currentScanResponse.asStateFlow()
|
||||
|
||||
val innerNavigationState = _innerNavigationState.asStateFlow()
|
||||
|
||||
val onDone = MutableSharedFlow<Unit>()
|
||||
|
||||
val stackNavigation = StackNavigation<OnboardingNoteRoute>()
|
||||
|
||||
private val _uiState = MutableStateFlow(
|
||||
OnboardingNoteState(),
|
||||
)
|
||||
|
||||
val uiState: StateFlow<OnboardingNoteState> = _uiState
|
||||
|
||||
fun updateStepForNewRoute(route: OnboardingNoteRoute) {
|
||||
_innerNavigationState.value = innerNavigationState.value.copy(
|
||||
stackSize = route.stepNum(),
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
fun onChildBack(currentRoute: OnboardingNoteRoute) {
|
||||
// TODO [REDACTED_TASK_KEY]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.model
|
||||
|
||||
data class OnboardingNoteState(
|
||||
val cardArtworkUrl: String? = null,
|
||||
)
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.route
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
internal sealed class OnboardingNoteRoute {
|
||||
|
||||
@Serializable
|
||||
data object CreateWallet : OnboardingNoteRoute()
|
||||
|
||||
@Serializable
|
||||
data object TopUp : OnboardingNoteRoute()
|
||||
}
|
||||
|
||||
internal const val ONBOARDING_NOTE_STEPS_COUNT = 3
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue