Updated on 2026-08-14
This commit is contained in:
commit
788d7064b5
217 changed files with 6061 additions and 1892 deletions
|
|
@ -112,6 +112,7 @@ dependencies {
|
|||
implementation(projects.features.sendV2.api)
|
||||
implementation(projects.features.tokenRecieve.api)
|
||||
implementation(projects.features.yieldSupply.api)
|
||||
implementation(projects.features.commonFeatures.api)
|
||||
|
||||
implementation(deps.decompose.ext.compose)
|
||||
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDeta
|
|||
import com.tangem.feature.tokendetails.presentation.tokendetails.route.TokenDetailsBottomSheetConfig
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.TokenDetailsScreen
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.TokenDetailsScreenLegacy
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.bottomsheet.AddFundsBottomSheetComponent
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.bottomsheet.ChooseAddressBottomSheetComponent
|
||||
import com.tangem.features.commonfeatures.api.addfunds.AddFundsComponent
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.bottomsheet.CloreMigrationBottomSheetComponent
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.bottomsheet.DynamicAddressesBottomSheetComponent
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.bottomsheet.TransferBottomSheetComponent
|
||||
|
|
@ -47,6 +47,7 @@ internal class DefaultTokenDetailsComponent @AssistedInject constructor(
|
|||
expressTransactionsComponentFactory: ExpressTransactionsComponent.Factory,
|
||||
private val tokenReceiveComponentFactory: TokenReceiveComponent.Factory,
|
||||
private val yieldSupplyWarningComponentFactory: YieldSupplyDepositedWarningComponent.Factory,
|
||||
private val addFundsComponentFactory: AddFundsComponent.Factory,
|
||||
yieldSupplyComponentFactory: YieldSupplyComponent.Factory,
|
||||
private val ratingComponentFactory: RatingComponent.Factory,
|
||||
) : TokenDetailsComponent, AppComponentContext by appComponentContext {
|
||||
|
|
@ -177,9 +178,15 @@ internal class DefaultTokenDetailsComponent @AssistedInject constructor(
|
|||
dynamicAddressesDelegate = model.dynamicAddressesDelegate,
|
||||
onDismiss = model.bottomSheetNavigation::dismiss,
|
||||
)
|
||||
is TokenDetailsBottomSheetConfig.AddFunds -> AddFundsBottomSheetComponent(
|
||||
stateFlow = model.addFundsUiState,
|
||||
onDismiss = model.bottomSheetNavigation::dismiss,
|
||||
is TokenDetailsBottomSheetConfig.AddFunds -> addFundsComponentFactory.create(
|
||||
context = childByContext(componentContext),
|
||||
params = AddFundsComponent.Params(
|
||||
launchMode = AddFundsComponent.LaunchMode.TokenActionsOnly(
|
||||
userWalletId = route.userWalletId,
|
||||
currency = route.currency,
|
||||
),
|
||||
onDismiss = model.bottomSheetNavigation::dismiss,
|
||||
),
|
||||
)
|
||||
is TokenDetailsBottomSheetConfig.Transfer -> TransferBottomSheetComponent(
|
||||
stateFlow = model.transferUiState,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import com.tangem.common.routing.deeplink.DeeplinkConst.TRANSACTION_ID_KEY
|
|||
import com.tangem.common.routing.deeplink.DeeplinkConst.TYPE_KEY
|
||||
import com.tangem.common.routing.deeplink.DeeplinkConst.WALLET_ID_KEY
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.domain.account.fetcher.SingleAccountListFetcher
|
||||
import com.tangem.domain.account.status.utils.CryptoCurrencyBalanceFetcher
|
||||
import com.tangem.domain.account.supplier.SingleAccountListSupplier
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
|
|
@ -47,6 +48,7 @@ internal class DefaultTokenDetailsDeepLinkHandler @AssistedInject constructor(
|
|||
private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
|
||||
private val walletBalanceFetcher: WalletBalanceFetcher,
|
||||
private val singleAccountListSupplier: SingleAccountListSupplier,
|
||||
private val singleAccountListFetcher: SingleAccountListFetcher,
|
||||
) : TokenDetailsDeepLinkHandler {
|
||||
|
||||
init {
|
||||
|
|
@ -81,6 +83,9 @@ internal class DefaultTokenDetailsDeepLinkHandler @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
// Refresh the portfolio before searching so a token just added on the backend is present locally.
|
||||
refreshAccountsIfNeeded(userWallet)
|
||||
|
||||
val cryptoCurrency = findCryptoCurrency(userWallet = userWallet, networkId = networkId, tokenId = tokenId)
|
||||
|
||||
if (cryptoCurrency == null) {
|
||||
|
|
@ -91,6 +96,8 @@ internal class DefaultTokenDetailsDeepLinkHandler @AssistedInject constructor(
|
|||
|- $TOKEN_ID_KEY: $tokenId
|
||||
""".trimIndent(),
|
||||
)
|
||||
// Token is not in the response (not indexed yet / backend error): go to main, do not add.
|
||||
appRouter.popTo(AppRoute.Wallet)
|
||||
return@launch
|
||||
}
|
||||
|
||||
|
|
@ -123,6 +130,21 @@ internal class DefaultTokenDetailsDeepLinkHandler @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes wallet accounts so a token just added on the backend appears in the local portfolio.
|
||||
*
|
||||
* Only when the app was open on push tap ([isFromOnNewIntent]) and the wallet is multi-currency:
|
||||
* on cold start the fresh list is already loaded by the regular auth flow, and single-currency
|
||||
* wallets have a fixed token. The fetch is best-effort — on failure we fall through and try the
|
||||
* current cache, so existing tokens (e.g. swap/onramp pushes) still open without regression.
|
||||
*/
|
||||
private suspend fun refreshAccountsIfNeeded(userWallet: UserWallet) {
|
||||
if (isFromOnNewIntent && userWallet.isMultiCurrency) {
|
||||
singleAccountListFetcher(SingleAccountListFetcher.Params(userWalletId = userWallet.walletId))
|
||||
.onLeft { TangemLogger.e("Error on refreshing wallet accounts", it) }
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchCurrency(userWallet: UserWallet, cryptoCurrency: CryptoCurrency) {
|
||||
val isMultiCurrency = userWallet.isMultiCurrency
|
||||
when {
|
||||
|
|
|
|||
|
|
@ -559,7 +559,12 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onAddFundsClick() {
|
||||
bottomSheetNavigation.activate(TokenDetailsBottomSheetConfig.AddFunds)
|
||||
bottomSheetNavigation.activate(
|
||||
TokenDetailsBottomSheetConfig.AddFunds(
|
||||
userWalletId = userWalletId,
|
||||
currency = cryptoCurrency,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
override fun onTransferClick() {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.tangem.core.decompose.navigation.Route
|
|||
import com.tangem.domain.models.TokenReceiveConfig
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.NetworkAddress
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.tokens.model.details.TokenAction
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
|
|
@ -32,7 +33,10 @@ sealed class TokenDetailsBottomSheetConfig : Route {
|
|||
data object DynamicAddresses : TokenDetailsBottomSheetConfig()
|
||||
|
||||
@Serializable
|
||||
data object AddFunds : TokenDetailsBottomSheetConfig()
|
||||
data class AddFunds(
|
||||
val userWalletId: UserWalletId,
|
||||
val currency: CryptoCurrency,
|
||||
) : TokenDetailsBottomSheetConfig()
|
||||
|
||||
@Serializable
|
||||
data object Transfer : TokenDetailsBottomSheetConfig()
|
||||
|
|
|
|||
|
|
@ -1,59 +0,0 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.ui.bottomsheet
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.AddFundsUM
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import com.tangem.core.ui.R as CoreR
|
||||
|
||||
internal class AddFundsBottomSheetComponent(
|
||||
private val stateFlow: StateFlow<AddFundsUM>,
|
||||
private val onDismiss: () -> Unit,
|
||||
) : ComposableBottomSheetComponent {
|
||||
|
||||
override fun dismiss() {
|
||||
onDismiss()
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun BottomSheet() {
|
||||
val state by stateFlow.collectAsStateWithLifecycle()
|
||||
|
||||
val config = remember(state) {
|
||||
TangemBottomSheetConfig(
|
||||
isShown = true,
|
||||
onDismissRequest = ::dismiss,
|
||||
content = state,
|
||||
)
|
||||
}
|
||||
|
||||
TangemModalBottomSheet<AddFundsUM>(
|
||||
config = config,
|
||||
containerColor = TangemTheme.colors2.surface.level2,
|
||||
title = {
|
||||
TangemModalBottomSheetTitle(
|
||||
title = resourceReference(CoreR.string.common_get_token),
|
||||
endIconRes = CoreR.drawable.ic_close_24,
|
||||
onEndClick = ::dismiss,
|
||||
)
|
||||
},
|
||||
content = { contentState ->
|
||||
AddFundsBottomSheetContent(
|
||||
state = contentState,
|
||||
onCloseClick = ::dismiss,
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens2.x4),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,167 +0,0 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.ui.bottomsheet
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.common.ui.tokenaction.TokenActionRow
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.ds.button.SecondaryTangemButton
|
||||
import com.tangem.core.ui.ds.button.TangemButtonShape
|
||||
import com.tangem.core.ui.ds.button.TangemButtonSize
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.LocalHazeState
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.AddFundsUM
|
||||
import dev.chrisbanes.haze.rememberHazeState
|
||||
import com.tangem.core.ui.R as CoreR
|
||||
|
||||
@Composable
|
||||
internal fun AddFundsBottomSheetContent(state: AddFundsUM, onCloseClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2),
|
||||
) {
|
||||
BuyActionRow(state = state)
|
||||
SwapActionRow(state = state)
|
||||
ReceiveActionRow(state = state)
|
||||
|
||||
SpacerH(TangemTheme.dimens2.x2)
|
||||
|
||||
CompositionLocalProvider(LocalHazeState provides rememberHazeState()) {
|
||||
SecondaryTangemButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = onCloseClick,
|
||||
text = resourceReference(CoreR.string.common_close),
|
||||
size = TangemButtonSize.X12,
|
||||
shape = TangemButtonShape.Rounded,
|
||||
)
|
||||
}
|
||||
|
||||
SpacerH(TangemTheme.dimens2.x4)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BuyActionRow(state: AddFundsUM) {
|
||||
val row = (state as? AddFundsUM.Content)?.buy
|
||||
if (state is AddFundsUM.Content && row == null) return
|
||||
ActionRow(
|
||||
iconRes = CoreR.drawable.ic_credit_card_20,
|
||||
title = resourceReference(CoreR.string.common_buy),
|
||||
description = resourceReference(CoreR.string.quick_action_buy_description),
|
||||
row = row,
|
||||
isLoading = state is AddFundsUM.Loading,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SwapActionRow(state: AddFundsUM) {
|
||||
val row = (state as? AddFundsUM.Content)?.swap
|
||||
if (state is AddFundsUM.Content && row == null) return
|
||||
ActionRow(
|
||||
iconRes = CoreR.drawable.ic_exchange_mini_24,
|
||||
title = resourceReference(CoreR.string.common_swap),
|
||||
description = resourceReference(CoreR.string.quick_action_swap_description),
|
||||
row = row,
|
||||
isLoading = state is AddFundsUM.Loading,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ReceiveActionRow(state: AddFundsUM) {
|
||||
val row = (state as? AddFundsUM.Content)?.receive
|
||||
if (state is AddFundsUM.Content && row == null) return
|
||||
ActionRow(
|
||||
iconRes = CoreR.drawable.ic_qrcode_new_24,
|
||||
title = resourceReference(CoreR.string.common_receive),
|
||||
description = resourceReference(CoreR.string.quick_action_receive_description),
|
||||
row = row,
|
||||
isLoading = state is AddFundsUM.Loading,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ActionRow(
|
||||
iconRes: Int,
|
||||
title: TextReference,
|
||||
description: TextReference,
|
||||
row: AddFundsUM.Row?,
|
||||
isLoading: Boolean,
|
||||
) {
|
||||
if (isLoading || row?.isLoading == true) {
|
||||
TokenActionRow(
|
||||
iconRes = iconRes,
|
||||
title = title,
|
||||
description = description,
|
||||
tailContent = { TailLoader() },
|
||||
)
|
||||
} else {
|
||||
TokenActionRow(
|
||||
iconRes = iconRes,
|
||||
title = title,
|
||||
description = description,
|
||||
onClick = row?.onClick,
|
||||
onLongClick = row?.onLongClick,
|
||||
isEnabled = row?.isEnabled == true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TailLoader() {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(20.dp),
|
||||
color = TangemTheme.colors2.graphic.neutral.tertiary,
|
||||
strokeWidth = 2.dp,
|
||||
)
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Preview(widthDp = 360, showBackground = true)
|
||||
@Composable
|
||||
private fun Preview(@PreviewParameter(AddFundsPreviewProvider::class) state: AddFundsUM) {
|
||||
TangemThemePreviewRedesign {
|
||||
CompositionLocalProvider(LocalRedesignEnabled provides true) {
|
||||
AddFundsBottomSheetContent(
|
||||
state = state,
|
||||
onCloseClick = {},
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class AddFundsPreviewProvider : PreviewParameterProvider<AddFundsUM> {
|
||||
override val values: Sequence<AddFundsUM> = sequenceOf(
|
||||
AddFundsUM.Loading,
|
||||
AddFundsUM.Content(
|
||||
buy = AddFundsUM.Row(isLoading = false, isEnabled = true, onClick = {}),
|
||||
swap = AddFundsUM.Row(isLoading = false, isEnabled = true, onClick = {}),
|
||||
receive = AddFundsUM.Row(isLoading = false, isEnabled = true, onClick = {}, onLongClick = {}),
|
||||
),
|
||||
AddFundsUM.Content(
|
||||
buy = AddFundsUM.Row(isLoading = false, isEnabled = false, onClick = {}),
|
||||
swap = AddFundsUM.Row(isLoading = false, isEnabled = false, onClick = {}),
|
||||
receive = AddFundsUM.Row(isLoading = false, isEnabled = true, onClick = {}, onLongClick = {}),
|
||||
),
|
||||
AddFundsUM.Content(
|
||||
buy = null,
|
||||
swap = null,
|
||||
receive = AddFundsUM.Row(isLoading = false, isEnabled = true, onClick = {}, onLongClick = {}),
|
||||
),
|
||||
)
|
||||
}
|
||||
// endregion
|
||||
|
|
@ -10,6 +10,7 @@ import com.tangem.common.routing.deeplink.DeeplinkConst.TRANSACTION_ID_KEY
|
|||
import com.tangem.common.routing.deeplink.DeeplinkConst.TYPE_KEY
|
||||
import com.tangem.common.routing.deeplink.DeeplinkConst.WALLET_ID_KEY
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.domain.account.fetcher.SingleAccountListFetcher
|
||||
import com.tangem.domain.account.models.AccountList
|
||||
import com.tangem.domain.account.status.utils.CryptoCurrencyBalanceFetcher
|
||||
import com.tangem.domain.account.supplier.SingleAccountListSupplier
|
||||
|
|
@ -50,6 +51,7 @@ class DefaultTokenDetailsDeepLinkHandlerTest {
|
|||
private val getUserWalletUseCase: GetUserWalletUseCase = mockk()
|
||||
private val walletBalanceFetcher: WalletBalanceFetcher = mockk()
|
||||
private val singleAccountListSupplier: SingleAccountListSupplier = mockk()
|
||||
private val singleAccountListFetcher: SingleAccountListFetcher = mockk()
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
|
|
@ -57,6 +59,8 @@ class DefaultTokenDetailsDeepLinkHandlerTest {
|
|||
mockkObject(TangemLogger)
|
||||
every { analyticsEventHandler.send(any()) } just Runs
|
||||
every { appRouter.push(any(), any()) } just Runs
|
||||
every { appRouter.popTo(route = any(), onComplete = any()) } just Runs
|
||||
coEvery { singleAccountListFetcher.invoke(any()) } returns Either.Right(Unit)
|
||||
val userWallet: UserWallet = mockk()
|
||||
every { userWallet.walletId } returns mockk()
|
||||
every { getSelectedWalletSync() } returns Either.Right(
|
||||
|
|
@ -461,6 +465,151 @@ class DefaultTokenDetailsDeepLinkHandlerTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN multicurrency wallet AND isFromOnNewIntent WHEN handle deeplink THEN refresh wallet accounts`() =
|
||||
runTest {
|
||||
val userWalletId = UserWalletId("011")
|
||||
val cryptoCurrency = mockCryptoCurrency()
|
||||
mockMultiCurrencyWallet(userWalletId)
|
||||
mockSelectWallet(userWalletId)
|
||||
coEvery { singleAccountListSupplier.getSyncOrNull(userWalletId) } returns AccountList.empty(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = listOf(cryptoCurrency),
|
||||
)
|
||||
every {
|
||||
cryptoCurrencyBalanceFetcher.invoke(userWalletId = userWalletId, currency = cryptoCurrency)
|
||||
} just Runs
|
||||
|
||||
createHandler(scope = this, defaultQueryParams(), isFromOnNewIntent = true)
|
||||
advanceUntilIdle()
|
||||
|
||||
coVerify { singleAccountListFetcher.invoke(SingleAccountListFetcher.Params(userWalletId)) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN multicurrency wallet AND NOT isFromOnNewIntent WHEN handle deeplink THEN do not refresh accounts`() =
|
||||
runTest {
|
||||
val userWalletId = UserWalletId("011")
|
||||
val cryptoCurrency = mockCryptoCurrency()
|
||||
mockMultiCurrencyWallet(userWalletId)
|
||||
mockSelectWallet(userWalletId)
|
||||
coEvery { singleAccountListSupplier.getSyncOrNull(userWalletId) } returns AccountList.empty(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = listOf(cryptoCurrency),
|
||||
)
|
||||
|
||||
createHandler(scope = this, defaultQueryParams(), isFromOnNewIntent = false)
|
||||
advanceUntilIdle()
|
||||
|
||||
coVerify(exactly = 0) { singleAccountListFetcher.invoke(any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN single currency wallet AND isFromOnNewIntent WHEN handle deeplink THEN do not refresh accounts`() =
|
||||
runTest {
|
||||
val userWalletId = UserWalletId("011")
|
||||
val cryptoCurrency = mockCryptoCurrency()
|
||||
mockSingleCurrencyWallet(userWalletId)
|
||||
mockSelectWallet(userWalletId)
|
||||
coEvery { singleAccountListSupplier.getSyncOrNull(userWalletId) } returns AccountList.empty(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = listOf(cryptoCurrency),
|
||||
)
|
||||
every { walletDeepLinkActionTrigger.selectWallet(userWalletId) } just Runs
|
||||
coEvery {
|
||||
walletBalanceFetcher.invoke(WalletBalanceFetcher.Params(userWalletId = userWalletId))
|
||||
} returns mockk()
|
||||
|
||||
createHandler(scope = this, defaultQueryParams(), isFromOnNewIntent = true)
|
||||
advanceUntilIdle()
|
||||
|
||||
coVerify(exactly = 0) { singleAccountListFetcher.invoke(any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN crypto not found WHEN handle deeplink THEN redirect to main`() = runTest {
|
||||
val userWalletId = UserWalletId("011")
|
||||
mockMultiCurrencyWallet(userWalletId)
|
||||
mockSelectWallet(userWalletId)
|
||||
coEvery { singleAccountListSupplier.getSyncOrNull(userWalletId) } returns null
|
||||
|
||||
createHandler(scope = this, defaultQueryParams(), isFromOnNewIntent = true)
|
||||
advanceUntilIdle()
|
||||
|
||||
verify { appRouter.popTo(route = AppRoute.Wallet, onComplete = any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN refresh failed AND token in cache WHEN handle deeplink THEN push new route`() = runTest {
|
||||
val userWalletId = UserWalletId("011")
|
||||
val cryptoCurrency = mockCryptoCurrency()
|
||||
mockMultiCurrencyWallet(userWalletId)
|
||||
mockSelectWallet(userWalletId)
|
||||
coEvery {
|
||||
singleAccountListFetcher.invoke(SingleAccountListFetcher.Params(userWalletId))
|
||||
} returns Either.Left(IllegalStateException("service unavailable"))
|
||||
coEvery { singleAccountListSupplier.getSyncOrNull(userWalletId) } returns AccountList.empty(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencies = listOf(cryptoCurrency),
|
||||
)
|
||||
every {
|
||||
cryptoCurrencyBalanceFetcher.invoke(userWalletId = userWalletId, currency = cryptoCurrency)
|
||||
} just Runs
|
||||
val expectedRoute = AppRoute.CurrencyDetails(userWalletId = userWalletId, currency = cryptoCurrency)
|
||||
|
||||
createHandler(scope = this, defaultQueryParams(), isFromOnNewIntent = true)
|
||||
advanceUntilIdle()
|
||||
|
||||
verify {
|
||||
appRouter.push(route = expectedRoute, onComplete = any())
|
||||
}
|
||||
}
|
||||
|
||||
private fun defaultQueryParams() = mapOf(
|
||||
WALLET_ID_KEY to "011",
|
||||
NETWORK_ID_KEY to "123",
|
||||
TOKEN_ID_KEY to "321",
|
||||
DERIVATION_PATH_KEY to "777",
|
||||
)
|
||||
|
||||
private fun mockCryptoCurrency() = mockk<CryptoCurrency> {
|
||||
every { network } returns mockk {
|
||||
every { rawId } returns "123"
|
||||
every { derivationPath } returns Network.DerivationPath.Card(value = "777")
|
||||
}
|
||||
every { id } returns CryptoCurrency.ID(
|
||||
prefix = CryptoCurrency.ID.Prefix.TOKEN_PREFIX,
|
||||
body = CryptoCurrency.ID.Body.NetworkIdWithDerivationPath(rawId = "321", derivationPath = "777"),
|
||||
suffix = CryptoCurrency.ID.Suffix.RawID("321"),
|
||||
)
|
||||
}
|
||||
|
||||
private fun mockMultiCurrencyWallet(userWalletId: UserWalletId) {
|
||||
every { getUserWalletUseCase.invoke(userWalletId) } returns Either.Right(
|
||||
value = mockk {
|
||||
every { isMultiCurrency } returns true
|
||||
every { walletId } returns userWalletId
|
||||
every { isLocked } returns false
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun mockSingleCurrencyWallet(userWalletId: UserWalletId) {
|
||||
every { getUserWalletUseCase.invoke(userWalletId) } returns Either.Right(
|
||||
value = mockk {
|
||||
every { isMultiCurrency } returns false
|
||||
every { walletId } returns userWalletId
|
||||
every { isLocked } returns false
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun mockSelectWallet(userWalletId: UserWalletId) {
|
||||
coEvery { selectWalletUseCase.invoke(userWalletId) } returns Either.Right(
|
||||
value = mockk { every { walletId } returns userWalletId },
|
||||
)
|
||||
}
|
||||
|
||||
private fun createHandler(
|
||||
scope: CoroutineScope,
|
||||
queryParams: Map<String, String>,
|
||||
|
|
@ -479,6 +628,7 @@ class DefaultTokenDetailsDeepLinkHandlerTest {
|
|||
getUserWalletUseCase = getUserWalletUseCase,
|
||||
walletBalanceFetcher = walletBalanceFetcher,
|
||||
singleAccountListSupplier = singleAccountListSupplier,
|
||||
singleAccountListFetcher = singleAccountListFetcher,
|
||||
getSelectedWalletSyncUseCase = getSelectedWalletSync,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue