Updated on 2026-08-14

This commit is contained in:
Tangem 2025-06-20 16:50:40 +05:00
parent f9feacf074
commit 07f63b47e4
15 changed files with 245 additions and 32 deletions

View file

@ -1,3 +1,6 @@
package com.tangem.features.send.v2.api
interface SendFeatureToggles
interface SendFeatureToggles {
val isSendRedesignEnabled: Boolean
}

View file

@ -4,5 +4,8 @@ import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.features.send.v2.api.SendFeatureToggles
internal class DefaultSendFeatureToggles(
@Suppress("UnusedPrivateMember") private val featureToggles: FeatureTogglesManager,
) : SendFeatureToggles
private val featureToggles: FeatureTogglesManager,
) : SendFeatureToggles {
override val isSendRedesignEnabled: Boolean
get() = featureToggles.isFeatureEnabled("SEND_REDESIGN_ENABLED")
}

View file

@ -37,6 +37,7 @@ import com.tangem.domain.wallets.models.isMultiCurrency
import com.tangem.domain.wallets.models.requireColdWallet
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.send.v2.api.SendComponent
import com.tangem.features.send.v2.api.SendFeatureToggles
import com.tangem.features.send.v2.common.CommonSendRoute
import com.tangem.features.send.v2.common.PredefinedValues
import com.tangem.features.send.v2.common.SendConfirmAlertFactory
@ -87,6 +88,7 @@ internal class SendModel @Inject constructor(
private val createTransferTransactionUseCase: CreateTransferTransactionUseCase,
private val getFeeUseCase: GetFeeUseCase,
private val sendAmountUpdateQRTrigger: SendAmountUpdateQRTrigger,
private val sendFeatureToggles: SendFeatureToggles,
) : Model(), SendComponentCallback {
private val params: SendComponent.Params = paramsContainer.require()
@ -357,6 +359,7 @@ internal class SendModel @Inject constructor(
amountUM = AmountState.Empty(),
destinationUM = SendDestinationInitialStateTransformer(
cryptoCurrency = cryptoCurrency,
isRedesignEnabled = sendFeatureToggles.isSendRedesignEnabled,
).transform(DestinationUM.Empty()),
feeUM = FeeUM.Empty(),
confirmUM = ConfirmUM.Empty,

View file

@ -23,6 +23,7 @@ import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.isLocked
import com.tangem.domain.wallets.models.isMultiCurrency
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.features.send.v2.api.SendFeatureToggles
import com.tangem.features.send.v2.common.PredefinedValues
import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents
import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents.SendScreenSource
@ -65,6 +66,7 @@ internal class SendDestinationModel @Inject constructor(
private val listenToQrScanningUseCase: ListenToQrScanningUseCase,
private val parseQrCodeUseCase: ParseQrCodeUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
private val sendFeatureToggles: SendFeatureToggles,
) : Model(), SendDestinationClickIntents {
private val params: SendDestinationComponentParams = paramsContainer.require()
@ -90,6 +92,7 @@ internal class SendDestinationModel @Inject constructor(
_uiState.update(
SendDestinationInitialStateTransformer(
cryptoCurrency = cryptoCurrency,
isRedesignEnabled = sendFeatureToggles.isSendRedesignEnabled,
isInitialized = true,
),
)
@ -297,6 +300,7 @@ internal class SendDestinationModel @Inject constructor(
flow2 = params.currentRoute,
transform = { state, route -> state to route },
).onEach { (state, route) ->
val isRedesignEnabled = sendFeatureToggles.isSendRedesignEnabled
params.callback.onNavigationResult(
NavigationUM.Content(
title = params.title,
@ -319,8 +323,16 @@ internal class SendDestinationModel @Inject constructor(
}
params.onBackClick()
},
additionalIconRes = R.drawable.ic_qrcode_scan_24,
additionalIconClick = ::onQrCodeScanClick,
additionalIconRes = if (isRedesignEnabled) {
null
} else {
R.drawable.ic_qrcode_scan_24
},
additionalIconClick = if (isRedesignEnabled) {
null
} else {
::onQrCodeScanClick
},
primaryButton = ButtonsUM.PrimaryButtonUM(
text = if (route.isEditMode) {
resourceReference(R.string.common_continue)

View file

@ -13,6 +13,7 @@ import com.tangem.utils.transformer.Transformer
internal class SendDestinationInitialStateTransformer(
val cryptoCurrency: CryptoCurrency,
val isRedesignEnabled: Boolean,
val isInitialized: Boolean = false,
) : Transformer<DestinationUM> {
override fun transform(prevState: DestinationUM): DestinationUM {
@ -54,6 +55,7 @@ internal class SendDestinationInitialStateTransformer(
recent = loadingListState(RECENT_KEY_TAG, RECENT_DEFAULT_COUNT),
networkName = cryptoCurrency.network.name,
isValidating = false,
isRedesignEnabled = isRedesignEnabled,
)
}
}

View file

@ -16,6 +16,10 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.containers.FooterContainer
import com.tangem.core.ui.components.inputrow.InputRowRecipient
@ -60,10 +64,13 @@ internal fun SendDestinationContent(
isError = isError,
isValidating = isValidating,
onAddressChange = clickIntents::onRecipientAddressValueChange,
onQrCodeClick = clickIntents::onQrCodeScanClick,
isRedesignEnabled = state.isRedesignEnabled,
)
memoField(
memoField = memoField,
onMemoChange = clickIntents::onRecipientMemoValueChange,
isRedesignEnabled = state.isRedesignEnabled,
)
listHeaderItem(
titleRes = R.string.send_recipient_wallets_title,
@ -100,16 +107,23 @@ internal fun SendDestinationContent(
}
}
@Suppress("LongParameterList")
private fun LazyListScope.addressItem(
address: DestinationTextFieldUM.RecipientAddress,
networkName: String,
isError: Boolean,
isValidating: Boolean,
onAddressChange: (String, EnterAddressSource) -> Unit,
onQrCodeClick: () -> Unit,
isRedesignEnabled: Boolean,
) {
item(key = ADDRESS_FIELD_KEY) {
FooterContainer(
footer = resourceReference(R.string.send_recipient_address_footer, wrappedList(networkName)),
footer = if (isRedesignEnabled) {
buildHighlightedFooterText(networkName)
} else {
resourceReference(R.string.send_recipient_address_footer, wrappedList(networkName))
},
) {
InputRowRecipient(
value = address.value,
@ -117,6 +131,8 @@ private fun LazyListScope.addressItem(
placeholder = address.placeholder,
onValueChange = { onAddressChange(it, EnterAddressSource.InputField) },
onPasteClick = { onAddressChange(it, EnterAddressSource.PasteButton) },
onQrCodeClick = onQrCodeClick,
isRedesignEnabled = isRedesignEnabled,
isError = isError,
isLoading = isValidating,
error = address.error,
@ -131,9 +147,37 @@ private fun LazyListScope.addressItem(
}
}
@Composable
private fun buildHighlightedFooterText(networkName: String): TextReference {
return annotatedReference(
buildAnnotatedString {
val styledText = stringResourceSafe(
R.string.send_recipient_address_footer_highlighted_part,
networkName,
)
val styledColor = TangemTheme.colors.text.secondary
appendWithStyledPlaceholder(
template = stringResourceSafe(
R.string.send_recipient_address_footer_v2,
networkName,
),
placeholder = networkName,
) {
withStyle(SpanStyle(fontWeight = FontWeight.Medium)) {
appendColored(
text = styledText,
color = styledColor,
)
}
}
},
)
}
private fun LazyListScope.memoField(
memoField: DestinationTextFieldUM.RecipientMemo?,
onMemoChange: (String, Boolean) -> Unit,
isRedesignEnabled: Boolean,
) {
if (memoField != null) {
item(key = MEMO_FIELD_KEY) {
@ -142,7 +186,24 @@ private fun LazyListScope.memoField(
value = memoField.value,
label = memoField.label,
placeholder = placeholder,
footer = resourceReference(R.string.send_recipient_memo_footer),
footer = if (isRedesignEnabled) {
annotatedReference(
buildAnnotatedString {
append(stringResourceSafe(R.string.send_recipient_memo_footer_v2))
append("\n")
withStyle(SpanStyle(fontWeight = FontWeight.Medium)) {
appendColored(
text = stringResourceSafe(
R.string.send_recipient_memo_footer_v2_highlighted,
),
color = TangemTheme.colors.text.secondary,
)
}
},
)
} else {
resourceReference(R.string.send_recipient_memo_footer)
},
onValueChange = { onMemoChange(it, false) },
onPasteClick = { onMemoChange(it, true) },
modifier = Modifier.padding(top = 20.dp),
@ -151,6 +212,7 @@ private fun LazyListScope.memoField(
error = memoField.error,
isReadOnly = !memoField.isEnabled,
isValuePasted = memoField.isValuePasted,
isRedesignEnabled = isRedesignEnabled,
)
}
}

View file

@ -20,6 +20,7 @@ import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.components.containers.FooterContainer
@Suppress("LongMethod", "LongParameterList")
@Composable
internal fun TextFieldWithPaste(
value: String,
@ -27,6 +28,7 @@ internal fun TextFieldWithPaste(
label: TextReference,
onValueChange: (String) -> Unit,
onPasteClick: (String) -> Unit,
isRedesignEnabled: Boolean,
modifier: Modifier = Modifier,
footer: TextReference? = null,
labelStyle: TextStyle = TangemTheme.typography.body2,
@ -96,6 +98,18 @@ internal fun TextFieldWithPaste(
PasteButton(
isPasteButtonVisible = value.isBlank(),
onClick = onPasteClick,
backgroundColorEnabled = if (isRedesignEnabled) {
TangemTheme.colors.button.secondary
} else {
TangemTheme.colors.button.primary
},
textColor = if (isRedesignEnabled) {
TangemTheme.colors.text.primary1
} else {
TangemTheme.colors.text.primary2
},
modifier = Modifier
.padding(start = TangemTheme.dimens.spacing8),
)
}
}

View file

@ -17,6 +17,7 @@ internal sealed class DestinationUM {
val networkName: String,
val isValidating: Boolean = false,
val isInitialized: Boolean = false,
val isRedesignEnabled: Boolean = false,
) : DestinationUM()
data class Empty(