Updated on 2026-08-14

This commit is contained in:
Tangem 2024-04-16 13:54:16 +05:00
commit 0e4ee4a7fe
11 changed files with 294 additions and 120 deletions

View file

@ -422,7 +422,7 @@
<string name="send_additional_field_already_included">Already included in the entered address</string>
<string name="send_alert_fee_coverage_subract_text">Subtract</string>
<string name="send_alert_fee_coverage_title">Not enough funds to cover the network fee. Do you want to subtract the amount required to cover the fee?</string>
<string name="send_alert_fee_too_high_text">The commission amount is %@ times the recommended amount. Make sure that the custom settings are correct.</string>
<string name="send_alert_fee_too_high_text">The commission amount is %s times the recommended amount. Make sure that the custom settings are correct.</string>
<string name="send_alert_fee_too_low_text">You specified a commission below the recommended amount, which could cause a delay in your transaction. Continue?</string>
<string name="send_alert_transaction_failed_text">Reason: %1$s\nCode: %2$s</string>
<string name="send_alert_transaction_failed_title">The transaction is not completed</string>

View file

@ -33,4 +33,15 @@ class GetFixedTxHistoryItemsUseCase(
}
}.mapLeft { TxHistoryListError.DataError(it) }
}
suspend fun getSync(
userWalletId: UserWalletId,
currency: CryptoCurrency,
pageSize: Int = DEFAULT_PAGE_SIZE,
refresh: Boolean = false,
): Either<TxHistoryListError, List<TxHistoryItem>> {
return Either.catch {
repository.getFixedSizeTxHistoryItems(userWalletId, currency, pageSize, refresh)
}.mapLeft { TxHistoryListError.DataError(it) }
}
}

View file

@ -5,10 +5,11 @@ import com.tangem.core.ui.extensions.TextReference
data class SendRecipientListContent(
val id: String,
val title: TextReference,
val subtitle: TextReference,
val title: TextReference = TextReference.EMPTY,
val subtitle: TextReference = TextReference.EMPTY,
val timestamp: TextReference? = null,
val subtitleEndOffset: Int = 0,
@DrawableRes val subtitleIconRes: Int? = null,
val isVisible: Boolean = true,
val isLoading: Boolean = false,
)

View file

@ -4,6 +4,7 @@ import arrow.core.getOrElse
import com.tangem.blockchain.common.TransactionData
import com.tangem.core.ui.components.currency.tokenicon.converter.CryptoCurrencyToIconStateConverter
import com.tangem.core.ui.event.consumedEvent
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
@ -17,12 +18,14 @@ import com.tangem.features.send.impl.presentation.state.amount.SendAmountSubtrac
import com.tangem.features.send.impl.presentation.state.confirm.SendConfirmStateConverter
import com.tangem.features.send.impl.presentation.state.fee.SendFeeStateConverter
import com.tangem.features.send.impl.presentation.state.fields.SendAmountFieldConverter
import com.tangem.features.send.impl.presentation.state.recipient.SendRecipientListConverter
import com.tangem.features.send.impl.presentation.state.recipient.SendRecipientHistoryListConverter
import com.tangem.features.send.impl.presentation.state.recipient.SendRecipientStateConverter
import com.tangem.features.send.impl.presentation.state.recipient.SendRecipientWalletListConverter
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
import com.tangem.utils.Provider
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList
import timber.log.Timber
@Suppress("LongParameterList")
@ -77,9 +80,11 @@ internal class SendStateFactory(
isTapHelpPreviewEnabledProvider = isTapHelpPreviewEnabledProvider,
)
}
private val recipientListStateConverter by lazy(LazyThreadSafetyMode.NONE) {
SendRecipientListConverter(
currentStateProvider = currentStateProvider,
private val recipientWalletListStateConverter by lazy(LazyThreadSafetyMode.NONE) {
SendRecipientWalletListConverter()
}
private val recipientHistoryListStateConverter by lazy(LazyThreadSafetyMode.NONE) {
SendRecipientHistoryListConverter(
cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
)
}
@ -123,11 +128,23 @@ internal class SendStateFactory(
//endregion
//region recipient
fun onLoadedRecipientList(wallets: List<AvailableWallet?>, txHistory: List<TxHistoryItem>): SendUiState =
recipientListStateConverter.convert(
wallets = wallets,
txHistory = txHistory,
fun onLoadedWalletsList(wallets: List<AvailableWallet?>): SendUiState {
val state = currentStateProvider()
return state.copy(
recipientState = state.recipientState?.copy(
wallets = recipientWalletListStateConverter.convert(wallets),
),
)
}
fun onLoadedHistoryList(txHistory: List<TxHistoryItem>): SendUiState {
val state = currentStateProvider()
return state.copy(
recipientState = state.recipientState?.copy(
recent = recipientHistoryListStateConverter.convert(txHistory),
),
)
}
fun onRecipientAddressValueChange(value: String, isXAddress: Boolean = false): SendUiState {
val state = currentStateProvider()
@ -228,6 +245,22 @@ internal class SendStateFactory(
),
)
}
fun getHiddenRecentListState(isAddressInWallet: Boolean, isValidAddress: Boolean): SendUiState {
val state = currentStateProvider()
val recipientState = state.recipientState ?: return state
val isNotValid = isAddressInWallet || !isValidAddress
return state.copy(
recipientState = recipientState.copy(
recent = recipientState.recent.map { recent ->
recent.copy(isVisible = isNotValid && (recent.isLoading || recent.title != TextReference.EMPTY))
}.toPersistentList(),
wallets = recipientState.wallets.map { wallet ->
wallet.copy(isVisible = isNotValid && (wallet.isLoading || wallet.title != TextReference.EMPTY))
}.toPersistentList(),
),
)
}
//endregion
//region send

View file

@ -10,49 +10,26 @@ import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.txhistory.models.TxHistoryItem
import com.tangem.features.send.impl.R
import com.tangem.features.send.impl.presentation.domain.AvailableWallet
import com.tangem.features.send.impl.presentation.domain.SendRecipientListContent
import com.tangem.features.send.impl.presentation.state.SendUiState
import com.tangem.features.send.impl.presentation.state.recipient.utils.RECENT_DEFAULT_COUNT
import com.tangem.features.send.impl.presentation.state.recipient.utils.RECENT_KEY_TAG
import com.tangem.features.send.impl.presentation.state.recipient.utils.emptyListState
import com.tangem.utils.Provider
import com.tangem.utils.converter.Converter
import com.tangem.utils.toFormattedCurrencyString
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toPersistentList
internal class SendRecipientListConverter(
private val currentStateProvider: Provider<SendUiState>,
internal class SendRecipientHistoryListConverter(
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
) {
) : Converter<List<TxHistoryItem>, ImmutableList<SendRecipientListContent>> {
fun convert(wallets: List<AvailableWallet?>, txHistory: List<TxHistoryItem>): SendUiState {
override fun convert(value: List<TxHistoryItem>): ImmutableList<SendRecipientListContent> {
val cryptoCurrency = cryptoCurrencyStatusProvider().currency
val state = currentStateProvider()
val recipientState = state.recipientState ?: return state
return state.copy(
recipientState = recipientState.copy(
wallets = wallets.filterWallets(),
recent = txHistory.filterRecipients(cryptoCurrency),
),
)
}
private fun List<AvailableWallet?>.filterWallets() = this.filterNotNull()
.groupBy { item -> item.name }
.values.map {
it.mapIndexed { index, item ->
val name = if (it.size > 1) {
"${item.name} ${index.inc()}"
} else {
item.name
}
SendRecipientListContent(
id = item.address,
title = TextReference.Str(item.address),
subtitle = TextReference.Str(name),
)
}
return value.filterRecipients(cryptoCurrency).ifEmpty {
emptyListState(RECENT_KEY_TAG, RECENT_DEFAULT_COUNT)
}
.flatten()
.toPersistentList()
}
private fun List<TxHistoryItem>.filterRecipients(cryptoCurrency: CryptoCurrency) = this.filter { item ->
val isTransfer = item.type == TxHistoryItem.TransactionType.Transfer
@ -65,9 +42,9 @@ internal class SendRecipientListConverter(
isTransfer && isSingleAddress && isNotContract
}
.take(RECENT_LIST_SIZE)
.map { tx ->
.mapIndexed { index, tx ->
SendRecipientListContent(
id = tx.txHash,
id = "$RECENT_KEY_TAG$index",
title = tx.extractAddress(),
subtitle = stringReference(tx.getAmount(cryptoCurrency).trim()),
timestamp = tx.extractTimestamp(),

View file

@ -2,10 +2,10 @@ package com.tangem.features.send.impl.presentation.state.recipient
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.send.impl.presentation.state.SendStates
import com.tangem.features.send.impl.presentation.state.recipient.utils.*
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
import com.tangem.utils.Provider
import com.tangem.utils.converter.Converter
import kotlinx.collections.immutable.persistentListOf
internal class SendRecipientStateConverter(
private val clickIntents: SendClickIntents,
@ -26,8 +26,8 @@ internal class SendRecipientStateConverter(
memoTextField = memoFieldConverter.convertOrNull(value.memo),
network = cryptoCurrencyStatusProvider().currency.network.name,
isPrimaryButtonEnabled = false,
wallets = persistentListOf(),
recent = persistentListOf(),
wallets = loadingListState(WALLET_KEY_TAG, WALLET_DEFAULT_COUNT),
recent = loadingListState(RECENT_KEY_TAG, RECENT_DEFAULT_COUNT),
)
}

View file

@ -0,0 +1,39 @@
package com.tangem.features.send.impl.presentation.state.recipient
import com.tangem.core.ui.extensions.TextReference
import com.tangem.features.send.impl.presentation.domain.AvailableWallet
import com.tangem.features.send.impl.presentation.domain.SendRecipientListContent
import com.tangem.features.send.impl.presentation.state.recipient.utils.WALLET_DEFAULT_COUNT
import com.tangem.features.send.impl.presentation.state.recipient.utils.WALLET_KEY_TAG
import com.tangem.features.send.impl.presentation.state.recipient.utils.emptyListState
import com.tangem.utils.converter.Converter
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.toPersistentList
internal class SendRecipientWalletListConverter :
Converter<List<AvailableWallet?>, PersistentList<SendRecipientListContent>> {
override fun convert(value: List<AvailableWallet?>): PersistentList<SendRecipientListContent> {
return value.filterWallets().ifEmpty {
emptyListState(WALLET_KEY_TAG, WALLET_DEFAULT_COUNT)
}
}
private fun List<AvailableWallet?>.filterWallets() = this.filterNotNull()
.groupBy { item -> item.name }
.values.map {
it.mapIndexed { index, item ->
val name = if (it.size > 1) {
"${item.name} ${index.inc()}"
} else {
item.name
}
SendRecipientListContent(
id = "${WALLET_KEY_TAG}$index",
title = TextReference.Str(item.address),
subtitle = TextReference.Str(name),
)
}
}
.flatten()
.toPersistentList()
}

View file

@ -0,0 +1,32 @@
package com.tangem.features.send.impl.presentation.state.recipient.utils
import com.tangem.features.send.impl.presentation.domain.SendRecipientListContent
import kotlinx.collections.immutable.toPersistentList
internal const val WALLET_DEFAULT_COUNT = 1
internal const val RECENT_DEFAULT_COUNT = 3
internal const val WALLET_KEY_TAG = "wallet"
internal const val RECENT_KEY_TAG = "recent"
internal fun loadingListState(tag: String, count: Int) = buildList {
repeat(count) {
add(
SendRecipientListContent(
id = "$tag$it",
isLoading = true,
),
)
}
}.toPersistentList()
internal fun emptyListState(tag: String, count: Int) = buildList {
repeat(count) {
add(
SendRecipientListContent(
id = "$tag$it",
isLoading = false,
isVisible = false,
),
)
}
}.toPersistentList()

View file

@ -1,6 +1,10 @@
package com.tangem.features.send.impl.presentation.ui.recipient
import androidx.annotation.DrawableRes
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
@ -9,6 +13,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.painterResource
@ -16,6 +21,8 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import com.tangem.core.ui.components.CircleShimmer
import com.tangem.core.ui.components.RectangleShimmer
import com.tangem.core.ui.components.atoms.text.EllipsisText
import com.tangem.core.ui.components.atoms.text.TextEllipsis
import com.tangem.core.ui.components.icons.identicon.IdentIcon
@ -43,9 +50,42 @@ fun ListItemWithIcon(
info: String? = null,
subtitleEndOffset: Int = 0,
@DrawableRes subtitleIconRes: Int? = null,
isLoading: Boolean = false,
) {
AnimatedContent(
targetState = isLoading,
label = "Recent List Content Animation",
transitionSpec = { fadeIn().togetherWith(fadeOut()) },
) { isLoadingState ->
if (isLoadingState) {
ListItemLoading(modifier = modifier)
} else {
ListItemWithIcon(
title = title,
subtitle = subtitle,
onClick = onClick,
info = info,
subtitleEndOffset = subtitleEndOffset,
subtitleIconRes = subtitleIconRes,
modifier = modifier,
)
}
}
}
@Composable
private fun ListItemWithIcon(
title: String,
subtitle: String,
onClick: () -> Unit,
modifier: Modifier = Modifier,
info: String? = null,
subtitleEndOffset: Int = 0,
@DrawableRes subtitleIconRes: Int? = null,
) {
val hapticFeedback = rememberHapticFeedback(state = title, onAction = onClick)
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = modifier
.fillMaxWidth()
.clickable { hapticFeedback() }
@ -55,13 +95,12 @@ fun ListItemWithIcon(
address = title,
modifier = Modifier
.padding(vertical = TangemTheme.dimens.spacing8)
.size(TangemTheme.dimens.size40)
.clip(RoundedCornerShape(TangemTheme.dimens.radius20)),
.size(TangemTheme.dimens.size36)
.clip(RoundedCornerShape(TangemTheme.dimens.radius18)),
)
Column(
modifier = Modifier
.padding(vertical = TangemTheme.dimens.spacing10)
.padding(start = TangemTheme.dimens.spacing12),
modifier = Modifier.padding(start = TangemTheme.dimens.spacing12),
verticalArrangement = Arrangement.SpaceBetween,
) {
EllipsisText(
text = title,
@ -102,6 +141,43 @@ fun ListItemWithIcon(
}
}
@Composable
private fun ListItemLoading(modifier: Modifier = Modifier) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = modifier
.fillMaxWidth()
.padding(horizontal = TangemTheme.dimens.spacing12),
) {
CircleShimmer(
modifier = Modifier
.padding(vertical = TangemTheme.dimens.spacing8)
.size(TangemTheme.dimens.size36),
)
Column(
modifier = Modifier
.height(TangemTheme.dimens.size32)
.padding(start = TangemTheme.dimens.spacing12),
verticalArrangement = Arrangement.SpaceBetween,
) {
RectangleShimmer(
radius = TangemTheme.dimens.radius3,
modifier = Modifier.size(
width = TangemTheme.dimens.spacing70,
height = TangemTheme.dimens.spacing12,
),
)
RectangleShimmer(
radius = TangemTheme.dimens.radius3,
modifier = Modifier.size(
width = TangemTheme.dimens.spacing52,
height = TangemTheme.dimens.spacing12,
),
)
}
}
}
// region preview
@Preview
@Composable
@ -115,6 +191,7 @@ private fun ListItemWithIconPreview_Light(
subtitleEndOffset = config.subtitleEndOffset,
subtitleIconRes = config.iconRes,
onClick = {},
isLoading = config.isLoading,
)
}
}
@ -131,6 +208,7 @@ private fun ListItemWithIconPreview_Dark(
subtitleEndOffset = config.subtitleEndOffset,
subtitleIconRes = config.iconRes,
onClick = {},
isLoading = config.isLoading,
)
}
}
@ -141,6 +219,7 @@ private data class ListItemWithIconPreviewConfig(
val info: String? = null,
val subtitleEndOffset: Int = 0,
val iconRes: Int? = null,
val isLoading: Boolean = false,
)
private class ListItemWithIconPreviewProvider : CollectionPreviewParameterProvider<ListItemWithIconPreviewConfig>(
@ -163,6 +242,14 @@ private class ListItemWithIconPreviewProvider : CollectionPreviewParameterProvid
title = "0x34B4492A412D84A6E606288f3Bd714b89135D4dE",
subtitle = "Wallet",
),
ListItemWithIconPreviewConfig(
title = "0x34B4492A412D84A6E606288f3Bd714b89135D4dE",
subtitle = "0.000000000000000000000000000000 BTC",
info = "0.0.0000 at 00:00",
subtitleEndOffset = "BTC".length,
iconRes = R.drawable.ic_arrow_down_24,
isLoading = true,
),
),
)
//endregion

View file

@ -2,8 +2,8 @@ package com.tangem.features.send.impl.presentation.ui.recipient
import androidx.annotation.StringRes
import androidx.compose.animation.*
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
@ -76,13 +76,13 @@ internal fun SendRecipientContent(
listItem(
list = wallets,
clickIntents = clickIntents,
isLast = recipients.isEmpty(),
isLast = recipients.any { !it.isVisible },
isBalanceHidden = isBalanceHidden,
)
listHeaderItem(
titleRes = R.string.send_recent_transactions,
isVisible = recipients.isNotEmpty() && recipients.first().isVisible,
isFirst = wallets.isEmpty(),
isFirst = wallets.any { !it.isVisible },
)
listItem(
list = recipients,
@ -143,20 +143,9 @@ private fun LazyListScope.memoField(memoField: SendTextField.RecipientMemo?, onM
}
}
@OptIn(ExperimentalFoundationApi::class)
private fun LazyListScope.listHeaderItem(@StringRes titleRes: Int, isVisible: Boolean, isFirst: Boolean) {
item(
key = titleRes,
) {
AnimatedVisibility(
visible = isVisible,
label = "Header Appearance Animation",
enter = slideInVertically() + fadeIn(),
exit = slideOutVertically() + fadeOut(),
modifier = Modifier
.animateItemPlacement()
.animateContentSize(),
) {
item(key = titleRes) {
AnimateRecentAppearance(isVisible) {
val (topPadding, paddingFromTop) = if (isFirst) {
TangemTheme.dimens.spacing20 to TangemTheme.dimens.spacing12
} else {
@ -192,7 +181,6 @@ private fun LazyListScope.listHeaderItem(@StringRes titleRes: Int, isVisible: Bo
}
}
@OptIn(ExperimentalFoundationApi::class)
private fun LazyListScope.listItem(
list: ImmutableList<SendRecipientListContent>,
clickIntents: SendClickIntents,
@ -206,22 +194,20 @@ private fun LazyListScope.listItem(
) { index ->
val item = list[index]
val title = item.title.resolveReference()
AnimatedVisibility(
visible = item.isVisible,
label = "Header Appearance Animation",
enter = slideInVertically() + fadeIn(),
exit = slideOutVertically() + fadeOut(),
modifier = Modifier
.animateItemPlacement()
.animateContentSize(),
) {
AnimateRecentAppearance(item.isVisible) {
ListItemWithIcon(
title = title,
subtitle = if (isBalanceHidden) STARS else item.subtitle.resolveReference(),
info = item.timestamp?.resolveReference(),
subtitleEndOffset = item.subtitleEndOffset,
subtitleIconRes = item.subtitleIconRes,
onClick = { clickIntents.onRecipientAddressValueChange(title, EnterAddressSource.RecentAddress) },
onClick = {
clickIntents.onRecipientAddressValueChange(
title,
EnterAddressSource.RecentAddress,
)
},
isLoading = item.isLoading,
modifier = Modifier
.then(
if (isLast && index == list.lastIndex) {
@ -240,4 +226,22 @@ private fun LazyListScope.listItem(
)
}
}
}
@Composable
private fun AnimateRecentAppearance(isVisible: Boolean, content: @Composable () -> Unit) {
AnimatedContent(
targetState = isVisible,
label = "Item Appearance Animation",
transitionSpec = {
(slideInHorizontally() + fadeIn())
.togetherWith(slideOutVertically() + fadeOut())
},
) {
if (it) {
content()
} else {
Box(modifier = Modifier.fillMaxWidth())
}
}
}

View file

@ -30,7 +30,6 @@ import com.tangem.domain.transaction.usecase.CreateTransactionUseCase
import com.tangem.domain.transaction.usecase.GetFeeUseCase
import com.tangem.domain.transaction.usecase.IsFeeApproximateUseCase
import com.tangem.domain.transaction.usecase.SendTransactionUseCase
import com.tangem.domain.txhistory.models.TxHistoryItem
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
import com.tangem.domain.txhistory.usecase.GetFixedTxHistoryItemsUseCase
import com.tangem.domain.wallets.models.UserWallet
@ -56,7 +55,6 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.JobHolder
import com.tangem.utils.coroutines.saveIn
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.collections.immutable.toPersistentList
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import timber.log.Timber
@ -183,7 +181,6 @@ internal class SendViewModel @Inject constructor(
private var balanceJobHolder = JobHolder()
private var balanceHidingJobHolder = JobHolder()
private var recipientsJobHolder = JobHolder()
private var feeJobHolder = JobHolder()
private var addressValidationJobHolder = JobHolder()
private var memoValidationJobHolder = JobHolder()
@ -374,34 +371,33 @@ internal class SendViewModel @Inject constructor(
}
private fun getWalletsAndRecent() {
combine(
flow = getUserWallets().conflate(),
flow2 = getTxHistory().conflate(),
) { wallets, txHistory ->
uiState = stateFactory.onLoadedRecipientList(
wallets = wallets,
txHistory = txHistory,
)
getUserWallets()
viewModelScope.launch(dispatchers.main) {
getTxHistory()
}
.flowOn(dispatchers.io)
.launchIn(viewModelScope)
.saveIn(recipientsJobHolder)
}
private fun getUserWallets(): Flow<List<AvailableWallet?>> {
return getWalletsUseCase()
private fun getUserWallets() {
getWalletsUseCase()
.conflate()
.distinctUntilChanged()
.map { userWallets ->
.onEach { userWallets ->
coroutineScope {
userWallets
.filterNot { it.walletId == userWalletId || it.isLocked }
.map { wallet ->
async(dispatchers.io) {
wallet.toAvailableWallet()
}
}
}.awaitAll()
runCatching {
userWallets
.filterNot { it.walletId == userWalletId || it.isLocked }
.map { wallet ->
async(dispatchers.io) { wallet.toAvailableWallet() }
}.awaitAll()
}.onSuccess { result ->
uiState = stateFactory.onLoadedWalletsList(wallets = result)
}.onFailure {
uiState = stateFactory.onLoadedWalletsList(wallets = emptyList())
}
}
}
.flowOn(dispatchers.main)
.launchIn(viewModelScope)
}
private suspend fun UserWallet.toAvailableWallet(): AvailableWallet? {
@ -431,14 +427,12 @@ internal class SendViewModel @Inject constructor(
}
}
private fun getTxHistory(): Flow<List<TxHistoryItem>> {
return getFixedTxHistoryItemsUseCase(
private suspend fun getTxHistory() {
val txHistoryList = getFixedTxHistoryItemsUseCase.getSync(
userWalletId = userWalletId,
currency = cryptoCurrency,
).fold(
ifRight = { it.distinctUntilChanged() },
ifLeft = { emptyFlow() },
)
).getOrElse { emptyList() }
uiState = stateFactory.onLoadedHistoryList(txHistory = txHistoryList)
}
private fun onStateActive() {
@ -636,13 +630,9 @@ internal class SendViewModel @Inject constructor(
}
private fun onEnteredValidAddress(isValidAddress: Boolean, isAddressInWallet: Boolean) {
val recipientState = uiState.recipientState ?: return
val isVisible = isAddressInWallet || !isValidAddress
uiState = uiState.copy(
recipientState = recipientState.copy(
recent = recipientState.recent.map { it.copy(isVisible = isVisible) }.toPersistentList(),
wallets = recipientState.wallets.map { it.copy(isVisible = isVisible) }.toPersistentList(),
),
uiState = stateFactory.getHiddenRecentListState(
isAddressInWallet = isAddressInWallet,
isValidAddress = isValidAddress,
)
}