From 96632ac507e0627b74cc1106c140d5e01eb68173 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 17 May 2024 18:36:24 +0500 Subject: [PATCH 01/21] Updated on 2026-08-14 --- .../send/impl/presentation/viewmodel/SendViewModel.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 fc9c9af998..a74e2df9ab 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 @@ -409,8 +409,8 @@ internal class SendViewModel @Inject constructor( .orEmpty() }.onSuccess { result -> combine(*result.toTypedArray()) { it } - .onEach { - userWallets = it.filterNotNull().toList() + .onEach { wallets -> + userWallets = wallets.filter { it.address.isNotBlank() }.toList() uiState = stateFactory.onLoadedWalletsList(wallets = userWallets) } .flowOn(dispatchers.main) @@ -421,7 +421,7 @@ internal class SendViewModel @Inject constructor( } } - private suspend fun List.toAvailableWallets(): List> = + private suspend fun List.toAvailableWallets(): List> = filterNot { it.walletId == userWalletId || it.isLocked } .mapNotNull { wallet -> val status = if (!wallet.isMultiCurrency) { From 047cdd4924f98388d7216618546fcb1dc3b3bbac Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 17 May 2024 18:36:41 +0500 Subject: [PATCH 02/21] Updated on 2026-08-14 --- gradle/dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 4ed044e049..a202e204d9 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -86,7 +86,7 @@ markdown = "0.7.2" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "release-app_5.10-632" +tangemBlockchainSdk = "release-app_5.10-636" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "release-app_5.10-353" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From c04abfe0bbc22e59bc87687208e8313e3d11c7b6 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 20 May 2024 18:08:47 +0500 Subject: [PATCH 03/21] Updated on 2026-08-14 --- .../ui/components/fields/AmountTextField.kt | 60 ++++++++++++------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt b/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt index 521245b940..2713162e91 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt @@ -28,6 +28,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import com.tangem.core.ui.components.fields.visualtransformations.AmountVisualTransformation import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.utils.* +import java.math.BigDecimal import java.text.DecimalFormat /** @@ -123,32 +124,49 @@ fun AmountTextField( private fun prepareEnter(oldValue: String, newValue: String, decimalFormat: DecimalFormat, decimals: Int): String { val decimalSymbol = decimalFormat.decimalFormatSymbols.decimalSeparator return if (decimalFormat.isValidSymbols(newValue)) { - val parsedValue = newValue.parseBigDecimalOrNull()?.toPlainString() - ?: if (newValue.isBlank()) "" else oldValue - val replacedWithSymbol = if (parsedValue.findLast { it != decimalSymbol } != null) { - when { - parsedValue.findLast { it == COMMA_SEPARATOR } != null -> { - parsedValue.replace(COMMA_SEPARATOR, decimalSymbol) - } - parsedValue.findLast { it == POINT_SEPARATOR } != null -> { - parsedValue.replace(POINT_SEPARATOR, decimalSymbol) - } - else -> parsedValue - } - } else { - parsedValue - } - val joinedSymbol = if (newValue.endsWith(COMMA_SEPARATOR) || newValue.endsWith(POINT_SEPARATOR)) { - replacedWithSymbol.plus(decimalSymbol) - } else { - replacedWithSymbol - } - decimalFormat.getValidatedNumberWithFixedDecimals(joinedSymbol, decimals) + val parsedDecimal = newValue.parseBigDecimalOrNull() + val parsedValue = parsedDecimal?.toPlainString() ?: if (newValue.isBlank()) "" else oldValue + + val replacedWithSymbol = parsedValue.replaceDecimalSymbol(decimalSymbol) + val joinedSymbol = replacedWithSymbol.preserveDecimalSymbol(newValue, decimalSymbol) + val withPreservedZeros = joinedSymbol.preserveTrailingZeros(newValue, parsedDecimal, decimalSymbol) + decimalFormat.getValidatedNumberWithFixedDecimals(withPreservedZeros, decimals) } else { oldValue } } +private fun String.replaceDecimalSymbol(decimalSymbol: Char) = if (this.findLast { it != decimalSymbol } != null) { + when { + this.findLast { it == COMMA_SEPARATOR } != null -> { + this.replace(COMMA_SEPARATOR, decimalSymbol) + } + this.findLast { it == POINT_SEPARATOR } != null -> { + this.replace(POINT_SEPARATOR, decimalSymbol) + } + else -> this + } +} else { + this +} + +private fun String.preserveDecimalSymbol(newValue: String, decimalSymbol: Char) = if ( + newValue.endsWith(COMMA_SEPARATOR) || newValue.endsWith(POINT_SEPARATOR) +) { + this.plus(decimalSymbol) +} else { + this +} + +private fun String.preserveTrailingZeros(newValue: String, parsedDecimal: BigDecimal?, decimalSymbol: Char): String { + val trailingZeros = newValue.split(decimalSymbol).getOrNull(1)?.takeLastWhile { it == '0' }.orEmpty() + return if (parsedDecimal?.scale() == 0 && trailingZeros.isNotEmpty()) { + "$this$decimalSymbol$trailingZeros" + } else { + this.plus(trailingZeros) + } +} + private fun DecimalFormat.isValidSymbols(text: String): Boolean { return checkDecimalSeparatorDuplicate(text) } From 8f5a03e936ff47def4104e8cefe9ef041e31095d Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 20 May 2024 18:10:00 +0500 Subject: [PATCH 04/21] Updated on 2026-08-14 --- .../presentation/state/SendStateFactory.kt | 8 +++++ .../impl/presentation/state/SendUiState.kt | 1 - .../amount/SendAmountReduceToConverter.kt | 3 -- .../confirm/SendConfirmStateConverter.kt | 1 - .../state/confirm/SendNotificationFactory.kt | 6 ++-- .../presentation/state/fee/FeeCalculation.kt | 36 +++++-------------- .../presentation/viewmodel/SendViewModel.kt | 2 +- 7 files changed, 21 insertions(+), 36 deletions(-) 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 631cbdf100..f2ea95f678 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 @@ -296,6 +296,7 @@ internal class SendStateFactory( balance = balance, amountValue = amountValue, feeValue = feeValue, + reduceAmountBy = state.sendState?.reduceAmountBy, ), ) } @@ -331,6 +332,12 @@ internal class SendStateFactory( fun getSendNotificationState(notifications: ImmutableList): SendUiState { val state = currentStateProvider() val sendState = state.sendState ?: return state + val reducedBy = sendState.reduceAmountBy.takeIf { + notifications.none { + it is SendNotification.Error.ExistentialDeposit || + it is SendNotification.Error.TransactionLimitError + } + } return state.copy( sendState = sendState.copy( isPrimaryButtonEnabled = isPrimaryButtonEnabled( @@ -338,6 +345,7 @@ internal class SendStateFactory( isSending = sendState.isSending, notifications = notifications, ), + reduceAmountBy = reducedBy, notifications = notifications, showTapHelp = sendState.showTapHelp && notifications.isEmpty(), ), diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt index 84adf93793..c7aa7ddff0 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt @@ -142,7 +142,6 @@ internal sealed class SendStates { val txUrl: String, val ignoreAmountReduce: Boolean, val reduceAmountBy: BigDecimal?, - val reduceAmountTo: BigDecimal?, val isFromConfirmation: Boolean, val showTapHelp: Boolean, val notifications: ImmutableList, diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountReduceToConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountReduceToConverter.kt index 5b40022bbb..76fd7e2654 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountReduceToConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountReduceToConverter.kt @@ -38,9 +38,6 @@ internal class SendAmountReduceToConverter( val isZero = if (amountTextField.isFiatValue) decimalFiatValue.isNullOrZero() else value.isZero() return state.copyWrapped( isEditState = isEditState, - sendState = state.sendState?.copy( - reduceAmountBy = value, - ), amountState = amountState.copy( isPrimaryButtonEnabled = !isExceedBalance && !isZero, amountTextField = amountTextField.copy( diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendConfirmStateConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendConfirmStateConverter.kt index 6e6e67132b..254a979807 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendConfirmStateConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendConfirmStateConverter.kt @@ -17,7 +17,6 @@ internal class SendConfirmStateConverter( txUrl = "", ignoreAmountReduce = false, reduceAmountBy = null, - reduceAmountTo = null, isFromConfirmation = true, showTapHelp = isTapHelpPreviewEnabledProvider(), notifications = persistentListOf(), diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt index ad764dbb71..f6a100ff4e 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt @@ -60,18 +60,20 @@ internal class SendNotificationFactory( val amountValue = amountState.amountTextField.cryptoAmount.value ?: BigDecimal.ZERO val feeValue = feeState.fee?.amount?.value ?: BigDecimal.ZERO + val reduceAmountBy = sendState.reduceAmountBy ?: BigDecimal.ZERO val isFeeCoverage = checkFeeCoverage( isSubtractAvailable = isSubtractAvailableProvider(), balance = balance, amountValue = amountValue, feeValue = feeValue, + reduceAmountBy = reduceAmountBy, ) val sendingAmount = checkAndCalculateSubtractedAmount( isAmountSubtractAvailable = isFeeCoverage, cryptoCurrencyStatus = cryptoCurrencyStatusProvider(), amountValue = amountValue, feeValue = feeValue, - reduceAmountBy = sendState.reduceAmountBy, + reduceAmountBy = reduceAmountBy, ) buildList { // errors @@ -211,7 +213,7 @@ internal class SendNotificationFactory( ), onConfirmClick = { clickIntents.onAmountReduceClick( - reduceAmountBy = currencyDeposit, + reduceAmountBy = currencyDeposit.minus(diff), clazz = SendNotification.Error.ExistentialDeposit::class.java, ) }, diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeCalculation.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeCalculation.kt index a8a93bcd32..b500af1923 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeCalculation.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeCalculation.kt @@ -16,7 +16,7 @@ internal fun checkAndCalculateSubtractedAmount( cryptoCurrencyStatus: CryptoCurrencyStatus, amountValue: BigDecimal, feeValue: BigDecimal, - reduceAmountBy: BigDecimal?, + reduceAmountBy: BigDecimal, ): BigDecimal { val balance = cryptoCurrencyStatus.value.amount ?: return amountValue val isFeeCoverage = checkFeeCoverage( @@ -24,17 +24,12 @@ internal fun checkAndCalculateSubtractedAmount( balance = balance, amountValue = amountValue, feeValue = feeValue, + reduceAmountBy = reduceAmountBy, ) - val subtractedAmount = calculateSubtractedAmount( - isFeeCoverage = isFeeCoverage, - cryptoCurrencyStatus = cryptoCurrencyStatus, - amountValue = amountValue, - feeValue = feeValue, - ) - return if (reduceAmountBy != null) { - subtractedAmount.minus(reduceAmountBy) + return if (isFeeCoverage) { + balance.minus(reduceAmountBy).minus(feeValue) } else { - subtractedAmount + amountValue.minus(reduceAmountBy) } } @@ -46,26 +41,11 @@ internal fun checkFeeCoverage( balance: BigDecimal, amountValue: BigDecimal, feeValue: BigDecimal, + reduceAmountBy: BigDecimal?, ): Boolean { if (!isSubtractAvailable) return false - return balance < amountValue + feeValue && balance > feeValue && balance >= amountValue -} - -/** - * Calculates subtracted amount - */ -private fun calculateSubtractedAmount( - isFeeCoverage: Boolean, - cryptoCurrencyStatus: CryptoCurrencyStatus, - amountValue: BigDecimal, - feeValue: BigDecimal, -): BigDecimal { - val balance = cryptoCurrencyStatus.value.amount ?: return amountValue - return if (isFeeCoverage) { - minOf(amountValue, balance.minus(feeValue)) - } else { - amountValue - } + val amountWithReduced = (reduceAmountBy ?: BigDecimal.ZERO) + amountValue + return balance < amountWithReduced + feeValue && balance > feeValue && balance >= amountWithReduced } /** 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 a74e2df9ab..078ff442a9 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 @@ -823,7 +823,7 @@ internal class SendViewModel @Inject constructor( cryptoCurrencyStatus = cryptoCurrencyStatus, amountValue = amountValue, feeValue = feeValue, - reduceAmountBy = uiState.sendState?.reduceAmountBy, + reduceAmountBy = uiState.sendState?.reduceAmountBy ?: BigDecimal.ZERO, ) viewModelScope.launch(dispatchers.main) { From dd45e6b4d5509095c0e2ffa8328c3977d4d1c95e Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 20 May 2024 20:05:02 +0500 Subject: [PATCH 05/21] Updated on 2026-08-14 --- .../tokens/GetNetworkAddressesUseCase.kt | 17 ++++++++++------- .../SendRecipientWalletListConverter.kt | 2 ++ .../presentation/viewmodel/SendViewModel.kt | 18 ++++++++++-------- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetNetworkAddressesUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetNetworkAddressesUseCase.kt index 0cc768a333..aea1cad8eb 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetNetworkAddressesUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetNetworkAddressesUseCase.kt @@ -11,14 +11,17 @@ class GetNetworkAddressesUseCase( internal val networksRepository: NetworksRepository, ) { - operator fun invoke(userWalletId: UserWalletId, network: Network): Flow = + operator fun invoke(userWalletId: UserWalletId, network: Network): Flow> = networksRepository.getNetworkStatusesUpdates(userWalletId, setOf(network)) .map { networkStatuses -> - when (val networkStatus = networkStatuses.singleOrNull { it.network.id == network.id }?.value) { - is NetworkStatus.NoAccount -> networkStatus.address.defaultAddress.value - is NetworkStatus.Unreachable -> networkStatus.address?.defaultAddress?.value.orEmpty() - is NetworkStatus.Verified -> networkStatus.address.defaultAddress.value - else -> "" - } + networkStatuses.filter { it.network.id == network.id } + .map { networkStatus -> + when (val status = networkStatus.value) { + is NetworkStatus.NoAccount -> status.address.defaultAddress.value + is NetworkStatus.Unreachable -> status.address?.defaultAddress?.value.orEmpty() + is NetworkStatus.Verified -> status.address.defaultAddress.value + else -> "" + } + } } } \ No newline at end of file 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 index 6b92680baf..dae1452696 100644 --- 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 @@ -9,6 +9,7 @@ import com.tangem.features.send.impl.presentation.state.recipient.utils.emptyLis import com.tangem.utils.converter.Converter import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.toPersistentList +import kotlinx.coroutines.flow.filter internal class SendRecipientWalletListConverter : Converter, PersistentList> { @@ -21,6 +22,7 @@ internal class SendRecipientWalletListConverter : private fun List.filterWallets(): PersistentList { var walletsCounter = 0 return this.filterNotNull() + .filter { it.address.isNotBlank() } .groupBy { item -> item.name } .values.map { it.mapIndexed { index, item -> 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 078ff442a9..ff49afe1c3 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 @@ -410,7 +410,7 @@ internal class SendViewModel @Inject constructor( }.onSuccess { result -> combine(*result.toTypedArray()) { it } .onEach { wallets -> - userWallets = wallets.filter { it.address.isNotBlank() }.toList() + userWallets = wallets.flatMap { it }.toList() uiState = stateFactory.onLoadedWalletsList(wallets = userWallets) } .flowOn(dispatchers.main) @@ -421,7 +421,7 @@ internal class SendViewModel @Inject constructor( } } - private suspend fun List.toAvailableWallets(): List> = + private suspend fun List.toAvailableWallets(): List>> = filterNot { it.walletId == userWalletId || it.isLocked } .mapNotNull { wallet -> val status = if (!wallet.isMultiCurrency) { @@ -435,12 +435,14 @@ internal class SendViewModel @Inject constructor( } else { getNetworkAddressesUseCase(wallet.walletId, cryptoCurrency.network) } - status?.map { address -> - AvailableWallet( - name = wallet.name, - address = address, - userWalletId = wallet.walletId, - ) + status?.map { addresses -> + addresses.map { address -> + AvailableWallet( + name = wallet.name, + address = address, + userWalletId = wallet.walletId, + ) + } } } From 01e2cfec97bc83202b613b7cf0816d69fa9dfc4c Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 21 May 2024 16:53:21 +0500 Subject: [PATCH 06/21] Updated on 2026-08-14 --- .../impl/presentation/viewmodel/SendViewModel.kt | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) 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 ff49afe1c3..db5be852ce 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 @@ -376,8 +376,11 @@ internal class SendViewModel @Inject constructor( feeCryptoCurrencyStatus = feeCurrencyStatus subscribeOnQRScannerResult() when { - uiState.sendState?.isSuccess == true -> { - stateRouter.showSend() + uiState.sendState?.isSuccess != true -> { + uiState = stateFactory.getReadyState() + getWalletsAndRecent() + stateRouter.showRecipient() + updateNotifications() } transactionId != null && amount != null && destinationAddress != null -> { loadFee() @@ -385,12 +388,6 @@ internal class SendViewModel @Inject constructor( stateRouter.showSend() updateNotifications() } - else -> { - uiState = stateFactory.getReadyState() - getWalletsAndRecent() - stateRouter.showRecipient() - updateNotifications() - } } } From 144bbc3be56263b0b69ccd62697ba5bf39062ff0 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 21 May 2024 16:55:42 +0500 Subject: [PATCH 07/21] Updated on 2026-08-14 --- .../impl/presentation/ui/SendNavigationButtons.kt | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt index 93cc9067fd..3f5d13ae28 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt @@ -4,7 +4,10 @@ import androidx.compose.animation.* import androidx.compose.animation.core.tween import androidx.compose.foundation.background import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Icon import androidx.compose.material3.Text @@ -35,6 +38,7 @@ import com.tangem.features.send.impl.presentation.state.SendUiCurrentScreen import com.tangem.features.send.impl.presentation.state.SendUiState import com.tangem.features.send.impl.presentation.state.SendUiStateType import com.tangem.features.send.impl.presentation.utils.getFiatFormatted +import com.tangem.features.send.impl.presentation.utils.getFiatString @Composable internal fun SendNavigationButtons( @@ -180,12 +184,12 @@ private fun SendingText( currencySymbol = feeState.appCurrency.symbol, currencyCode = feeState.appCurrency.code, ) - val feeValue = getFiatFormatted( + val feeValue = getFiatString( value = feeState.fee?.amount?.value, - currencySymbol = feeState.appCurrency.symbol, - currencyCode = feeState.appCurrency.code, + rate = feeState.rate, + appCurrency = feeState.appCurrency, ) - val textResource = remember(sendingValue, feeValue) { + val textResource = remember(uiState) { resourceReference( id = R.string.send_summary_transaction_description, formatArgs = wrappedList(sendingValue, feeValue), From c6045568af3707eaee16f67282830c739d8694a7 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 21 May 2024 16:57:16 +0500 Subject: [PATCH 08/21] Updated on 2026-08-14 --- .../presentation/viewmodel/SendViewModel.kt | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) 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 db5be852ce..921c3124e0 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 @@ -58,7 +58,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.coroutines.delay import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import timber.log.Timber @@ -73,7 +72,6 @@ internal class SendViewModel @Inject constructor( private val dispatchers: CoroutineDispatcherProvider, private val getUserWalletUseCase: GetUserWalletUseCase, private val getCurrencyStatusUpdatesUseCase: GetCurrencyStatusUpdatesUseCase, - private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase, private val getNetworkCoinStatusUseCase: GetNetworkCoinStatusUseCase, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val getWalletsUseCase: GetWalletsUseCase, @@ -96,6 +94,8 @@ internal class SendViewModel @Inject constructor( private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase, private val listenToQrScanningUseCase: ListenToQrScanningUseCase, private val addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase, + private val updateDelayedCurrencyStatusUseCase: UpdateDelayedNetworkStatusUseCase, + private val fetchPendingTransactionsUseCase: FetchPendingTransactionsUseCase, currencyChecksRepository: CurrencyChecksRepository, isFeeApproximateUseCase: IsFeeApproximateUseCase, validateWalletMemoUseCase: ValidateWalletMemoUseCase, @@ -866,8 +866,8 @@ internal class SendViewModel @Inject constructor( ifRight = { uiState = stateFactory.getSendingStateUpdate(isSending = false) updateTransactionStatus(txData) - scheduleBalanceUpdate() addTokenToWalletIfNeeded() + scheduleUpdates() sendScreenAnalyticSender.sendTransaction() }, ) @@ -894,12 +894,17 @@ internal class SendViewModel @Inject constructor( uiState = stateFactory.getTransactionSendState(txData, txUrl) } - private fun scheduleBalanceUpdate() { - viewModelScope.launch(dispatchers.io) { - delay(BALANCE_UPDATE_DELAY) - fetchCurrencyStatusUseCase.invoke( - userWalletId = userWalletId, - id = cryptoCurrency.id, + private fun scheduleUpdates() { + viewModelScope.launch(dispatchers.main) { + // we should update network to find pending tx after 1 sec + fetchPendingTransactionsUseCase(userWallet.walletId, setOf(cryptoCurrency.network)) + } + viewModelScope.launch(dispatchers.main) { + // we should update network for new balance + updateDelayedCurrencyStatusUseCase( + userWalletId = userWallet.walletId, + network = cryptoCurrency.network, + delayMillis = BALANCE_UPDATE_DELAY, refresh = true, ) } From 4f2135e34861e273fd82b50e41ba3ebed2398e10 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 21 May 2024 18:58:30 +0500 Subject: [PATCH 09/21] Updated on 2026-08-14 --- .../repository/DefaultNetworksRepository.kt | 25 +++++++++++-- .../tokens/GetNetworkAddressesUseCase.kt | 19 ++-------- .../tokens/repository/NetworksRepository.kt | 2 + .../SendRecipientWalletListConverter.kt | 31 +++++++++------- .../presentation/viewmodel/SendViewModel.kt | 37 ++++++++----------- 5 files changed, 61 insertions(+), 53 deletions(-) diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt index f25a65291c..57f55c1aab 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt @@ -1,6 +1,7 @@ package com.tangem.data.tokens.repository import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.common.address.AddressType import com.tangem.data.common.cache.CacheRegistry import com.tangem.data.tokens.utils.CardCryptoCurrenciesFactory import com.tangem.data.tokens.utils.NetworkStatusFactory @@ -77,6 +78,21 @@ internal class DefaultNetworksRepository( return blockchain == Blockchain.Aptos } + override suspend fun getNetworkAddresses(userWalletId: UserWalletId, network: Network): List { + // Get list of currencies matching [network] + val currencies = getCurrencies(userWalletId) + .filter { currency -> network.id == currency.network.id } + + // There is no currencies matching given [networks] in [userWalletId] + if (currencies.toList().isEmpty()) return emptyList() + + return currencies.toList().map { currency -> + walletManagersFacade.getAddresses(userWalletId, currency.network) + .firstOrNull { it.type == AddressType.Default } + ?.value.orEmpty() + } + } + private suspend fun fetchNetworksStatusesIfCacheExpired( userWalletId: UserWalletId, networks: Set, @@ -174,11 +190,16 @@ internal class DefaultNetworksRepository( } private suspend fun getCurrencies(userWalletId: UserWalletId, networks: Set): Sequence { + val currencies = getCurrencies(userWalletId) + return currencies.filter { networks.contains(it.network) } + } + + private suspend fun getCurrencies(userWalletId: UserWalletId): Sequence { val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) { "Unable to find user wallet with provided ID: $userWalletId" } - val currencies = if (userWallet.isMultiCurrency) { + return if (userWallet.isMultiCurrency) { val response = requireNotNull(userTokensStore.getSyncOrNull(userWalletId)) { "Unable to find tokens response for user wallet with provided ID: $userWalletId" } @@ -194,8 +215,6 @@ internal class DefaultNetworksRepository( sequenceOf(currency) } } - - return currencies.filter { networks.contains(it.network) } } private suspend fun invalidateCacheKeyIfNeeded( diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetNetworkAddressesUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetNetworkAddressesUseCase.kt index aea1cad8eb..0fa37a5a07 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetNetworkAddressesUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetNetworkAddressesUseCase.kt @@ -1,27 +1,14 @@ package com.tangem.domain.tokens import com.tangem.domain.tokens.model.Network -import com.tangem.domain.tokens.model.NetworkStatus import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.wallets.models.UserWalletId -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map class GetNetworkAddressesUseCase( internal val networksRepository: NetworksRepository, ) { - operator fun invoke(userWalletId: UserWalletId, network: Network): Flow> = - networksRepository.getNetworkStatusesUpdates(userWalletId, setOf(network)) - .map { networkStatuses -> - networkStatuses.filter { it.network.id == network.id } - .map { networkStatus -> - when (val status = networkStatus.value) { - is NetworkStatus.NoAccount -> status.address.defaultAddress.value - is NetworkStatus.Unreachable -> status.address?.defaultAddress?.value.orEmpty() - is NetworkStatus.Verified -> status.address.defaultAddress.value - else -> "" - } - } - } + suspend fun invokeSync(userWalletId: UserWalletId, network: Network): List { + return networksRepository.getNetworkAddresses(userWalletId, network) + } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/NetworksRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/NetworksRepository.kt index 6ec3fbb11a..ebece1356e 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/NetworksRepository.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/NetworksRepository.kt @@ -45,4 +45,6 @@ interface NetworksRepository { ): Set fun isNeedToCreateAccountWithoutReserve(network: Network): Boolean + + suspend fun getNetworkAddresses(userWalletId: UserWalletId, network: Network): List } \ No newline at end of file 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 index dae1452696..35d56f9846 100644 --- 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 @@ -9,7 +9,6 @@ import com.tangem.features.send.impl.presentation.state.recipient.utils.emptyLis import com.tangem.utils.converter.Converter import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.toPersistentList -import kotlinx.coroutines.flow.filter internal class SendRecipientWalletListConverter : Converter, PersistentList> { @@ -24,19 +23,25 @@ internal class SendRecipientWalletListConverter : return this.filterNotNull() .filter { it.address.isNotBlank() } .groupBy { item -> item.name } - .values.map { - it.mapIndexed { index, item -> - val name = if (it.size > 1) { - "${item.name} ${index.inc()}" - } else { - item.name + .values.map { wallets -> + val groupedByWallet = wallets.groupBy { it.userWalletId } + var i = 0 + groupedByWallet + .flatMap { item -> + item.value.map { wallet -> + val name = if (groupedByWallet.size > 1) { + "${wallet.name} ${++i}" + } else { + wallet.name + } + + SendRecipientListContent( + id = "${WALLET_KEY_TAG}${walletsCounter++}", + title = TextReference.Str(wallet.address), + subtitle = TextReference.Str(name), + ) + } } - SendRecipientListContent( - id = "${WALLET_KEY_TAG}${walletsCounter++}", - title = TextReference.Str(item.address), - subtitle = TextReference.Str(name), - ) - } } .flatten() .toPersistentList() 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 921c3124e0..65d93773c7 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 @@ -4,6 +4,7 @@ import android.os.SystemClock import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue +import androidx.compose.ui.util.fastDistinctBy import androidx.lifecycle.* import arrow.core.Either import arrow.core.getOrElse @@ -271,6 +272,7 @@ internal class SendViewModel @Inject constructor( .saveIn(balanceHidingJobHolder) } + // TODO [REDACTED_JIRA] private fun getCurrenciesStatusUpdates(isSingleWalletWithToken: Boolean, isMultiCurrency: Boolean) { if (cryptoCurrency is CryptoCurrency.Coin) { getCurrencyStatusUpdates( @@ -405,43 +407,36 @@ internal class SendViewModel @Inject constructor( ?.toAvailableWallets() .orEmpty() }.onSuccess { result -> - combine(*result.toTypedArray()) { it } - .onEach { wallets -> - userWallets = wallets.flatMap { it }.toList() - uiState = stateFactory.onLoadedWalletsList(wallets = userWallets) - } - .flowOn(dispatchers.main) - .launchIn(viewModelScope) + userWallets = result + uiState = stateFactory.onLoadedWalletsList(wallets = userWallets) }.onFailure { uiState = stateFactory.onLoadedWalletsList(wallets = emptyList()) } } } - private suspend fun List.toAvailableWallets(): List>> = + private suspend fun List.toAvailableWallets(): List = filterNot { it.walletId == userWalletId || it.isLocked } .mapNotNull { wallet -> - val status = if (!wallet.isMultiCurrency) { + val addresses = if (!wallet.isMultiCurrency) { getCryptoCurrencyUseCase(wallet.walletId).getOrNull()?.let { if (it.network.id == cryptoCurrency.network.id) { - getNetworkAddressesUseCase(wallet.walletId, it.network) + getNetworkAddressesUseCase.invokeSync(wallet.walletId, it.network) } else { null } } } else { - getNetworkAddressesUseCase(wallet.walletId, cryptoCurrency.network) + getNetworkAddressesUseCase.invokeSync(wallet.walletId, cryptoCurrency.network) } - status?.map { addresses -> - addresses.map { address -> - AvailableWallet( - name = wallet.name, - address = address, - userWalletId = wallet.walletId, - ) - } - } - } + addresses?.map { address -> + AvailableWallet( + name = wallet.name, + address = address, + userWalletId = wallet.walletId, + ) + }?.fastDistinctBy { it.address } + }.flatten() private suspend fun getTxHistory() { val txHistoryList = getFixedTxHistoryItemsUseCase.getSync( From 982766e5c663615a3458fcbec753519ddb634dfe Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 21 May 2024 16:53:21 +0500 Subject: [PATCH 10/21] Updated on 2026-08-14 --- .../impl/presentation/viewmodel/SendViewModel.kt | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) 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 ff49afe1c3..db5be852ce 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 @@ -376,8 +376,11 @@ internal class SendViewModel @Inject constructor( feeCryptoCurrencyStatus = feeCurrencyStatus subscribeOnQRScannerResult() when { - uiState.sendState?.isSuccess == true -> { - stateRouter.showSend() + uiState.sendState?.isSuccess != true -> { + uiState = stateFactory.getReadyState() + getWalletsAndRecent() + stateRouter.showRecipient() + updateNotifications() } transactionId != null && amount != null && destinationAddress != null -> { loadFee() @@ -385,12 +388,6 @@ internal class SendViewModel @Inject constructor( stateRouter.showSend() updateNotifications() } - else -> { - uiState = stateFactory.getReadyState() - getWalletsAndRecent() - stateRouter.showRecipient() - updateNotifications() - } } } From 01a49e405eadc4762814f31f2d0a3ebcf7271143 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 21 May 2024 16:55:42 +0500 Subject: [PATCH 11/21] Updated on 2026-08-14 --- .../impl/presentation/ui/SendNavigationButtons.kt | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt index 93cc9067fd..3f5d13ae28 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt @@ -4,7 +4,10 @@ import androidx.compose.animation.* import androidx.compose.animation.core.tween import androidx.compose.foundation.background import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Icon import androidx.compose.material3.Text @@ -35,6 +38,7 @@ import com.tangem.features.send.impl.presentation.state.SendUiCurrentScreen import com.tangem.features.send.impl.presentation.state.SendUiState import com.tangem.features.send.impl.presentation.state.SendUiStateType import com.tangem.features.send.impl.presentation.utils.getFiatFormatted +import com.tangem.features.send.impl.presentation.utils.getFiatString @Composable internal fun SendNavigationButtons( @@ -180,12 +184,12 @@ private fun SendingText( currencySymbol = feeState.appCurrency.symbol, currencyCode = feeState.appCurrency.code, ) - val feeValue = getFiatFormatted( + val feeValue = getFiatString( value = feeState.fee?.amount?.value, - currencySymbol = feeState.appCurrency.symbol, - currencyCode = feeState.appCurrency.code, + rate = feeState.rate, + appCurrency = feeState.appCurrency, ) - val textResource = remember(sendingValue, feeValue) { + val textResource = remember(uiState) { resourceReference( id = R.string.send_summary_transaction_description, formatArgs = wrappedList(sendingValue, feeValue), From e63ca27981818d44f1266a17bcd219c226e635eb Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 21 May 2024 16:57:16 +0500 Subject: [PATCH 12/21] Updated on 2026-08-14 --- .../presentation/viewmodel/SendViewModel.kt | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) 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 db5be852ce..921c3124e0 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 @@ -58,7 +58,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.coroutines.delay import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import timber.log.Timber @@ -73,7 +72,6 @@ internal class SendViewModel @Inject constructor( private val dispatchers: CoroutineDispatcherProvider, private val getUserWalletUseCase: GetUserWalletUseCase, private val getCurrencyStatusUpdatesUseCase: GetCurrencyStatusUpdatesUseCase, - private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase, private val getNetworkCoinStatusUseCase: GetNetworkCoinStatusUseCase, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val getWalletsUseCase: GetWalletsUseCase, @@ -96,6 +94,8 @@ internal class SendViewModel @Inject constructor( private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase, private val listenToQrScanningUseCase: ListenToQrScanningUseCase, private val addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase, + private val updateDelayedCurrencyStatusUseCase: UpdateDelayedNetworkStatusUseCase, + private val fetchPendingTransactionsUseCase: FetchPendingTransactionsUseCase, currencyChecksRepository: CurrencyChecksRepository, isFeeApproximateUseCase: IsFeeApproximateUseCase, validateWalletMemoUseCase: ValidateWalletMemoUseCase, @@ -866,8 +866,8 @@ internal class SendViewModel @Inject constructor( ifRight = { uiState = stateFactory.getSendingStateUpdate(isSending = false) updateTransactionStatus(txData) - scheduleBalanceUpdate() addTokenToWalletIfNeeded() + scheduleUpdates() sendScreenAnalyticSender.sendTransaction() }, ) @@ -894,12 +894,17 @@ internal class SendViewModel @Inject constructor( uiState = stateFactory.getTransactionSendState(txData, txUrl) } - private fun scheduleBalanceUpdate() { - viewModelScope.launch(dispatchers.io) { - delay(BALANCE_UPDATE_DELAY) - fetchCurrencyStatusUseCase.invoke( - userWalletId = userWalletId, - id = cryptoCurrency.id, + private fun scheduleUpdates() { + viewModelScope.launch(dispatchers.main) { + // we should update network to find pending tx after 1 sec + fetchPendingTransactionsUseCase(userWallet.walletId, setOf(cryptoCurrency.network)) + } + viewModelScope.launch(dispatchers.main) { + // we should update network for new balance + updateDelayedCurrencyStatusUseCase( + userWalletId = userWallet.walletId, + network = cryptoCurrency.network, + delayMillis = BALANCE_UPDATE_DELAY, refresh = true, ) } From 1b4df199307d424a51e7643c72562890b8b978e3 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 21 May 2024 18:58:30 +0500 Subject: [PATCH 13/21] Updated on 2026-08-14 --- .../repository/DefaultNetworksRepository.kt | 25 +++++++++++-- .../tokens/GetNetworkAddressesUseCase.kt | 19 ++-------- .../tokens/repository/NetworksRepository.kt | 2 + .../SendRecipientWalletListConverter.kt | 31 +++++++++------- .../presentation/viewmodel/SendViewModel.kt | 37 ++++++++----------- 5 files changed, 61 insertions(+), 53 deletions(-) diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt index f25a65291c..57f55c1aab 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt @@ -1,6 +1,7 @@ package com.tangem.data.tokens.repository import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.common.address.AddressType import com.tangem.data.common.cache.CacheRegistry import com.tangem.data.tokens.utils.CardCryptoCurrenciesFactory import com.tangem.data.tokens.utils.NetworkStatusFactory @@ -77,6 +78,21 @@ internal class DefaultNetworksRepository( return blockchain == Blockchain.Aptos } + override suspend fun getNetworkAddresses(userWalletId: UserWalletId, network: Network): List { + // Get list of currencies matching [network] + val currencies = getCurrencies(userWalletId) + .filter { currency -> network.id == currency.network.id } + + // There is no currencies matching given [networks] in [userWalletId] + if (currencies.toList().isEmpty()) return emptyList() + + return currencies.toList().map { currency -> + walletManagersFacade.getAddresses(userWalletId, currency.network) + .firstOrNull { it.type == AddressType.Default } + ?.value.orEmpty() + } + } + private suspend fun fetchNetworksStatusesIfCacheExpired( userWalletId: UserWalletId, networks: Set, @@ -174,11 +190,16 @@ internal class DefaultNetworksRepository( } private suspend fun getCurrencies(userWalletId: UserWalletId, networks: Set): Sequence { + val currencies = getCurrencies(userWalletId) + return currencies.filter { networks.contains(it.network) } + } + + private suspend fun getCurrencies(userWalletId: UserWalletId): Sequence { val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) { "Unable to find user wallet with provided ID: $userWalletId" } - val currencies = if (userWallet.isMultiCurrency) { + return if (userWallet.isMultiCurrency) { val response = requireNotNull(userTokensStore.getSyncOrNull(userWalletId)) { "Unable to find tokens response for user wallet with provided ID: $userWalletId" } @@ -194,8 +215,6 @@ internal class DefaultNetworksRepository( sequenceOf(currency) } } - - return currencies.filter { networks.contains(it.network) } } private suspend fun invalidateCacheKeyIfNeeded( diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetNetworkAddressesUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetNetworkAddressesUseCase.kt index aea1cad8eb..0fa37a5a07 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetNetworkAddressesUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetNetworkAddressesUseCase.kt @@ -1,27 +1,14 @@ package com.tangem.domain.tokens import com.tangem.domain.tokens.model.Network -import com.tangem.domain.tokens.model.NetworkStatus import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.wallets.models.UserWalletId -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map class GetNetworkAddressesUseCase( internal val networksRepository: NetworksRepository, ) { - operator fun invoke(userWalletId: UserWalletId, network: Network): Flow> = - networksRepository.getNetworkStatusesUpdates(userWalletId, setOf(network)) - .map { networkStatuses -> - networkStatuses.filter { it.network.id == network.id } - .map { networkStatus -> - when (val status = networkStatus.value) { - is NetworkStatus.NoAccount -> status.address.defaultAddress.value - is NetworkStatus.Unreachable -> status.address?.defaultAddress?.value.orEmpty() - is NetworkStatus.Verified -> status.address.defaultAddress.value - else -> "" - } - } - } + suspend fun invokeSync(userWalletId: UserWalletId, network: Network): List { + return networksRepository.getNetworkAddresses(userWalletId, network) + } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/NetworksRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/NetworksRepository.kt index 6ec3fbb11a..ebece1356e 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/NetworksRepository.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/NetworksRepository.kt @@ -45,4 +45,6 @@ interface NetworksRepository { ): Set fun isNeedToCreateAccountWithoutReserve(network: Network): Boolean + + suspend fun getNetworkAddresses(userWalletId: UserWalletId, network: Network): List } \ No newline at end of file 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 index dae1452696..35d56f9846 100644 --- 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 @@ -9,7 +9,6 @@ import com.tangem.features.send.impl.presentation.state.recipient.utils.emptyLis import com.tangem.utils.converter.Converter import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.toPersistentList -import kotlinx.coroutines.flow.filter internal class SendRecipientWalletListConverter : Converter, PersistentList> { @@ -24,19 +23,25 @@ internal class SendRecipientWalletListConverter : return this.filterNotNull() .filter { it.address.isNotBlank() } .groupBy { item -> item.name } - .values.map { - it.mapIndexed { index, item -> - val name = if (it.size > 1) { - "${item.name} ${index.inc()}" - } else { - item.name + .values.map { wallets -> + val groupedByWallet = wallets.groupBy { it.userWalletId } + var i = 0 + groupedByWallet + .flatMap { item -> + item.value.map { wallet -> + val name = if (groupedByWallet.size > 1) { + "${wallet.name} ${++i}" + } else { + wallet.name + } + + SendRecipientListContent( + id = "${WALLET_KEY_TAG}${walletsCounter++}", + title = TextReference.Str(wallet.address), + subtitle = TextReference.Str(name), + ) + } } - SendRecipientListContent( - id = "${WALLET_KEY_TAG}${walletsCounter++}", - title = TextReference.Str(item.address), - subtitle = TextReference.Str(name), - ) - } } .flatten() .toPersistentList() 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 921c3124e0..65d93773c7 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 @@ -4,6 +4,7 @@ import android.os.SystemClock import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue +import androidx.compose.ui.util.fastDistinctBy import androidx.lifecycle.* import arrow.core.Either import arrow.core.getOrElse @@ -271,6 +272,7 @@ internal class SendViewModel @Inject constructor( .saveIn(balanceHidingJobHolder) } + // TODO [REDACTED_JIRA] private fun getCurrenciesStatusUpdates(isSingleWalletWithToken: Boolean, isMultiCurrency: Boolean) { if (cryptoCurrency is CryptoCurrency.Coin) { getCurrencyStatusUpdates( @@ -405,43 +407,36 @@ internal class SendViewModel @Inject constructor( ?.toAvailableWallets() .orEmpty() }.onSuccess { result -> - combine(*result.toTypedArray()) { it } - .onEach { wallets -> - userWallets = wallets.flatMap { it }.toList() - uiState = stateFactory.onLoadedWalletsList(wallets = userWallets) - } - .flowOn(dispatchers.main) - .launchIn(viewModelScope) + userWallets = result + uiState = stateFactory.onLoadedWalletsList(wallets = userWallets) }.onFailure { uiState = stateFactory.onLoadedWalletsList(wallets = emptyList()) } } } - private suspend fun List.toAvailableWallets(): List>> = + private suspend fun List.toAvailableWallets(): List = filterNot { it.walletId == userWalletId || it.isLocked } .mapNotNull { wallet -> - val status = if (!wallet.isMultiCurrency) { + val addresses = if (!wallet.isMultiCurrency) { getCryptoCurrencyUseCase(wallet.walletId).getOrNull()?.let { if (it.network.id == cryptoCurrency.network.id) { - getNetworkAddressesUseCase(wallet.walletId, it.network) + getNetworkAddressesUseCase.invokeSync(wallet.walletId, it.network) } else { null } } } else { - getNetworkAddressesUseCase(wallet.walletId, cryptoCurrency.network) + getNetworkAddressesUseCase.invokeSync(wallet.walletId, cryptoCurrency.network) } - status?.map { addresses -> - addresses.map { address -> - AvailableWallet( - name = wallet.name, - address = address, - userWalletId = wallet.walletId, - ) - } - } - } + addresses?.map { address -> + AvailableWallet( + name = wallet.name, + address = address, + userWalletId = wallet.walletId, + ) + }?.fastDistinctBy { it.address } + }.flatten() private suspend fun getTxHistory() { val txHistoryList = getFixedTxHistoryItemsUseCase.getSync( From 27f3c6afdad0b2d3197a3d7ba564a955af7058bc Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 22 May 2024 15:46:06 +0500 Subject: [PATCH 14/21] Updated on 2026-08-14 --- .../main/java/com/tangem/tap/di/Qualifiers.kt | 2 ++ .../tangem/utils/coroutines/DelayedWork.kt | 7 ++++++ .../utils/di/DelayedWorkCoroutineModule.kt | 23 +++++++++++++++++++ .../presentation/viewmodel/SendViewModel.kt | 9 ++++---- 4 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 core/utils/src/main/java/com/tangem/utils/coroutines/DelayedWork.kt create mode 100644 core/utils/src/main/java/com/tangem/utils/di/DelayedWorkCoroutineModule.kt diff --git a/app/src/main/java/com/tangem/tap/di/Qualifiers.kt b/app/src/main/java/com/tangem/tap/di/Qualifiers.kt index df7fab7779..94ba51a71e 100644 --- a/app/src/main/java/com/tangem/tap/di/Qualifiers.kt +++ b/app/src/main/java/com/tangem/tap/di/Qualifiers.kt @@ -1,8 +1,10 @@ @file:Suppress("Filename") + package com.tangem.tap.di import javax.inject.Qualifier +@Deprecated("Use one in Core Utils") @Qualifier @Retention(AnnotationRetention.BINARY) annotation class DelayedWork \ No newline at end of file diff --git a/core/utils/src/main/java/com/tangem/utils/coroutines/DelayedWork.kt b/core/utils/src/main/java/com/tangem/utils/coroutines/DelayedWork.kt new file mode 100644 index 0000000000..8c822c02ed --- /dev/null +++ b/core/utils/src/main/java/com/tangem/utils/coroutines/DelayedWork.kt @@ -0,0 +1,7 @@ +package com.tangem.utils.coroutines + +import javax.inject.Qualifier + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class DelayedWork \ No newline at end of file diff --git a/core/utils/src/main/java/com/tangem/utils/di/DelayedWorkCoroutineModule.kt b/core/utils/src/main/java/com/tangem/utils/di/DelayedWorkCoroutineModule.kt new file mode 100644 index 0000000000..4f5ad6e74f --- /dev/null +++ b/core/utils/src/main/java/com/tangem/utils/di/DelayedWorkCoroutineModule.kt @@ -0,0 +1,23 @@ +package com.tangem.utils.di + +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import com.tangem.utils.coroutines.DelayedWork +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object DelayedWorkCoroutineModule { + + @Provides + @Singleton + @DelayedWork + fun provideDelayedWorkCoroutineScope(coroutineDispatcherProvider: CoroutineDispatcherProvider): CoroutineScope { + return CoroutineScope(SupervisorJob() + coroutineDispatcherProvider.io) + } +} \ 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 65d93773c7..df163fdfff 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 @@ -56,9 +56,11 @@ import com.tangem.features.send.impl.presentation.state.fee.* import com.tangem.lib.crypto.BlockchainUtils import com.tangem.utils.Provider import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import com.tangem.utils.coroutines.DelayedWork import com.tangem.utils.coroutines.JobHolder import com.tangem.utils.coroutines.saveIn import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import timber.log.Timber @@ -97,6 +99,7 @@ internal class SendViewModel @Inject constructor( private val addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase, private val updateDelayedCurrencyStatusUseCase: UpdateDelayedNetworkStatusUseCase, private val fetchPendingTransactionsUseCase: FetchPendingTransactionsUseCase, + @DelayedWork private val coroutineScope: CoroutineScope, currencyChecksRepository: CurrencyChecksRepository, isFeeApproximateUseCase: IsFeeApproximateUseCase, validateWalletMemoUseCase: ValidateWalletMemoUseCase, @@ -890,11 +893,9 @@ internal class SendViewModel @Inject constructor( } private fun scheduleUpdates() { - viewModelScope.launch(dispatchers.main) { + coroutineScope.launch { // we should update network to find pending tx after 1 sec fetchPendingTransactionsUseCase(userWallet.walletId, setOf(cryptoCurrency.network)) - } - viewModelScope.launch(dispatchers.main) { // we should update network for new balance updateDelayedCurrencyStatusUseCase( userWalletId = userWallet.walletId, @@ -954,7 +955,7 @@ internal class SendViewModel @Inject constructor( private companion object { const val CHECK_FEE_UPDATE_DELAY = 60_000L - const val BALANCE_UPDATE_DELAY = 10_000L + const val BALANCE_UPDATE_DELAY = 11_000L const val RU_LOCALE = "ru" const val EN_LOCALE = "en" From a204ef0bd65fd29c77f0786aee6608f835ae4739 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 22 May 2024 15:46:28 +0500 Subject: [PATCH 15/21] Updated on 2026-08-14 --- .../ui/components/fields/AmountTextField.kt | 8 ++--- .../previewdata/RecipientStatePreviewData.kt | 23 ++++++++++++-- .../ui/recipient/ListItemWithIcon.kt | 15 ++++++---- .../ui/recipient/SendRecipientContent.kt | 30 +++++++++++++++++-- 4 files changed, 62 insertions(+), 14 deletions(-) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt b/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt index 2713162e91..9db1abac3b 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt @@ -160,10 +160,10 @@ private fun String.preserveDecimalSymbol(newValue: String, decimalSymbol: Char) private fun String.preserveTrailingZeros(newValue: String, parsedDecimal: BigDecimal?, decimalSymbol: Char): String { val trailingZeros = newValue.split(decimalSymbol).getOrNull(1)?.takeLastWhile { it == '0' }.orEmpty() - return if (parsedDecimal?.scale() == 0 && trailingZeros.isNotEmpty()) { - "$this$decimalSymbol$trailingZeros" - } else { - this.plus(trailingZeros) + return when { + this.endsWith('0') -> this + parsedDecimal?.scale() == 0 && trailingZeros.isNotEmpty() -> "$this$decimalSymbol$trailingZeros" + else -> this.plus(trailingZeros) } } diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/previewdata/RecipientStatePreviewData.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/previewdata/RecipientStatePreviewData.kt index f3008dc000..65ab1d68bf 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/previewdata/RecipientStatePreviewData.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/previewdata/RecipientStatePreviewData.kt @@ -2,12 +2,25 @@ package com.tangem.features.send.impl.presentation.state.previewdata import androidx.compose.foundation.text.KeyboardOptions import com.tangem.core.ui.extensions.stringReference +import com.tangem.features.send.impl.R +import com.tangem.features.send.impl.presentation.domain.SendRecipientListContent import com.tangem.features.send.impl.presentation.state.SendStates import com.tangem.features.send.impl.presentation.state.fields.SendTextField import kotlinx.collections.immutable.persistentListOf internal object RecipientStatePreviewData { + private val defaultRecentItem = SendRecipientListContent( + id = "sanctus", + title = stringReference("address"), + subtitle = stringReference("0.001 BTC"), + timestamp = stringReference("1.01.1970, 00:00"), + subtitleEndOffset = 0, + subtitleIconRes = R.drawable.ic_arrow_down_24, + isVisible = true, + isLoading = false, + ) + val recipientState = SendStates.RecipientState( addressTextField = SendTextField.RecipientAddress( value = "0x23948239805671983476598176", @@ -19,8 +32,14 @@ internal object RecipientStatePreviewData { error = null, ), memoTextField = null, - recent = persistentListOf(), - wallets = persistentListOf(), + recent = persistentListOf( + defaultRecentItem.copy(id = "1"), + defaultRecentItem.copy(id = "2"), + defaultRecentItem.copy(id = "3"), + ), + wallets = persistentListOf( + defaultRecentItem.copy(id = "4", subtitle = stringReference("Wallet")), + ), network = "Ethereum", isValidating = false, isPrimaryButtonEnabled = true, 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 9ef9f60a21..153a2686a1 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 @@ -95,11 +95,13 @@ private fun ListItemWithIcon( address = title, modifier = Modifier .padding(vertical = TangemTheme.dimens.spacing8) - .size(TangemTheme.dimens.size36) - .clip(RoundedCornerShape(TangemTheme.dimens.radius18)), + .size(TangemTheme.dimens.size40) + .clip(RoundedCornerShape(TangemTheme.dimens.radius20)), ) Column( - modifier = Modifier.padding(start = TangemTheme.dimens.spacing12), + modifier = Modifier + .height(TangemTheme.dimens.size36) + .padding(start = TangemTheme.dimens.spacing12), verticalArrangement = Arrangement.SpaceBetween, ) { EllipsisText( @@ -118,7 +120,8 @@ private fun ListItemWithIcon( tint = TangemTheme.colors.icon.informative, modifier = Modifier .size(TangemTheme.dimens.size16) - .background(TangemTheme.colors.background.tertiary, CircleShape), + .background(TangemTheme.colors.background.tertiary, CircleShape) + .padding(TangemTheme.dimens.spacing2), ) } val (text, offset) = remember(subtitle, info) { @@ -152,11 +155,11 @@ private fun ListItemLoading(modifier: Modifier = Modifier) { CircleShimmer( modifier = Modifier .padding(vertical = TangemTheme.dimens.spacing8) - .size(TangemTheme.dimens.size36), + .size(TangemTheme.dimens.size40), ) Column( modifier = Modifier - .height(TangemTheme.dimens.size32) + .height(TangemTheme.dimens.size36) .padding(start = TangemTheme.dimens.spacing12), verticalArrangement = Arrangement.SpaceBetween, ) { 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 e37a0ced3d..66fca69ea6 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 @@ -17,6 +17,9 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider import com.tangem.common.Strings.STARS import com.tangem.core.ui.components.inputrow.InputRowRecipient import com.tangem.core.ui.extensions.resolveReference @@ -26,6 +29,8 @@ import com.tangem.features.send.impl.presentation.analytics.EnterAddressSource import com.tangem.features.send.impl.presentation.domain.SendRecipientListContent import com.tangem.features.send.impl.presentation.state.SendStates import com.tangem.features.send.impl.presentation.state.fields.SendTextField +import com.tangem.features.send.impl.presentation.state.previewdata.RecipientStatePreviewData +import com.tangem.features.send.impl.presentation.state.previewdata.SendClickIntentsStub import com.tangem.features.send.impl.presentation.ui.common.FooterContainer import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents import kotlinx.collections.immutable.ImmutableList @@ -150,7 +155,7 @@ private fun LazyListScope.listHeaderItem(@StringRes titleRes: Int, isVisible: Bo TangemTheme.dimens.spacing0 to TangemTheme.dimens.spacing8 } val topRadius = if (isFirst) { - TangemTheme.dimens.radius12 + TangemTheme.dimens.radius16 } else { TangemTheme.dimens.radius0 } @@ -170,7 +175,7 @@ private fun LazyListScope.listHeaderItem(@StringRes titleRes: Int, isVisible: Bo .background(TangemTheme.colors.background.action) .padding( top = paddingFromTop, - bottom = TangemTheme.dimens.spacing8, + bottom = TangemTheme.dimens.spacing12, start = TangemTheme.dimens.spacing12, end = TangemTheme.dimens.spacing12, ), @@ -242,4 +247,25 @@ private fun AnimateRecentAppearance(isVisible: Boolean, content: @Composable () Box(modifier = Modifier.fillMaxWidth()) } } +} + +@Preview(widthDp = 360, heightDp = 800) +@Composable +private fun SendRecipientContent_Preview( + @PreviewParameter(SendRecipientContentPreviewProvider::class) recipientState: SendStates.RecipientState, +) { + TangemTheme(isDark = false) { + SendRecipientContent( + uiState = recipientState, + clickIntents = SendClickIntentsStub, + isBalanceHidden = false, + ) + } +} + +private class SendRecipientContentPreviewProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + RecipientStatePreviewData.recipientState, + ) } \ No newline at end of file From 6d8bb28f02c4014842fbf54c6d533047632153e3 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 22 May 2024 15:48:14 +0500 Subject: [PATCH 16/21] Updated on 2026-08-14 --- .../repository/DefaultNetworksRepository.kt | 15 ++++++++--- .../tokens/model/CryptoCurrencyAddress.kt | 6 +++++ .../tokens/GetNetworkAddressesUseCase.kt | 3 ++- .../tokens/repository/NetworksRepository.kt | 3 ++- .../presentation/domain/AvailableWallet.kt | 2 ++ .../presentation/viewmodel/SendViewModel.kt | 25 ++++++++++++------- 6 files changed, 39 insertions(+), 15 deletions(-) create mode 100644 domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/CryptoCurrencyAddress.kt diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt index 57f55c1aab..fe6ab4046f 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt @@ -13,6 +13,7 @@ import com.tangem.domain.common.extensions.fromNetworkId import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.demo.DemoConfig import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.model.CryptoCurrencyAddress import com.tangem.domain.tokens.model.Network import com.tangem.domain.tokens.model.NetworkStatus import com.tangem.domain.tokens.repository.NetworksRepository @@ -78,7 +79,10 @@ internal class DefaultNetworksRepository( return blockchain == Blockchain.Aptos } - override suspend fun getNetworkAddresses(userWalletId: UserWalletId, network: Network): List { + override suspend fun getNetworkAddresses( + userWalletId: UserWalletId, + network: Network, + ): List { // Get list of currencies matching [network] val currencies = getCurrencies(userWalletId) .filter { currency -> network.id == currency.network.id } @@ -87,9 +91,12 @@ internal class DefaultNetworksRepository( if (currencies.toList().isEmpty()) return emptyList() return currencies.toList().map { currency -> - walletManagersFacade.getAddresses(userWalletId, currency.network) - .firstOrNull { it.type == AddressType.Default } - ?.value.orEmpty() + CryptoCurrencyAddress( + cryptoCurrency = currency, + address = walletManagersFacade.getAddresses(userWalletId, currency.network) + .firstOrNull { it.type == AddressType.Default } + ?.value.orEmpty(), + ) } } diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/CryptoCurrencyAddress.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/CryptoCurrencyAddress.kt new file mode 100644 index 0000000000..73b8902323 --- /dev/null +++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/CryptoCurrencyAddress.kt @@ -0,0 +1,6 @@ +package com.tangem.domain.tokens.model + +data class CryptoCurrencyAddress( + val cryptoCurrency: CryptoCurrency, + val address: String, +) \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetNetworkAddressesUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetNetworkAddressesUseCase.kt index 0fa37a5a07..654c263b3e 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetNetworkAddressesUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetNetworkAddressesUseCase.kt @@ -1,5 +1,6 @@ package com.tangem.domain.tokens +import com.tangem.domain.tokens.model.CryptoCurrencyAddress import com.tangem.domain.tokens.model.Network import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.wallets.models.UserWalletId @@ -8,7 +9,7 @@ class GetNetworkAddressesUseCase( internal val networksRepository: NetworksRepository, ) { - suspend fun invokeSync(userWalletId: UserWalletId, network: Network): List { + suspend fun invokeSync(userWalletId: UserWalletId, network: Network): List { return networksRepository.getNetworkAddresses(userWalletId, network) } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/NetworksRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/NetworksRepository.kt index ebece1356e..271cdb4fcf 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/NetworksRepository.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/NetworksRepository.kt @@ -1,5 +1,6 @@ package com.tangem.domain.tokens.repository +import com.tangem.domain.tokens.model.CryptoCurrencyAddress import com.tangem.domain.tokens.model.Network import com.tangem.domain.tokens.model.NetworkStatus import com.tangem.domain.wallets.models.UserWalletId @@ -46,5 +47,5 @@ interface NetworksRepository { fun isNeedToCreateAccountWithoutReserve(network: Network): Boolean - suspend fun getNetworkAddresses(userWalletId: UserWalletId, network: Network): List + suspend fun getNetworkAddresses(userWalletId: UserWalletId, network: Network): List } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/domain/AvailableWallet.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/domain/AvailableWallet.kt index 8a7d5e7f3a..b6959e4a35 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/domain/AvailableWallet.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/domain/AvailableWallet.kt @@ -1,6 +1,7 @@ package com.tangem.features.send.impl.presentation.domain import androidx.compose.runtime.Immutable +import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.wallets.models.UserWalletId /** @@ -15,4 +16,5 @@ data class AvailableWallet( val name: String, val userWalletId: UserWalletId, val address: String, + val cryptoCurrency: CryptoCurrency, ) \ 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 df163fdfff..c14b26757a 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 @@ -418,8 +418,11 @@ internal class SendViewModel @Inject constructor( } } - private suspend fun List.toAvailableWallets(): List = - filterNot { it.walletId == userWalletId || it.isLocked } + private suspend fun List.toAvailableWallets(): List { + val currentAddress: String = kotlin.runCatching { + cryptoCurrencyStatus.value.networkAddress?.defaultAddress?.value + }.getOrNull().orEmpty() + return filterNot { it.isLocked } .mapNotNull { wallet -> val addresses = if (!wallet.isMultiCurrency) { getCryptoCurrencyUseCase(wallet.walletId).getOrNull()?.let { @@ -432,14 +435,18 @@ internal class SendViewModel @Inject constructor( } else { getNetworkAddressesUseCase.invokeSync(wallet.walletId, cryptoCurrency.network) } - addresses?.map { address -> - AvailableWallet( - name = wallet.name, - address = address, - userWalletId = wallet.walletId, - ) - }?.fastDistinctBy { it.address } + addresses + ?.filter { it.address != currentAddress } + ?.map { (cryptoCurrency, address) -> + AvailableWallet( + name = wallet.name, + address = address, + cryptoCurrency = cryptoCurrency, + userWalletId = wallet.walletId, + ) + }?.fastDistinctBy { it.address } }.flatten() + } private suspend fun getTxHistory() { val txHistoryList = getFixedTxHistoryItemsUseCase.getSync( From 41ce3bd24c445ad29bb309ebf56cca07ee477bb6 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 22 May 2024 12:39:53 +0100 Subject: [PATCH 17/21] Updated on 2026-08-14 --- .../domain/tokens/repository/MockNetworksRepository.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockNetworksRepository.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockNetworksRepository.kt index ecf1d2a23b..34abccfa04 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockNetworksRepository.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockNetworksRepository.kt @@ -3,6 +3,7 @@ package com.tangem.domain.tokens.repository import arrow.core.Either import arrow.core.getOrElse import com.tangem.domain.core.error.DataError +import com.tangem.domain.tokens.model.CryptoCurrencyAddress import com.tangem.domain.tokens.model.Network import com.tangem.domain.tokens.model.NetworkStatus import com.tangem.domain.wallets.models.UserWalletId @@ -34,4 +35,10 @@ internal class MockNetworksRepository( } override fun isNeedToCreateAccountWithoutReserve(network: Network) = false + override suspend fun getNetworkAddresses( + userWalletId: UserWalletId, + network: Network, + ): List { + return emptyList() + } } \ No newline at end of file From 77103de5d774e1b2b30d1d534c9ec6004c91b7dc Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 22 May 2024 18:22:41 +0500 Subject: [PATCH 18/21] Updated on 2026-08-14 --- .../features/send/impl/presentation/viewmodel/SendViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 c14b26757a..2a17cfe57a 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 @@ -807,7 +807,7 @@ internal class SendViewModel @Inject constructor( } uiState = sendNotificationFactory.dismissNotificationState(clazz) - feeReload() + updateNotifications() } override fun onNotificationCancel(clazz: Class) { From b2196196e73478af7d6ef7c4672a85e9ba0783b2 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 22 May 2024 18:51:03 +0500 Subject: [PATCH 19/21] Updated on 2026-08-14 --- .../com/tangem/core/ui/components/fields/AmountTextField.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt b/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt index 9db1abac3b..7c185e50d6 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt @@ -161,7 +161,7 @@ private fun String.preserveDecimalSymbol(newValue: String, decimalSymbol: Char) private fun String.preserveTrailingZeros(newValue: String, parsedDecimal: BigDecimal?, decimalSymbol: Char): String { val trailingZeros = newValue.split(decimalSymbol).getOrNull(1)?.takeLastWhile { it == '0' }.orEmpty() return when { - this.endsWith('0') -> this + this.endsWith('0') && parsedDecimal?.scale() != 0 -> this parsedDecimal?.scale() == 0 && trailingZeros.isNotEmpty() -> "$this$decimalSymbol$trailingZeros" else -> this.plus(trailingZeros) } From 489dc59ec5ad0938fa62ad4a3864f1c06dcf37d6 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 22 May 2024 18:51:44 +0500 Subject: [PATCH 20/21] Updated on 2026-08-14 --- gradle/dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index a202e204d9..f7ce17b6c2 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -86,7 +86,7 @@ markdown = "0.7.2" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "release-app_5.10-636" +tangemBlockchainSdk = "release-app_5.10-639" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "release-app_5.10-353" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From 5c63a8d29cb22a0acc15c940bd3df30c784b5653 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 23 May 2024 11:08:06 +0500 Subject: [PATCH 21/21] Updated on 2026-08-14 --- .../features/send/impl/presentation/ui/SendNavigationButtons.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt index e31aa8a82a..672f385fc0 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt @@ -38,7 +38,6 @@ import com.tangem.core.ui.utils.BigDecimalFormatter import com.tangem.features.send.impl.presentation.state.SendUiCurrentScreen import com.tangem.features.send.impl.presentation.state.SendUiState import com.tangem.features.send.impl.presentation.state.SendUiStateType -import com.tangem.features.send.impl.presentation.utils.getFiatFormatted import com.tangem.features.send.impl.presentation.utils.getFiatString @Composable