diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index 1251772f83..9310a0153a 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -422,7 +422,7 @@
Already included in the entered address
Subtract
Not enough funds to cover the network fee. Do you want to subtract the amount required to cover the fee?
- The commission amount is %@ times the recommended amount. Make sure that the custom settings are correct.
+ The commission amount is %s times the recommended amount. Make sure that the custom settings are correct.
You specified a commission below the recommended amount, which could cause a delay in your transaction. Continue?
Reason: %1$s\nCode: %2$s
The transaction is not completed
diff --git a/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetFixedTxHistoryItemsUseCase.kt b/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetFixedTxHistoryItemsUseCase.kt
index aa93bec246..8b5a463618 100644
--- a/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetFixedTxHistoryItemsUseCase.kt
+++ b/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetFixedTxHistoryItemsUseCase.kt
@@ -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> {
+ return Either.catch {
+ repository.getFixedSizeTxHistoryItems(userWalletId, currency, pageSize, refresh)
+ }.mapLeft { TxHistoryListError.DataError(it) }
+ }
}
\ No newline at end of file
diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/domain/SendRecipientListContent.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/domain/SendRecipientListContent.kt
index 0837f1f27c..03e9414c0a 100644
--- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/domain/SendRecipientListContent.kt
+++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/domain/SendRecipientListContent.kt
@@ -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,
)
\ No newline at end of file
diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt
index 6861d18b0f..f3f304b7ff 100644
--- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt
+++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt
@@ -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, txHistory: List): SendUiState =
- recipientListStateConverter.convert(
- wallets = wallets,
- txHistory = txHistory,
+ fun onLoadedWalletsList(wallets: List): SendUiState {
+ val state = currentStateProvider()
+ return state.copy(
+ recipientState = state.recipientState?.copy(
+ wallets = recipientWalletListStateConverter.convert(wallets),
+ ),
)
+ }
+
+ fun onLoadedHistoryList(txHistory: List): 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
diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientListConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientHistoryListConverter.kt
similarity index 71%
rename from features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientListConverter.kt
rename to features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientHistoryListConverter.kt
index 046c7a153d..f6ccb6ba98 100644
--- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientListConverter.kt
+++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientHistoryListConverter.kt
@@ -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,
+internal class SendRecipientHistoryListConverter(
private val cryptoCurrencyStatusProvider: Provider,
-) {
+) : Converter, ImmutableList> {
- fun convert(wallets: List, txHistory: List): SendUiState {
+ override fun convert(value: List): ImmutableList {
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.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.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(),
diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientStateConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientStateConverter.kt
index dd7db52c45..9b3f770fcf 100644
--- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientStateConverter.kt
+++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientStateConverter.kt
@@ -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),
)
}
diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientWalletListConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientWalletListConverter.kt
new file mode 100644
index 0000000000..855e5d3a3b
--- /dev/null
+++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientWalletListConverter.kt
@@ -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, PersistentList> {
+ override fun convert(value: List): PersistentList {
+ return value.filterWallets().ifEmpty {
+ emptyListState(WALLET_KEY_TAG, WALLET_DEFAULT_COUNT)
+ }
+ }
+
+ private fun List.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()
+}
\ No newline at end of file
diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/utils/RecentListUtils.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/utils/RecentListUtils.kt
new file mode 100644
index 0000000000..c2d5ad9739
--- /dev/null
+++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/utils/RecentListUtils.kt
@@ -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()
\ No newline at end of file
diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/recipient/ListItemWithIcon.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/recipient/ListItemWithIcon.kt
index c33885f290..9ef9f60a21 100644
--- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/recipient/ListItemWithIcon.kt
+++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/recipient/ListItemWithIcon.kt
@@ -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(
@@ -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
\ No newline at end of file
diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/recipient/SendRecipientContent.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/recipient/SendRecipientContent.kt
index eb9b2b544a..f728ff97cb 100644
--- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/recipient/SendRecipientContent.kt
+++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/recipient/SendRecipientContent.kt
@@ -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,
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())
+ }
+ }
}
\ No newline at end of file
diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt
index c92d4b895d..2a3112371e 100644
--- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt
+++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt
@@ -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> {
- 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> {
- 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,
)
}