Updated on 2026-08-14
This commit is contained in:
commit
a70d51e55d
19 changed files with 343 additions and 49 deletions
|
|
@ -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,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,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,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue