Updated on 2026-08-14
This commit is contained in:
parent
e5475e26e1
commit
7e42240fc6
8 changed files with 199 additions and 17 deletions
|
|
@ -57,4 +57,11 @@ data class AccountStatusList(
|
|||
groupType = groupType,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun AccountStatusList.hasMultiCurrencyAccount(): Boolean = accountStatuses.any { status ->
|
||||
when (status) {
|
||||
is AccountStatus.CryptoPortfolio -> status.tokenList.flattenCurrencies().size > 1
|
||||
is AccountStatus.Payment -> false
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
package com.tangem.domain.account.models
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.tokenlist.TokenList
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
internal class AccountStatusListExtTest {
|
||||
|
||||
@Test
|
||||
fun `GIVEN no accounts WHEN hasMultiCurrencyAccount THEN returns false`() {
|
||||
val accountList = createAccountStatusList(accountStatuses = emptyList())
|
||||
|
||||
val result = accountList.hasMultiCurrencyAccount()
|
||||
|
||||
assertThat(result).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN only Payment accounts WHEN hasMultiCurrencyAccount THEN returns false`() {
|
||||
val accountList = createAccountStatusList(
|
||||
accountStatuses = listOf(mockk<AccountStatus.Payment>()),
|
||||
)
|
||||
|
||||
val result = accountList.hasMultiCurrencyAccount()
|
||||
|
||||
assertThat(result).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN CryptoPortfolio with single currency WHEN hasMultiCurrencyAccount THEN returns false`() {
|
||||
val accountList = createAccountStatusList(
|
||||
accountStatuses = listOf(cryptoPortfolioWithCurrencies(count = 1)),
|
||||
)
|
||||
|
||||
val result = accountList.hasMultiCurrencyAccount()
|
||||
|
||||
assertThat(result).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN CryptoPortfolio with no currencies WHEN hasMultiCurrencyAccount THEN returns false`() {
|
||||
val accountList = createAccountStatusList(
|
||||
accountStatuses = listOf(cryptoPortfolioWithCurrencies(count = 0)),
|
||||
)
|
||||
|
||||
val result = accountList.hasMultiCurrencyAccount()
|
||||
|
||||
assertThat(result).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN CryptoPortfolio with multiple currencies WHEN hasMultiCurrencyAccount THEN returns true`() {
|
||||
val accountList = createAccountStatusList(
|
||||
accountStatuses = listOf(cryptoPortfolioWithCurrencies(count = 2)),
|
||||
)
|
||||
|
||||
val result = accountList.hasMultiCurrencyAccount()
|
||||
|
||||
assertThat(result).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN mix of single and multi currency portfolios WHEN hasMultiCurrencyAccount THEN returns true`() {
|
||||
val accountList = createAccountStatusList(
|
||||
accountStatuses = listOf(
|
||||
cryptoPortfolioWithCurrencies(count = 1),
|
||||
cryptoPortfolioWithCurrencies(count = 3),
|
||||
),
|
||||
)
|
||||
|
||||
val result = accountList.hasMultiCurrencyAccount()
|
||||
|
||||
assertThat(result).isTrue()
|
||||
}
|
||||
|
||||
private fun createAccountStatusList(accountStatuses: List<AccountStatus>): AccountStatusList {
|
||||
return mockk {
|
||||
every { this@mockk.accountStatuses } returns accountStatuses
|
||||
}
|
||||
}
|
||||
|
||||
private fun cryptoPortfolioWithCurrencies(count: Int): AccountStatus.CryptoPortfolio {
|
||||
val tokenList = mockk<TokenList> {
|
||||
every { flattenCurrencies() } returns List(count) { mockk<CryptoCurrencyStatus>() }
|
||||
}
|
||||
return mockk {
|
||||
every { this@mockk.tokenList } returns tokenList
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.feature.wallet.child.managetokens
|
|||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
import com.arkivanov.decompose.extensions.compose.subscribeAsState
|
||||
import com.arkivanov.decompose.router.slot.childSlot
|
||||
|
|
@ -48,9 +49,11 @@ internal class AddAndManageBottomSheetComponent(
|
|||
@Composable
|
||||
override fun BottomSheet() {
|
||||
val portfolioSelectorSlot by portfolioSelectorSlot.subscribeAsState()
|
||||
val state by model.state.collectAsStateWithLifecycle()
|
||||
|
||||
AddAndManageBottomSheetContent(
|
||||
onAddTokensClick = model::onAddTokensClick,
|
||||
shouldShowOrganizeButton = state.shouldShowOrganize,
|
||||
onOrganizeTokensClick = model::onOrganizeTokensClick,
|
||||
onDismiss = ::dismiss,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
|
|||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.domain.account.models.hasMultiCurrencyAccount
|
||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.feature.wallet.child.managetokens.AddAndManageBottomSheetComponent
|
||||
import com.tangem.feature.wallet.child.managetokens.analytics.PortfolioAnalyticsEvent
|
||||
|
|
@ -14,7 +16,10 @@ import com.tangem.features.commonfeatures.api.portfolioselector.PortfolioFetcher
|
|||
import com.tangem.features.commonfeatures.api.portfolioselector.PortfolioSelectorComponent
|
||||
import com.tangem.features.commonfeatures.api.portfolioselector.PortfolioSelectorController
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -25,18 +30,20 @@ internal class AddAndManageModel @Inject constructor(
|
|||
private val portfolioFetcherFactory: PortfolioFetcher.Factory,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
val portfolioSelectorController: PortfolioSelectorController,
|
||||
private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<AddAndManageBottomSheetComponent.Params>()
|
||||
|
||||
val portfolioSelectorNavigation: SlotNavigation<Unit> = SlotNavigation()
|
||||
|
||||
val portfolioFetcher: PortfolioFetcher by lazy {
|
||||
portfolioFetcherFactory.create(
|
||||
mode = PortfolioFetcher.Mode.Wallet(params.userWalletId),
|
||||
scope = modelScope,
|
||||
)
|
||||
}
|
||||
val state: StateFlow<AddAndManageState>
|
||||
field = MutableStateFlow(AddAndManageState(shouldShowOrganize = true))
|
||||
|
||||
val portfolioSelectorCallback = object : PortfolioSelectorComponent.BottomSheetCallback {
|
||||
override val onDismiss: () -> Unit = { portfolioSelectorNavigation.dismiss() }
|
||||
|
|
@ -45,6 +52,7 @@ internal class AddAndManageModel @Inject constructor(
|
|||
|
||||
init {
|
||||
observeAccountSelection()
|
||||
updateShouldShowOrganizeButtonState()
|
||||
}
|
||||
|
||||
fun onAddTokensClick() {
|
||||
|
|
@ -85,4 +93,12 @@ internal class AddAndManageModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateShouldShowOrganizeButtonState() {
|
||||
modelScope.launch {
|
||||
val accountStatusesList = singleAccountStatusListSupplier.getSyncOrNull(params.userWalletId)
|
||||
val hasMultiCurrencyAccount = accountStatusesList?.hasMultiCurrencyAccount() == true
|
||||
state.update { it.copy(shouldShowOrganize = hasMultiCurrencyAccount) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.tangem.feature.wallet.child.managetokens.model
|
||||
|
||||
data class AddAndManageState(
|
||||
val shouldShowOrganize: Boolean,
|
||||
)
|
||||
|
|
@ -31,6 +31,7 @@ import com.tangem.core.ui.res.TangemThemePreview
|
|||
@Composable
|
||||
internal fun AddAndManageBottomSheetContent(
|
||||
onAddTokensClick: () -> Unit,
|
||||
shouldShowOrganizeButton: Boolean,
|
||||
onOrganizeTokensClick: () -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
|
|
@ -53,6 +54,7 @@ internal fun AddAndManageBottomSheetContent(
|
|||
content = {
|
||||
AddAndManageContent(
|
||||
onAddTokensClick = onAddTokensClick,
|
||||
shouldShowOrganizeButton = shouldShowOrganizeButton,
|
||||
onOrganizeTokensClick = onOrganizeTokensClick,
|
||||
)
|
||||
},
|
||||
|
|
@ -60,7 +62,11 @@ internal fun AddAndManageBottomSheetContent(
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun AddAndManageContent(onAddTokensClick: () -> Unit, onOrganizeTokensClick: () -> Unit) {
|
||||
private fun AddAndManageContent(
|
||||
onAddTokensClick: () -> Unit,
|
||||
shouldShowOrganizeButton: Boolean,
|
||||
onOrganizeTokensClick: () -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(
|
||||
start = 16.dp,
|
||||
|
|
@ -75,23 +81,25 @@ private fun AddAndManageContent(onAddTokensClick: () -> Unit, onOrganizeTokensCl
|
|||
onClick = onAddTokensClick,
|
||||
modifier = Modifier.roundedShapeItemDecoration(
|
||||
currentIndex = 0,
|
||||
lastIndex = 1,
|
||||
addDefaultPadding = false,
|
||||
backgroundColor = TangemTheme.colors.background.action,
|
||||
),
|
||||
)
|
||||
AddAndManageRow(
|
||||
iconRes = R.drawable.ic_filter_default_24,
|
||||
title = ResR.string.add_and_manage_sheet_organize_title,
|
||||
subtitle = ResR.string.add_and_manage_sheet_organize_subtitle,
|
||||
onClick = onOrganizeTokensClick,
|
||||
modifier = Modifier.roundedShapeItemDecoration(
|
||||
currentIndex = 1,
|
||||
lastIndex = 1,
|
||||
lastIndex = if (shouldShowOrganizeButton) 1 else 0,
|
||||
addDefaultPadding = false,
|
||||
backgroundColor = TangemTheme.colors.background.action,
|
||||
),
|
||||
)
|
||||
if (shouldShowOrganizeButton) {
|
||||
AddAndManageRow(
|
||||
iconRes = R.drawable.ic_filter_default_24,
|
||||
title = ResR.string.add_and_manage_sheet_organize_title,
|
||||
subtitle = ResR.string.add_and_manage_sheet_organize_subtitle,
|
||||
onClick = onOrganizeTokensClick,
|
||||
modifier = Modifier.roundedShapeItemDecoration(
|
||||
currentIndex = 1,
|
||||
lastIndex = 1,
|
||||
addDefaultPadding = false,
|
||||
backgroundColor = TangemTheme.colors.background.action,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -152,6 +160,7 @@ private fun AddAndManageBottomSheetContent_Preview() {
|
|||
TangemThemePreview {
|
||||
AddAndManageContent(
|
||||
onAddTokensClick = {},
|
||||
shouldShowOrganizeButton = true,
|
||||
onOrganizeTokensClick = {},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import com.tangem.core.ui.components.tokenlist.state.PortfolioTokensListItemUM
|
|||
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.domain.account.models.AccountStatusList
|
||||
import com.tangem.domain.account.models.hasMultiCurrencyAccount
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||
import com.tangem.domain.models.TotalFiatBalance
|
||||
|
|
@ -40,7 +41,7 @@ internal class TokenListStateConverter(
|
|||
private val clickIntents: WalletClickIntents,
|
||||
private val yieldModuleApyMap: Map<String, BigDecimal>,
|
||||
private val stakingAvailabilityMap: Map<CryptoCurrency, StakingAvailability>,
|
||||
private val shouldShowMainPromo: Boolean,
|
||||
shouldShowMainPromo: Boolean,
|
||||
private val isAddAndManageTokensEnabled: Boolean,
|
||||
) : Converter<WalletTokensListState, WalletTokensListState> {
|
||||
|
||||
|
|
@ -169,7 +170,8 @@ internal class TokenListStateConverter(
|
|||
}
|
||||
|
||||
private fun getOrganizeTokensButtonStateV2(accountList: AccountStatusList): WalletOrganizeTokensButtonConfig? {
|
||||
return if (accountList.flattenCurrencies().size > 1 && !isSingleCurrencyWalletWithToken()) {
|
||||
val shouldShowOrganizeIfOldButton = accountList.hasMultiCurrencyAccount() || isAddAndManageTokensEnabled
|
||||
return if (shouldShowOrganizeIfOldButton && !isSingleCurrencyWalletWithToken()) {
|
||||
WalletOrganizeTokensButtonConfig(
|
||||
textRes = organizeButtonTextRes(),
|
||||
iconRes = organizeButtonIconRes(),
|
||||
|
|
|
|||
|
|
@ -4,12 +4,18 @@ import com.google.common.truth.Truth.assertThat
|
|||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.core.decompose.model.MutableParamsContainer
|
||||
import com.tangem.domain.account.models.AccountStatusList
|
||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.tokenlist.TokenList
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.child.managetokens.AddAndManageBottomSheetComponent
|
||||
import com.tangem.features.commonfeatures.api.portfolioselector.PortfolioFetcher
|
||||
import com.tangem.features.commonfeatures.api.portfolioselector.PortfolioSelectorController
|
||||
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.slot
|
||||
|
|
@ -38,6 +44,9 @@ internal class AddAndManageModelTest {
|
|||
private val portfolioSelectorController: PortfolioSelectorController = mockk(relaxed = true) {
|
||||
every { selectedAccount } returns flowOf(null)
|
||||
}
|
||||
private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier = mockk(relaxed = true) {
|
||||
coEvery { getSyncOrNull(any<UserWalletId>()) } returns null
|
||||
}
|
||||
|
||||
private val onDismiss: () -> Unit = mockk(relaxed = true)
|
||||
private val onOrganizeTokensClick: () -> Unit = mockk(relaxed = true)
|
||||
|
|
@ -58,6 +67,7 @@ internal class AddAndManageModelTest {
|
|||
portfolioFetcherFactory = portfolioFetcherFactory,
|
||||
analyticsEventHandler = analyticsEventHandler,
|
||||
portfolioSelectorController = portfolioSelectorController,
|
||||
singleAccountStatusListSupplier = singleAccountStatusListSupplier,
|
||||
)
|
||||
|
||||
@Test
|
||||
|
|
@ -98,4 +108,38 @@ internal class AddAndManageModelTest {
|
|||
verify(exactly = 1) { onDismiss() }
|
||||
verify(exactly = 1) { onOrganizeTokensClick() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN wallet has multi currency account WHEN model is created THEN shouldShowOrganize is true`() = runTest {
|
||||
coEvery { singleAccountStatusListSupplier.getSyncOrNull(userWalletId) } returns
|
||||
accountStatusListWithCurrencyCounts(2)
|
||||
|
||||
val model = createModel()
|
||||
|
||||
assertThat(model.state.value.shouldShowOrganize).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN wallet has no multi currency account WHEN model is created THEN shouldShowOrganize is false`() = runTest {
|
||||
coEvery { singleAccountStatusListSupplier.getSyncOrNull(userWalletId) } returns
|
||||
accountStatusListWithCurrencyCounts(1)
|
||||
|
||||
val model = createModel()
|
||||
|
||||
assertThat(model.state.value.shouldShowOrganize).isFalse()
|
||||
}
|
||||
|
||||
private fun accountStatusListWithCurrencyCounts(vararg currencyCounts: Int): AccountStatusList {
|
||||
val statuses: List<AccountStatus> = currencyCounts.map { count ->
|
||||
val tokenList = mockk<TokenList> {
|
||||
every { flattenCurrencies() } returns List(count) { mockk<CryptoCurrencyStatus>() }
|
||||
}
|
||||
mockk<AccountStatus.CryptoPortfolio> {
|
||||
every { this@mockk.tokenList } returns tokenList
|
||||
}
|
||||
}
|
||||
return mockk {
|
||||
every { accountStatuses } returns statuses
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue