Updated on 2026-08-14
This commit is contained in:
parent
717b8e2df2
commit
422e406d15
21 changed files with 258 additions and 112 deletions
|
|
@ -7,4 +7,5 @@ import com.squareup.moshi.JsonClass
|
|||
data class ValidationResponse(
|
||||
@Json(name = "status") val status: String,
|
||||
@Json(name = "result_type") val resultType: String,
|
||||
@Json(name = "description") val description: String?,
|
||||
)
|
||||
|
|
@ -39,6 +39,7 @@ internal object BlockAidMapper {
|
|||
} else {
|
||||
mapSimulationSuccessResult(from.simulation.accountSummary)
|
||||
},
|
||||
description = from.validation.description,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ class BlockAidMapperTest {
|
|||
spenders = mapOf("spender" to spenderDetails),
|
||||
)
|
||||
val response = TransactionScanResponse(
|
||||
validation = ValidationResponse(status = "Success", resultType = "Benign"),
|
||||
validation = ValidationResponse(status = "Success", resultType = "Benign", description = ""),
|
||||
simulation = SimulationResponse(
|
||||
status = "Success",
|
||||
accountSummary = AccountSummaryResponse(
|
||||
|
|
@ -79,7 +79,7 @@ class BlockAidMapperTest {
|
|||
outTransfer = listOf(Transfer(value = "1.5", rawValue = "0x2")),
|
||||
)
|
||||
val response = TransactionScanResponse(
|
||||
validation = ValidationResponse(status = "Success", resultType = "Benign"),
|
||||
validation = ValidationResponse(status = "Success", resultType = "Benign", description = ""),
|
||||
simulation = SimulationResponse(
|
||||
status = "Success",
|
||||
accountSummary = AccountSummaryResponse(
|
||||
|
|
@ -105,7 +105,7 @@ class BlockAidMapperTest {
|
|||
@Test
|
||||
fun `when response error validation rhen returns failed to validate`() {
|
||||
val response = TransactionScanResponse(
|
||||
validation = ValidationResponse(status = "Error", resultType = "Benign"),
|
||||
validation = ValidationResponse(status = "Error", resultType = "Benign", description = ""),
|
||||
simulation = SimulationResponse(
|
||||
status = "Success",
|
||||
accountSummary = AccountSummaryResponse(emptyList(), emptyList(), null),
|
||||
|
|
@ -120,7 +120,7 @@ class BlockAidMapperTest {
|
|||
@Test
|
||||
fun `when response not benign then returns validation unsafe`() {
|
||||
val response = TransactionScanResponse(
|
||||
validation = ValidationResponse(status = "Success", resultType = "Phishing"),
|
||||
validation = ValidationResponse(status = "Success", resultType = "Phishing", description = ""),
|
||||
simulation = SimulationResponse(
|
||||
status = "Success",
|
||||
accountSummary = AccountSummaryResponse(emptyList(), emptyList(), null),
|
||||
|
|
@ -134,7 +134,7 @@ class BlockAidMapperTest {
|
|||
@Test
|
||||
fun `when response simulation not success then returns simulation failed ro simulate`() {
|
||||
val response = TransactionScanResponse(
|
||||
validation = ValidationResponse(status = "Success", resultType = "Benign"),
|
||||
validation = ValidationResponse(status = "Success", resultType = "Benign", description = ""),
|
||||
simulation = SimulationResponse(
|
||||
status = "Error",
|
||||
accountSummary = AccountSummaryResponse(emptyList(), emptyList(), null),
|
||||
|
|
@ -148,7 +148,7 @@ class BlockAidMapperTest {
|
|||
@Test
|
||||
fun `when response simulation is empty then returns failed to simulate`() {
|
||||
val txResponse = TransactionScanResponse(
|
||||
validation = ValidationResponse(status = "Success", resultType = "Benign"),
|
||||
validation = ValidationResponse(status = "Success", resultType = "Benign", description = ""),
|
||||
simulation = SimulationResponse(
|
||||
status = "Success",
|
||||
accountSummary = AccountSummaryResponse(
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ internal class WcEthSendTransactionUseCase @AssistedInject constructor(
|
|||
emitAll(delegate.invoke(transactionData))
|
||||
}
|
||||
|
||||
override suspend fun getAmount(): Amount? {
|
||||
override fun getAmount(): Amount? {
|
||||
return approvalAmount
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ internal class WcEthSignTransactionUseCase @AssistedInject constructor(
|
|||
return dAppFee
|
||||
}
|
||||
|
||||
override suspend fun getAmount(): Amount? {
|
||||
override fun getAmount(): Amount? {
|
||||
return approvalAmount
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,5 +8,6 @@ package com.domain.blockaid.models.transaction
|
|||
*/
|
||||
data class CheckTransactionResult(
|
||||
val validation: ValidationResult,
|
||||
val description: String? = null,
|
||||
val simulation: SimulationResult,
|
||||
)
|
||||
|
|
@ -44,6 +44,6 @@ interface WcMutableFee {
|
|||
* [updateAmount] triggered a new [TransactionData] emit
|
||||
*/
|
||||
interface WcApproval {
|
||||
suspend fun getAmount(): Amount?
|
||||
fun getAmount(): Amount?
|
||||
fun updateAmount(amount: Amount?)
|
||||
}
|
||||
|
|
@ -60,6 +60,7 @@ internal abstract class WcCommonTransactionComponentDelegate(
|
|||
is WcTransactionRoutes.Transaction,
|
||||
is WcTransactionRoutes.TransactionRequestInfo,
|
||||
is WcTransactionRoutes.Alert,
|
||||
is WcTransactionRoutes.CustomAllowance,
|
||||
-> stackNavigation?.pop()
|
||||
else -> Unit
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ import com.tangem.core.decompose.context.AppComponentContext
|
|||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.features.walletconnect.connections.components.AlertsComponentV2
|
||||
import com.tangem.features.walletconnect.connections.utils.WcAlertsFactory.createCommonTransactionAppInfoAlertUM
|
||||
import com.tangem.features.walletconnect.transaction.components.send.WcCustomAllowanceComponent
|
||||
import com.tangem.features.walletconnect.transaction.entity.common.WcCommonTransactionModel
|
||||
import com.tangem.features.walletconnect.transaction.model.WcSendTransactionModel
|
||||
import com.tangem.features.walletconnect.transaction.routes.WcTransactionRoutes
|
||||
|
||||
internal fun getWcCommonScreen(
|
||||
|
|
@ -23,5 +25,12 @@ internal fun getWcCommonScreen(
|
|||
appComponentContext = appComponentContext,
|
||||
model = model,
|
||||
)
|
||||
is WcTransactionRoutes.CustomAllowance -> WcCustomAllowanceComponent(
|
||||
appComponentContext = appComponentContext,
|
||||
model = model,
|
||||
onClickDoneCustomAllowance = { value, isUnlimited ->
|
||||
(model as? WcSendTransactionModel)?.onClickDoneCustomAllowance(value, isUnlimited)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package com.tangem.features.walletconnect.transaction.components.send
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.features.walletconnect.transaction.entity.common.WcCommonTransactionModel
|
||||
import com.tangem.features.walletconnect.transaction.entity.send.WcSendTransactionUM
|
||||
import com.tangem.features.walletconnect.transaction.ui.approve.WcCustomAllowanceContent
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class WcCustomAllowanceComponent(
|
||||
private val appComponentContext: AppComponentContext,
|
||||
private val model: WcCommonTransactionModel,
|
||||
private val onClickDoneCustomAllowance: (BigDecimal, Boolean) -> Unit,
|
||||
) : AppComponentContext by appComponentContext, ComposableBottomSheetComponent {
|
||||
|
||||
override fun dismiss() {
|
||||
model.dismiss()
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun BottomSheet() {
|
||||
val content by model.uiState.collectAsStateWithLifecycle()
|
||||
val spendAllowance = (content as? WcSendTransactionUM)?.spendAllowance
|
||||
if (spendAllowance != null) {
|
||||
WcCustomAllowanceContent(
|
||||
state = spendAllowance,
|
||||
onClickDone = { value, isUnlimited ->
|
||||
router.pop()
|
||||
onClickDoneCustomAllowance(value, isUnlimited)
|
||||
},
|
||||
onBack = router::pop,
|
||||
onDismiss = ::dismiss,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -27,6 +27,7 @@ internal class WcSendTransactionComponent(
|
|||
onClickTransactionRequest = model::showTransactionRequest,
|
||||
onBack = router::pop,
|
||||
onDismiss = ::dismiss,
|
||||
onClickAllowToSpend = model::onClickAllowToSpend,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,14 @@
|
|||
package com.tangem.features.walletconnect.transaction.entity.approve
|
||||
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal data class WcSpendAllowanceUM(
|
||||
val amountValue: BigDecimal,
|
||||
val isUnlimited: Boolean,
|
||||
val amountText: TextReference,
|
||||
val tokenSymbol: String,
|
||||
val tokenImageUrl: String?,
|
||||
)
|
||||
val networkIconRes: Int?,
|
||||
) : TangemBottomSheetConfigContent
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.features.walletconnect.transaction.entity.send
|
||||
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.features.walletconnect.transaction.entity.approve.WcSpendAllowanceUM
|
||||
import com.tangem.features.walletconnect.transaction.entity.blockaid.WcSendReceiveTransactionCheckResultsUM
|
||||
import com.tangem.features.walletconnect.transaction.entity.common.WcCommonTransactionUM
|
||||
import com.tangem.features.walletconnect.transaction.entity.common.WcNetworkInfoUM
|
||||
|
|
@ -10,6 +11,7 @@ import com.tangem.features.walletconnect.transaction.entity.common.WcTransaction
|
|||
internal data class WcSendTransactionUM(
|
||||
val transaction: WcSendTransactionItemUM,
|
||||
override val transactionRequestInfo: WcTransactionRequestInfoUM,
|
||||
val spendAllowance: WcSpendAllowanceUM? = null,
|
||||
) : WcCommonTransactionUM
|
||||
|
||||
internal data class WcSendTransactionItemUM(
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import com.tangem.features.walletconnect.transaction.ui.blockaid.WcSendAndReceiv
|
|||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import java.math.BigDecimal
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
|
|
@ -46,6 +47,8 @@ internal class WcSendTransactionModel @Inject constructor(
|
|||
|
||||
val stackNavigation = StackNavigation<WcTransactionRoutes>()
|
||||
|
||||
private var wcApproval: WcApproval? = null
|
||||
|
||||
init {
|
||||
@Suppress("UnusedPrivateMember")
|
||||
modelScope.launch {
|
||||
|
|
@ -77,6 +80,7 @@ internal class WcSendTransactionModel @Inject constructor(
|
|||
val isSecurityCheckContent = securityCheck is Lce.Content
|
||||
var isApprovalMethod = isSecurityCheckContent &&
|
||||
securityCheck.content is BlockAidTransactionCheck.Result.Approval
|
||||
wcApproval = useCase as? WcApproval
|
||||
buildUiState(securityCheck, useCase, signState)
|
||||
}
|
||||
else -> unknownMethodRunnable()
|
||||
|
|
@ -90,7 +94,9 @@ internal class WcSendTransactionModel @Inject constructor(
|
|||
signState: WcSignState<*>,
|
||||
) {
|
||||
val blockAidState = when (securityCheck) {
|
||||
is Lce.Content -> blockAidUiConverter.convert(securityCheck.content.result)
|
||||
is Lce.Content -> blockAidUiConverter.convert(
|
||||
WcSendAndReceiveBlockAidUiConverter.Input(securityCheck.content.result, wcApproval?.getAmount()),
|
||||
)
|
||||
is Lce.Error -> WcSendReceiveTransactionCheckResultsUM(isLoading = false)
|
||||
is Lce.Loading -> WcSendReceiveTransactionCheckResultsUM(isLoading = true)
|
||||
}
|
||||
|
|
@ -108,6 +114,7 @@ internal class WcSendTransactionModel @Inject constructor(
|
|||
) as? WcSendTransactionUM
|
||||
transactionUM = transactionUM?.copy(
|
||||
transaction = transactionUM.transaction.copy(estimatedWalletChanges = blockAidState),
|
||||
spendAllowance = blockAidState.spendAllowance,
|
||||
)
|
||||
_uiState.emit(transactionUM)
|
||||
}
|
||||
|
|
@ -120,6 +127,17 @@ internal class WcSendTransactionModel @Inject constructor(
|
|||
stackNavigation.pushNew(WcTransactionRoutes.TransactionRequestInfo)
|
||||
}
|
||||
|
||||
fun onClickDoneCustomAllowance(value: BigDecimal, isUnlimited: Boolean) {
|
||||
val maxValue = if (isUnlimited) Double.MAX_VALUE.toBigDecimal() else value
|
||||
wcApproval?.getAmount()?.let { currentAmount ->
|
||||
wcApproval?.updateAmount(currentAmount.copy(maxValue = maxValue))
|
||||
}
|
||||
}
|
||||
|
||||
fun onClickAllowToSpend() {
|
||||
wcApproval?.let { stackNavigation.pushNew(WcTransactionRoutes.CustomAllowance) }
|
||||
}
|
||||
|
||||
private fun showVerifiedAlert(appName: String) {
|
||||
stackNavigation.pushNew(WcTransactionRoutes.Alert(WcTransactionRoutes.Alert.Type.Verified(appName)))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@ internal sealed class WcTransactionRoutes : TangemBottomSheetConfigContent, Rout
|
|||
@Serializable
|
||||
data object TransactionRequestInfo : WcTransactionRoutes()
|
||||
|
||||
@Serializable
|
||||
data object CustomAllowance : WcTransactionRoutes()
|
||||
|
||||
@Serializable
|
||||
data class Alert(val type: Type) : WcTransactionRoutes() {
|
||||
@Serializable
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import androidx.compose.foundation.text.KeyboardOptions
|
|||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
|
|
@ -23,91 +23,126 @@ import androidx.compose.ui.tooling.preview.Devices
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil.compose.AsyncImage
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
import com.tangem.core.ui.components.TangemSwitch
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
|
||||
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetWithFooter
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
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.walletconnect.impl.R
|
||||
import com.tangem.features.walletconnect.transaction.entity.approve.WcCustomAllowanceUM
|
||||
import com.tangem.features.walletconnect.transaction.entity.approve.WcSpendAllowanceUM
|
||||
import java.math.BigDecimal
|
||||
|
||||
@Composable
|
||||
internal fun WcCustomAllowanceContent(
|
||||
allowance: WcCustomAllowanceUM,
|
||||
onAmountChange: (String) -> Unit,
|
||||
onToggleChange: (Boolean) -> Unit,
|
||||
onClickDone: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
state: WcSpendAllowanceUM,
|
||||
onClickDone: (BigDecimal, Boolean) -> Unit,
|
||||
onBack: () -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.tertiary)
|
||||
.fillMaxWidth()
|
||||
.padding(TangemTheme.dimens.size16),
|
||||
) {
|
||||
AmountTextField(
|
||||
allowance = allowance,
|
||||
onAmountChange = onAmountChange,
|
||||
)
|
||||
var amountText by remember { mutableStateOf(state.amountValue.toPlainString()) }
|
||||
var isUnlimited by remember { mutableStateOf(state.isUnlimited) }
|
||||
|
||||
Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing16))
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(TangemTheme.dimens.radius14))
|
||||
.background(color = TangemTheme.colors.background.action)
|
||||
.padding(TangemTheme.dimens.spacing16)
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.wc_unlimited_amount),
|
||||
style = TangemTheme.typography.body1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
TangemSwitch(
|
||||
checked = allowance.isUnlimited,
|
||||
onCheckedChange = onToggleChange,
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing60))
|
||||
|
||||
PrimaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResourceSafe(R.string.common_done),
|
||||
onClick = onClickDone,
|
||||
)
|
||||
val amount: BigDecimal by remember(amountText) {
|
||||
mutableStateOf(amountText.toBigDecimalOrNull() ?: state.amountValue)
|
||||
}
|
||||
|
||||
TangemModalBottomSheetWithFooter<TangemBottomSheetConfigContent.Empty>(
|
||||
config = TangemBottomSheetConfig(
|
||||
isShown = true,
|
||||
onDismissRequest = onDismiss,
|
||||
content = TangemBottomSheetConfigContent.Empty,
|
||||
),
|
||||
containerColor = TangemTheme.colors.background.tertiary,
|
||||
onBack = onBack,
|
||||
title = {
|
||||
TangemModalBottomSheetTitle(
|
||||
title = resourceReference(R.string.wc_custom_allowance_title),
|
||||
startIconRes = R.drawable.ic_back_24,
|
||||
onStartClick = onBack,
|
||||
)
|
||||
},
|
||||
content = {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(TangemTheme.colors.background.tertiary)
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
) {
|
||||
AmountTextField(
|
||||
amountText = amountText,
|
||||
onAmountChange = { amountText = it },
|
||||
isEnabled = !isUnlimited,
|
||||
tokenImageUrl = state.tokenImageUrl,
|
||||
networkIconRes = state.networkIconRes,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(14.dp))
|
||||
.background(TangemTheme.colors.background.action)
|
||||
.padding(16.dp)
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.wc_unlimited_amount),
|
||||
style = TangemTheme.typography.body1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
TangemSwitch(
|
||||
checked = isUnlimited,
|
||||
onCheckedChange = { isUnlimited = it },
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(60.dp))
|
||||
}
|
||||
},
|
||||
footer = {
|
||||
PrimaryButton(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
text = stringResourceSafe(R.string.common_done),
|
||||
onClick = { onClickDone(amount, isUnlimited) },
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AmountTextField(
|
||||
allowance: WcCustomAllowanceUM,
|
||||
amountText: String,
|
||||
onAmountChange: (String) -> Unit,
|
||||
isEnabled: Boolean,
|
||||
tokenImageUrl: String?,
|
||||
@DrawableRes networkIconRes: Int?,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = modifier
|
||||
.clip(RoundedCornerShape(TangemTheme.dimens.radius14))
|
||||
.background(color = TangemTheme.colors.background.action)
|
||||
.padding(
|
||||
end = TangemTheme.dimens.size8,
|
||||
top = TangemTheme.dimens.size4,
|
||||
bottom = TangemTheme.dimens.size4,
|
||||
)
|
||||
.clip(RoundedCornerShape(14.dp))
|
||||
.background(TangemTheme.colors.background.action)
|
||||
.padding(end = 8.dp, top = 4.dp, bottom = 4.dp)
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
TextField(
|
||||
value = allowance.amountText,
|
||||
value = amountText,
|
||||
onValueChange = onAmountChange,
|
||||
enabled = isEnabled,
|
||||
label = {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.send_amount_label),
|
||||
|
|
@ -127,6 +162,9 @@ private fun AmountTextField(
|
|||
unfocusedTextColor = TangemTheme.colors.text.primary1,
|
||||
focusedPlaceholderColor = TangemTheme.colors.text.disabled,
|
||||
unfocusedPlaceholderColor = TangemTheme.colors.text.disabled,
|
||||
disabledContainerColor = Color.Transparent,
|
||||
disabledTextColor = TangemTheme.colors.text.disabled,
|
||||
disabledIndicatorColor = Color.Transparent,
|
||||
focusedIndicatorColor = Color.Transparent,
|
||||
unfocusedIndicatorColor = Color.Transparent,
|
||||
cursorColor = TangemTheme.colors.icon.primary1,
|
||||
|
|
@ -134,40 +172,44 @@ private fun AmountTextField(
|
|||
)
|
||||
|
||||
TokenWithNetworkIcon(
|
||||
tokenIconUrl = allowance.tokenIconUrl,
|
||||
networkIconRes = allowance.networkIconRes,
|
||||
tokenIconUrl = tokenImageUrl,
|
||||
networkIconRes = networkIconRes,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TokenWithNetworkIcon(
|
||||
tokenIconUrl: String,
|
||||
@DrawableRes networkIconRes: Int,
|
||||
tokenIconUrl: String?,
|
||||
@DrawableRes networkIconRes: Int?,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.size(TangemTheme.dimens.size48),
|
||||
modifier = modifier.size(48.dp),
|
||||
) {
|
||||
AsyncImage(
|
||||
model = tokenIconUrl,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.aspectRatio(1f)
|
||||
.clip(CircleShape)
|
||||
.align(Alignment.Center)
|
||||
.padding(TangemTheme.dimens.spacing8),
|
||||
model = tokenIconUrl,
|
||||
contentDescription = null,
|
||||
)
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopEnd)
|
||||
.size(TangemTheme.dimens.size18)
|
||||
.border(TangemTheme.dimens.size2, TangemTheme.colors.background.action, CircleShape)
|
||||
.padding(TangemTheme.dimens.spacing2),
|
||||
painter = painterResource(id = networkIconRes),
|
||||
contentDescription = null,
|
||||
.padding(8.dp),
|
||||
placeholder = painterResource(R.drawable.ic_nft_placeholder_20),
|
||||
error = painterResource(R.drawable.ic_nft_placeholder_20),
|
||||
)
|
||||
|
||||
if (networkIconRes != null) {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopEnd)
|
||||
.size(18.dp)
|
||||
.border(2.dp, TangemTheme.colors.background.action, CircleShape)
|
||||
.padding(2.dp),
|
||||
painter = painterResource(networkIconRes),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -175,10 +217,10 @@ private fun TokenWithNetworkIcon(
|
|||
@Preview(showBackground = true, device = Devices.PIXEL_7_PRO)
|
||||
@Preview(showBackground = true, device = Devices.PIXEL_7_PRO, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
internal fun PreviewWcCustomAllowanceContent(
|
||||
@PreviewParameter(WcCustomAllowanceStateProvider::class) state: WcCustomAllowanceUM,
|
||||
@PreviewParameter(WcCustomAllowanceStateProvider::class) state: WcSpendAllowanceUM,
|
||||
) {
|
||||
TangemThemePreview {
|
||||
TangemModalBottomSheet<WcCustomAllowanceUM>(
|
||||
TangemModalBottomSheet<WcSpendAllowanceUM>(
|
||||
config = TangemBottomSheetConfig(
|
||||
isShown = true,
|
||||
onDismissRequest = {},
|
||||
|
|
@ -196,22 +238,24 @@ internal fun PreviewWcCustomAllowanceContent(
|
|||
},
|
||||
content = {
|
||||
WcCustomAllowanceContent(
|
||||
allowance = state,
|
||||
onAmountChange = {},
|
||||
onToggleChange = {},
|
||||
onClickDone = {},
|
||||
state = state,
|
||||
onClickDone = { _, _ -> },
|
||||
onBack = {},
|
||||
onDismiss = {},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class WcCustomAllowanceStateProvider : CollectionPreviewParameterProvider<WcCustomAllowanceUM>(
|
||||
private class WcCustomAllowanceStateProvider : CollectionPreviewParameterProvider<WcSpendAllowanceUM>(
|
||||
listOf(
|
||||
WcCustomAllowanceUM(
|
||||
WcSpendAllowanceUM(
|
||||
networkIconRes = R.drawable.img_eth_22,
|
||||
tokenIconUrl = "https://tangem.com",
|
||||
amountText = "100",
|
||||
tokenImageUrl = "https://tangem.com",
|
||||
amountText = TextReference.Str("100"),
|
||||
amountValue = BigDecimal("100"),
|
||||
tokenSymbol = "ETH",
|
||||
isUnlimited = false,
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.features.walletconnect.transaction.ui.approve
|
|||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
|
|
@ -22,13 +23,18 @@ import com.tangem.features.walletconnect.transaction.entity.approve.WcSpendAllow
|
|||
import com.tangem.features.walletconnect.transaction.ui.common.WcSmallTitleItem
|
||||
|
||||
@Composable
|
||||
internal fun WcSpendAllowanceItem(spendAllowance: WcSpendAllowanceUM, modifier: Modifier = Modifier) {
|
||||
internal fun WcSpendAllowanceItem(
|
||||
spendAllowance: WcSpendAllowanceUM,
|
||||
onClickAllowToSpend: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.clip(RoundedCornerShape(14.dp))
|
||||
.background(TangemTheme.colors.background.action)
|
||||
.fillMaxWidth()
|
||||
.padding(end = 12.dp, bottom = 12.dp),
|
||||
.padding(end = 12.dp, bottom = 12.dp)
|
||||
.clickable { onClickAllowToSpend() },
|
||||
) {
|
||||
WcSmallTitleItem(R.string.wc_allow_to_spend)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,11 @@ import com.tangem.features.walletconnect.transaction.ui.approve.WcSpendAllowance
|
|||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
@Composable
|
||||
internal fun TransactionCheckResultsItem(item: WcSendReceiveTransactionCheckResultsUM, modifier: Modifier = Modifier) {
|
||||
internal fun TransactionCheckResultsItem(
|
||||
item: WcSendReceiveTransactionCheckResultsUM,
|
||||
onClickAllowToSpend: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth(),
|
||||
|
|
@ -38,7 +42,7 @@ internal fun TransactionCheckResultsItem(item: WcSendReceiveTransactionCheckResu
|
|||
if (item.estimatedWalletChanges != null) {
|
||||
WcEstimatedWalletChangesItem(item.estimatedWalletChanges)
|
||||
} else if (item.spendAllowance != null) {
|
||||
WcSpendAllowanceItem(item.spendAllowance)
|
||||
WcSpendAllowanceItem(item.spendAllowance, onClickAllowToSpend)
|
||||
} else {
|
||||
WcEstimatedWalletChangesNotLoadedItem()
|
||||
}
|
||||
|
|
@ -57,7 +61,7 @@ private fun TransactionCheckResultsItemPreview(
|
|||
modifier = Modifier
|
||||
.background(TangemTheme.colors.background.tertiary),
|
||||
) {
|
||||
TransactionCheckResultsItem(item = item)
|
||||
TransactionCheckResultsItem(item = item, {})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.domain.blockaid.models.transaction.SimulationResult
|
|||
import com.domain.blockaid.models.transaction.ValidationResult
|
||||
import com.domain.blockaid.models.transaction.simultation.AmountInfo
|
||||
import com.domain.blockaid.models.transaction.simultation.SimulationData
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
|
|
@ -21,21 +22,20 @@ import javax.inject.Inject
|
|||
|
||||
internal const val DECIMALS_AMOUNT = 2
|
||||
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
internal class WcSendAndReceiveBlockAidUiConverter @Inject constructor(
|
||||
private val estimatedWalletChangeUMConverter: WcEstimatedWalletChangeUMConverter,
|
||||
private val spendAllowanceUMConverter: WcSpendAllowanceUMConverter,
|
||||
) :
|
||||
Converter<CheckTransactionResult, WcSendReceiveTransactionCheckResultsUM> {
|
||||
override fun convert(value: CheckTransactionResult): WcSendReceiveTransactionCheckResultsUM {
|
||||
) : Converter<WcSendAndReceiveBlockAidUiConverter.Input, WcSendReceiveTransactionCheckResultsUM> {
|
||||
override fun convert(value: Input): WcSendReceiveTransactionCheckResultsUM {
|
||||
val description = value.result.description
|
||||
return WcSendReceiveTransactionCheckResultsUM(
|
||||
isLoading = false,
|
||||
notificationText = when (value.validation) {
|
||||
notificationText = when (value.result.validation) {
|
||||
ValidationResult.SAFE, ValidationResult.FAILED_TO_VALIDATE -> null
|
||||
// TODO("Add text after approve with designer")
|
||||
ValidationResult.UNSAFE ->
|
||||
TextReference.Str("The transaction approves tokens to a known malicious address")
|
||||
ValidationResult.UNSAFE -> if (!description.isNullOrEmpty()) TextReference.Str(description) else null
|
||||
},
|
||||
estimatedWalletChanges = (value.simulation as? SimulationResult.Success)?.data?.let { data ->
|
||||
estimatedWalletChanges = (value.result.simulation as? SimulationResult.Success)?.data?.let { data ->
|
||||
when (data) {
|
||||
is SimulationData.Approve -> null
|
||||
is SimulationData.SendAndReceive -> {
|
||||
|
|
@ -82,15 +82,21 @@ internal class WcSendAndReceiveBlockAidUiConverter @Inject constructor(
|
|||
}
|
||||
}
|
||||
},
|
||||
spendAllowance = (value.simulation as? SimulationResult.Success)?.data?.let { data ->
|
||||
spendAllowance = (value.result.simulation as? SimulationResult.Success)?.data?.let { data ->
|
||||
when (data) {
|
||||
is SimulationData.SendAndReceive -> null
|
||||
is SimulationData.Approve -> data.approvedAmounts.map { spendAllowanceUMConverter.convert(it) }
|
||||
.firstOrNull()
|
||||
is SimulationData.Approve -> value.approvedAmount?.let {
|
||||
data.approvedAmounts.map { spendAllowanceUMConverter.convert(it) }.firstOrNull()
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
data class Input(
|
||||
val result: CheckTransactionResult,
|
||||
val approvedAmount: Amount?,
|
||||
)
|
||||
}
|
||||
|
||||
internal fun BigDecimal.amountText() = format { crypto("", DECIMALS_AMOUNT) }
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.features.walletconnect.transaction.ui.blockaid
|
|||
|
||||
import com.domain.blockaid.models.transaction.simultation.ApprovedAmount
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.getActiveIconRes
|
||||
import com.tangem.features.walletconnect.impl.R
|
||||
import com.tangem.features.walletconnect.transaction.entity.approve.WcSpendAllowanceUM
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
|
@ -10,12 +11,15 @@ import javax.inject.Inject
|
|||
internal class WcSpendAllowanceUMConverter @Inject constructor() : Converter<ApprovedAmount, WcSpendAllowanceUM> {
|
||||
|
||||
override fun convert(value: ApprovedAmount) = WcSpendAllowanceUM(
|
||||
amountValue = value.approvedAmount,
|
||||
amountText = if (value.isUnlimited) {
|
||||
TextReference.Res(R.string.wc_common_unlimited)
|
||||
} else {
|
||||
TextReference.Str(value.approvedAmount.amountText())
|
||||
},
|
||||
isUnlimited = value.isUnlimited,
|
||||
tokenSymbol = value.tokenInfo.symbol,
|
||||
tokenImageUrl = value.tokenInfo.logoUrl,
|
||||
networkIconRes = value.tokenInfo.chainId?.toString()?.let { getActiveIconRes(it) },
|
||||
)
|
||||
}
|
||||
|
|
@ -46,6 +46,7 @@ internal fun WcSendTransactionModalBottomSheet(
|
|||
onClickTransactionRequest: () -> Unit,
|
||||
onBack: () -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
onClickAllowToSpend: () -> Unit,
|
||||
) {
|
||||
TangemModalBottomSheetWithFooter<TangemBottomSheetConfigContent.Empty>(
|
||||
config = TangemBottomSheetConfig(
|
||||
|
|
@ -92,7 +93,7 @@ internal fun WcSendTransactionModalBottomSheet(
|
|||
}
|
||||
Column(modifier = Modifier.padding(top = 16.dp)) {
|
||||
if (state.estimatedWalletChanges != null) {
|
||||
TransactionCheckResultsItem(state.estimatedWalletChanges)
|
||||
TransactionCheckResultsItem(state.estimatedWalletChanges, onClickAllowToSpend)
|
||||
}
|
||||
Spacer(Modifier.height(16.dp))
|
||||
WcSendTransactionItems(
|
||||
|
|
@ -140,7 +141,7 @@ private fun WcSendTransactionBottomSheetPreview(
|
|||
)
|
||||
},
|
||||
content = {
|
||||
WcSendTransactionModalBottomSheet(state, {}, {}, {})
|
||||
WcSendTransactionModalBottomSheet(state, {}, {}, {}, {})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue