Updated on 2026-08-14
This commit is contained in:
parent
2f5b70dfaa
commit
e18f70eb71
13 changed files with 135 additions and 36 deletions
|
|
@ -75,6 +75,7 @@ internal class ProxyAppRouter(
|
|||
try {
|
||||
block()
|
||||
} catch (e: Throwable) {
|
||||
Timber.e(e)
|
||||
onComplete(false)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
) {
|
||||
|
|
|
|||
9
core/ui/src/main/res/drawable/ic_convert_24.xml
Normal file
9
core/ui/src/main/res/drawable/ic_convert_24.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M7.95,5.1C8.583,4.733 9.233,4.458 9.9,4.275C10.567,4.092 11.25,4 11.95,4C14.183,4 16.083,4.775 17.65,6.325C19.217,7.875 20,9.767 20,12V12.175L21.6,10.575L23,11.975L19,15.975L15,11.975L16.4,10.575L18,12.175V12C18,10.333 17.413,8.917 16.237,7.75C15.063,6.583 13.633,6 11.95,6C11.517,6 11.092,6.05 10.675,6.15C10.258,6.25 9.85,6.4 9.45,6.6L7.95,5.1ZM1,12.025L5,8.025L9,12.025L7.6,13.425L6,11.825V12C6,13.667 6.588,15.083 7.762,16.25C8.938,17.417 10.367,18 12.05,18C12.483,18 12.908,17.95 13.325,17.85C13.742,17.75 14.15,17.6 14.55,17.4L16.05,18.9C15.417,19.267 14.767,19.542 14.1,19.725C13.433,19.908 12.75,20 12.05,20C9.817,20 7.917,19.225 6.35,17.675C4.783,16.125 4,14.233 4,12V11.825L2.4,13.425L1,12.025Z"
|
||||
android:fillColor="#1F1F1F"/>
|
||||
</vector>
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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<String>
|
||||
}
|
||||
|
||||
@Singleton
|
||||
internal class DefaultSwapAmountUpdateTrigger @Inject constructor() :
|
||||
SwapAmountUpdateTrigger,
|
||||
SwapAmountUpdateListener {
|
||||
|
||||
override val updateAmountTriggerFlow: Flow<String>
|
||||
field = MutableSharedFlow<String>()
|
||||
|
||||
override suspend fun triggerUpdateAmount(amountValue: String) {
|
||||
updateAmountTriggerFlow.emit(amountValue)
|
||||
}
|
||||
}
|
||||
|
|
@ -38,5 +38,6 @@ internal class SwapAmountComponent @AssistedInject constructor(
|
|||
|
||||
interface ModelCallback : SwapNavigationModelCallback {
|
||||
fun onAmountResult(amountUM: SwapAmountUM)
|
||||
fun onSeparatorClick(lastAmount: String)
|
||||
}
|
||||
}
|
||||
|
|
@ -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<CryptoCurrencyStatus>,
|
||||
override val secondaryCryptoCurrency: CryptoCurrency?,
|
||||
val title: TextReference,
|
||||
val callback: SwapAmountComponent.ModelCallback,
|
||||
val currentRoute: Flow<SwapRoute.Amount>,
|
||||
val currentRoute: Flow<SwapAmountRoute>,
|
||||
) : SwapAmountComponentParams()
|
||||
|
||||
data class AmountBlockParams(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.features.swap.v2.impl.amount
|
||||
|
||||
/**
|
||||
* Common interface for routing to swap amount
|
||||
*/
|
||||
interface SwapAmountRoute {
|
||||
val isEditMode: Boolean
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -8,4 +8,5 @@ internal interface SwapAmountClickIntents : AmountScreenClickIntents {
|
|||
fun onExpandEditField(selectedAmountType: SwapAmountType)
|
||||
fun onInfoClick()
|
||||
fun onSelectTokenClick()
|
||||
fun onSeparatorClick()
|
||||
}
|
||||
|
|
@ -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 ->
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ internal object SwapAmountClickIntentsStub : SwapAmountClickIntents {
|
|||
|
||||
override fun onSelectTokenClick() {}
|
||||
|
||||
override fun onSeparatorClick() {}
|
||||
|
||||
override fun onAmountValueChange(value: String) {}
|
||||
|
||||
override fun onAmountPasteTriggerDismiss() {}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue