From cf8009da88c31102eb54810c193195ebb5aa48e9 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 18 Dec 2024 11:07:32 +0300 Subject: [PATCH 1/8] Updated on 2026-08-14 --- .../wallet/presentation/wallet/domain/Wallet2CobrandImage.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/Wallet2CobrandImage.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/Wallet2CobrandImage.kt index 033f180725..80663dd7de 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/Wallet2CobrandImage.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/Wallet2CobrandImage.kt @@ -161,7 +161,7 @@ internal enum class Wallet2CobrandImage( Pastel( cards2ResId = R.drawable.ill_pastel_cards3_120_106, cards3ResId = R.drawable.ill_pastel_cards3_120_106, - batchIds = setOf("AF43", "AF44", "AF45"), + batchIds = setOf("AF43", "AF44", "AF45", "AF78", "AF79", "AF80"), ), RedPanda( @@ -204,7 +204,7 @@ internal enum class Wallet2CobrandImage( Vivid( cards2ResId = R.drawable.ill_vivid_cards3_120_106, cards3ResId = R.drawable.ill_vivid_cards3_120_106, - batchIds = setOf("AF40", "AF41", "AF42"), + batchIds = setOf("AF40", "AF41", "AF42", "AF75", "AF76", "AF77"), ), VoltInu( From d2d93773863b8f5d19c58042ce9f8834e336a493 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 18 Dec 2024 13:16:08 +0300 Subject: [PATCH 2/8] Updated on 2026-08-14 --- .../tokendetails/TokenDetailsPreviewData.kt | 1 + .../presentation/tokendetails/state/StakingBlockUM.kt | 1 + .../factory/TokenDetailsLoadedBalanceConverter.kt | 10 +++++++++- .../ui/components/staking/TokenStakingBlock.kt | 1 + 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt index 358d295129..672da4f66c 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt @@ -152,6 +152,7 @@ internal object TokenDetailsPreviewData { formatArgs = wrappedList("Solana"), ), iconState = iconState, + isEnabled = true, onStakeClicked = {}, ) diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/StakingBlockUM.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/StakingBlockUM.kt index 0072e90707..789ddc5ec5 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/StakingBlockUM.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/StakingBlockUM.kt @@ -24,6 +24,7 @@ internal sealed interface StakingBlockUM { val iconState: IconState, val titleText: TextReference, val subtitleText: TextReference, + val isEnabled: Boolean, val onStakeClicked: () -> Unit, ) : StakingBlockUM } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt index a8bf99a969..2a9be57347 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt @@ -158,7 +158,7 @@ internal class TokenDetailsLoadedBalanceConverter( return when { stakingCryptoAmount.isNullOrZero() && stakingEntryInfo != null -> { if (pendingBalances.isEmpty()) { - getStakeAvailableState(stakingEntryInfo, iconState) + getStakeAvailableState(stakingEntryInfo, iconState, isStakingButtonEnabled(status)) } else { getStakedBlockWithFiatAmount(status, pendingBalances.sumOf { it.amount }, null) } @@ -170,6 +170,12 @@ internal class TokenDetailsLoadedBalanceConverter( } } + private fun isStakingButtonEnabled(status: CryptoCurrencyStatus): Boolean { + return status.value is CryptoCurrencyStatus.Loaded || + status.value is CryptoCurrencyStatus.NoQuote || + status.value is CryptoCurrencyStatus.Custom + } + private fun getStakedBlockWithFiatAmount( status: CryptoCurrencyStatus, stakingAmount: BigDecimal?, @@ -210,6 +216,7 @@ internal class TokenDetailsLoadedBalanceConverter( private fun getStakeAvailableState( stakingEntryInfo: StakingEntryInfo, iconState: IconState, + isEnabled: Boolean, ): StakingBlockUM.StakeAvailable { val apr = stakingEntryInfo.apr.format { percent() } return StakingBlockUM.StakeAvailable( @@ -222,6 +229,7 @@ internal class TokenDetailsLoadedBalanceConverter( formatArgs = wrappedList(stakingEntryInfo.tokenSymbol), ), iconState = iconState, + isEnabled = isEnabled, onStakeClicked = clickIntents::onStakeBannerClick, ) } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/staking/TokenStakingBlock.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/staking/TokenStakingBlock.kt index a8ba9f84c2..484de66cba 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/staking/TokenStakingBlock.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/staking/TokenStakingBlock.kt @@ -112,6 +112,7 @@ private fun StakingAvailableContent(state: StakingBlockUM.StakeAvailable, modifi SecondaryButton( modifier = Modifier.fillMaxWidth(), text = stringResource(id = R.string.common_stake), + enabled = state.isEnabled, onClick = state.onStakeClicked, ) } From d28ebee0f0cb51ab0ea054888dd44c061f843dc1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 18 Dec 2024 13:16:57 +0300 Subject: [PATCH 3/8] Updated on 2026-08-14 --- core/res/src/main/res/values-ru/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 92629c43f6..f6655e8627 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -5,7 +5,7 @@ Продать Это может занять несколько секунд. Пожалуйста, попробуйте позже. Данные ещё не загрузились - Это действие в данный момент недоступно, пожалуйста попробуйте позже или обновите данные, сделав свайп экрана вниз. + Это действие в данный момент недоступно, пожалуйста, попробуйте позже или обновите данные, сделав свайп экрана вниз. Кнопка недоступна Выберите токен Не нашли свой токен? Перейдите в раздел «Рынок» на главной странице и добавьте его в свой портфель для обмена. From 7bc1381aa9e1fbd547d5d590d9c102c2bc2601a7 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 18 Dec 2024 17:25:13 +0300 Subject: [PATCH 4/8] Updated on 2026-08-14 --- app/src/main/java/com/tangem/tap/LockUserWalletsTimer.kt | 8 +++++--- .../com/tangem/feature/swap/viewmodels/SwapViewModel.kt | 8 ++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/LockUserWalletsTimer.kt b/app/src/main/java/com/tangem/tap/LockUserWalletsTimer.kt index 18f3cff503..33d7f81b88 100644 --- a/app/src/main/java/com/tangem/tap/LockUserWalletsTimer.kt +++ b/app/src/main/java/com/tangem/tap/LockUserWalletsTimer.kt @@ -30,7 +30,7 @@ internal class LockUserWalletsTimer( lifecycle.addObserver(this) } - override fun onResume(owner: LifecycleOwner) { + override fun onStart(owner: LifecycleOwner) { owner.lifecycleScope.launch { val wasApplicationStopped = settingsRepository.wasApplicationStopped() val shouldOpenWelcomeScreenOnResume = settingsRepository.shouldOpenWelcomeScreenOnResume() @@ -45,8 +45,6 @@ internal class LockUserWalletsTimer( settingsRepository.setWasApplicationStopped(value = false) - start() - if (shouldOpenWelcomeScreenOnResume) { store.dispatchNavigationAction { replaceAll(AppRoute.Welcome()) } settingsRepository.setShouldOpenWelcomeScreenOnResume(value = false) @@ -54,6 +52,10 @@ internal class LockUserWalletsTimer( } } + override fun onResume(owner: LifecycleOwner) { + start() + } + override fun onStop(owner: LifecycleOwner) { Timber.i("Owner stopped") diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt index 431017f917..13de36639d 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt @@ -185,6 +185,14 @@ internal class SwapViewModel @Inject constructor( }.launchIn(viewModelScope) } + override fun onStart(owner: LifecycleOwner) { + startLoadingQuotesFromLastState(true) + } + + override fun onStop(owner: LifecycleOwner) { + singleTaskScheduler.cancelTask() + } + override fun onCleared() { singleTaskScheduler.cancelTask() super.onCleared() From 1cc16506e1a61c8c070ea35c63bbeca3c40aa11e Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 18 Dec 2024 22:57:32 +0500 Subject: [PATCH 5/8] Updated on 2026-08-14 --- .../data/onramp/DefaultOnrampRepository.kt | 1 + .../tangem/domain/onramp/model/OnrampQuote.kt | 6 +- .../providers/entity/ProviderListItemUM.kt | 48 ++++++---- .../onramp/providers/entity/ProviderListUM.kt | 7 +- .../providers/model/SelectProviderModel.kt | 87 +++++++++++++------ .../previewData/SelectProviderPreviewData.kt | 72 ++++++++++----- .../providers/ui/SelectProviderBottomSheet.kt | 19 +++- 7 files changed, 162 insertions(+), 78 deletions(-) diff --git a/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampRepository.kt b/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampRepository.kt index 85dbdb56f6..25a92979ef 100644 --- a/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampRepository.kt +++ b/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampRepository.kt @@ -468,6 +468,7 @@ internal class DefaultOnrampRepository( OnrampQuote.Error( paymentMethod = paymentMethod, provider = provider, + fromAmount = fromOnrampAmount, error = onrampError, ) } diff --git a/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampQuote.kt b/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampQuote.kt index c511e04b8a..c36638db32 100644 --- a/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampQuote.kt +++ b/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampQuote.kt @@ -6,11 +6,12 @@ sealed class OnrampQuote { abstract val paymentMethod: OnrampPaymentMethod abstract val provider: OnrampProvider + abstract val fromAmount: OnrampAmount data class Data( override val paymentMethod: OnrampPaymentMethod, override val provider: OnrampProvider, - val fromAmount: OnrampAmount, + override val fromAmount: OnrampAmount, val toAmount: OnrampAmount, val minFromAmount: OnrampAmount, val maxFromAmount: OnrampAmount, @@ -19,13 +20,14 @@ sealed class OnrampQuote { data class AmountError( override val paymentMethod: OnrampPaymentMethod, override val provider: OnrampProvider, - val fromAmount: OnrampAmount, + override val fromAmount: OnrampAmount, val error: OnrampError.AmountError, ) : OnrampQuote() data class Error( override val paymentMethod: OnrampPaymentMethod, override val provider: OnrampProvider, + override val fromAmount: OnrampAmount, val error: OnrampError, ) : OnrampQuote() } \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/entity/ProviderListItemUM.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/entity/ProviderListItemUM.kt index 0e6727be5b..f3c9d5341b 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/entity/ProviderListItemUM.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/entity/ProviderListItemUM.kt @@ -6,30 +6,42 @@ internal sealed interface ProviderListItemUM { val providerId: String val name: String + val imageUrl: String - data class Available( - override val providerId: String, - override val name: String, - val imageUrl: String, - val rate: String, - val isBestRate: Boolean, - val diffRate: TextReference?, - val isSelected: Boolean, - val onClick: () -> Unit, - ) : ProviderListItemUM + sealed interface Available : ProviderListItemUM { + val onClick: () -> Unit + val providerResult: SelectProviderResult + val isBestRate: Boolean + val isSelected: Boolean - data class AvailableWithError( - override val providerId: String, - override val name: String, - val imageUrl: String, - val subtitle: TextReference, - val onClick: () -> Unit, - ) : ProviderListItemUM + data class Content( + override val providerId: String, + override val name: String, + override val onClick: () -> Unit, + override val imageUrl: String, + override val providerResult: SelectProviderResult, + override val isBestRate: Boolean, + override val isSelected: Boolean, + val rate: String, + val diffRate: TextReference?, + ) : Available + + data class WithError( + override val providerId: String, + override val name: String, + override val onClick: () -> Unit, + override val imageUrl: String, + override val providerResult: SelectProviderResult, + override val isBestRate: Boolean, + override val isSelected: Boolean, + val subtitle: TextReference, + ) : Available + } data class Unavailable( override val providerId: String, override val name: String, - val imageUrl: String, + override val imageUrl: String, val subtitle: TextReference, ) : ProviderListItemUM } \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/entity/ProviderListUM.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/entity/ProviderListUM.kt index 28350458da..b4f1435144 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/entity/ProviderListUM.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/entity/ProviderListUM.kt @@ -4,13 +4,14 @@ import com.tangem.domain.onramp.model.OnrampPaymentMethod import kotlinx.collections.immutable.ImmutableList internal data class SelectPaymentAndProviderUM( - val paymentMethods: ImmutableList, - val selectedPaymentMethod: SelectProviderUM, + val paymentMethods: ImmutableList, + val selectedPaymentMethod: PaymentProviderUM, + val selectedProviderId: String, val isPaymentMethodClickEnabled: Boolean, val onPaymentMethodClick: () -> Unit, ) -internal data class SelectProviderUM( +internal data class PaymentProviderUM( val paymentMethod: OnrampPaymentMethod, val providers: ImmutableList, ) \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/model/SelectProviderModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/model/SelectProviderModel.kt index 1c94f39113..e85759d361 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/model/SelectProviderModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/model/SelectProviderModel.kt @@ -72,9 +72,21 @@ internal class SelectProviderModel @Inject constructor( }, ifRight = { it }, ) + + val filteredEmptyMethods = methods.mapNotNull { method -> + val providers = getOnrampProviderWithQuoteUseCase(method).getOrNull() + if (!providers.isNullOrEmpty()) { + PaymentProviderUM( + paymentMethod = method, + providers = providers.toProvidersListItems(), + ) + } else { + null + } + } _state.value = state.value.copy( - paymentMethods = methods.toPersistentList(), - isPaymentMethodClickEnabled = methods.isNotEmpty(), + paymentMethods = filteredEmptyMethods.toPersistentList(), + isPaymentMethodClickEnabled = filteredEmptyMethods.isNotEmpty(), ) } } @@ -111,13 +123,19 @@ internal class SelectProviderModel @Inject constructor( private fun getInitialState(): SelectPaymentAndProviderUM { return SelectPaymentAndProviderUM( - paymentMethods = persistentListOf(params.selectedPaymentMethod), + paymentMethods = persistentListOf( + PaymentProviderUM( + paymentMethod = params.selectedPaymentMethod, + providers = emptyList().toImmutableList(), + ), + ), isPaymentMethodClickEnabled = false, onPaymentMethodClick = ::openPaymentMethods, - selectedPaymentMethod = SelectProviderUM( + selectedPaymentMethod = PaymentProviderUM( paymentMethod = params.selectedPaymentMethod, providers = emptyList().toImmutableList(), ), + selectedProviderId = params.selectedProviderId, ) } @@ -131,16 +149,18 @@ internal class SelectProviderModel @Inject constructor( ) } - private fun ImmutableList.toPaymentUMList(): ImmutableList = map { method -> + private fun ImmutableList.toPaymentUMList(): ImmutableList = map { method -> PaymentMethodUM( - id = method.id, - imageUrl = method.imageUrl, - name = method.name, + id = method.paymentMethod.id, + imageUrl = method.paymentMethod.imageUrl, + name = method.paymentMethod.name, onSelect = { onPaymentMethodSelected(method) }, ) }.toPersistentList() - private fun onPaymentMethodSelected(paymentMethod: OnrampPaymentMethod) { + private fun onPaymentMethodSelected(methodContainer: PaymentProviderUM) { + val paymentMethod = methodContainer.paymentMethod + val firstProvider = methodContainer.providers.firstOrNull() analyticsEventHandler.send(OnrampAnalyticsEvent.OnPaymentMethodChosen(paymentMethod = paymentMethod.name)) modelScope.launch { saveSelectedPaymentMethod.invoke(paymentMethod) @@ -151,6 +171,10 @@ internal class SelectProviderModel @Inject constructor( ), ) } + if (firstProvider is ProviderListItemUM.Available) { + onProviderSelected(firstProvider.providerResult, firstProvider.isBestRate) + } + bottomSheetNavigation.dismiss() } } @@ -165,6 +189,8 @@ internal class SelectProviderModel @Inject constructor( val hasBestProvider = isMultipleQuotes && isOtherQuotesHasData return sorted.map { quote -> + val isSelectedProvider = state.value.selectedProviderId == quote.provider.id + when (quote) { is OnrampProviderWithQuote.Data -> { val rate = quote.toAmount.value.format { @@ -173,28 +199,29 @@ internal class SelectProviderModel @Inject constructor( val rateDiff = bestProvider?.toAmount?.value?.let { bestRate -> BigDecimal.ONE - quote.toAmount.value / bestRate } - val isSelectedProvider = quote.provider.id == params.selectedProviderId - val isSelectedPayment = quote.paymentMethod.id == params.selectedPaymentMethod.id val isBestProvider = quote == bestProvider && hasBestProvider - ProviderListItemUM.Available( + val providerResult = SelectProviderResult.ProviderWithQuote( + paymentMethod = quote.paymentMethod, + provider = quote.provider, + fromAmount = quote.fromAmount, + toAmount = quote.toAmount, + ) + ProviderListItemUM.Available.Content( providerId = quote.provider.id, imageUrl = quote.provider.info.imageLarge, name = quote.provider.info.name, rate = rate, - isSelected = isSelectedProvider && isSelectedPayment, isBestRate = isBestProvider, diffRate = stringReference("$MINUS${rateDiff.format { percent() }}") .takeIf { hasBestProvider }, + providerResult = providerResult, + isSelected = isSelectedProvider, onClick = { onProviderSelected( - result = SelectProviderResult.ProviderWithQuote( - paymentMethod = quote.paymentMethod, - provider = quote.provider, - fromAmount = quote.fromAmount, - toAmount = quote.toAmount, - ), + result = providerResult, isBestRate = isBestProvider, ) + params.onDismiss() }, ) } @@ -207,21 +234,25 @@ internal class SelectProviderModel @Inject constructor( is OnrampError.AmountError.TooBigError -> R.string.express_provider_max_amount is OnrampError.AmountError.TooSmallError -> R.string.express_provider_min_amount } - - ProviderListItemUM.AvailableWithError( + val providerResult = SelectProviderResult.ProviderWithError( + paymentMethod = quoteError.paymentMethod, + provider = quote.provider, + quoteError = quote.quoteError, + ) + ProviderListItemUM.Available.WithError( providerId = quote.provider.id, imageUrl = quote.provider.info.imageLarge, name = quote.provider.info.name, subtitle = resourceReference(errorSubtitleRes, wrappedList(amount)), + providerResult = providerResult, + isBestRate = false, + isSelected = isSelectedProvider, onClick = { onProviderSelected( - result = SelectProviderResult.ProviderWithError( - paymentMethod = quoteError.paymentMethod, - provider = quote.provider, - quoteError = quote.quoteError, - ), + result = providerResult, isBestRate = bestProvider == quote, ) + params.onDismiss() }, ) } @@ -247,8 +278,10 @@ internal class SelectProviderModel @Inject constructor( tokenSymbol = params.cryptoCurrency.symbol, ), ) + _state.update { + it.copy(selectedProviderId = result.provider.id) + } params.onProviderClick(result, isBestRate) - params.onDismiss() } private fun sendOnrampErrorEvent(error: OnrampError) { diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/model/previewData/SelectProviderPreviewData.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/model/previewData/SelectProviderPreviewData.kt index 4110250555..f4a23e8734 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/model/previewData/SelectProviderPreviewData.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/model/previewData/SelectProviderPreviewData.kt @@ -2,59 +2,83 @@ package com.tangem.features.onramp.providers.model.previewData import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.stringReference -import com.tangem.domain.onramp.model.OnrampPaymentMethod -import com.tangem.domain.onramp.model.PaymentMethodType +import com.tangem.domain.onramp.model.* +import com.tangem.features.onramp.providers.entity.PaymentProviderUM import com.tangem.features.onramp.providers.entity.ProviderListItemUM import com.tangem.features.onramp.providers.entity.SelectPaymentAndProviderUM -import com.tangem.features.onramp.providers.entity.SelectProviderUM +import com.tangem.features.onramp.providers.entity.SelectProviderResult import kotlinx.collections.immutable.persistentListOf +import java.math.BigDecimal internal object SelectProviderPreviewData { - private val paymentMethods = persistentListOf( - OnrampPaymentMethod( - id = "card", - name = "Card", - imageUrl = "https://s3.eu-central-1.amazonaws.com/tangem.api/express/PaymentMethods/visa-mc.png", - type = PaymentMethodType.CARD, - ), + private val paymentMethod = OnrampPaymentMethod( + id = "card", + name = "Card", + imageUrl = "https://s3.eu-central-1.amazonaws.com/tangem.api/express/PaymentMethods/visa-mc.png", + type = PaymentMethodType.CARD, ) - private val providerListUM = SelectProviderUM( - paymentMethod = paymentMethods.first(), + private val provider = OnrampProvider( + id = "mercuryo", + info = OnrampProviderInfo( + name = "Mercuryo", + imageLarge = "https://s3.eu-central-1.amazonaws.com/tangem.api/express/mercuryo1024.png", + termsOfUseLink = null, + privacyPolicyLink = null, + ), + paymentMethods = listOf(), + ) + + private val providerListUM = PaymentProviderUM( + paymentMethod = paymentMethod, providers = persistentListOf( - ProviderListItemUM.Available( - providerId = "mercuryo", - imageUrl = "https://s3.eu-central-1.amazonaws.com/tangem.api/express/mercuryo1024.png", - name = "Mercuryo", + ProviderListItemUM.Available.Content( + providerId = provider.id, + imageUrl = provider.info.imageLarge, + name = provider.info.name, rate = "1 BTC", isSelected = true, isBestRate = true, diffRate = TextReference.EMPTY, + providerResult = SelectProviderResult.ProviderWithQuote( + paymentMethod = paymentMethod, + provider = provider, + fromAmount = OnrampAmount(symbol = "$", value = BigDecimal.ONE, decimals = 2), + toAmount = OnrampAmount(symbol = "BTC", value = BigDecimal.ONE, decimals = 18), + + ), onClick = {}, ), - ProviderListItemUM.Available( - providerId = "mercuryo", - imageUrl = "https://s3.eu-central-1.amazonaws.com/tangem.api/express/mercuryo1024.png", - name = "Mercuryo", + ProviderListItemUM.Available.Content( + providerId = provider.id, + imageUrl = provider.info.imageLarge, + name = provider.info.name, rate = "1 BTC", isSelected = false, isBestRate = false, diffRate = stringReference("-0.03%"), + providerResult = SelectProviderResult.ProviderWithQuote( + paymentMethod = paymentMethod, + provider = provider, + fromAmount = OnrampAmount(symbol = "$", value = BigDecimal.ONE, decimals = 2), + toAmount = OnrampAmount(symbol = "BTC", value = BigDecimal.ONE, decimals = 18), + ), onClick = {}, ), ProviderListItemUM.Unavailable( - providerId = "mercuryo", - imageUrl = "https://s3.eu-central-1.amazonaws.com/tangem.api/express/mercuryo1024.png", - name = "Mercuryo", + providerId = provider.id, + imageUrl = provider.info.imageLarge, + name = provider.info.name, subtitle = stringReference("Avaiable from 0,00413 BTC"), ), ), ) val state = SelectPaymentAndProviderUM( - paymentMethods = paymentMethods, + paymentMethods = persistentListOf(providerListUM), selectedPaymentMethod = providerListUM, + selectedProviderId = provider.id, isPaymentMethodClickEnabled = false, onPaymentMethodClick = {}, ) diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/ui/SelectProviderBottomSheet.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/ui/SelectProviderBottomSheet.kt index 9918d1f05e..071e09b4b6 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/ui/SelectProviderBottomSheet.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/ui/SelectProviderBottomSheet.kt @@ -124,16 +124,17 @@ private fun PaymentMethodBlock( @Composable private fun ProviderItem(state: ProviderListItemUM, modifier: Modifier = Modifier) { when (state) { - is ProviderListItemUM.Available -> AvailableProviderItem( + is ProviderListItemUM.Available.Content -> AvailableProviderItem( modifier = modifier, state = state, ) - is ProviderListItemUM.AvailableWithError -> { + is ProviderListItemUM.Available.WithError -> { UnavailableProviderItem( modifier = modifier.clickable(onClick = state.onClick), imageUrl = state.imageUrl, providerName = state.name, subtitle = state.subtitle, + isSelected = state.isSelected, ) } is ProviderListItemUM.Unavailable -> UnavailableProviderItem( @@ -141,12 +142,13 @@ private fun ProviderItem(state: ProviderListItemUM, modifier: Modifier = Modifie imageUrl = state.imageUrl, providerName = state.name, subtitle = state.subtitle, + isSelected = false, ) } } @Composable -private fun AvailableProviderItem(state: ProviderListItemUM.Available, modifier: Modifier = Modifier) { +private fun AvailableProviderItem(state: ProviderListItemUM.Available.Content, modifier: Modifier = Modifier) { Row( modifier = modifier .then( @@ -220,10 +222,19 @@ private fun UnavailableProviderItem( imageUrl: String, providerName: String, subtitle: TextReference, + isSelected: Boolean, modifier: Modifier = Modifier, ) { Row( - modifier = modifier.padding(all = TangemTheme.dimens.spacing12), + modifier = modifier + .then( + if (isSelected) { + Modifier.selectedBorder() + } else { + Modifier.clip(RoundedCornerShape(16.dp)) + }, + ) + .padding(all = TangemTheme.dimens.spacing12), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12), ) { From 7fcabe68fa9d717e74a523d6941bf79c965fde14 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 18 Dec 2024 22:56:10 +0500 Subject: [PATCH 6/8] Updated on 2026-08-14 --- .../main/model/OnrampMainComponentModel.kt | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/model/OnrampMainComponentModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/model/OnrampMainComponentModel.kt index ddcbb59d1c..ef27622623 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/model/OnrampMainComponentModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/model/OnrampMainComponentModel.kt @@ -278,10 +278,11 @@ internal class OnrampMainComponentModel @Inject constructor( private fun handleQuoteResult(quotes: List) { sendOnrampQuotesErrorAnalytic(quotes) - val quote = quotes.firstOrNull() + val quote = quotes.firstOrNull { it !is OnrampQuote.Error } if (quote == null) { _state.update { stateFactory.getErrorState() } + lastAmount.value = BigDecimal.ZERO return } @@ -295,15 +296,17 @@ internal class OnrampMainComponentModel @Inject constructor( val isBestProvider = quote == bestProvider && hasBestProvider - if (quote is OnrampQuote.Data && lastAmount.value != quote.fromAmount.value) { + if (lastAmount.value != quote.fromAmount.value) { lastAmount.value = quote.fromAmount.value - analyticsEventHandler.send( - OnrampAnalyticsEvent.ProviderCalculated( - providerName = quote.provider.info.name, - tokenSymbol = params.cryptoCurrency.symbol, - paymentMethod = quote.paymentMethod.name, - ), - ) + if (quote is OnrampQuote.Data) { + analyticsEventHandler.send( + OnrampAnalyticsEvent.ProviderCalculated( + providerName = quote.provider.info.name, + tokenSymbol = params.cryptoCurrency.symbol, + paymentMethod = quote.paymentMethod.name, + ), + ) + } _state.update { amountStateFactory.getAmountSecondaryUpdatedState( quote = quote, From 71a0fd55673a0396261e8f1d4c859fac97ebdaba Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 18 Dec 2024 22:56:29 +0500 Subject: [PATCH 7/8] Updated on 2026-08-14 --- .../confirmresidency/ConfirmResidencyComponent.kt | 2 +- .../DefaultConfirmResidencyComponent.kt | 10 ++++++++-- .../entity/ConfirmResidencyBottomSheetConfig.kt | 2 +- .../model/ConfirmResidencyModel.kt | 12 +++++++----- .../onramp/main/DefaultOnrampMainComponent.kt | 2 +- .../onramp/main/model/OnrampMainComponentModel.kt | 14 +++++++++----- 6 files changed, 27 insertions(+), 15 deletions(-) diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/confirmresidency/ConfirmResidencyComponent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/confirmresidency/ConfirmResidencyComponent.kt index feb6cd9c68..7dda243f37 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/confirmresidency/ConfirmResidencyComponent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/confirmresidency/ConfirmResidencyComponent.kt @@ -10,7 +10,7 @@ internal interface ConfirmResidencyComponent : ComposableBottomSheetComponent { data class Params( val cryptoCurrency: CryptoCurrency, val country: OnrampCountry, - val onDismiss: (OnrampCountry) -> Unit, + val onDismiss: () -> Unit, ) interface Factory : ComponentFactory diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/confirmresidency/DefaultConfirmResidencyComponent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/confirmresidency/DefaultConfirmResidencyComponent.kt index 62dd39d0dd..0a88231cc3 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/confirmresidency/DefaultConfirmResidencyComponent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/confirmresidency/DefaultConfirmResidencyComponent.kt @@ -66,11 +66,17 @@ internal class DefaultConfirmResidencyComponent @AssistedInject constructor( config: ConfirmResidencyBottomSheetConfig, componentContext: ComponentContext, ): ComposableBottomSheetComponent = when (config) { - ConfirmResidencyBottomSheetConfig.SelectCountry -> selectCountryComponentFactory.create( + is ConfirmResidencyBottomSheetConfig.SelectCountry -> selectCountryComponentFactory.create( context = childByContext(componentContext), params = SelectCountryComponent.Params( cryptoCurrency = params.cryptoCurrency, - onDismiss = model.bottomSheetNavigation::dismiss, + onDismiss = { + // Dismiss country select sheet + model.bottomSheetNavigation.dismiss() + + // Dismiss confirm residency sheet + config.onDismiss() + }, ), ) } diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/confirmresidency/entity/ConfirmResidencyBottomSheetConfig.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/confirmresidency/entity/ConfirmResidencyBottomSheetConfig.kt index baacb6b92a..0900dfebc2 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/confirmresidency/entity/ConfirmResidencyBottomSheetConfig.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/confirmresidency/entity/ConfirmResidencyBottomSheetConfig.kt @@ -6,5 +6,5 @@ import kotlinx.serialization.Serializable internal sealed class ConfirmResidencyBottomSheetConfig { @Serializable - data object SelectCountry : ConfirmResidencyBottomSheetConfig() + data class SelectCountry(val onDismiss: () -> Unit) : ConfirmResidencyBottomSheetConfig() } \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/confirmresidency/model/ConfirmResidencyModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/confirmresidency/model/ConfirmResidencyModel.kt index 5237af3c2b..f622253ea1 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/confirmresidency/model/ConfirmResidencyModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/confirmresidency/model/ConfirmResidencyModel.kt @@ -10,6 +10,7 @@ import com.tangem.core.decompose.navigation.Router import com.tangem.core.ui.extensions.resourceReference import com.tangem.domain.onramp.OnrampSaveDefaultCountryUseCase import com.tangem.domain.onramp.analytics.OnrampAnalyticsEvent +import com.tangem.domain.onramp.model.OnrampCountry import com.tangem.features.onramp.confirmresidency.ConfirmResidencyComponent import com.tangem.features.onramp.confirmresidency.entity.ConfirmResidencyBottomSheetConfig import com.tangem.features.onramp.confirmresidency.entity.ConfirmResidencyUM @@ -35,7 +36,7 @@ internal class ConfirmResidencyModel @Inject constructor( country = params.country.name, countryFlagUrl = params.country.image, isCountrySupported = params.country.onrampAvailable, - primaryButtonConfig = getPrimaryButtonConfig(), + primaryButtonConfig = getPrimaryButtonConfig(params.country), secondaryButtonConfig = ConfirmResidencyUM.ActionButtonConfig( onClick = ::onChangeClick, text = resourceReference(R.string.common_change), @@ -47,11 +48,12 @@ internal class ConfirmResidencyModel @Inject constructor( analyticsEventHandler.send(OnrampAnalyticsEvent.ResidenceConfirmScreenOpened(params.country.name)) } - private fun getPrimaryButtonConfig() = if (params.country.onrampAvailable) { + private fun getPrimaryButtonConfig(country: OnrampCountry) = if (country.onrampAvailable) { ConfirmResidencyUM.ActionButtonConfig( onClick = { - analyticsEventHandler.send(OnrampAnalyticsEvent.OnResidenceConfirm(params.country.name)) - modelScope.launch { saveDefaultCountryUseCase.invoke(params.country) } + analyticsEventHandler.send(OnrampAnalyticsEvent.OnResidenceConfirm(country.name)) + modelScope.launch { saveDefaultCountryUseCase.invoke(country) } + params.onDismiss() }, text = resourceReference(R.string.common_confirm), ) @@ -67,6 +69,6 @@ internal class ConfirmResidencyModel @Inject constructor( private fun onChangeClick() { analyticsEventHandler.send(OnrampAnalyticsEvent.OnResidenceChange) - bottomSheetNavigation.activate(ConfirmResidencyBottomSheetConfig.SelectCountry) + bottomSheetNavigation.activate(ConfirmResidencyBottomSheetConfig.SelectCountry(params.onDismiss)) } } \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/DefaultOnrampMainComponent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/DefaultOnrampMainComponent.kt index 739d1a4d6e..5164c91d5b 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/DefaultOnrampMainComponent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/DefaultOnrampMainComponent.kt @@ -58,7 +58,7 @@ internal class DefaultOnrampMainComponent @AssistedInject constructor( country = config.country, onDismiss = { model.bottomSheetNavigation.dismiss() - model.handleOnrampAvailable(it.defaultCurrency) + model.handleOnrampAvailable() }, ), ) diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/model/OnrampMainComponentModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/model/OnrampMainComponentModel.kt index ef27622623..865ec89ec6 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/model/OnrampMainComponentModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/model/OnrampMainComponentModel.kt @@ -19,7 +19,6 @@ import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.onramp.* import com.tangem.domain.onramp.analytics.OnrampAnalyticsEvent import com.tangem.domain.onramp.model.OnrampAvailability -import com.tangem.domain.onramp.model.OnrampCurrency import com.tangem.domain.onramp.model.OnrampProviderWithQuote import com.tangem.domain.onramp.model.OnrampQuote import com.tangem.domain.onramp.model.error.OnrampError @@ -105,8 +104,7 @@ internal class OnrampMainComponentModel @Inject constructor( ) } - fun handleOnrampAvailable(currency: OnrampCurrency) { - _state.update { stateFactory.getReadyState(currency) } + fun handleOnrampAvailable() { subscribeToCountryAndCurrencyUpdates() subscribeToQuotesUpdate() } @@ -125,7 +123,7 @@ internal class OnrampMainComponentModel @Inject constructor( private fun handleOnrampAvailability(availability: OnrampAvailability) { when (availability) { - is OnrampAvailability.Available -> handleOnrampAvailable(availability.currency) + is OnrampAvailability.Available -> handleOnrampAvailable() is OnrampAvailability.ConfirmResidency, is OnrampAvailability.NotSupported, -> bottomSheetNavigation.activate(OnrampMainBottomSheetConfig.ConfirmResidency(availability.country)) @@ -139,7 +137,13 @@ internal class OnrampMainComponentModel @Inject constructor( ifLeft = ::handleOnrampError, ifRight = { country -> if (country == null) return@onEach - _state.update { amountStateFactory.getUpdatedCurrencyState(country.defaultCurrency) } + _state.update { + if (it is OnrampMainComponentUM.InitialLoading) { + stateFactory.getReadyState(country.defaultCurrency) + } else { + amountStateFactory.getUpdatedCurrencyState(country.defaultCurrency) + } + } updatePairsAndQuotes() }, ) From 4b4f11266530a3965c49c3e01999b4f686d5c1c9 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 18 Dec 2024 21:31:38 +0300 Subject: [PATCH 8/8] Updated on 2026-08-14 --- core/res/src/main/res/values-uk-rUA/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/res/src/main/res/values-uk-rUA/strings.xml b/core/res/src/main/res/values-uk-rUA/strings.xml index 7a2ba44136..e93c5799e5 100644 --- a/core/res/src/main/res/values-uk-rUA/strings.xml +++ b/core/res/src/main/res/values-uk-rUA/strings.xml @@ -12,6 +12,7 @@ Немає доступних токенів для обміну з обраним токеном. Будь ласка, оберіть інший. Немає вільної пари Щоб скористатися функцією обміну, у вашому портфелі має бути щонайменше 2 токени. + Додайте токени У вас додано лише 1 токен. Щоб скористатися функцією обміну, потрібно додати мінімум 2 токени. Додайте токени Оберіть токен для отримання @@ -324,6 +325,7 @@ Розкажіть, будь ласка, яку картку або кільце ви маєте? Привіт, команда підтримки, Будь ласка, розкажіть нам більше про вашу проблему. Кожна дрібниця може допомогти. + Раніше активований гаманець Мої пропозиції Не вдається відсканувати картку/кільце Звернення в підтримку @@ -598,6 +600,7 @@ Пошук за країною Недоступно Пошук по валюті + Використовуючи сервіс покупки, ви погоджуєтесь з %1$s та %2$s Сума покупки не може бути більше ніж %s Сума покупки повинна бути не менше %s Для данної валюти немає доступних провайдерів