diff --git a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt
index 743496098a..0725d8d517 100644
--- a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt
+++ b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt
@@ -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,
diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt
index 165823622e..b70510260e 100644
--- a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt
+++ b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt
@@ -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()
}
}
diff --git a/core/ui/src/main/res/drawable/ic_sync_56.xml b/core/ui/src/main/res/drawable/ic_sync_56.xml
new file mode 100644
index 0000000000..112dd64452
--- /dev/null
+++ b/core/ui/src/main/res/drawable/ic_sync_56.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
diff --git a/features/biometry/impl/src/main/kotlin/com/tangem/features/biometry/impl/ui/AskBiometry.kt b/features/biometry/impl/src/main/kotlin/com/tangem/features/biometry/impl/ui/AskBiometry.kt
index a3726bf381..447e3a1238 100644
--- a/features/biometry/impl/src/main/kotlin/com/tangem/features/biometry/impl/ui/AskBiometry.kt
+++ b/features/biometry/impl/src/main/kotlin/com/tangem/features/biometry/impl/ui/AskBiometry.kt
@@ -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,
- )
}
}
diff --git a/features/create-wallet-start/impl/src/main/kotlin/com/tangem/features/createwalletstart/CreateWalletStartModel.kt b/features/create-wallet-start/impl/src/main/kotlin/com/tangem/features/createwalletstart/CreateWalletStartModel.kt
index 8c15de536f..91f69a736d 100644
--- a/features/create-wallet-start/impl/src/main/kotlin/com/tangem/features/createwalletstart/CreateWalletStartModel.kt
+++ b/features/create-wallet-start/impl/src/main/kotlin/com/tangem/features/createwalletstart/CreateWalletStartModel.kt
@@ -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
diff --git a/features/create-wallet-start/impl/src/test/kotlin/com/tangem/features/createwalletstart/CreateWalletStartModelTest.kt b/features/create-wallet-start/impl/src/test/kotlin/com/tangem/features/createwalletstart/CreateWalletStartModelTest.kt
index bd7640bef5..54b19b9b4f 100644
--- a/features/create-wallet-start/impl/src/test/kotlin/com/tangem/features/createwalletstart/CreateWalletStartModelTest.kt
+++ b/features/create-wallet-start/impl/src/test/kotlin/com/tangem/features/createwalletstart/CreateWalletStartModelTest.kt
@@ -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()
diff --git a/features/onboarding-v2/api/src/main/kotlin/com/tangem/features/onboarding/v2/entry/OnboardingEntryComponent.kt b/features/onboarding-v2/api/src/main/kotlin/com/tangem/features/onboarding/v2/entry/OnboardingEntryComponent.kt
index d4776bf833..a8314aff42 100644
--- a/features/onboarding-v2/api/src/main/kotlin/com/tangem/features/onboarding/v2/entry/OnboardingEntryComponent.kt
+++ b/features/onboarding-v2/api/src/main/kotlin/com/tangem/features/onboarding/v2/entry/OnboardingEntryComponent.kt
@@ -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
diff --git a/features/onboarding-v2/impl/build.gradle.kts b/features/onboarding-v2/impl/build.gradle.kts
index f4022573af..a2fadc93e1 100644
--- a/features/onboarding-v2/impl/build.gradle.kts
+++ b/features/onboarding-v2/impl/build.gradle.kts
@@ -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)
diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/addresssync/DefaultAddressSyncComponent.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/addresssync/DefaultAddressSyncComponent.kt
index f5999ae461..4717f961c2 100644
--- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/addresssync/DefaultAddressSyncComponent.kt
+++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/addresssync/DefaultAddressSyncComponent.kt
@@ -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)
+ }
+ }
}
}
diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/addresssync/model/AddressSyncContract.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/addresssync/model/AddressSyncContract.kt
new file mode 100644
index 0000000000..f283b138e2
--- /dev/null
+++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/addresssync/model/AddressSyncContract.kt
@@ -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()
+}
\ No newline at end of file
diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/addresssync/model/AddressSyncIntent.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/addresssync/model/AddressSyncIntent.kt
deleted file mode 100644
index 0776ea3cc7..0000000000
--- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/addresssync/model/AddressSyncIntent.kt
+++ /dev/null
@@ -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
-}
\ No newline at end of file
diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/addresssync/model/AddressSyncModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/addresssync/model/AddressSyncModel.kt
index a73fa83fae..eb902893a3 100644
--- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/addresssync/model/AddressSyncModel.kt
+++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/addresssync/model/AddressSyncModel.kt
@@ -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()
+ private val walletId = (params.parentParams.mode as OnboardingMultiWalletComponent.Mode.AddressSync).userWalletId
val stackNavigation = StackNavigation()
+ val state: StateFlow
+ field = MutableStateFlow(
+ 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
}
diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/addresssync/navigation/AddressSyncStep.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/addresssync/navigation/AddressSyncStep.kt
index c03a4fc662..f9d48107ca 100644
--- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/addresssync/navigation/AddressSyncStep.kt
+++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/addresssync/navigation/AddressSyncStep.kt
@@ -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),
diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/addresssync/ui/AddressSyncButtonScreen.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/addresssync/ui/AddressSyncButtonScreen.kt
new file mode 100644
index 0000000000..94e2d60121
--- /dev/null
+++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/addresssync/ui/AddressSyncButtonScreen.kt
@@ -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 = {},
+ )
+ }
+}
\ No newline at end of file
diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/model/OnboardingEntryModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/model/OnboardingEntryModel.kt
index 5747977a6d..aab619c0e9 100644
--- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/model/OnboardingEntryModel.kt
+++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/model/OnboardingEntryModel.kt
@@ -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")
}
diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/api/OnboardingMultiWalletComponent.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/api/OnboardingMultiWalletComponent.kt
index 9e4e023699..ed4280ad63 100644
--- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/api/OnboardingMultiWalletComponent.kt
+++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/api/OnboardingMultiWalletComponent.kt
@@ -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
diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/finalize/model/MultiWalletFinalizeModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/finalize/model/MultiWalletFinalizeModel.kt
index 9dc1d725b0..b6431a21af 100644
--- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/finalize/model/MultiWalletFinalizeModel.kt
+++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/finalize/model/MultiWalletFinalizeModel.kt
@@ -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(
diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/model/OnboardingMultiWalletModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/model/OnboardingMultiWalletModel.kt
index 050a14f74e..0d3c27e10a 100644
--- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/model/OnboardingMultiWalletModel.kt
+++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/model/OnboardingMultiWalletModel.kt
@@ -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
diff --git a/features/onboarding-v2/impl/src/test/kotlin/com/tangem/features/onboarding/v2/addresssync/model/AddressSyncModelTest.kt b/features/onboarding-v2/impl/src/test/kotlin/com/tangem/features/onboarding/v2/addresssync/model/AddressSyncModelTest.kt
index f784c38c74..8282ed6639 100644
--- a/features/onboarding-v2/impl/src/test/kotlin/com/tangem/features/onboarding/v2/addresssync/model/AddressSyncModelTest.kt
+++ b/features/onboarding-v2/impl/src/test/kotlin/com/tangem/features/onboarding/v2/addresssync/model/AddressSyncModelTest.kt
@@ -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() } 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(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,
)
}