Updated on 2026-08-14

This commit is contained in:
Tangem 2026-04-23 10:45:35 +01:00
parent e1c819d421
commit 0e169a5418
19 changed files with 292 additions and 37 deletions

View file

@ -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,

View file

@ -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()
}
}

View 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>

View file

@ -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,
)
}
}

View file

@ -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

View file

@ -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()

View file

@ -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>

View file

@ -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)

View file

@ -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)
}
}
}
}

View file

@ -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()
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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),

View file

@ -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 = {},
)
}
}

View file

@ -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")
}

View file

@ -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>

View file

@ -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(

View file

@ -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

View file

@ -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,
)
}