Updated on 2026-08-14
This commit is contained in:
parent
de3bdb3cb0
commit
56164d0b8b
36 changed files with 255 additions and 124 deletions
|
|
@ -188,8 +188,8 @@ dependencies {
|
|||
implementation(projects.features.stories.impl)
|
||||
implementation(projects.features.txhistory.api)
|
||||
implementation(projects.features.txhistory.impl)
|
||||
implementation(projects.features.askBiometry.api)
|
||||
implementation(projects.features.askBiometry.impl)
|
||||
implementation(projects.features.biometry.api)
|
||||
implementation(projects.features.biometry.impl)
|
||||
implementation(projects.features.nft.api)
|
||||
implementation(projects.features.nft.impl)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
package com.tangem.features.askbiometry
|
||||
|
||||
interface AskBiometryFeatureToggles {
|
||||
val isEnabled: Boolean
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ plugins {
|
|||
}
|
||||
|
||||
android {
|
||||
namespace = "com.tangem.features.askbiometry"
|
||||
namespace = "com.tangem.features.biometry"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.features.askbiometry
|
||||
package com.tangem.features.biometry
|
||||
|
||||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.tangem.features.biometry
|
||||
|
||||
interface BiometryFeatureToggles {
|
||||
val isAskForBiometryEnabled: Boolean
|
||||
}
|
||||
|
|
@ -8,11 +8,11 @@ plugins {
|
|||
}
|
||||
|
||||
android {
|
||||
namespace = "com.tangem.features.askbiometry.impl"
|
||||
namespace = "com.tangem.features.biometry.impl"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.features.askBiometry.api)
|
||||
api(projects.features.biometry.api)
|
||||
|
||||
/** Core modules */
|
||||
implementation(projects.core.ui)
|
||||
|
|
@ -1,21 +1,26 @@
|
|||
package com.tangem.features.askbiometry.impl
|
||||
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.askbiometry.AskBiometryComponent
|
||||
import com.tangem.features.askbiometry.impl.model.AskBiometryModel
|
||||
import com.tangem.features.askbiometry.impl.ui.AskBiometry
|
||||
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,
|
||||
|
|
@ -23,15 +28,23 @@ internal class DefaultAskBiometryComponent @AssistedInject constructor(
|
|||
) : 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 bsConfig = remember(this) {
|
||||
val bsShown by bsShown.collectAsStateWithLifecycle()
|
||||
val bsConfig = remember(this, bsShown) {
|
||||
TangemBottomSheetConfig(
|
||||
isShown = true,
|
||||
isShown = bsShown,
|
||||
onDismissRequest = ::dismiss,
|
||||
content = TangemBottomSheetConfigContent.Empty,
|
||||
)
|
||||
|
|
@ -48,7 +61,7 @@ internal class DefaultAskBiometryComponent @AssistedInject constructor(
|
|||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
AskBiometry(
|
||||
modifier = modifier,
|
||||
modifier = modifier.navigationBarsPadding(),
|
||||
state = state,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
package com.tangem.features.askbiometry.impl
|
||||
package com.tangem.features.biometry.impl
|
||||
|
||||
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
|
||||
import com.tangem.features.askbiometry.AskBiometryFeatureToggles
|
||||
import com.tangem.features.biometry.BiometryFeatureToggles
|
||||
import javax.inject.Inject
|
||||
|
||||
class DefaultAskBiometryFeatureToggles @Inject constructor(
|
||||
internal class DefaultBiometryFeatureToggles @Inject constructor(
|
||||
private val featureTogglesManager: FeatureTogglesManager,
|
||||
) : AskBiometryFeatureToggles {
|
||||
override val isEnabled: Boolean
|
||||
) : BiometryFeatureToggles {
|
||||
override val isAskForBiometryEnabled: Boolean
|
||||
get() = featureTogglesManager.isFeatureEnabled("ASK_BIOMETRY_REFACTORING_ENABLED")
|
||||
}
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
package com.tangem.features.askbiometry.impl.di
|
||||
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.askbiometry.AskBiometryComponent
|
||||
import com.tangem.features.askbiometry.AskBiometryFeatureToggles
|
||||
import com.tangem.features.askbiometry.impl.DefaultAskBiometryComponent
|
||||
import com.tangem.features.askbiometry.impl.DefaultAskBiometryFeatureToggles
|
||||
import com.tangem.features.askbiometry.impl.model.AskBiometryModel
|
||||
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
|
||||
|
|
@ -19,7 +19,7 @@ import dagger.multibindings.IntoMap
|
|||
internal interface FeatureModule {
|
||||
|
||||
@Binds
|
||||
fun bindFeatureToggles(impl: DefaultAskBiometryFeatureToggles): AskBiometryFeatureToggles
|
||||
fun bindFeatureToggles(impl: DefaultBiometryFeatureToggles): BiometryFeatureToggles
|
||||
|
||||
@Binds
|
||||
fun bindComponentFactory(impl: DefaultAskBiometryComponent.Factory): AskBiometryComponent.Factory
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.features.askbiometry.impl.model
|
||||
package com.tangem.features.biometry.impl.model
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
|
|
@ -18,10 +18,12 @@ 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.askbiometry.AskBiometryComponent
|
||||
import com.tangem.features.askbiometry.impl.ui.state.AskBiometryUM
|
||||
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
|
||||
|
|
@ -52,9 +54,11 @@ internal class AskBiometryModel @Inject constructor(
|
|||
bottomSheetVariant = params.bottomSheetVariant,
|
||||
onAllowClick = ::onAllowClick,
|
||||
onDontAllowClick = ::dontAllow,
|
||||
onDismiss = ::dismiss,
|
||||
),
|
||||
)
|
||||
val uiState = _uiState.asStateFlow()
|
||||
val dismissBSFlow = MutableSharedFlow<Unit>()
|
||||
|
||||
init {
|
||||
modelScope.launch {
|
||||
|
|
@ -72,7 +76,11 @@ internal class AskBiometryModel @Inject constructor(
|
|||
}
|
||||
|
||||
fun dismiss() {
|
||||
dontAllow()
|
||||
modelScope.launch {
|
||||
dismissBSFlow.emit(Unit)
|
||||
delay(timeMillis = 500)
|
||||
dontAllow()
|
||||
}
|
||||
}
|
||||
|
||||
private fun allowToUseBiometrics() {
|
||||
|
|
@ -107,6 +115,11 @@ internal class AskBiometryModel @Inject constructor(
|
|||
isBiometricsRequestPolicy = userWallet.hasAccessCode,
|
||||
)
|
||||
|
||||
if (_uiState.value.bottomSheetVariant) {
|
||||
dismissBSFlow.emit(Unit)
|
||||
delay(timeMillis = 500)
|
||||
}
|
||||
|
||||
params.modelCallbacks.onAllowed()
|
||||
}
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.features.askbiometry.impl.ui
|
||||
package com.tangem.features.biometry.impl.ui
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
|
|
@ -14,12 +14,11 @@ 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.components.atoms.Hand
|
||||
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.askbiometry.impl.ui.state.AskBiometryUM
|
||||
import com.tangem.features.askbiometry.impl.R
|
||||
import com.tangem.features.biometry.impl.ui.state.AskBiometryUM
|
||||
import com.tangem.features.biometry.R
|
||||
|
||||
@Composable
|
||||
internal fun AskBiometry(state: AskBiometryUM, modifier: Modifier = Modifier) {
|
||||
|
|
@ -31,15 +30,16 @@ internal fun AskBiometry(state: AskBiometryUM, modifier: Modifier = Modifier) {
|
|||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
if (state.bottomSheetVariant) {
|
||||
Header(onCloseClick = state.onDontAllowClick)
|
||||
Header(onCloseClick = state.onDismiss)
|
||||
}
|
||||
|
||||
SpacerH32()
|
||||
SpacerHHalf()
|
||||
|
||||
Column(
|
||||
modifier = Modifier.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Title(modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing22))
|
||||
Title(modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing56))
|
||||
SpacerH32()
|
||||
Description(
|
||||
modifier = Modifier
|
||||
|
|
@ -47,6 +47,8 @@ internal fun AskBiometry(state: AskBiometryUM, modifier: Modifier = Modifier) {
|
|||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
|
||||
SpacerHHalf()
|
||||
}
|
||||
Footer(state = state)
|
||||
SpacerH16()
|
||||
|
|
@ -54,29 +56,23 @@ internal fun AskBiometry(state: AskBiometryUM, modifier: Modifier = Modifier) {
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun Header(onCloseClick: () -> Unit) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
|
||||
private fun Header(onCloseClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End,
|
||||
) {
|
||||
Hand()
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End,
|
||||
IconButton(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size32),
|
||||
onClick = onCloseClick,
|
||||
) {
|
||||
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()
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.features.askbiometry.impl.ui.state
|
||||
package com.tangem.features.biometry.impl.ui.state
|
||||
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
||||
|
|
@ -8,4 +8,5 @@ internal data class AskBiometryUM(
|
|||
val error: TextReference? = null,
|
||||
val onAllowClick: () -> Unit = {},
|
||||
val onDontAllowClick: () -> Unit = {},
|
||||
val onDismiss: () -> Unit = {},
|
||||
)
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -14,11 +14,13 @@ 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
|
||||
|
|
@ -110,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,8 +1,9 @@
|
|||
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.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
|
|
@ -14,8 +15,11 @@ 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
|
||||
|
|
@ -26,6 +30,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@ModelScoped
|
||||
internal class OnboardingEntryModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
|
|
@ -33,6 +38,8 @@ internal class OnboardingEntryModel @Inject constructor(
|
|||
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>()
|
||||
|
|
@ -78,7 +85,7 @@ internal class OnboardingEntryModel @Inject constructor(
|
|||
ProductType.Note -> OnboardingRoute.Note(
|
||||
scanResponse = scanResponse,
|
||||
titleProvider = titleProvider,
|
||||
onDone = ::navigateToWalletScreen,
|
||||
onDone = ::navigateToFinalScreenFlow,
|
||||
)
|
||||
else -> error("Unsupported")
|
||||
}
|
||||
|
|
@ -86,34 +93,58 @@ internal class OnboardingEntryModel @Inject constructor(
|
|||
|
||||
private fun onMultiWalletOnboardingDone(userWallet: UserWallet) {
|
||||
if (userWallet.scanResponse.cardTypesResolver.isMultiwalletAllowed()) {
|
||||
stackNavigation.navigate {
|
||||
listOf(OnboardingRoute.ManageTokens(userWallet))
|
||||
}
|
||||
stackNavigation.replaceAll(OnboardingRoute.ManageTokens(userWallet))
|
||||
} else {
|
||||
stackNavigation.navigate {
|
||||
listOf(OnboardingRoute.Done(onDone = ::navigateToWalletScreen))
|
||||
}
|
||||
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,6 +1,7 @@
|
|||
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
|
||||
|
|
@ -13,6 +14,7 @@ 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,
|
||||
) {
|
||||
|
||||
|
|
@ -57,7 +59,14 @@ internal class OnboardingChildFactory @Inject constructor(
|
|||
onDone = route.onDone,
|
||||
),
|
||||
)
|
||||
else -> Unit
|
||||
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
|
||||
|
||||
|
|
@ -34,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()
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ 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,34 +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,
|
||||
)
|
||||
}
|
||||
is OnboardingRoute.Note -> {
|
||||
(it.instance as ComposableContentComponent).Content(
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
is OnboardingRoute.None -> {}
|
||||
}
|
||||
(it.instance as? ComposableContentComponent)?.Content(modifier = modifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -70,7 +70,7 @@ internal class DefaultOnboardingMultiWalletComponent @AssistedInject constructor
|
|||
|
||||
private val innerNavigationStateFlow = instanceKeeper.getOrCreateSimple(key = "innerNavigationState") {
|
||||
MutableStateFlow(
|
||||
MultiWalletInnerNavigationState(1, 5),
|
||||
MultiWalletInnerNavigationState(1, 6),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class MultiWalletBackupComponent(
|
|||
params.innerNavigation.update {
|
||||
it.copy(
|
||||
stackSize = 5,
|
||||
stackMaxSize = 8,
|
||||
stackMaxSize = 9,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class Wallet1ChooseOptionComponent(
|
|||
params.innerNavigation.update {
|
||||
it.copy(
|
||||
stackSize = 3,
|
||||
stackMaxSize = 8,
|
||||
stackMaxSize = 9,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ internal class MultiWalletCreateWalletComponent(
|
|||
params.innerNavigation.update {
|
||||
it.copy(
|
||||
stackSize = 2,
|
||||
stackMaxSize = 8,
|
||||
stackMaxSize = 9,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ internal class MultiWalletFinalizeComponent(
|
|||
params.innerNavigation.update {
|
||||
it.copy(
|
||||
stackSize = 7,
|
||||
stackMaxSize = 8,
|
||||
stackMaxSize = 9,
|
||||
)
|
||||
}
|
||||
params.parentParams.titleProvider.changeTitle(
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ internal class Wallet1ScanPrimaryComponent(
|
|||
params.innerNavigation.update {
|
||||
it.copy(
|
||||
stackSize = 4,
|
||||
stackMaxSize = 8,
|
||||
stackMaxSize = 9,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ internal class MultiWalletSeedPhraseComponent(
|
|||
params.innerNavigation.update { st ->
|
||||
st.copy(
|
||||
stackSize = 3 + it.order,
|
||||
stackMaxSize = 10,
|
||||
stackMaxSize = 11,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ internal class SetConfirmationStateInitTransformer(
|
|||
private val isImplicitExit: Boolean
|
||||
get() = pendingAction == null && pendingActions?.isEmpty() == true || isTronStakedBalance
|
||||
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
val actionType = when {
|
||||
isEnter -> StakingActionCommonType.Enter(
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ internal class AddStakingNotificationsTransformer(
|
|||
isSubtractAvailable = isSubtractAvailable,
|
||||
)
|
||||
|
||||
@Suppress("LongMethod")
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||
val balance = cryptoCurrencyStatus.value.amount.orZero()
|
||||
|
|
|
|||
|
|
@ -107,6 +107,7 @@ dependencies {
|
|||
implementation(projects.features.tokendetails.api)
|
||||
implementation(projects.features.wallet.api)
|
||||
implementation(projects.features.walletSettings.api)
|
||||
implementation(projects.features.biometry.api)
|
||||
|
||||
/** Common modules */
|
||||
implementation(projects.common)
|
||||
|
|
|
|||
|
|
@ -13,13 +13,16 @@ 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.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.core.ui.decompose.ComposableDialogComponent
|
||||
import com.tangem.core.ui.utils.findActivity
|
||||
import com.tangem.feature.wallet.child.wallet.model.WalletModel
|
||||
import com.tangem.feature.wallet.navigation.WalletRoute
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.WalletScreen
|
||||
import com.tangem.feature.walletsettings.component.RenameWalletComponent
|
||||
import com.tangem.features.biometry.AskBiometryComponent
|
||||
import com.tangem.features.markets.entry.MarketsEntryComponent
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
|
|
@ -31,6 +34,7 @@ internal class WalletComponent @AssistedInject constructor(
|
|||
@Assisted navigate: (WalletRoute) -> Unit,
|
||||
private val renameWalletComponentFactory: RenameWalletComponent.Factory,
|
||||
private val marketsEntryComponentFactory: MarketsEntryComponent.Factory,
|
||||
private val askBiometryComponentFactory: AskBiometryComponent.Factory,
|
||||
) : ComposableContentComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: WalletModel = getOrCreateModel()
|
||||
|
|
@ -56,6 +60,15 @@ internal class WalletComponent @AssistedInject constructor(
|
|||
),
|
||||
)
|
||||
}
|
||||
is WalletDialogConfig.AskForBiometry -> {
|
||||
askBiometryComponentFactory.create(
|
||||
context = childByContext(componentContext),
|
||||
params = AskBiometryComponent.Params(
|
||||
bottomSheetVariant = true,
|
||||
modelCallbacks = model.askBiometryModelCallbacks,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
@ -74,7 +87,11 @@ internal class WalletComponent @AssistedInject constructor(
|
|||
marketsEntryComponent = marketsEntryComponent,
|
||||
)
|
||||
|
||||
dialog.child?.instance?.Dialog()
|
||||
when (val dialog = dialog.child?.instance) {
|
||||
is ComposableDialogComponent -> dialog.Dialog()
|
||||
is ComposableBottomSheetComponent -> dialog.BottomSheet()
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
|
|
|
|||
|
|
@ -2,7 +2,11 @@ package com.tangem.feature.wallet.child.wallet.model
|
|||
|
||||
import androidx.compose.runtime.Stable
|
||||
import arrow.core.getOrElse
|
||||
import com.arkivanov.decompose.router.slot.activate
|
||||
import com.arkivanov.decompose.router.slot.dismiss
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
|
|
@ -23,12 +27,15 @@ import com.tangem.feature.wallet.presentation.wallet.domain.WalletNameMigrationU
|
|||
import com.tangem.feature.wallet.presentation.wallet.loaders.WalletScreenContentLoader
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.PushNotificationsBottomSheetConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent.DemonstrateWalletsScrollPreview.Direction
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.*
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider
|
||||
import com.tangem.features.biometry.AskBiometryComponent
|
||||
import com.tangem.features.biometry.BiometryFeatureToggles
|
||||
import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION
|
||||
import com.tangem.features.pushnotifications.api.utils.getPushPermissionOrNull
|
||||
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
|
||||
|
|
@ -67,11 +74,13 @@ internal class WalletModel @Inject constructor(
|
|||
private val tokenListStore: MultiWalletTokenListStore,
|
||||
private val onrampStatusFactory: OnrampStatusFactory,
|
||||
private val walletFeatureToggles: WalletFeatureToggles,
|
||||
private val biometryFeatureToggles: BiometryFeatureToggles,
|
||||
private val analyticsEventsHandler: AnalyticsEventHandler,
|
||||
val screenLifecycleProvider: ScreenLifecycleProvider,
|
||||
val innerWalletRouter: InnerWalletRouter,
|
||||
analyticsEventsHandler: AnalyticsEventHandler,
|
||||
) : Model() {
|
||||
|
||||
val askBiometryModelCallbacks = AskBiometryModelCallbacks()
|
||||
val uiState: StateFlow<WalletScreenState> = stateHolder.uiState
|
||||
|
||||
private val walletsUpdateJobHolder = JobHolder()
|
||||
|
|
@ -116,7 +125,15 @@ internal class WalletModel @Inject constructor(
|
|||
modelScope.launch(dispatchers.main) {
|
||||
withContext(dispatchers.io) { delay(timeMillis = 1_800) }
|
||||
|
||||
if (isShowSaveWalletScreenEnabled()) innerWalletRouter.openSaveUserWalletScreen()
|
||||
if (isShowSaveWalletScreenEnabled()) {
|
||||
if (biometryFeatureToggles.isAskForBiometryEnabled) {
|
||||
innerWalletRouter.dialogNavigation.activate(
|
||||
configuration = WalletDialogConfig.AskForBiometry,
|
||||
)
|
||||
} else {
|
||||
innerWalletRouter.openSaveUserWalletScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -446,6 +463,18 @@ internal class WalletModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
inner class AskBiometryModelCallbacks : AskBiometryComponent.ModelCallbacks {
|
||||
override fun onAllowed() {
|
||||
analyticsEventsHandler.send(MainScreenAnalyticsEvent.EnableBiometrics(AnalyticsParam.OnOffState.On))
|
||||
innerWalletRouter.dialogNavigation.dismiss()
|
||||
}
|
||||
|
||||
override fun onDenied() {
|
||||
analyticsEventsHandler.send(MainScreenAnalyticsEvent.EnableBiometrics(AnalyticsParam.OnOffState.Off))
|
||||
innerWalletRouter.dialogNavigation.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val REFRESH_WALLET_BACKGROUND_TIMER_MILLIS = 10000L
|
||||
const val EXPRESS_STATUS_UPDATE_DELAY = 10000L
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
|||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.feature.wallet.navigation.WalletRoute
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogConfig
|
||||
import com.tangem.features.biometry.BiometryFeatureToggles
|
||||
import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
|
|
@ -25,6 +26,7 @@ internal class DefaultWalletRouter @Inject constructor(
|
|||
private val urlOpener: UrlOpener,
|
||||
private val reduxStateHolder: ReduxStateHolder,
|
||||
private val onboardingV2FeatureToggles: OnboardingV2FeatureToggles,
|
||||
private val biometryFeatureToggles: BiometryFeatureToggles,
|
||||
) : InnerWalletRouter {
|
||||
|
||||
override val dialogNavigation: SlotNavigation<WalletDialogConfig> = SlotNavigation()
|
||||
|
|
@ -98,7 +100,9 @@ internal class DefaultWalletRouter @Inject constructor(
|
|||
}
|
||||
|
||||
override fun openSaveUserWalletScreen() {
|
||||
router.push(AppRoute.SaveWallet)
|
||||
if (biometryFeatureToggles.isAskForBiometryEnabled.not()) {
|
||||
router.push(AppRoute.SaveWallet)
|
||||
}
|
||||
}
|
||||
|
||||
override fun isWalletLastScreen(): Boolean {
|
||||
|
|
|
|||
|
|
@ -13,4 +13,7 @@ internal sealed interface WalletDialogConfig {
|
|||
|
||||
@Serializable
|
||||
data class RenameWallet(val userWalletId: UserWalletId, val currentName: String) : WalletDialogConfig
|
||||
|
||||
@Serializable
|
||||
data object AskForBiometry : WalletDialogConfig
|
||||
}
|
||||
|
|
@ -228,8 +228,8 @@ include(":features:stories:impl")
|
|||
include(":features:txhistory:api")
|
||||
include(":features:txhistory:impl")
|
||||
|
||||
include(":features:ask-biometry:api")
|
||||
include(":features:ask-biometry:impl")
|
||||
include(":features:biometry:api")
|
||||
include(":features:biometry:impl")
|
||||
|
||||
include(":features:nft:api")
|
||||
include(":features:nft:impl")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue