Updated on 2026-08-14
This commit is contained in:
parent
e1c819d421
commit
0e169a5418
19 changed files with 292 additions and 37 deletions
|
|
@ -261,7 +261,7 @@ internal class ChildFactory @Inject constructor(
|
|||
is AppRoute.Onboarding.Mode.UpgradeHotWallet ->
|
||||
OnboardingEntryComponent.Mode.UpgradeHotWallet(mode.userWalletId)
|
||||
is AppRoute.Onboarding.Mode.AddressSync ->
|
||||
OnboardingEntryComponent.Mode.AddressSync
|
||||
OnboardingEntryComponent.Mode.AddressSync(mode.userWalletId)
|
||||
},
|
||||
),
|
||||
componentFactory = onboardingEntryComponentFactory,
|
||||
|
|
|
|||
|
|
@ -330,7 +330,7 @@ sealed class AppRoute(val path: String) : Route {
|
|||
data object RecreateWalletTwin : Mode() // reset twins
|
||||
data object ContinueFinalize : Mode() // continue finalize process (unfinished backup dialog)
|
||||
data class UpgradeHotWallet(val userWalletId: UserWalletId) : Mode() // upgrade hot wallet
|
||||
data object AddressSync : Mode()
|
||||
data class AddressSync(val userWalletId: UserWalletId) : Mode()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
21
core/ui/src/main/res/drawable/ic_sync_56.xml
Normal file
21
core/ui/src/main/res/drawable/ic_sync_56.xml
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="56dp"
|
||||
android:height="56dp"
|
||||
android:viewportWidth="56"
|
||||
android:viewportHeight="56">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M28,0L28,0A28,28 0,0 1,56 28L56,28A28,28 0,0 1,28 56L28,56A28,28 0,0 1,0 28L0,28A28,28 0,0 1,28 0z"/>
|
||||
<path
|
||||
android:pathData="M0,0h56v56h-56z"
|
||||
android:strokeAlpha="0.1"
|
||||
android:fillColor="#0099FF"
|
||||
android:fillAlpha="0.1"/>
|
||||
<path
|
||||
android:pathData="M15.29,30.658C15.583,30.465 15.946,30.411 16.284,30.508L22.394,32.28C23.03,32.465 23.397,33.13 23.212,33.766C23.028,34.403 22.362,34.769 21.726,34.585L18.727,33.715C19.703,35.3 21.072,36.613 22.708,37.522C24.591,38.569 26.743,39.033 28.89,38.857C31.037,38.681 33.084,37.873 34.772,36.534C36.459,35.195 37.712,33.384 38.371,31.333C38.574,30.702 39.25,30.355 39.881,30.558C40.512,30.761 40.859,31.437 40.656,32.068C39.852,34.571 38.324,36.779 36.264,38.414C34.205,40.048 31.706,41.035 29.086,41.25C26.466,41.464 23.84,40.897 21.542,39.62C19.575,38.526 17.924,36.953 16.735,35.056L16.352,38.122C16.27,38.78 15.671,39.246 15.013,39.164C14.356,39.082 13.889,38.483 13.971,37.825L14.759,31.512C14.802,31.164 14.997,30.851 15.29,30.658Z"
|
||||
android:fillColor="#0099FF"/>
|
||||
<path
|
||||
android:pathData="M26.914,14.751C29.534,14.536 32.16,15.104 34.458,16.381C36.425,17.474 38.076,19.048 39.264,20.945L39.648,17.878C39.73,17.221 40.329,16.754 40.987,16.836C41.644,16.919 42.111,17.518 42.029,18.175L41.241,24.489C41.198,24.837 41.004,25.149 40.711,25.342C40.418,25.535 40.054,25.59 39.716,25.493L33.606,23.721C32.97,23.536 32.603,22.871 32.787,22.235C32.972,21.598 33.638,21.232 34.274,21.416L37.273,22.285C36.297,20.7 34.928,19.388 33.292,18.479C31.409,17.432 29.257,16.967 27.11,17.143C24.963,17.319 22.916,18.128 21.228,19.467C19.541,20.806 18.288,22.617 17.629,24.668C17.426,25.298 16.75,25.645 16.119,25.443C15.488,25.24 15.141,24.564 15.344,23.933C16.148,21.43 17.677,19.222 19.736,17.587C21.795,15.953 24.294,14.966 26.914,14.751Z"
|
||||
android:fillColor="#0099FF"/>
|
||||
</group>
|
||||
</vector>
|
||||
|
|
@ -126,6 +126,16 @@ private fun Footer(state: AskBiometryUM, modifier: Modifier = Modifier) {
|
|||
.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
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,
|
||||
)
|
||||
|
||||
SpacerH16()
|
||||
|
||||
PrimaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
showProgress = state.shouldShowProgress,
|
||||
|
|
@ -142,16 +152,6 @@ private fun Footer(state: AskBiometryUM, modifier: Modifier = Modifier) {
|
|||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -239,7 +239,7 @@ internal class CreateWalletStartModel @Inject constructor(
|
|||
val route = if (onboardingV2FeatureToggles.isAddressSyncEnabled) {
|
||||
AppRoute.Onboarding(
|
||||
scanResponse = scanResponse,
|
||||
mode = AppRoute.Onboarding.Mode.AddressSync,
|
||||
mode = AppRoute.Onboarding.Mode.AddressSync(userWalletId = userWallet.walletId),
|
||||
)
|
||||
} else {
|
||||
AppRoute.Wallet
|
||||
|
|
|
|||
|
|
@ -536,7 +536,9 @@ internal class CreateWalletStartModelTest {
|
|||
routes = arrayOf(
|
||||
AppRoute.Onboarding(
|
||||
scanResponse = testScanResponse,
|
||||
mode = AppRoute.Onboarding.Mode.AddressSync,
|
||||
mode = AppRoute.Onboarding.Mode.AddressSync(
|
||||
userWalletId = testUserWalletId,
|
||||
),
|
||||
)
|
||||
),
|
||||
onComplete = any()
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ interface OnboardingEntryComponent : ComposableContentComponent {
|
|||
data object RecreateWalletTwin : Mode()
|
||||
data object ContinueFinalize : Mode()
|
||||
data class UpgradeHotWallet(val userWalletId: UserWalletId) : Mode()
|
||||
data object AddressSync : Mode()
|
||||
data class AddressSync(val userWalletId: UserWalletId) : Mode()
|
||||
}
|
||||
|
||||
interface Factory : ComponentFactory<Params, OnboardingEntryComponent>
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ dependencies {
|
|||
implementation(projects.common)
|
||||
|
||||
/** Domain */
|
||||
implementation(projects.domain.account)
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.feedback)
|
||||
implementation(projects.domain.feedback.models)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
package com.tangem.features.onboarding.v2.addresssync
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.arkivanov.decompose.DelicateDecomposeApi
|
||||
import com.arkivanov.decompose.extensions.compose.stack.Children
|
||||
import com.arkivanov.decompose.router.stack.ChildStack
|
||||
|
|
@ -15,7 +18,9 @@ import com.tangem.core.ui.decompose.ComposableContentComponent
|
|||
import com.tangem.features.biometry.AskBiometryComponent
|
||||
import com.tangem.features.onboarding.v2.addresssync.model.AddressSyncIntent
|
||||
import com.tangem.features.onboarding.v2.addresssync.model.AddressSyncModel
|
||||
import com.tangem.features.onboarding.v2.addresssync.model.AddressSyncState
|
||||
import com.tangem.features.onboarding.v2.addresssync.navigation.AddressSyncStep
|
||||
import com.tangem.features.onboarding.v2.addresssync.ui.AddressSyncButtonScreen
|
||||
import com.tangem.features.onboarding.v2.addresssync.ui.AddressSyncContent
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.MultiWalletChildParams
|
||||
import com.tangem.features.pushnotifications.api.PushNotificationsComponent
|
||||
|
|
@ -65,7 +70,20 @@ internal class DefaultAddressSyncComponent(
|
|||
return when (step) {
|
||||
AddressSyncStep.ASK_BIOMETRY -> createAskBiometryComponent(childContext)
|
||||
AddressSyncStep.ASK_NOTIFICATIONS -> createPushNotificationComponent(childContext)
|
||||
AddressSyncStep.ADDRESS_SYNC -> TODO("Will be implemented during [REDACTED_TASK_KEY]")
|
||||
AddressSyncStep.ADDRESS_SYNC -> ComposableContentComponent {
|
||||
val state by model.state.collectAsStateWithLifecycle()
|
||||
when (state) {
|
||||
AddressSyncState.Loading -> Unit // todo shimmers will be implemented during [REDACTED_TASK_KEY]
|
||||
is AddressSyncState.Success -> AddressSyncButtonScreen(
|
||||
state = state as AddressSyncState.Success,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
onSyncClick = {
|
||||
model.onIntent(AddressSyncIntent.Sync)
|
||||
},
|
||||
)
|
||||
AddressSyncState.NoTokens -> router.replaceAll(AppRoute.Wallet)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
package com.tangem.features.onboarding.v2.addresssync.model
|
||||
|
||||
import com.tangem.features.onboarding.v2.addresssync.navigation.AddressSyncStep
|
||||
|
||||
internal sealed interface AddressSyncIntent {
|
||||
data class Next(val step: AddressSyncStep) : AddressSyncIntent
|
||||
data object Sync : AddressSyncIntent
|
||||
}
|
||||
|
||||
internal sealed class AddressSyncState {
|
||||
data object Loading : AddressSyncState()
|
||||
data class Success(val currenciesCount: Int) : AddressSyncState()
|
||||
data object NoTokens : AddressSyncState()
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
package com.tangem.features.onboarding.v2.addresssync.model
|
||||
|
||||
import com.tangem.features.onboarding.v2.addresssync.navigation.AddressSyncStep
|
||||
|
||||
sealed interface AddressSyncIntent {
|
||||
data class Next(val step: AddressSyncStep) : AddressSyncIntent
|
||||
data object Back : AddressSyncIntent
|
||||
}
|
||||
|
|
@ -2,35 +2,45 @@ package com.tangem.features.onboarding.v2.addresssync.model
|
|||
|
||||
import com.arkivanov.decompose.DelicateDecomposeApi
|
||||
import com.arkivanov.decompose.router.stack.StackNavigation
|
||||
import com.arkivanov.decompose.router.stack.pop
|
||||
import com.arkivanov.decompose.router.stack.replaceCurrent
|
||||
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
|
||||
import com.tangem.domain.account.supplier.MultiAccountListSupplier
|
||||
import com.tangem.domain.settings.CanUseBiometryUseCase
|
||||
import com.tangem.domain.settings.ShouldAskPermissionUseCase
|
||||
import com.tangem.domain.settings.ShouldShowAskBiometryUseCase
|
||||
import com.tangem.domain.tokens.MultiWalletAccountListFetcher
|
||||
import com.tangem.features.onboarding.v2.addresssync.navigation.AddressSyncStep
|
||||
import com.tangem.features.onboarding.v2.multiwallet.api.OnboardingMultiWalletComponent
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.MultiWalletChildParams
|
||||
import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@OptIn(DelicateDecomposeApi::class)
|
||||
@Suppress("LongParameterList")
|
||||
@ModelScoped
|
||||
internal class AddressSyncModel @Inject constructor(
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val shouldShowAskBiometryUseCase: ShouldShowAskBiometryUseCase,
|
||||
private val canUseBiometryUseCase: CanUseBiometryUseCase,
|
||||
private val shouldAskPermissionUseCase: ShouldAskPermissionUseCase,
|
||||
private val multiWalletAccountListFetcher: MultiWalletAccountListFetcher,
|
||||
private val multiAccountListSupplier: MultiAccountListSupplier,
|
||||
paramsContainer: ParamsContainer,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<MultiWalletChildParams>()
|
||||
private val walletId = (params.parentParams.mode as OnboardingMultiWalletComponent.Mode.AddressSync).userWalletId
|
||||
val stackNavigation = StackNavigation<AddressSyncStep>()
|
||||
val state: StateFlow<AddressSyncState>
|
||||
field = MutableStateFlow<AddressSyncState>(
|
||||
value = AddressSyncState.Loading,
|
||||
)
|
||||
|
||||
init {
|
||||
params.innerNavigation.update { innerNavigationState ->
|
||||
|
|
@ -42,12 +52,13 @@ internal class AddressSyncModel @Inject constructor(
|
|||
modelScope.launch {
|
||||
trySkippingScreen(AddressSyncStep.ASK_BIOMETRY)
|
||||
}
|
||||
fetchWalletCrypto()
|
||||
}
|
||||
|
||||
fun onIntent(intent: AddressSyncIntent) {
|
||||
when (intent) {
|
||||
is AddressSyncIntent.Next -> nextScreen(intent)
|
||||
AddressSyncIntent.Back -> goBack()
|
||||
AddressSyncIntent.Sync -> startSyncing()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -76,10 +87,6 @@ internal class AddressSyncModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun goBack() {
|
||||
stackNavigation.pop()
|
||||
}
|
||||
|
||||
private fun updateStepperPage(next: AddressSyncIntent.Next) {
|
||||
params.innerNavigation.update { innerNavigationState ->
|
||||
innerNavigationState.copy(
|
||||
|
|
@ -94,6 +101,37 @@ internal class AddressSyncModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun fetchWalletCrypto() {
|
||||
modelScope.launch {
|
||||
multiWalletAccountListFetcher.invoke(
|
||||
params = MultiWalletAccountListFetcher.Params(userWalletId = walletId),
|
||||
)
|
||||
handleAddressSyncStep()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun handleAddressSyncStep() {
|
||||
multiAccountListSupplier()
|
||||
.map { accountLists ->
|
||||
accountLists
|
||||
.first { it.userWalletId == walletId }
|
||||
.flattenCurrencies()
|
||||
}
|
||||
.onEach { currencies ->
|
||||
val updatedState = if (currencies.isEmpty()) {
|
||||
AddressSyncState.NoTokens
|
||||
} else {
|
||||
AddressSyncState.Success(currenciesCount = currencies.size)
|
||||
}
|
||||
state.value = updatedState
|
||||
}
|
||||
.collect()
|
||||
}
|
||||
|
||||
private fun startSyncing() {
|
||||
TODO("Will be implemented during [REDACTED_TASK_KEY]")
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val ADDRESS_SYNC_MAX_STEPS = 3
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.features.onboarding.v2.addresssync.navigation
|
|||
import androidx.annotation.StringRes
|
||||
import com.tangem.features.onboarding.v2.impl.R
|
||||
|
||||
enum class AddressSyncStep(val pageNumber: Int, @StringRes val stringId: Int) {
|
||||
internal enum class AddressSyncStep(val pageNumber: Int, @StringRes val stringId: Int) {
|
||||
ASK_BIOMETRY(pageNumber = 1, stringId = R.string.onboarding_navbar_title_biometrics),
|
||||
ASK_NOTIFICATIONS(pageNumber = 2, stringId = R.string.onboarding_title_notifications),
|
||||
ADDRESS_SYNC(pageNumber = 3, stringId = R.string.onboarding_navbar_title_last_step),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,105 @@
|
|||
package com.tangem.features.onboarding.v2.addresssync.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Icon
|
||||
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.painterResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.components.PrimaryButtonIconEnd
|
||||
import com.tangem.core.ui.components.SpacerHHalf
|
||||
import com.tangem.core.ui.components.SpacerHMax
|
||||
import com.tangem.core.ui.extensions.pluralStringResourceSafe
|
||||
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.addresssync.model.AddressSyncState
|
||||
import com.tangem.features.onboarding.v2.impl.R
|
||||
|
||||
@Composable
|
||||
internal fun AddressSyncButtonScreen(
|
||||
state: AddressSyncState.Success,
|
||||
onSyncClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.padding(top = TangemTheme.dimens.spacing56),
|
||||
) {
|
||||
SpacerHHalf()
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_sync_56),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.accent,
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterHorizontally)
|
||||
.size(TangemTheme.dimens.size56),
|
||||
)
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.onboarding_address_sync_title),
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterHorizontally)
|
||||
.padding(
|
||||
start = TangemTheme.dimens.spacing34,
|
||||
end = TangemTheme.dimens.spacing34,
|
||||
top = TangemTheme.dimens.spacing28,
|
||||
),
|
||||
)
|
||||
AddressSyncDescription(state.currenciesCount)
|
||||
SpacerHMax()
|
||||
PrimaryButtonIconEnd(
|
||||
text = stringResourceSafe(R.string.common_generate_addresses),
|
||||
onClick = onSyncClick,
|
||||
iconResId = R.drawable.ic_tangem_24,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
top = TangemTheme.dimens.spacing154,
|
||||
bottom = TangemTheme.dimens.spacing16,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.AddressSyncDescription(currenciesCount: Int) {
|
||||
Text(
|
||||
text = pluralStringResourceSafe(
|
||||
id = R.plurals.onboarding_address_sync_description,
|
||||
count = currenciesCount,
|
||||
currenciesCount,
|
||||
),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterHorizontally)
|
||||
.padding(
|
||||
start = TangemTheme.dimens.spacing34,
|
||||
end = TangemTheme.dimens.spacing34,
|
||||
top = TangemTheme.dimens.spacing28,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun AddressSyncButtonScreenPreview() {
|
||||
TangemThemePreview {
|
||||
AddressSyncButtonScreen(
|
||||
state = AddressSyncState.Success(currenciesCount = 2),
|
||||
onSyncClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -82,7 +82,7 @@ internal class OnboardingEntryModel @Inject constructor(
|
|||
is Mode.UpgradeHotWallet -> OnboardingMultiWalletComponent.Mode.UpgradeHotWallet(
|
||||
userWalletId = mode.userWalletId,
|
||||
)
|
||||
is Mode.AddressSync -> OnboardingMultiWalletComponent.Mode.AddressSync
|
||||
is Mode.AddressSync -> OnboardingMultiWalletComponent.Mode.AddressSync(mode.userWalletId)
|
||||
else -> error("Incorrect onboarding type")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ interface OnboardingMultiWalletComponent : ComposableContentComponent, InnerNavi
|
|||
data object AddBackup : Mode()
|
||||
data object ContinueFinalize : Mode()
|
||||
data class UpgradeHotWallet(val userWalletId: UserWalletId) : Mode()
|
||||
data object AddressSync : Mode()
|
||||
data class AddressSync(val userWalletId: UserWalletId) : Mode()
|
||||
}
|
||||
|
||||
interface Factory : ComponentFactory<Params, OnboardingMultiWalletComponent>
|
||||
|
|
|
|||
|
|
@ -265,7 +265,7 @@ internal class MultiWalletFinalizeModel @Inject constructor(
|
|||
val userWallet = when (params.parentParams.mode) {
|
||||
OnboardingMultiWalletComponent.Mode.Onboarding,
|
||||
OnboardingMultiWalletComponent.Mode.ContinueFinalize,
|
||||
OnboardingMultiWalletComponent.Mode.AddressSync,
|
||||
is OnboardingMultiWalletComponent.Mode.AddressSync,
|
||||
-> {
|
||||
saveWalletUseCase.invoke(
|
||||
userWallet = userWalletCreated.copy(
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ internal class OnboardingMultiWalletModel @Inject constructor(
|
|||
onConfirm = {
|
||||
modelScope.launch {
|
||||
onboardingRepository.clearUnfinishedFinalizeOnboarding()
|
||||
if (params.mode == OnboardingMultiWalletComponent.Mode.AddressSync) {
|
||||
if (params.mode is OnboardingMultiWalletComponent.Mode.AddressSync) {
|
||||
router.replaceAll(AppRoute.Wallet)
|
||||
} else {
|
||||
router.pop()
|
||||
|
|
@ -128,7 +128,7 @@ internal class OnboardingMultiWalletModel @Inject constructor(
|
|||
params.mode == OnboardingMultiWalletComponent.Mode.ContinueFinalize -> {
|
||||
OnboardingMultiWalletState.Step.Finalize
|
||||
}
|
||||
params.mode == OnboardingMultiWalletComponent.Mode.AddressSync -> {
|
||||
params.mode is OnboardingMultiWalletComponent.Mode.AddressSync -> {
|
||||
OnboardingMultiWalletState.Step.AddressSync
|
||||
}
|
||||
// Add backup button
|
||||
|
|
|
|||
|
|
@ -1,23 +1,32 @@
|
|||
package com.tangem.features.onboarding.v2.addresssync.model
|
||||
|
||||
import arrow.core.Either
|
||||
import com.arkivanov.decompose.router.stack.StackNavigation
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.domain.account.models.AccountList
|
||||
import com.tangem.domain.account.supplier.MultiAccountListSupplier
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.settings.CanUseBiometryUseCase
|
||||
import com.tangem.domain.settings.ShouldAskPermissionUseCase
|
||||
import com.tangem.domain.settings.ShouldShowAskBiometryUseCase
|
||||
import com.tangem.domain.tokens.MultiWalletAccountListFetcher
|
||||
import com.tangem.features.onboarding.v2.TitleProvider
|
||||
import com.tangem.features.onboarding.v2.addresssync.navigation.AddressSyncStep
|
||||
import com.tangem.features.onboarding.v2.multiwallet.api.OnboardingMultiWalletComponent
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.MultiWalletInnerNavigationState
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.MultiWalletChildParams
|
||||
import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION
|
||||
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.advanceUntilIdle
|
||||
|
|
@ -31,6 +40,8 @@ internal class AddressSyncModelTest {
|
|||
private val shouldShowAskBiometryUseCase: ShouldShowAskBiometryUseCase = mockk()
|
||||
private val canUseBiometryUseCase: CanUseBiometryUseCase = mockk()
|
||||
private val shouldAskPermissionUseCase: ShouldAskPermissionUseCase = mockk()
|
||||
private val multiWalletAccountListFetcher: MultiWalletAccountListFetcher = mockk()
|
||||
private val multiAccountListSupplier: MultiAccountListSupplier = mockk()
|
||||
private val paramsContainer: ParamsContainer = mockk()
|
||||
private val testInnerNavigation = MutableStateFlow(
|
||||
value = MultiWalletInnerNavigationState(
|
||||
|
|
@ -39,10 +50,12 @@ internal class AddressSyncModelTest {
|
|||
)
|
||||
)
|
||||
private val titleProvider: TitleProvider = mockk(relaxUnitFun = true)
|
||||
private val walletId = UserWalletId("011")
|
||||
private val params: MultiWalletChildParams = mockk {
|
||||
every { innerNavigation } returns testInnerNavigation
|
||||
every { parentParams } returns mockk {
|
||||
every { titleProvider } returns this@AddressSyncModelTest.titleProvider
|
||||
every { mode } returns OnboardingMultiWalletComponent.Mode.AddressSync(walletId)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -51,6 +64,10 @@ internal class AddressSyncModelTest {
|
|||
coEvery { canUseBiometryUseCase.strict() } returns false
|
||||
coEvery { shouldShowAskBiometryUseCase() } returns false
|
||||
coEvery { shouldAskPermissionUseCase(PUSH_PERMISSION) } returns false
|
||||
coEvery { multiWalletAccountListFetcher.invoke(any()) } returns Either.Right(Unit)
|
||||
every { multiAccountListSupplier() } returns flowOf(
|
||||
listOf(AccountList.empty(userWalletId = walletId)),
|
||||
)
|
||||
every { paramsContainer.require<MultiWalletChildParams>() } returns params
|
||||
}
|
||||
|
||||
|
|
@ -139,6 +156,51 @@ internal class AddressSyncModelTest {
|
|||
assertStepperAndTitleFor(AddressSyncStep.ADDRESS_SYNC)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN multiAccountListSupplier emits no currencies WHEN model is created THEN state is NoTokens`() = runTest {
|
||||
every { multiAccountListSupplier() } returns flowOf(
|
||||
listOf(
|
||||
AccountList.empty(
|
||||
userWalletId = walletId,
|
||||
cryptoCurrencies = emptyList(),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
val model = createModel(this)
|
||||
advanceUntilIdle()
|
||||
|
||||
coVerify {
|
||||
multiWalletAccountListFetcher.invoke(
|
||||
params = MultiWalletAccountListFetcher.Params(userWalletId = walletId)
|
||||
)
|
||||
}
|
||||
assert(model.state.value == AddressSyncState.NoTokens)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN multiAccountListSupplier emits currencies WHEN model is created THEN state is Success`() = runTest {
|
||||
val currencies = listOf<CryptoCurrency>(mockk(), mockk(), mockk())
|
||||
every { multiAccountListSupplier() } returns flowOf(
|
||||
listOf(
|
||||
AccountList.empty(
|
||||
userWalletId = walletId,
|
||||
cryptoCurrencies = currencies,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
val model = createModel(this)
|
||||
advanceUntilIdle()
|
||||
|
||||
coVerify {
|
||||
multiWalletAccountListFetcher.invoke(
|
||||
params = MultiWalletAccountListFetcher.Params(userWalletId = walletId)
|
||||
)
|
||||
}
|
||||
assert(model.state.value == AddressSyncState.Success(currenciesCount = currencies.size))
|
||||
}
|
||||
|
||||
private fun assertStepperAndTitleFor(step: AddressSyncStep) {
|
||||
assert(testInnerNavigation.value.stackSize == step.pageNumber)
|
||||
verify { titleProvider.changeTitle(resourceReference(step.stringId)) }
|
||||
|
|
@ -160,6 +222,8 @@ internal class AddressSyncModelTest {
|
|||
shouldShowAskBiometryUseCase = shouldShowAskBiometryUseCase,
|
||||
canUseBiometryUseCase = canUseBiometryUseCase,
|
||||
shouldAskPermissionUseCase = shouldAskPermissionUseCase,
|
||||
multiWalletAccountListFetcher = multiWalletAccountListFetcher,
|
||||
multiAccountListSupplier = multiAccountListSupplier,
|
||||
paramsContainer = paramsContainer,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue