Updated on 2026-08-14
This commit is contained in:
parent
315d7c9d6c
commit
ab2f0f58bb
4 changed files with 85 additions and 12 deletions
|
|
@ -10,11 +10,16 @@ import com.tangem.features.walletconnect.transaction.routes.WcTransactionRoutes
|
||||||
internal object WcAlertsFactory {
|
internal object WcAlertsFactory {
|
||||||
|
|
||||||
fun createCommonTransactionAppInfoAlertUM(alertType: WcTransactionRoutes.Alert.Type) = when (alertType) {
|
fun createCommonTransactionAppInfoAlertUM(alertType: WcTransactionRoutes.Alert.Type) = when (alertType) {
|
||||||
is WcTransactionRoutes.Alert.Type.Verified -> createVerifiedDomainAlert(alertType.appName)
|
is WcTransactionRoutes.Alert.Type.Verified ->
|
||||||
is WcTransactionRoutes.Alert.Type.UnknownDomain -> createUnknownDomainAlert()
|
createVerifiedDomainAlert(alertType.appName)
|
||||||
is WcTransactionRoutes.Alert.Type.UnsafeDomain -> createUnsafeDomainAlert()
|
is WcTransactionRoutes.Alert.Type.UnknownDomain ->
|
||||||
is WcTransactionRoutes.Alert.Type.MaliciousDApp ->
|
createUnknownDomainAlert()
|
||||||
|
is WcTransactionRoutes.Alert.Type.UnsafeDomain ->
|
||||||
|
createUnsafeDomainAlert()
|
||||||
|
is WcTransactionRoutes.Alert.Type.MaliciousInfo ->
|
||||||
createMaliciousDAppAlert(alertType.description, alertType.onClick)
|
createMaliciousDAppAlert(alertType.description, alertType.onClick)
|
||||||
|
is WcTransactionRoutes.Alert.Type.UnknownError ->
|
||||||
|
createUnknownErrorAlert(alertType.errorMessage, alertType.onDismiss)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createUnknownDomainAlert(activeButtonOnClick: (() -> Unit)? = null): MessageBottomSheetUMV2 {
|
fun createUnknownDomainAlert(activeButtonOnClick: (() -> Unit)? = null): MessageBottomSheetUMV2 {
|
||||||
|
|
@ -69,8 +74,32 @@ internal object WcAlertsFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun createUnknownErrorAlert(errorMessage: String?, onDismiss: () -> Unit): MessageBottomSheetUMV2 {
|
||||||
|
return messageBottomSheetUM {
|
||||||
|
infoBlock {
|
||||||
|
icon(R.drawable.img_attention_20) {
|
||||||
|
type = MessageBottomSheetUMV2.Icon.Type.Warning
|
||||||
|
backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.SameAsTint
|
||||||
|
}
|
||||||
|
title = resourceReference(R.string.wc_alert_unknown_error_title)
|
||||||
|
body = if (errorMessage.isNullOrEmpty()) {
|
||||||
|
resourceReference(R.string.wc_alert_unknown_error_description_no_error_code)
|
||||||
|
} else {
|
||||||
|
resourceReference(
|
||||||
|
R.string.wc_alert_unknown_error_description,
|
||||||
|
wrappedList(errorMessage),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
secondaryButton {
|
||||||
|
text = resourceReference(R.string.balance_hidden_got_it_button)
|
||||||
|
onClick { onDismiss() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun createMaliciousDAppAlert(
|
private fun createMaliciousDAppAlert(
|
||||||
description: String,
|
description: String?,
|
||||||
activeButtonOnClick: (() -> Unit),
|
activeButtonOnClick: (() -> Unit),
|
||||||
): MessageBottomSheetUMV2 {
|
): MessageBottomSheetUMV2 {
|
||||||
return messageBottomSheetUM {
|
return messageBottomSheetUM {
|
||||||
|
|
@ -80,7 +109,9 @@ internal object WcAlertsFactory {
|
||||||
backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.SameAsTint
|
backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.SameAsTint
|
||||||
}
|
}
|
||||||
title = resourceReference(R.string.security_alert_title)
|
title = resourceReference(R.string.security_alert_title)
|
||||||
body = TextReference.Str(description)
|
if (!description.isNullOrEmpty()) {
|
||||||
|
body = TextReference.Str(description)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
primaryButton {
|
primaryButton {
|
||||||
text = resourceReference(R.string.common_cancel)
|
text = resourceReference(R.string.common_cancel)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
package com.tangem.features.walletconnect.transaction.model
|
package com.tangem.features.walletconnect.transaction.model
|
||||||
|
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
|
import arrow.core.Either
|
||||||
import com.arkivanov.decompose.router.stack.StackNavigation
|
import com.arkivanov.decompose.router.stack.StackNavigation
|
||||||
|
import com.arkivanov.decompose.router.stack.pop
|
||||||
import com.arkivanov.decompose.router.stack.pushNew
|
import com.arkivanov.decompose.router.stack.pushNew
|
||||||
|
import com.domain.blockaid.models.transaction.ValidationResult
|
||||||
import com.tangem.blockchain.common.TransactionData
|
import com.tangem.blockchain.common.TransactionData
|
||||||
import com.tangem.core.decompose.di.ModelScoped
|
import com.tangem.core.decompose.di.ModelScoped
|
||||||
import com.tangem.core.decompose.model.Model
|
import com.tangem.core.decompose.model.Model
|
||||||
|
|
@ -48,6 +51,7 @@ internal class WcSendTransactionModel @Inject constructor(
|
||||||
val stackNavigation = StackNavigation<WcTransactionRoutes>()
|
val stackNavigation = StackNavigation<WcTransactionRoutes>()
|
||||||
|
|
||||||
private var wcApproval: WcApproval? = null
|
private var wcApproval: WcApproval? = null
|
||||||
|
private var sign: () -> Unit = {}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@Suppress("UnusedPrivateMember")
|
@Suppress("UnusedPrivateMember")
|
||||||
|
|
@ -66,7 +70,7 @@ internal class WcSendTransactionModel @Inject constructor(
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
.collectLatest {
|
.collectLatest {
|
||||||
val (signState, securityCheck) = it
|
val (signState, securityCheck) = it
|
||||||
if (signingIsDone(signState)) return@collectLatest
|
if (signingIsDone(signState, useCase)) return@collectLatest
|
||||||
val signModel: Any = signState.signModel
|
val signModel: Any = signState.signModel
|
||||||
val isMutableFee = useCase is WcMutableFee
|
val isMutableFee = useCase is WcMutableFee
|
||||||
val dAppFee = if (isMutableFee) useCase.dAppFee() else null
|
val dAppFee = if (isMutableFee) useCase.dAppFee() else null
|
||||||
|
|
@ -81,6 +85,7 @@ internal class WcSendTransactionModel @Inject constructor(
|
||||||
var isApprovalMethod = isSecurityCheckContent &&
|
var isApprovalMethod = isSecurityCheckContent &&
|
||||||
securityCheck.content is BlockAidTransactionCheck.Result.Approval
|
securityCheck.content is BlockAidTransactionCheck.Result.Approval
|
||||||
wcApproval = useCase as? WcApproval
|
wcApproval = useCase as? WcApproval
|
||||||
|
sign = { useCase.sign() }
|
||||||
buildUiState(securityCheck, useCase, signState)
|
buildUiState(securityCheck, useCase, signState)
|
||||||
}
|
}
|
||||||
else -> unknownMethodRunnable()
|
else -> unknownMethodRunnable()
|
||||||
|
|
@ -107,7 +112,7 @@ internal class WcSendTransactionModel @Inject constructor(
|
||||||
actions = WcTransactionActionsUM(
|
actions = WcTransactionActionsUM(
|
||||||
onShowVerifiedAlert = ::showVerifiedAlert,
|
onShowVerifiedAlert = ::showVerifiedAlert,
|
||||||
onDismiss = { cancel(useCase) },
|
onDismiss = { cancel(useCase) },
|
||||||
onSign = useCase::sign,
|
onSign = { onSign(securityCheck.getOrNull()) },
|
||||||
onCopy = { copyData(useCase.rawSdkRequest.request.params) },
|
onCopy = { copyData(useCase.rawSdkRequest.request.params) },
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -127,6 +132,14 @@ internal class WcSendTransactionModel @Inject constructor(
|
||||||
stackNavigation.pushNew(WcTransactionRoutes.TransactionRequestInfo)
|
stackNavigation.pushNew(WcTransactionRoutes.TransactionRequestInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun onSign(securityCheck: BlockAidTransactionCheck.Result?) {
|
||||||
|
if (securityCheck?.result?.validation == ValidationResult.UNSAFE) {
|
||||||
|
showMaliciousAlert(securityCheck.result.description)
|
||||||
|
} else {
|
||||||
|
sign()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun onClickDoneCustomAllowance(value: BigDecimal, isUnlimited: Boolean) {
|
fun onClickDoneCustomAllowance(value: BigDecimal, isUnlimited: Boolean) {
|
||||||
val maxValue = if (isUnlimited) Double.MAX_VALUE.toBigDecimal() else value
|
val maxValue = if (isUnlimited) Double.MAX_VALUE.toBigDecimal() else value
|
||||||
wcApproval?.getAmount()?.let { currentAmount ->
|
wcApproval?.getAmount()?.let { currentAmount ->
|
||||||
|
|
@ -142,14 +155,36 @@ internal class WcSendTransactionModel @Inject constructor(
|
||||||
stackNavigation.pushNew(WcTransactionRoutes.Alert(WcTransactionRoutes.Alert.Type.Verified(appName)))
|
stackNavigation.pushNew(WcTransactionRoutes.Alert(WcTransactionRoutes.Alert.Type.Verified(appName)))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun signingIsDone(signState: WcSignState<*>): Boolean {
|
private fun showMaliciousAlert(description: String?) {
|
||||||
|
val type = WcTransactionRoutes.Alert.Type.MaliciousInfo(description = description, onClick = ::signFromAlert)
|
||||||
|
stackNavigation.pushNew(WcTransactionRoutes.Alert(type))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun signFromAlert() {
|
||||||
|
stackNavigation.pop()
|
||||||
|
sign()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun signingIsDone(signState: WcSignState<*>, useCase: WcSignUseCase<*>): Boolean {
|
||||||
(signState.domainStep as? WcSignStep.Result)?.result?.let {
|
(signState.domainStep as? WcSignStep.Result)?.result?.let {
|
||||||
router.pop()
|
handleSigningError(it, useCase)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleSigningError(result: Either<Throwable, Unit>, useCase: WcSignUseCase<*>) {
|
||||||
|
if (result.isLeft()) {
|
||||||
|
val error = WcTransactionRoutes.Alert.Type.UnknownError(
|
||||||
|
errorMessage = result.leftOrNull()?.message,
|
||||||
|
onDismiss = { cancel(useCase) },
|
||||||
|
)
|
||||||
|
stackNavigation.pushNew(WcTransactionRoutes.Alert(error))
|
||||||
|
} else {
|
||||||
|
router.pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun cancel(useCase: WcSignUseCase<*>) {
|
private fun cancel(useCase: WcSignUseCase<*>) {
|
||||||
useCase.cancel()
|
useCase.cancel()
|
||||||
router.pop()
|
router.pop()
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,8 @@ internal sealed class WcTransactionRoutes : TangemBottomSheetConfigContent, Rout
|
||||||
data class Verified(val appName: String) : Type()
|
data class Verified(val appName: String) : Type()
|
||||||
data object UnknownDomain : Type()
|
data object UnknownDomain : Type()
|
||||||
data object UnsafeDomain : Type()
|
data object UnsafeDomain : Type()
|
||||||
data class MaliciousDApp(val description: String, val onClick: () -> Unit) : Type()
|
data class MaliciousInfo(val description: String?, val onClick: () -> Unit) : Type()
|
||||||
|
data class UnknownError(val errorMessage: String?, val onDismiss: () -> Unit) : Type()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ import android.content.res.Configuration
|
||||||
import androidx.compose.animation.animateContentSize
|
import androidx.compose.animation.animateContentSize
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
|
@ -68,9 +69,14 @@ internal fun WcEstimatedWalletChangesItem(item: WcEstimatedWalletChangesUM, modi
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.items.size > MAX_CHANGES_SIZE) {
|
if (item.items.size > MAX_CHANGES_SIZE) {
|
||||||
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clickable { isExpanded = !isExpanded }
|
.clickable(
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
indication = null,
|
||||||
|
onClick = { isExpanded = !isExpanded },
|
||||||
|
)
|
||||||
.padding(bottom = 12.dp, start = 12.dp),
|
.padding(bottom = 12.dp, start = 12.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue