Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-16 13:48:50 +03:00
parent 1470d4b08d
commit 8beb80f5a7
13 changed files with 778 additions and 34 deletions

View file

@ -0,0 +1,38 @@
package com.tangem.features.send.v2.networkselection
import androidx.compose.runtime.Composable
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.features.send.v2.api.NetworkSelectionComponent
import com.tangem.features.send.v2.networkselection.model.NetworkSelectionModel
import com.tangem.features.send.v2.networkselection.ui.NetworkSelectionScreen
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
internal class DefaultNetworkSelectionComponent @AssistedInject constructor(
@Assisted context: AppComponentContext,
@Assisted private val params: NetworkSelectionComponent.Params,
) : NetworkSelectionComponent, AppComponentContext by context {
private val model: NetworkSelectionModel = getOrCreateModel(params)
override fun dismiss() {
params.onDismiss()
}
@Composable
override fun Dialog() {
val state = model.uiState.collectAsStateWithLifecycle()
NetworkSelectionScreen(state = state.value, onDismiss = ::dismiss)
}
@AssistedFactory
interface Factory : NetworkSelectionComponent.Factory {
override fun create(
context: AppComponentContext,
params: NetworkSelectionComponent.Params,
): DefaultNetworkSelectionComponent
}
}

View file

@ -0,0 +1,20 @@
package com.tangem.features.send.v2.networkselection.di
import com.tangem.features.send.v2.api.NetworkSelectionComponent
import com.tangem.features.send.v2.networkselection.DefaultNetworkSelectionComponent
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal interface NetworkSelectionFeatureModule {
@Binds
@Singleton
fun bindNetworkSelectionComponentFactory(
impl: DefaultNetworkSelectionComponent.Factory,
): NetworkSelectionComponent.Factory
}

View file

@ -0,0 +1,20 @@
package com.tangem.features.send.v2.networkselection.di
import com.tangem.core.decompose.di.ModelComponent
import com.tangem.core.decompose.model.Model
import com.tangem.features.send.v2.networkselection.model.NetworkSelectionModel
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
@Module
@InstallIn(ModelComponent::class)
internal interface NetworkSelectionModelModule {
@Binds
@IntoMap
@ClassKey(NetworkSelectionModel::class)
fun provideNetworkSelectionModel(model: NetworkSelectionModel): Model
}

View file

@ -0,0 +1,33 @@
package com.tangem.features.send.v2.networkselection.entity
import androidx.compose.runtime.Immutable
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.components.fields.entity.SearchBarUM
import com.tangem.core.ui.components.token.state.TokenItemState
import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.models.wallet.UserWalletId
import kotlinx.collections.immutable.ImmutableList
@Immutable
internal data class NetworkSelectionUM(
val searchBar: SearchBarUM,
val walletGroups: ImmutableList<WalletGroupUM>,
val isBalanceHidden: Boolean,
)
@Immutable
internal data class WalletGroupUM(
val userWalletId: UserWalletId,
val walletName: String,
val isExpanded: Boolean,
val onExpandToggle: () -> Unit,
val accounts: ImmutableList<AccountGroupUM>,
)
@Immutable
internal data class AccountGroupUM(
val accountName: TextReference,
val iconState: CurrencyIconState.CryptoPortfolio?,
val tokens: ImmutableList<TokenItemState>,
val hiddenTokensCount: Int,
)

View file

@ -0,0 +1,264 @@
package com.tangem.features.send.v2.networkselection.model
import androidx.compose.runtime.Stable
import com.tangem.common.getTotalCryptoAmount
import com.tangem.common.getTotalFiatAmount
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.common.ui.account.AccountIconItemStateConverter
import com.tangem.core.ui.components.account.AccountIconSize
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.components.currency.icon.CurrencyIconStateBuilder
import com.tangem.core.ui.components.fields.entity.SearchBarUM
import com.tangem.core.ui.components.token.state.TokenItemState
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.account.models.AccountStatusList
import com.tangem.domain.account.status.supplier.MultiAccountStatusListSupplier
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.models.StatusSource
import com.tangem.common.ui.account.toUM
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.account.filterCryptoPortfolio
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.features.send.v2.api.NetworkSelectionComponent
import com.tangem.features.send.v2.networkselection.entity.AccountGroupUM
import com.tangem.features.send.v2.networkselection.entity.NetworkSelectionUM
import com.tangem.features.send.v2.networkselection.entity.WalletGroupUM
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.*
import javax.inject.Inject
@Stable
@ModelScoped
internal class NetworkSelectionModel @Inject constructor(
paramsContainer: ParamsContainer,
override val dispatchers: CoroutineDispatcherProvider,
private val multiAccountStatusListSupplier: MultiAccountStatusListSupplier,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
) : Model() {
private val params: NetworkSelectionComponent.Params = paramsContainer.require()
private val searchQuery = MutableStateFlow("")
private val expandedWallets = MutableStateFlow(
params.walletGroups.firstOrNull()
?.userWalletId
?.let(::setOf)
.orEmpty(),
)
val uiState: StateFlow<NetworkSelectionUM> = createUiStateFlow()
private fun createUiStateFlow(): StateFlow<NetworkSelectionUM> {
val appCurrencyFlow = getSelectedAppCurrencyUseCase.invokeOrDefault()
val statusListFlow = multiAccountStatusListSupplier()
val balanceHidingFlow = getBalanceHidingSettingsUseCase()
return combine(
flow = searchQuery,
flow2 = expandedWallets,
flow3 = appCurrencyFlow,
flow4 = statusListFlow,
flow5 = balanceHidingFlow,
) { query, expanded, appCurrency, statusLists, balanceHidingSettings ->
buildState(
query = query,
expandedWallets = expanded,
appCurrency = appCurrency,
statusLists = statusLists,
isBalanceHidden = balanceHidingSettings.isBalanceHidden,
)
}.stateIn(
scope = modelScope,
started = SharingStarted.WhileSubscribed(),
initialValue = buildInitialState(),
)
}
private fun buildState(
query: String,
expandedWallets: Set<UserWalletId>,
appCurrency: AppCurrency,
statusLists: List<AccountStatusList>,
isBalanceHidden: Boolean,
): NetworkSelectionUM {
val walletGroups = params.walletGroups.mapNotNull { walletGroup ->
buildWalletGroup(
walletGroup = walletGroup,
query = query,
expandedWallets = expandedWallets,
appCurrency = appCurrency,
statusLists = statusLists,
)
}.toImmutableList()
return NetworkSelectionUM(
searchBar = createSearchBar(query),
walletGroups = walletGroups,
isBalanceHidden = isBalanceHidden,
)
}
private fun buildWalletGroup(
walletGroup: NetworkSelectionComponent.Params.WalletGroup,
query: String,
expandedWallets: Set<UserWalletId>,
appCurrency: AppCurrency,
statusLists: List<AccountStatusList>,
): WalletGroupUM? {
val statusList = statusLists.find { it.userWalletId == walletGroup.userWalletId }
val tokenBuildContext = TokenMappingParams(
userWalletId = walletGroup.userWalletId,
appCurrency = appCurrency,
statusMap = buildStatusMap(statusList),
)
val accounts = walletGroup.accounts.mapNotNull { accountGroup ->
val iconState = getAccountIcon(accountGroup.accountId, statusList)
buildAccountGroup(
accountGroup = accountGroup,
query = query,
context = tokenBuildContext,
iconState = iconState,
)
}.toImmutableList()
if (accounts.isEmpty()) return null
return WalletGroupUM(
userWalletId = walletGroup.userWalletId,
walletName = walletGroup.walletName,
isExpanded = walletGroup.userWalletId in expandedWallets,
onExpandToggle = { toggleWalletExpanded(walletGroup.userWalletId) },
accounts = accounts,
)
}
private fun buildStatusMap(statusList: AccountStatusList?): Map<CryptoCurrency.ID, CryptoCurrencyStatus> {
if (statusList == null) return emptyMap()
return statusList.accountStatuses
.filterCryptoPortfolio()
.flatMap { it.flattenCurrencies() }
.associateBy { it.currency.id }
}
private fun buildAccountGroup(
accountGroup: NetworkSelectionComponent.Params.AccountGroup,
query: String,
context: TokenMappingParams,
iconState: CurrencyIconState.CryptoPortfolio?,
): AccountGroupUM? {
val tokens = accountGroup.currencies
.filter { matchesQuery(it, query) }
.map { currency -> buildTokenItem(currency, context) }
.toImmutableList()
if (tokens.isEmpty()) return null
return AccountGroupUM(
accountName = accountGroup.accountName.toUM().value,
iconState = iconState,
tokens = tokens,
hiddenTokensCount = accountGroup.hiddenTokensCount,
)
}
private fun getAccountIcon(
accountId: AccountId,
statusList: AccountStatusList?,
): CurrencyIconState.CryptoPortfolio? {
if (statusList == null) return null
val account = statusList.accountStatuses
.filterCryptoPortfolio()
.find { it.accountId == accountId }
?.account ?: return null
return AccountIconItemStateConverter(size = AccountIconSize.ExtraSmall).convert(account)
}
private fun matchesQuery(currency: CryptoCurrency, query: String): Boolean {
if (query.isBlank()) return true
val lowerQuery = query.lowercase()
return currency.name.lowercase().contains(lowerQuery) ||
currency.symbol.lowercase().contains(lowerQuery) ||
currency.network.name.lowercase().contains(lowerQuery)
}
private fun buildTokenItem(currency: CryptoCurrency, context: TokenMappingParams): TokenItemState {
val status = context.statusMap[currency.id]
if (status == null) {
return TokenItemState.Loading(id = currency.id.value)
}
val cryptoAmount = status.getTotalCryptoAmount()
val fiatAmount = status.getTotalFiatAmount()
val isFlickering = status.value.sources.total == StatusSource.CACHE
return TokenItemState.Content(
id = currency.id.value,
iconState = CurrencyIconStateBuilder.build(currency),
titleState = TokenItemState.TitleState.Content(
text = stringReference(currency.name),
),
subtitleState = TokenItemState.SubtitleState.TextContent(
value = stringReference(currency.network.name),
),
fiatAmountState = TokenItemState.FiatAmountState.Content(
text = fiatAmount.format {
fiat(
fiatCurrencyCode = context.appCurrency.code,
fiatCurrencySymbol = context.appCurrency.symbol,
)
},
isFlickering = isFlickering,
),
subtitle2State = TokenItemState.Subtitle2State.TextContent(
text = cryptoAmount.format { crypto(currency) },
isFlickering = isFlickering,
),
onItemClick = { params.onTokenSelected(context.userWalletId, currency) },
onItemLongClick = null,
)
}
private fun toggleWalletExpanded(walletId: UserWalletId) {
expandedWallets.update { current ->
if (walletId in current) current - walletId else current + walletId
}
}
private fun createSearchBar(query: String): SearchBarUM {
return SearchBarUM(
placeholderText = resourceReference(com.tangem.core.ui.R.string.common_search_tokens),
query = query,
onQueryChange = { searchQuery.value = it },
isActive = query.isNotEmpty(),
onActiveChange = {},
)
}
private fun buildInitialState(): NetworkSelectionUM {
return NetworkSelectionUM(
searchBar = createSearchBar(""),
walletGroups = persistentListOf(),
isBalanceHidden = true,
)
}
private data class TokenMappingParams(
val userWalletId: UserWalletId,
val appCurrency: AppCurrency,
val statusMap: Map<CryptoCurrency.ID, CryptoCurrencyStatus>,
)
}

View file

@ -0,0 +1,300 @@
package com.tangem.features.send.v2.networkselection.ui
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.R
import com.tangem.core.ui.components.appbar.TangemTopAppBar
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
import com.tangem.core.ui.components.currency.icon.CurrencyIcon
import com.tangem.core.ui.components.fields.SearchBar
import com.tangem.core.ui.components.token.TokenItem
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
import com.tangem.core.ui.extensions.pluralStringResourceSafe
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.send.v2.networkselection.entity.AccountGroupUM
import com.tangem.features.send.v2.networkselection.entity.NetworkSelectionUM
import com.tangem.features.send.v2.networkselection.entity.WalletGroupUM
private const val CHEVRON_EXPANDED_ROTATION = 180f
private const val CHEVRON_COLLAPSED_ROTATION = 0f
@Composable
internal fun NetworkSelectionScreen(state: NetworkSelectionUM, onDismiss: () -> Unit) {
Dialog(
onDismissRequest = onDismiss,
properties = DialogProperties(
usePlatformDefaultWidth = false,
decorFitsSystemWindows = false,
),
) {
Column(
modifier = Modifier
.fillMaxSize()
.background(TangemTheme.colors.background.tertiary)
.systemBarsPadding()
.imePadding(),
) {
TangemTopAppBar(
title = stringResourceSafe(R.string.common_token_send),
startButton = TopAppBarButtonUM.Icon(
iconRes = R.drawable.ic_close_24,
onClicked = onDismiss,
),
)
SearchBar(
state = state.searchBar,
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing22),
)
Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing8))
if (state.walletGroups.isEmpty() && state.searchBar.query.isNotBlank()) {
NetworkSelectionEmpty(modifier = Modifier.weight(1f))
} else {
NetworkSelectionContent(state = state)
}
}
}
}
@Composable
private fun NetworkSelectionEmpty(modifier: Modifier = Modifier) {
Box(
contentAlignment = Alignment.Center,
modifier = modifier
.fillMaxWidth()
.padding(horizontal = TangemTheme.dimens.spacing16),
) {
Text(
text = stringResourceSafe(R.string.common_no_results),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.tertiary,
textAlign = TextAlign.Center,
)
}
}
@Composable
private fun NetworkSelectionContent(state: NetworkSelectionUM) {
LazyColumn(
modifier = Modifier.fillMaxWidth(),
contentPadding = PaddingValues(bottom = TangemTheme.dimens.spacing12),
) {
state.walletGroups.forEach { walletGroup ->
item(key = "wallet_header_${walletGroup.userWalletId}") {
WalletHeader(walletGroup = walletGroup)
}
walletGroup.accounts.forEach { accountGroup ->
accountGroupItems(
walletGroup = walletGroup,
accountGroup = accountGroup,
isBalanceHidden = state.isBalanceHidden,
)
}
}
}
}
private fun LazyListScope.accountGroupItems(
walletGroup: WalletGroupUM,
accountGroup: AccountGroupUM,
isBalanceHidden: Boolean,
) {
val hasHiddenTokens = accountGroup.hiddenTokensCount > 0
val lastIndex = accountGroup.tokens.lastIndex.inc() + if (hasHiddenTokens) 1 else 0
item(key = "account_${walletGroup.userWalletId}_${accountGroup.accountName}") {
AnimatedVisibility(
visible = walletGroup.isExpanded,
enter = fadeIn() + expandVertically(),
exit = fadeOut() + shrinkVertically(),
) {
AccountHeader(
accountGroup = accountGroup,
modifier = Modifier.roundedShapeItemDecoration(
currentIndex = 0,
lastIndex = lastIndex,
radius = TangemTheme.dimens.radius14,
backgroundColor = TangemTheme.colors.background.action,
),
)
}
}
itemsIndexed(
items = accountGroup.tokens,
key = { _, token -> "token_${walletGroup.userWalletId}_${token.id}" },
) { index, tokenState ->
val indexWithHeader = index.inc()
AnimatedVisibility(
visible = walletGroup.isExpanded,
enter = fadeIn() + expandVertically(),
exit = fadeOut() + shrinkVertically(),
) {
TokenItem(
state = tokenState,
isBalanceHidden = isBalanceHidden,
reorderableTokenListState = null,
modifier = Modifier.roundedShapeItemDecoration(
currentIndex = indexWithHeader,
lastIndex = lastIndex,
radius = TangemTheme.dimens.radius14,
backgroundColor = TangemTheme.colors.background.action,
),
)
}
}
if (hasHiddenTokens) {
item(
key = "hidden_${walletGroup.userWalletId}_${accountGroup.accountName}",
) {
AnimatedVisibility(
visible = walletGroup.isExpanded,
enter = fadeIn() + expandVertically(),
exit = fadeOut() + shrinkVertically(),
) {
HiddenTokensFooter(
count = accountGroup.hiddenTokensCount,
modifier = Modifier.roundedShapeItemDecoration(
currentIndex = lastIndex,
lastIndex = lastIndex,
radius = TangemTheme.dimens.radius14,
backgroundColor = TangemTheme.colors.background.action,
),
)
}
}
}
}
@Composable
private fun WalletHeader(walletGroup: WalletGroupUM) {
val chevronRotation by animateFloatAsState(
targetValue = if (walletGroup.isExpanded) CHEVRON_COLLAPSED_ROTATION else CHEVRON_EXPANDED_ROTATION,
label = "chevron_rotation",
)
Row(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = walletGroup.onExpandToggle)
.padding(
horizontal = TangemTheme.dimens.spacing16,
vertical = TangemTheme.dimens.spacing12,
),
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = walletGroup.walletName,
style = TangemTheme.typography.subtitle1,
color = TangemTheme.colors.text.primary1,
modifier = Modifier.weight(1f),
)
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.size(TangemTheme.dimens.size24)
.background(
color = TangemTheme.colors.button.secondary,
shape = CircleShape,
),
) {
Icon(
painter = painterResource(id = R.drawable.ic_chevron_up_24),
contentDescription = null,
tint = TangemTheme.colors.icon.informative,
modifier = Modifier
.size(TangemTheme.dimens.size16)
.rotate(chevronRotation),
)
}
}
}
@Composable
private fun AccountHeader(accountGroup: AccountGroupUM, modifier: Modifier = Modifier) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = modifier
.fillMaxWidth()
.padding(
start = TangemTheme.dimens.spacing12,
top = TangemTheme.dimens.spacing10,
bottom = TangemTheme.dimens.spacing6,
end = TangemTheme.dimens.spacing12,
),
) {
accountGroup.iconState?.let { iconState ->
CurrencyIcon(
state = iconState,
modifier = Modifier.size(TangemTheme.dimens.size18),
)
Spacer(modifier = Modifier.width(TangemTheme.dimens.spacing6))
}
Text(
text = accountGroup.accountName.resolveReference(),
style = TangemTheme.typography.caption1,
color = TangemTheme.colors.text.tertiary,
)
}
}
@Composable
private fun HiddenTokensFooter(count: Int, modifier: Modifier = Modifier) {
Column(modifier = modifier) {
HorizontalDivider(
thickness = 0.5.dp,
color = TangemTheme.colors.stroke.primary,
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing14),
)
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.padding(
start = TangemTheme.dimens.spacing12,
end = TangemTheme.dimens.spacing12,
top = TangemTheme.dimens.spacing10,
bottom = TangemTheme.dimens.spacing10,
),
) {
Icon(
painter = painterResource(id = R.drawable.ic_eye_off_outline_24),
contentDescription = null,
tint = TangemTheme.colors.icon.informative,
modifier = Modifier.size(TangemTheme.dimens.size16),
)
Spacer(modifier = Modifier.width(TangemTheme.dimens.spacing8))
Text(
text = pluralStringResourceSafe(
id = R.plurals.send_network_selection_hidden_tokens,
count = count,
count,
),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
)
}
}
}

View file

@ -281,9 +281,11 @@ internal class SendModel @Inject constructor(
}
is PredefinedValues.Content.QrCode -> {
val predefinedAmount = predefinedValues.amount?.parseBigDecimalOrNull()
val amount = predefinedAmount
?: (uiState.value.amountUM as? AmountState.Data)?.amountTextField?.cryptoAmount?.value
?: error("Invalid amount")
createTransferTransactionUseCase(
amount = predefinedAmount?.convertToSdkAmount(cryptoCurrencyStatus)
?: error("Invalid amount"),
amount = amount.convertToSdkAmount(cryptoCurrencyStatus),
memo = predefinedValues.memo,
destination = predefinedValues.address,
userWalletId = userWallet.walletId,
@ -422,13 +424,24 @@ internal class SendModel @Inject constructor(
feeCryptoCurrencyStatusFlow,
) { cryptoCurrencyStatus, feeCryptoCurrencyStatus ->
if (isAvailableForSend && currentRoute.value == initialRoute) {
router.replaceAll(Confirm)
if (isPredefinedAmountExceedsBalance(cryptoCurrencyStatus)) {
router.replaceAll(Amount(isEditMode = false))
} else {
router.replaceAll(Confirm)
}
} else if (isUnavailableForSend) {
showAlertError()
}
}.launchIn(modelScope)
}
private fun isPredefinedAmountExceedsBalance(cryptoCurrencyStatus: CryptoCurrencyStatus): Boolean {
val predefinedAmount = (predefinedValues as? PredefinedValues.Content)?.amount
?.parseBigDecimalOrNull() ?: return false
val balance = cryptoCurrencyStatus.value.amount ?: return false
return predefinedAmount > balance
}
private fun CryptoCurrencyStatus.hasAvailableStatus(): Boolean = this.value is CryptoCurrencyStatus.Loaded ||
this.value is CryptoCurrencyStatus.Custom ||
this.value is CryptoCurrencyStatus.NoQuote