Updated on 2026-08-14

This commit is contained in:
Tangem 2026-04-23 14:16:18 +01:00
commit a70d51e55d
19 changed files with 343 additions and 49 deletions

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,11 @@
package com.tangem.features.onboarding.v2.addresssync
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
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 +19,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 +71,22 @@ 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 -> LaunchedEffect(Unit) {
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,43 @@ internal class AddressSyncModel @Inject constructor(
)
}
private fun fetchWalletCrypto() {
modelScope.launch {
multiWalletAccountListFetcher(
params = MultiWalletAccountListFetcher.Params(userWalletId = walletId),
).fold(
ifLeft = {
state.value = AddressSyncState.NoTokens
},
ifRight = {
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,27 +1,33 @@
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.every
import io.mockk.mockk
import io.mockk.verify
import io.mockk.*
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
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
@ -31,6 +37,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 +47,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 +61,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
}
@ -59,8 +73,8 @@ internal class AddressSyncModelTest {
createModel(this)
val state = testInnerNavigation.value
assert(state.stackSize == AddressSyncStep.ASK_BIOMETRY.pageNumber)
assert(state.stackMaxSize == AddressSyncStep.entries.size)
Assertions.assertEquals(AddressSyncStep.ASK_BIOMETRY.pageNumber, state.stackSize)
Assertions.assertEquals(AddressSyncStep.entries.size, state.stackMaxSize)
}
@Test
@ -74,7 +88,7 @@ internal class AddressSyncModelTest {
model.onIntent(AddressSyncIntent.Next(step = AddressSyncStep.ASK_BIOMETRY))
advanceUntilIdle()
assert(stack == listOf(AddressSyncStep.ASK_BIOMETRY))
Assertions.assertEquals(listOf(AddressSyncStep.ASK_BIOMETRY), stack)
assertStepperAndTitleFor(AddressSyncStep.ASK_BIOMETRY)
}
@ -91,7 +105,7 @@ internal class AddressSyncModelTest {
model.onIntent(AddressSyncIntent.Next(step = AddressSyncStep.ASK_BIOMETRY))
advanceUntilIdle()
assert(stack == listOf(AddressSyncStep.ASK_NOTIFICATIONS))
Assertions.assertEquals(listOf(AddressSyncStep.ASK_NOTIFICATIONS), stack)
assertStepperAndTitleFor(AddressSyncStep.ASK_NOTIFICATIONS)
}
@ -107,7 +121,7 @@ internal class AddressSyncModelTest {
model.onIntent(AddressSyncIntent.Next(step = AddressSyncStep.ASK_BIOMETRY))
advanceUntilIdle()
assert(stack == listOf(AddressSyncStep.ADDRESS_SYNC))
Assertions.assertEquals(listOf(AddressSyncStep.ADDRESS_SYNC), stack)
assertStepperAndTitleFor(AddressSyncStep.ADDRESS_SYNC)
}
@ -121,7 +135,7 @@ internal class AddressSyncModelTest {
model.onIntent(AddressSyncIntent.Next(step = AddressSyncStep.ASK_NOTIFICATIONS))
advanceUntilIdle()
assert(stack == listOf(AddressSyncStep.ASK_NOTIFICATIONS))
Assertions.assertEquals(listOf(AddressSyncStep.ASK_NOTIFICATIONS), stack)
assertStepperAndTitleFor(AddressSyncStep.ASK_NOTIFICATIONS)
}
@ -135,12 +149,90 @@ internal class AddressSyncModelTest {
model.onIntent(AddressSyncIntent.Next(step = AddressSyncStep.ASK_NOTIFICATIONS))
advanceUntilIdle()
assert(stack == listOf(AddressSyncStep.ADDRESS_SYNC))
Assertions.assertEquals(listOf(AddressSyncStep.ADDRESS_SYNC), stack)
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)
)
}
Assertions.assertEquals(AddressSyncState.NoTokens, model.state.value)
}
@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)
)
}
Assertions.assertEquals(
AddressSyncState.Success(currenciesCount = currencies.size),
model.state.value,
)
}
@Test
fun `WHEN multiWalletAccountListFetcher emits error WHEN model is created THEN get NoToken state`() = runTest {
val currencies = listOf<CryptoCurrency>(mockk(), mockk(), mockk())
coEvery { multiWalletAccountListFetcher.invoke(any()) } returns Either.Left(
value = IllegalStateException("Test")
)
every { multiAccountListSupplier() } returns flowOf(
listOf(
AccountList.empty(
userWalletId = walletId,
cryptoCurrencies = currencies,
),
),
)
val model = createModel(this)
advanceUntilIdle()
coVerify {
multiWalletAccountListFetcher.invoke(
params = MultiWalletAccountListFetcher.Params(userWalletId = walletId)
)
}
Assertions.assertEquals(
AddressSyncState.NoTokens,
model.state.value,
)
}
private fun assertStepperAndTitleFor(step: AddressSyncStep) {
assert(testInnerNavigation.value.stackSize == step.pageNumber)
Assertions.assertEquals(step.pageNumber, testInnerNavigation.value.stackSize)
verify { titleProvider.changeTitle(resourceReference(step.stringId)) }
}
@ -160,6 +252,8 @@ internal class AddressSyncModelTest {
shouldShowAskBiometryUseCase = shouldShowAskBiometryUseCase,
canUseBiometryUseCase = canUseBiometryUseCase,
shouldAskPermissionUseCase = shouldAskPermissionUseCase,
multiWalletAccountListFetcher = multiWalletAccountListFetcher,
multiAccountListSupplier = multiAccountListSupplier,
paramsContainer = paramsContainer,
)
}