Updated on 2026-08-14
This commit is contained in:
parent
7b862a2494
commit
57cc063cdf
15 changed files with 191 additions and 28 deletions
|
|
@ -14,7 +14,12 @@ interface SendComponent : ComposableContentComponent {
|
|||
val amount: String? = null,
|
||||
val tag: String? = null,
|
||||
val destinationAddress: String? = null,
|
||||
val callback: ModelCallback? = null,
|
||||
)
|
||||
|
||||
interface Factory : ComponentFactory<Params, SendComponent>
|
||||
|
||||
interface ModelCallback {
|
||||
fun onConvertToAnotherToken(lastAmount: String)
|
||||
}
|
||||
}
|
||||
|
|
@ -3,4 +3,5 @@ package com.tangem.features.send.v2.api
|
|||
interface SendFeatureToggles {
|
||||
|
||||
val isSendRedesignEnabled: Boolean
|
||||
val isSendWithSwapEnabled: Boolean
|
||||
}
|
||||
|
|
@ -1,10 +1,17 @@
|
|||
package com.tangem.features.send.v2.api.subcomponents.destination
|
||||
|
||||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM
|
||||
|
||||
interface SendDestinationBlockComponent : ComposableContentComponent {
|
||||
|
||||
interface Factory :
|
||||
ComponentFactory<SendDestinationComponentParams.DestinationBlockParams, SendDestinationBlockComponent>
|
||||
interface Factory {
|
||||
fun create(
|
||||
context: AppComponentContext,
|
||||
params: SendDestinationComponentParams.DestinationBlockParams,
|
||||
onClick: () -> Unit,
|
||||
onResult: (DestinationUM) -> Unit,
|
||||
): SendDestinationBlockComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,8 @@ import com.tangem.features.send.v2.api.subcomponents.destination.entity.Destinat
|
|||
|
||||
interface SendDestinationComponent : ComposableContentComponent {
|
||||
|
||||
fun updateState(destinationUM: DestinationUM)
|
||||
|
||||
interface ModelCallback : NavigationModelCallback {
|
||||
fun onDestinationResult(destinationUM: DestinationUM)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,4 +8,6 @@ internal class DefaultSendFeatureToggles(
|
|||
) : SendFeatureToggles {
|
||||
override val isSendRedesignEnabled: Boolean
|
||||
get() = featureToggles.isFeatureEnabled("SEND_REDESIGN_ENABLED")
|
||||
override val isSendWithSwapEnabled: Boolean
|
||||
get() = featureToggles.isFeatureEnabled("SEND_VIA_SWAP_ENABLED")
|
||||
}
|
||||
|
|
@ -49,7 +49,7 @@ import com.tangem.features.send.v2.send.confirm.SendConfirmComponent
|
|||
import com.tangem.features.send.v2.send.success.SendConfirmSuccessComponent
|
||||
import com.tangem.features.send.v2.send.ui.state.SendUM
|
||||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponent
|
||||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountUpdateQRTrigger
|
||||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountUpdateTrigger
|
||||
import com.tangem.features.send.v2.subcomponents.destination.model.transformers.SendDestinationInitialStateTransformer
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeComponent
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
|
|
@ -89,7 +89,7 @@ internal class SendModel @Inject constructor(
|
|||
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
||||
private val createTransferTransactionUseCase: CreateTransferTransactionUseCase,
|
||||
private val getFeeUseCase: GetFeeUseCase,
|
||||
private val sendAmountUpdateQRTrigger: SendAmountUpdateQRTrigger,
|
||||
private val sendAmountUpdateTrigger: SendAmountUpdateTrigger,
|
||||
private val sendFeatureToggles: SendFeatureToggles,
|
||||
) : Model(), SendComponentCallback {
|
||||
|
||||
|
|
@ -153,6 +153,10 @@ internal class SendModel @Inject constructor(
|
|||
_uiState.update { sendUM }
|
||||
}
|
||||
|
||||
override fun onConvertToAnotherToken(lastAmount: String) {
|
||||
params.callback?.onConvertToAnotherToken(lastAmount = lastAmount)
|
||||
}
|
||||
|
||||
suspend fun loadFee(): Either<GetFeeError, TransactionFee> {
|
||||
val predefinedValues = predefinedValues
|
||||
val transferTransaction = if (predefinedValues is PredefinedValues.Content.Deeplink) {
|
||||
|
|
@ -349,7 +353,7 @@ internal class SendModel @Inject constructor(
|
|||
)
|
||||
// If it is in active state use flow to update value in amount component
|
||||
modelScope.launch {
|
||||
amount?.let { sendAmountUpdateQRTrigger.triggerUpdateAmount(it) }
|
||||
amount?.let { sendAmountUpdateTrigger.triggerUpdateAmount(it) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +1,17 @@
|
|||
package com.tangem.features.send.v2.subcomponents.amount
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.common.ui.amountScreen.AmountScreenContent
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.common.ui.navigationButtons.NavigationModelCallback
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponentParams.AmountParams
|
||||
import com.tangem.features.send.v2.subcomponents.amount.model.SendAmountModel
|
||||
import com.tangem.features.send.v2.subcomponents.amount.ui.SendAmountContent
|
||||
|
||||
internal class SendAmountComponent(
|
||||
appComponentContext: AppComponentContext,
|
||||
|
|
@ -29,15 +27,17 @@ internal class SendAmountComponent(
|
|||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
val isBalanceHidden by params.isBalanceHidingFlow.collectAsStateWithLifecycle()
|
||||
|
||||
AmountScreenContent(
|
||||
SendAmountContent(
|
||||
amountState = state,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
clickIntents = model,
|
||||
modifier = Modifier.background(TangemTheme.colors.background.tertiary),
|
||||
isSendWithSwapEnabled = model.isSendWithSwapEnabled,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
interface ModelCallback : NavigationModelCallback {
|
||||
fun onAmountResult(amountUM: AmountState, isResetPredefined: Boolean)
|
||||
fun onConvertToAnotherToken(lastAmount: String)
|
||||
}
|
||||
}
|
||||
|
|
@ -29,7 +29,7 @@ interface SendAmountReduceListener {
|
|||
* Trigger amount change from another component.
|
||||
* Different from another triggers because it takes raw string instead of BigDecimal
|
||||
*/
|
||||
interface SendAmountUpdateQRTrigger {
|
||||
interface SendAmountUpdateTrigger {
|
||||
suspend fun triggerUpdateAmount(amountValue: String)
|
||||
}
|
||||
|
||||
|
|
@ -37,7 +37,7 @@ interface SendAmountUpdateQRTrigger {
|
|||
* Trigger amount change from another component.
|
||||
* Different from another triggers because it takes raw string instead of BigDecimal
|
||||
*/
|
||||
interface SendAmountUpdateQRListener {
|
||||
interface SendAmountUpdateListener {
|
||||
val updateAmountTriggerFlow: Flow<String>
|
||||
}
|
||||
|
||||
|
|
@ -45,8 +45,8 @@ interface SendAmountUpdateQRListener {
|
|||
internal class DefaultSendAmountReduceTrigger @Inject constructor() :
|
||||
SendAmountReduceTrigger,
|
||||
SendAmountReduceListener,
|
||||
SendAmountUpdateQRTrigger,
|
||||
SendAmountUpdateQRListener {
|
||||
SendAmountUpdateTrigger,
|
||||
SendAmountUpdateListener {
|
||||
|
||||
override val reduceToTriggerFlow = MutableSharedFlow<BigDecimal>()
|
||||
override val reduceByTriggerFlow = MutableSharedFlow<ReduceByData>()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.features.send.v2.subcomponents.amount.di
|
||||
|
||||
import com.tangem.features.send.v2.subcomponents.amount.*
|
||||
import com.tangem.features.send.v2.subcomponents.amount.DefaultSendAmountReduceTrigger
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -22,9 +21,9 @@ internal interface SendAmountModule {
|
|||
|
||||
@Singleton
|
||||
@Binds
|
||||
fun provideSendAmountUpdateQRListener(impl: DefaultSendAmountReduceTrigger): SendAmountUpdateQRListener
|
||||
fun provideSendAmountUpdateListener(impl: DefaultSendAmountReduceTrigger): SendAmountUpdateListener
|
||||
|
||||
@Singleton
|
||||
@Binds
|
||||
fun provideSendAmountUpdateQRTrigger(impl: DefaultSendAmountReduceTrigger): SendAmountUpdateQRTrigger
|
||||
fun provideSendAmountUpdateTrigger(impl: DefaultSendAmountReduceTrigger): SendAmountUpdateTrigger
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.features.send.v2.subcomponents.amount.model
|
||||
|
||||
import com.tangem.common.ui.amountScreen.AmountScreenClickIntents
|
||||
|
||||
interface SendAmountClickIntents : AmountScreenClickIntents {
|
||||
|
||||
fun onConvertToAnotherToken()
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ package com.tangem.features.send.v2.subcomponents.amount.model
|
|||
|
||||
import androidx.compose.runtime.Stable
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.common.ui.amountScreen.AmountScreenClickIntents
|
||||
import com.tangem.common.ui.amountScreen.converters.*
|
||||
import com.tangem.common.ui.amountScreen.converters.field.AmountBoundaryUpdateTransformer
|
||||
import com.tangem.common.ui.amountScreen.converters.field.AmountFieldChangeTransformer
|
||||
|
|
@ -31,7 +30,7 @@ import com.tangem.features.send.v2.api.entity.PredefinedValues
|
|||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponentParams
|
||||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountReduceListener
|
||||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountUpdateQRListener
|
||||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountUpdateListener
|
||||
import com.tangem.features.send.v2.subcomponents.amount.analytics.SendAmountAnalyticEvents
|
||||
import com.tangem.features.send.v2.subcomponents.amount.analytics.SendAmountAnalyticEvents.SelectedCurrencyType
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeData
|
||||
|
|
@ -54,12 +53,12 @@ internal class SendAmountModel @Inject constructor(
|
|||
private val getMinimumTransactionAmountSyncUseCase: GetMinimumTransactionAmountSyncUseCase,
|
||||
private val sendAmountReduceListener: SendAmountReduceListener,
|
||||
private val feeReloadTrigger: SendFeeReloadTrigger,
|
||||
private val sendAmountUpdateQRListener: SendAmountUpdateQRListener,
|
||||
private val sendAmountUpdateListener: SendAmountUpdateListener,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val sendFeatureToggles: SendFeatureToggles,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||
) : Model(), AmountScreenClickIntents {
|
||||
) : Model(), SendAmountClickIntents {
|
||||
|
||||
private val params: SendAmountComponentParams = paramsContainer.require()
|
||||
private var appCurrency: AppCurrency = AppCurrency.Default
|
||||
|
|
@ -68,6 +67,8 @@ internal class SendAmountModel @Inject constructor(
|
|||
private val _uiState = MutableStateFlow(params.state)
|
||||
val uiState = _uiState.asStateFlow()
|
||||
|
||||
val isSendWithSwapEnabled = sendFeatureToggles.isSendWithSwapEnabled
|
||||
|
||||
private val analyticsCategoryName = params.analyticsCategoryName
|
||||
private var cryptoCurrencyStatus: CryptoCurrencyStatus = CryptoCurrencyStatus(
|
||||
currency = params.cryptoCurrency,
|
||||
|
|
@ -99,7 +100,7 @@ internal class SendAmountModel @Inject constructor(
|
|||
subscribeOnAmountReduceByTriggerUpdates()
|
||||
subscribeOnAmountReduceToTriggerUpdates()
|
||||
subscribeOnAmountIgnoreReduceTriggerUpdates()
|
||||
subscribeOnAmountUpdateQRTriggerUpdates()
|
||||
subscribeOnAmountUpdateTriggerUpdates()
|
||||
}
|
||||
initMinBoundary()
|
||||
}
|
||||
|
|
@ -217,6 +218,12 @@ internal class SendAmountModel @Inject constructor(
|
|||
saveResult()
|
||||
}
|
||||
|
||||
override fun onConvertToAnotherToken() {
|
||||
val amountFieldData = uiState.value as? AmountState.Data
|
||||
val amountParams = params as? SendAmountComponentParams.AmountParams
|
||||
amountParams?.callback?.onConvertToAnotherToken(amountFieldData?.amountTextField?.value.orEmpty())
|
||||
}
|
||||
|
||||
private fun subscribeOnAmountReduceToTriggerUpdates() {
|
||||
sendAmountReduceListener.reduceToTriggerFlow
|
||||
.onEach { reduceTo ->
|
||||
|
|
@ -261,8 +268,8 @@ internal class SendAmountModel @Inject constructor(
|
|||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun subscribeOnAmountUpdateQRTriggerUpdates() {
|
||||
sendAmountUpdateQRListener.updateAmountTriggerFlow
|
||||
private fun subscribeOnAmountUpdateTriggerUpdates() {
|
||||
sendAmountUpdateListener.updateAmountTriggerFlow
|
||||
.onEach { amount ->
|
||||
onAmountValueChange(amount)
|
||||
saveResult()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,109 @@
|
|||
package com.tangem.features.send.v2.subcomponents.amount.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.common.ui.amountScreen.AmountScreenContent
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.common.ui.amountScreen.preview.AmountStatePreviewData
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.subcomponents.amount.model.SendAmountClickIntents
|
||||
import com.tangem.features.send.v2.subcomponents.amount.ui.preview.SendAmountClickIntentsStub
|
||||
|
||||
@Composable
|
||||
fun SendAmountContent(
|
||||
amountState: AmountState,
|
||||
isBalanceHidden: Boolean,
|
||||
clickIntents: SendAmountClickIntents,
|
||||
isSendWithSwapEnabled: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(modifier = modifier.background(TangemTheme.colors.background.tertiary)) {
|
||||
AmountScreenContent(
|
||||
amountState = amountState,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
clickIntents = clickIntents,
|
||||
)
|
||||
if (isSendWithSwapEnabled) {
|
||||
SendConvertTokenButton(
|
||||
onConvertToAnother = clickIntents::onConvertToAnotherToken,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SendConvertTokenButton(onConvertToAnother: () -> Unit) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.fillMaxWidth()
|
||||
.clickable(
|
||||
indication = null,
|
||||
interactionSource = null,
|
||||
onClick = onConvertToAnother,
|
||||
),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(12.dp)
|
||||
.align(Alignment.Center),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_convert_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.background(TangemTheme.colors.control.unchecked, CircleShape)
|
||||
.padding(2.dp),
|
||||
)
|
||||
Text(
|
||||
text = stringResourceSafe(com.tangem.common.ui.R.string.send_amount_convert_to_another_token),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Composable
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
private fun SendAmountContent_Preview(@PreviewParameter(SendAmountContentPreviewProvider::class) params: AmountState) {
|
||||
TangemThemePreview {
|
||||
SendAmountContent(
|
||||
amountState = params,
|
||||
isBalanceHidden = true,
|
||||
clickIntents = SendAmountClickIntentsStub,
|
||||
isSendWithSwapEnabled = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class SendAmountContentPreviewProvider : PreviewParameterProvider<AmountState> {
|
||||
override val values: Sequence<AmountState>
|
||||
get() = sequenceOf(
|
||||
AmountStatePreviewData.amountStateV2,
|
||||
)
|
||||
}
|
||||
// endregion
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.tangem.features.send.v2.subcomponents.amount.ui.preview
|
||||
|
||||
import com.tangem.features.send.v2.subcomponents.amount.model.SendAmountClickIntents
|
||||
|
||||
internal object SendAmountClickIntentsStub : SendAmountClickIntents {
|
||||
override fun onConvertToAnotherToken() {}
|
||||
|
||||
override fun onAmountValueChange(value: String) {}
|
||||
|
||||
override fun onAmountPasteTriggerDismiss() {}
|
||||
|
||||
override fun onMaxValueClick() {}
|
||||
|
||||
override fun onCurrencyChangeClick(isFiat: Boolean) {}
|
||||
|
||||
override fun onAmountNext() {}
|
||||
}
|
||||
|
|
@ -21,8 +21,8 @@ import kotlinx.coroutines.flow.onEach
|
|||
internal class DefaultSendDestinationBlockComponent @AssistedInject constructor(
|
||||
@Assisted appComponentContext: AppComponentContext,
|
||||
@Assisted private val params: SendDestinationComponentParams.DestinationBlockParams,
|
||||
val onClick: () -> Unit,
|
||||
val onResult: (DestinationUM) -> Unit,
|
||||
@Assisted val onClick: () -> Unit,
|
||||
@Assisted val onResult: (DestinationUM) -> Unit,
|
||||
) : SendDestinationBlockComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: SendDestinationModel = getOrCreateModel(params = params)
|
||||
|
|
@ -54,6 +54,8 @@ internal class DefaultSendDestinationBlockComponent @AssistedInject constructor(
|
|||
override fun create(
|
||||
context: AppComponentContext,
|
||||
params: SendDestinationComponentParams.DestinationBlockParams,
|
||||
onClick: () -> Unit,
|
||||
onResult: (DestinationUM) -> Unit,
|
||||
): DefaultSendDestinationBlockComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@ internal class DefaultSendDestinationComponent @AssistedInject constructor(
|
|||
|
||||
private val model: SendDestinationModel = getOrCreateModel(params = params)
|
||||
|
||||
fun updateState(state: DestinationUM) = model.updateState(state)
|
||||
override fun updateState(destinationUM: DestinationUM) = model.updateState(destinationUM)
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue