From e18f70eb715ea21fc22d97c87e1b827d04c26a2a Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 3 Jul 2025 11:39:52 +0500 Subject: [PATCH] Updated on 2026-08-14 --- .../com/tangem/tap/routing/ProxyAppRouter.kt | 1 + .../amountScreen/ui/AmountFieldContainer.kt | 2 +- .../src/main/res/drawable/ic_convert_24.xml | 9 +++ .../subcomponents/SwapAmountUpdateTrigger.kt | 9 +++ .../amount/DefaultSwapAmountUpdateTrigger.kt | 28 ++++++++ .../v2/impl/amount/SwapAmountComponent.kt | 1 + .../impl/amount/SwapAmountComponentParams.kt | 5 +- .../swap/v2/impl/amount/SwapAmountRoute.kt | 8 +++ .../v2/impl/amount/di/SwapAmountModule.kt | 15 +++++ .../amount/model/SwapAmountClickIntents.kt | 1 + .../v2/impl/amount/model/SwapAmountModel.kt | 23 ++++++- .../v2/impl/amount/ui/SwapAmountContent.kt | 67 ++++++++++--------- .../ui/preview/SwapAmountClickIntentsStub.kt | 2 + 13 files changed, 135 insertions(+), 36 deletions(-) create mode 100644 core/ui/src/main/res/drawable/ic_convert_24.xml create mode 100644 features/swap-v2/api/src/main/java/com/tangem/features/swap/v2/api/subcomponents/SwapAmountUpdateTrigger.kt create mode 100644 features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/DefaultSwapAmountUpdateTrigger.kt create mode 100644 features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountRoute.kt diff --git a/app/src/main/java/com/tangem/tap/routing/ProxyAppRouter.kt b/app/src/main/java/com/tangem/tap/routing/ProxyAppRouter.kt index cdebe55b4a..492ca42a8b 100644 --- a/app/src/main/java/com/tangem/tap/routing/ProxyAppRouter.kt +++ b/app/src/main/java/com/tangem/tap/routing/ProxyAppRouter.kt @@ -75,6 +75,7 @@ internal class ProxyAppRouter( try { block() } catch (e: Throwable) { + Timber.e(e) onComplete(false) } } diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/ui/AmountFieldContainer.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/ui/AmountFieldContainer.kt index 7b4dfe4e57..fe714763b9 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/ui/AmountFieldContainer.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/ui/AmountFieldContainer.kt @@ -101,7 +101,7 @@ internal fun LazyListScope.amountFieldV2( ) { Column( horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp), modifier = modifier .padding(top = 48.dp, bottom = 28.dp), ) { diff --git a/core/ui/src/main/res/drawable/ic_convert_24.xml b/core/ui/src/main/res/drawable/ic_convert_24.xml new file mode 100644 index 0000000000..3c1c407a28 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_convert_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/features/swap-v2/api/src/main/java/com/tangem/features/swap/v2/api/subcomponents/SwapAmountUpdateTrigger.kt b/features/swap-v2/api/src/main/java/com/tangem/features/swap/v2/api/subcomponents/SwapAmountUpdateTrigger.kt new file mode 100644 index 0000000000..651312d68f --- /dev/null +++ b/features/swap-v2/api/src/main/java/com/tangem/features/swap/v2/api/subcomponents/SwapAmountUpdateTrigger.kt @@ -0,0 +1,9 @@ +package com.tangem.features.swap.v2.api.subcomponents + +/** + * Trigger swap amount change from another component. + * Different from another triggers because it takes raw string instead of BigDecimal + */ +interface SwapAmountUpdateTrigger { + suspend fun triggerUpdateAmount(amountValue: String) +} \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/DefaultSwapAmountUpdateTrigger.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/DefaultSwapAmountUpdateTrigger.kt new file mode 100644 index 0000000000..8be3a3c254 --- /dev/null +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/DefaultSwapAmountUpdateTrigger.kt @@ -0,0 +1,28 @@ +package com.tangem.features.swap.v2.impl.amount + +import com.tangem.features.swap.v2.api.subcomponents.SwapAmountUpdateTrigger +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import javax.inject.Inject +import javax.inject.Singleton + +/** + * Trigger amount change from another component. + * Different from another triggers because it takes raw string instead of BigDecimal + */ +interface SwapAmountUpdateListener { + val updateAmountTriggerFlow: Flow +} + +@Singleton +internal class DefaultSwapAmountUpdateTrigger @Inject constructor() : + SwapAmountUpdateTrigger, + SwapAmountUpdateListener { + + override val updateAmountTriggerFlow: Flow + field = MutableSharedFlow() + + override suspend fun triggerUpdateAmount(amountValue: String) { + updateAmountTriggerFlow.emit(amountValue) + } +} \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountComponent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountComponent.kt index aeb060452e..b987f350eb 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountComponent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountComponent.kt @@ -38,5 +38,6 @@ internal class SwapAmountComponent @AssistedInject constructor( interface ModelCallback : SwapNavigationModelCallback { fun onAmountResult(amountUM: SwapAmountUM) + fun onSeparatorClick(lastAmount: String) } } \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountComponentParams.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountComponentParams.kt index beb46d4941..0f09fecef4 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountComponentParams.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountComponentParams.kt @@ -1,11 +1,11 @@ package com.tangem.features.swap.v2.impl.amount +import com.tangem.core.ui.extensions.TextReference import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.swap.models.SwapDirection import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.wallets.models.UserWallet import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM -import com.tangem.features.swap.v2.impl.swap.SwapRoute import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow @@ -27,8 +27,9 @@ internal sealed class SwapAmountComponentParams { override val swapDirection: SwapDirection, override val primaryCryptoCurrencyStatusFlow: StateFlow, override val secondaryCryptoCurrency: CryptoCurrency?, + val title: TextReference, val callback: SwapAmountComponent.ModelCallback, - val currentRoute: Flow, + val currentRoute: Flow, ) : SwapAmountComponentParams() data class AmountBlockParams( diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountRoute.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountRoute.kt new file mode 100644 index 0000000000..721299ab4f --- /dev/null +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountRoute.kt @@ -0,0 +1,8 @@ +package com.tangem.features.swap.v2.impl.amount + +/** + * Common interface for routing to swap amount + */ +interface SwapAmountRoute { + val isEditMode: Boolean +} \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/di/SwapAmountModule.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/di/SwapAmountModule.kt index c80a8ee734..56c67b48db 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/di/SwapAmountModule.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/di/SwapAmountModule.kt @@ -2,10 +2,14 @@ package com.tangem.features.swap.v2.impl.amount.di import com.tangem.core.decompose.di.ModelComponent import com.tangem.core.decompose.model.Model +import com.tangem.features.swap.v2.api.subcomponents.SwapAmountUpdateTrigger +import com.tangem.features.swap.v2.impl.amount.DefaultSwapAmountUpdateTrigger +import com.tangem.features.swap.v2.impl.amount.SwapAmountUpdateListener import com.tangem.features.swap.v2.impl.amount.model.SwapAmountModel import dagger.Binds import dagger.Module import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent import dagger.multibindings.ClassKey import dagger.multibindings.IntoMap @@ -17,4 +21,15 @@ internal interface SwapAmountModule { @IntoMap @ClassKey(SwapAmountModel::class) fun provideSwapAmountModel(impl: SwapAmountModel): Model +} + +@InstallIn(SingletonComponent::class) +@Module +internal interface SwapAmountModuleBinds { + + @Binds + fun provideSwapAmountUpdateTrigger(impl: DefaultSwapAmountUpdateTrigger): SwapAmountUpdateTrigger + + @Binds + fun provideSwapAmountUpdateListener(impl: DefaultSwapAmountUpdateTrigger): SwapAmountUpdateListener } \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountClickIntents.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountClickIntents.kt index 34cf98f664..7e5a767bb6 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountClickIntents.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountClickIntents.kt @@ -8,4 +8,5 @@ internal interface SwapAmountClickIntents : AmountScreenClickIntents { fun onExpandEditField(selectedAmountType: SwapAmountType) fun onInfoClick() fun onSelectTokenClick() + fun onSeparatorClick() } \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt index 0b561eda91..81c5fc43e2 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt @@ -8,6 +8,7 @@ import com.tangem.common.ui.amountScreen.converters.MaxEnterAmountConverter import com.tangem.common.ui.amountScreen.models.AmountState import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.common.ui.navigationButtons.NavigationButton +import com.tangem.common.ui.navigationButtons.NavigationUM import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer @@ -32,6 +33,7 @@ import com.tangem.domain.transaction.usecase.GetAllowanceUseCase import com.tangem.features.swap.v2.impl.R import com.tangem.features.swap.v2.impl.amount.SwapAmountBlockComponent.SwapChooseProviderConfig import com.tangem.features.swap.v2.impl.amount.SwapAmountComponentParams +import com.tangem.features.swap.v2.impl.amount.SwapAmountUpdateListener import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountType import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM @@ -39,7 +41,6 @@ import com.tangem.features.swap.v2.impl.amount.model.converter.SwapAmountReadySt import com.tangem.features.swap.v2.impl.amount.model.transformers.* import com.tangem.features.swap.v2.impl.chooseprovider.SwapChooseProviderComponent import com.tangem.features.swap.v2.impl.choosetoken.fromSupported.SwapChooseTokenNetworkListener -import com.tangem.common.ui.navigationButtons.NavigationUM import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM.Content.DifferencePercent import com.tangem.utils.coroutines.CoroutineDispatcherProvider @@ -67,6 +68,7 @@ internal class SwapAmountModel @Inject constructor( private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val appRouter: AppRouter, private val swapAlertFactory: SwapAlertFactory, + private val swapAmountUpdateListener: SwapAmountUpdateListener, ) : Model(), SwapAmountClickIntents, SwapChooseProviderComponent.ModelCallback { private val params: SwapAmountComponentParams = paramsContainer.require() @@ -99,6 +101,7 @@ internal class SwapAmountModel @Inject constructor( } configAmountNavigation() subscribeOnCryptoCurrencyStatusFlow() + subscribeOnAmountUpdateTriggerUpdates() observeChooseSelectToken() // todo observe balance hiding flow } @@ -189,6 +192,14 @@ internal class SwapAmountModel @Inject constructor( ) } + override fun onSeparatorClick() { + // todo send with swap make swap types + val amountFieldData = uiState.value.primaryAmount.amountField as? AmountState.Data + val callback = (params as? SwapAmountComponentParams.AmountParams)?.callback ?: return + + callback.onSeparatorClick(lastAmount = amountFieldData?.amountTextField?.value.orEmpty()) + } + private fun subscribeOnCryptoCurrencyStatusFlow() { params.primaryCryptoCurrencyStatusFlow.onEach { primaryCurrencyStatus -> primaryCryptoCurrencyStatus = primaryCurrencyStatus @@ -210,6 +221,16 @@ internal class SwapAmountModel @Inject constructor( }.launchIn(modelScope) } + private fun subscribeOnAmountUpdateTriggerUpdates() { + swapAmountUpdateListener.updateAmountTriggerFlow + .onEach { + if (uiState.value is SwapAmountUM.Content) { + onAmountValueChange(it) + } + } + .launchIn(modelScope) + } + private fun observeChooseSelectToken() { swapChooseTokenNetworkListener.swapChooseTokenNetworkResultFlow .onEach { currency -> diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/SwapAmountContent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/SwapAmountContent.kt index 671ce82c48..212a0ca592 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/SwapAmountContent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/SwapAmountContent.kt @@ -27,11 +27,9 @@ import androidx.constraintlayout.compose.ConstraintLayout import com.tangem.common.ui.amountScreen.models.AmountState import com.tangem.common.ui.amountScreen.ui.AmountFieldV2 import com.tangem.core.ui.components.RectangleShimmer -import com.tangem.core.ui.components.SecondaryButton import com.tangem.core.ui.components.SpacerWMax import com.tangem.core.ui.components.TextShimmer import com.tangem.core.ui.components.atoms.text.EllipsisText -import com.tangem.core.ui.components.buttons.common.TangemButtonSize import com.tangem.core.ui.components.currency.icon.CurrencyIcon import com.tangem.core.ui.components.currency.icon.CurrencyIconState import com.tangem.core.ui.extensions.conditionalCompose @@ -81,6 +79,7 @@ internal fun SwapAmountContent( }, ) SwapAmountBlockSeparator( + onClick = clickIntents::onSeparatorClick, modifier = Modifier .constrainAs(middleButtonRef) { top.linkTo(amountFromRef.bottom) @@ -93,7 +92,7 @@ internal fun SwapAmountContent( } @Composable -private fun SwapAmountBlockSeparator(modifier: Modifier = Modifier) { +private fun SwapAmountBlockSeparator(onClick: () -> Unit, modifier: Modifier = Modifier) { // todo add swap type like [Swap, SendViaSwap, SendIncognito] Row( verticalAlignment = Alignment.CenterVertically, @@ -102,13 +101,11 @@ private fun SwapAmountBlockSeparator(modifier: Modifier = Modifier) { .heightIn(max = 28.dp) .clip(RoundedCornerShape(32.dp)) .background(TangemTheme.colors.background.secondary) - .padding( - vertical = 6.dp, - horizontal = 12.dp, - ), + .clickable(onClick = onClick) + .padding(vertical = 6.dp, horizontal = 12.dp), ) { Text( - text = "Convert", + text = stringResourceSafe(R.string.common_convert), style = TangemTheme.typography.caption1, color = TangemTheme.colors.text.tertiary, ) @@ -180,8 +177,7 @@ private fun SwapAmountEditBlock( Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(12.dp), - modifier = modifier - .padding(vertical = 48.dp), + modifier = modifier.padding(top = 48.dp, bottom = 28.dp), ) { if (amountFieldUM.amountField !is AmountState.Data) { TextShimmer( @@ -263,24 +259,10 @@ private fun SwapAmountInfo( @Composable private fun SwapAmountInfoMain(amountFieldUM: SwapAmountFieldUM, modifier: Modifier = Modifier) { AnimatedContent( - targetState = amountFieldUM !is SwapAmountFieldUM.Content, + targetState = amountFieldUM is SwapAmountFieldUM.Content, modifier = modifier, ) { isContent -> - if (isContent) { - Column( - verticalArrangement = Arrangement.spacedBy(2.dp), - ) { - TextShimmer( - style = TangemTheme.typography.subtitle2, - modifier = Modifier.width(56.dp), - ) - TextShimmer( - style = TangemTheme.typography.caption2, - modifier = Modifier.width(72.dp), - ) - } - } else { - val amountFieldUM = amountFieldUM as SwapAmountFieldUM.Content + if (isContent && amountFieldUM is SwapAmountFieldUM.Content) { Column( verticalArrangement = Arrangement.spacedBy(2.dp), ) { @@ -297,6 +279,19 @@ private fun SwapAmountInfoMain(amountFieldUM: SwapAmountFieldUM, modifier: Modif ellipsis = amountFieldUM.subtitleEllipsis, ) } + } else { + Column( + verticalArrangement = Arrangement.spacedBy(2.dp), + ) { + TextShimmer( + style = TangemTheme.typography.subtitle2, + modifier = Modifier.width(56.dp), + ) + TextShimmer( + style = TangemTheme.typography.caption2, + modifier = Modifier.width(72.dp), + ) + } } } } @@ -323,8 +318,7 @@ private fun SwapAmountInfoExtra( SwapAmountInfoQuote( quoteUM = amountUM.selectedQuote, swapRateType = amountUM.swapRateType, - onSelectTokenClick = - onSelectTokenClick, + onSelectTokenClick = onSelectTokenClick, ) } } @@ -352,11 +346,20 @@ private fun SwapAmountInfoExtra( @Composable private fun AmountMaxButton(onMaxAmountClick: () -> Unit) { - SecondaryButton( + Text( text = stringResourceSafe(R.string.send_max_amount), - size = TangemButtonSize.Small, - onClick = onMaxAmountClick, - modifier = Modifier.padding(end = 16.dp), + style = TangemTheme.typography.caption1, + color = TangemTheme.colors.text.primary1, + modifier = Modifier + .padding(end = 16.dp) + .clip(RoundedCornerShape(16.dp)) + .background(TangemTheme.colors.background.secondary) + .padding(horizontal = 12.dp, vertical = 4.dp) + .clickable( + interactionSource = remember { MutableInteractionSource() }, + indication = ripple(), + onClick = onMaxAmountClick, + ), ) } diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountClickIntentsStub.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountClickIntentsStub.kt index 194e61c3d3..1c60ad739a 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountClickIntentsStub.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountClickIntentsStub.kt @@ -10,6 +10,8 @@ internal object SwapAmountClickIntentsStub : SwapAmountClickIntents { override fun onSelectTokenClick() {} + override fun onSeparatorClick() {} + override fun onAmountValueChange(value: String) {} override fun onAmountPasteTriggerDismiss() {}