From 222b5d75d111b6c864d96b376b7d2e02f5fcdad8 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 27 May 2025 19:30:03 +0500 Subject: [PATCH] Updated on 2026-08-14 --- core/ui/build.gradle.kts | 1 + .../message/MessageBottomSheetUMV2.kt | 4 +- .../message/MessageBottomSheetV2.kt | 23 ++-- .../tangem/core/ui/extensions/ModifierExt.kt | 11 ++ .../components/AlertsComponentV2.kt | 27 +++++ .../connections/components/WcPairComponent.kt | 15 +-- .../connections/entity/WcAppInfoUM.kt | 8 +- .../entity/WcConnectedAppInfoUM.kt | 1 + .../model/WcConnectedAppInfoModel.kt | 11 ++ .../connections/model/WcPairModel.kt | 106 +++++++++++++++++- .../transformers/WcAppInfoTransformer.kt | 13 ++- .../WcDAppVerifiedStateConverter.kt | 21 ++++ .../connections/routes/WcAppInfoRoutes.kt | 5 +- .../connections/ui/InternalComponents.kt | 20 +++- .../connections/ui/WcAppInfoBS.kt | 7 +- .../connections/ui/WcConnectedAppInfoBS.kt | 4 +- .../entity/common/WcTransactionActionsUM.kt | 1 + .../common/WcTransactionAppInfoContentUM.kt | 4 +- .../model/WcSignTransactionModel.kt | 29 +++++ .../WcApproveTransactionModalBottomSheet.kt | 5 +- ...AddEthereumChainModalBottomSheetContent.kt | 14 ++- ...cSignTransactionModalBottomSheetContent.kt | 7 +- .../transaction/utils/WcTransactionUtils.kt | 11 +- 23 files changed, 300 insertions(+), 48 deletions(-) create mode 100644 features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/AlertsComponentV2.kt create mode 100644 features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/transformers/WcDAppVerifiedStateConverter.kt diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts index 7c76425a6e..cf822f3cb4 100644 --- a/core/ui/build.gradle.kts +++ b/core/ui/build.gradle.kts @@ -1,6 +1,7 @@ plugins { alias(deps.plugins.android.library) alias(deps.plugins.kotlin.android) + alias(deps.plugins.kotlin.serialization) id("configuration") } diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/message/MessageBottomSheetUMV2.kt b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/message/MessageBottomSheetUMV2.kt index 3617352f54..57c143b9b2 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/message/MessageBottomSheetUMV2.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/message/MessageBottomSheetUMV2.kt @@ -7,6 +7,7 @@ import com.tangem.core.ui.extensions.TextReference import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toPersistentList +import kotlinx.serialization.Serializable @Immutable data class MessageBottomSheetUMV2( @@ -24,6 +25,7 @@ data class MessageBottomSheetUMV2( val closeScope = CloseScope() @Immutable + @Serializable sealed interface Element @Immutable @@ -90,7 +92,7 @@ annotation class MessageBottomSheetV2Dsl // region: DSL -internal fun messageBottomSheetUM(init: @MessageBottomSheetV2Dsl MessageBottomSheetUMV2.() -> Unit) = +fun messageBottomSheetUM(init: @MessageBottomSheetV2Dsl MessageBottomSheetUMV2.() -> Unit) = MessageBottomSheetUMV2().apply(init) fun MessageBottomSheetUMV2.onDismiss(block: () -> Unit) = MessageBottomSheetUMV2().apply { onDismissRequest = block } diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/message/MessageBottomSheetV2.kt b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/message/MessageBottomSheetV2.kt index 541625fde2..407a375ade 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/message/MessageBottomSheetV2.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/message/MessageBottomSheetV2.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -33,23 +34,31 @@ import kotlinx.collections.immutable.toPersistentList @Composable fun MessageBottomSheetV2(state: MessageBottomSheetUMV2, onDismissRequest: () -> Unit) { + val stateWithOnDismiss = remember(state) { + state.copy( + onDismissRequest = { + state.onDismissRequest.invoke() + onDismissRequest() + }, + ) + } + val config = TangemBottomSheetConfig( isShown = true, - content = state, - onDismissRequest = onDismissRequest, + content = stateWithOnDismiss, + onDismissRequest = stateWithOnDismiss.onDismissRequest, ) TangemModalBottomSheet( - config, + config = config, title = { TangemModalBottomSheetTitle( endIconRes = R.drawable.ic_close_24, - onEndClick = state.onDismissRequest, + onEndClick = stateWithOnDismiss.onDismissRequest, ) }, - ) { content: MessageBottomSheetUMV2 -> - MessageBottomSheetV2Content(content.copy(onDismissRequest = onDismissRequest)) - } + content = { content: MessageBottomSheetUMV2 -> MessageBottomSheetV2Content(content) }, + ) } @Composable diff --git a/core/ui/src/main/java/com/tangem/core/ui/extensions/ModifierExt.kt b/core/ui/src/main/java/com/tangem/core/ui/extensions/ModifierExt.kt index 469e048635..a014910b24 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/extensions/ModifierExt.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/extensions/ModifierExt.kt @@ -27,4 +27,15 @@ fun Modifier.clickableSingle( indication = LocalIndication.current, interactionSource = remember { MutableInteractionSource() }, ) +} + +/** + * Conditionally applies a modifier based on a boolean condition. + */ +fun Modifier.conditional(condition: Boolean, modifier: Modifier.() -> Modifier): Modifier { + return if (condition) { + then(modifier(Modifier)) + } else { + this + } } \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/AlertsComponentV2.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/AlertsComponentV2.kt new file mode 100644 index 0000000000..aafe4306a1 --- /dev/null +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/AlertsComponentV2.kt @@ -0,0 +1,27 @@ +package com.tangem.features.walletconnect.connections.components + +import androidx.compose.runtime.Composable +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetUMV2 +import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetV2 +import com.tangem.core.ui.decompose.ComposableBottomSheetComponent +import kotlinx.collections.immutable.ImmutableList + +internal class AlertsComponentV2( + appComponentContext: AppComponentContext, + private val elements: ImmutableList, + private val onDismissRequest: () -> Unit = {}, +) : AppComponentContext by appComponentContext, ComposableBottomSheetComponent { + + override fun dismiss() { + router.pop() + } + + @Composable + override fun BottomSheet() { + MessageBottomSheetV2( + state = MessageBottomSheetUMV2(elements = elements, onDismissRequest = onDismissRequest), + onDismissRequest = ::dismiss, + ) + } +} \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcPairComponent.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcPairComponent.kt index 7ad310da6e..008bc8b7a3 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcPairComponent.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcPairComponent.kt @@ -6,7 +6,6 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.extensions.compose.subscribeAsState -import com.arkivanov.decompose.router.stack.StackNavigation import com.arkivanov.decompose.router.stack.childStack import com.arkivanov.decompose.router.stack.pop import com.tangem.core.decompose.context.AppComponentContext @@ -25,15 +24,14 @@ internal class WcPairComponent( params: Params, ) : AppComponentContext by appComponentContext, ComposableContentComponent { - private val stackNavigation = StackNavigation() + private val model: WcPairModel = getOrCreateModel(params = params) private val innerRouter = InnerRouter( - stackNavigation = stackNavigation, + stackNavigation = model.stackNavigation, popCallback = { onChildBack() }, ) - private val model: WcPairModel = getOrCreateModel(params = params, router = innerRouter) private val contentStack = childStack( key = "WcConnectionFlowStack", - source = stackNavigation, + source = model.stackNavigation, serializer = WcAppInfoRoutes.serializer(), initialConfiguration = WcAppInfoRoutes.AppInfo, handleBackButton = true, @@ -59,7 +57,7 @@ internal class WcPairComponent( is WcAppInfoRoutes.Alert, is WcAppInfoRoutes.SelectNetworks, is WcAppInfoRoutes.SelectWallet, - -> stackNavigation.pop() + -> model.stackNavigation.pop() } } @@ -77,7 +75,10 @@ internal class WcPairComponent( onDismiss = ::dismiss, model = model, ) - is WcAppInfoRoutes.Alert -> TODO() + is WcAppInfoRoutes.Alert -> AlertsComponentV2( + appComponentContext = appComponentContext, + elements = config.elements, + ) is WcAppInfoRoutes.SelectNetworks -> WcSelectNetworksComponent( appComponentContext = appComponentContext, params = WcSelectNetworksComponent.WcSelectNetworksParams( diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcAppInfoUM.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcAppInfoUM.kt index 6cdcb1729e..e222f95e6d 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcAppInfoUM.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcAppInfoUM.kt @@ -22,7 +22,7 @@ internal sealed class WcAppInfoUM : TangemBottomSheetConfigContent { data class Content( val appName: String, val appIcon: String, - val isVerified: Boolean, + val verifiedDAppState: VerifiedDAppState, val appSubtitle: String, val notification: WcAppInfoSecurityNotification?, val walletName: String, @@ -34,6 +34,12 @@ internal sealed class WcAppInfoUM : TangemBottomSheetConfigContent { ) : WcAppInfoUM() } +@Immutable +sealed class VerifiedDAppState { + data class Verified(val onVerifiedClick: () -> Unit) : VerifiedDAppState() + data object Unknown : VerifiedDAppState() +} + sealed class WcAppInfoSecurityNotification( title: TextReference, subtitle: TextReference, diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcConnectedAppInfoUM.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcConnectedAppInfoUM.kt index a632be44c8..43f68a162b 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcConnectedAppInfoUM.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcConnectedAppInfoUM.kt @@ -7,6 +7,7 @@ data class WcConnectedAppInfoUM( val appName: String, val appIcon: String, val isVerified: Boolean, + val verifiedDAppState: VerifiedDAppState, val appSubtitle: String, val walletName: String, val networks: ImmutableList, diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcConnectedAppInfoModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcConnectedAppInfoModel.kt index b3995702d8..3d6a535f18 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcConnectedAppInfoModel.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcConnectedAppInfoModel.kt @@ -13,6 +13,7 @@ import com.tangem.domain.walletconnect.model.WcSession import com.tangem.domain.walletconnect.usecase.WcSessionsUseCase import com.tangem.domain.walletconnect.usecase.disconnect.WcDisconnectUseCase import com.tangem.features.walletconnect.connections.components.WcConnectedAppInfoComponent +import com.tangem.features.walletconnect.connections.entity.VerifiedDAppState import com.tangem.features.walletconnect.connections.entity.WcConnectedAppInfoUM import com.tangem.features.walletconnect.connections.entity.WcNetworkInfoItem import com.tangem.features.walletconnect.connections.entity.WcPrimaryButtonConfig @@ -56,6 +57,7 @@ internal class WcConnectedAppInfoModel @Inject constructor( appName = session.sdkModel.appMetaData.name, appIcon = session.sdkModel.appMetaData.icons.firstOrNull().orEmpty(), isVerified = session.securityStatus == CheckDAppResult.SAFE, + verifiedDAppState = extractVerifiedState(session), appSubtitle = session.sdkModel.appMetaData.description, walletName = session.wallet.name, networks = session.networks @@ -79,6 +81,15 @@ internal class WcConnectedAppInfoModel @Inject constructor( } } + private fun extractVerifiedState(session: WcSession): VerifiedDAppState { + return VerifiedDAppState.Verified(onVerifiedClick = {}) + return if (session.securityStatus == CheckDAppResult.SAFE) { + VerifiedDAppState.Verified(onVerifiedClick = {}) + } else { + VerifiedDAppState.Unknown + } + } + private fun disconnect(session: WcSession) { uiState.update { state -> state?.copy( diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcPairModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcPairModel.kt index 95bfeffd96..8680417ff6 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcPairModel.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcPairModel.kt @@ -1,10 +1,18 @@ package com.tangem.features.walletconnect.connections.model import androidx.compose.runtime.Stable +import com.arkivanov.decompose.router.stack.StackNavigation +import com.arkivanov.decompose.router.stack.pop +import com.arkivanov.decompose.router.stack.pushNew +import com.domain.blockaid.models.dapp.CheckDAppResult import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.decompose.navigation.Router +import com.tangem.core.ui.R +import com.tangem.core.ui.components.bottomsheets.message.* +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.wrappedList import com.tangem.domain.models.network.Network import com.tangem.domain.walletconnect.model.WcPairRequest import com.tangem.domain.walletconnect.model.WcSessionApprove @@ -21,6 +29,7 @@ import com.tangem.features.walletconnect.connections.entity.WcPrimaryButtonConfi import com.tangem.features.walletconnect.connections.model.transformers.WcAppInfoTransformer import com.tangem.features.walletconnect.connections.model.transformers.WcAppInfoWalletChangedTransformer import com.tangem.features.walletconnect.connections.model.transformers.WcConnectButtonProgressTransformer +import com.tangem.features.walletconnect.connections.model.transformers.WcDAppVerifiedStateConverter import com.tangem.features.walletconnect.connections.routes.WcAppInfoRoutes import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.* @@ -52,11 +61,14 @@ internal class WcPairModel @Inject constructor( ), ) + val stackNavigation = StackNavigation() + private val selectedUserWalletFlow = MutableStateFlow(getWalletsUseCase.invokeSync().first { it.walletId == params.userWalletId }) private var proposalNetwork by Delegates.notNull() - internal var sessionProposal by Delegates.notNull() + private var sessionProposal by Delegates.notNull() private val additionallyEnabledNetworks = mutableSetOf() + private val dAppVerifiedStateConverter = WcDAppVerifiedStateConverter(onVerifiedClick = ::showVerifiedAlert) val appInfoUiState: StateFlow field = MutableStateFlow(createLoadingState()) @@ -87,16 +99,17 @@ internal class WcPairModel @Inject constructor( proposalNetwork = sessionProposal.proposalNetwork.getValue(selectedUserWalletFlow.value) appInfoUiState.transformerUpdate( WcAppInfoTransformer( - dAppSession = pairState.dAppSession, + dAppSession = sessionProposal, + dAppVerifiedStateConverter = dAppVerifiedStateConverter, onDismiss = ::rejectPairing, onConnect = ::onConnect, onWalletClick = { - router.push( + stackNavigation.pushNew( WcAppInfoRoutes.SelectWallet(selectedUserWalletFlow.value.walletId), ) }, onNetworksClick = { - router.push( + stackNavigation.pushNew( WcAppInfoRoutes.SelectNetworks( missingRequiredNetworks = proposalNetwork.missingRequired, requiredNetworks = proposalNetwork.required, @@ -120,7 +133,15 @@ internal class WcPairModel @Inject constructor( wcPairUseCase.reject() } - private fun onConnect() { + private fun onConnect(securityStatus: CheckDAppResult) { + when (securityStatus) { + CheckDAppResult.SAFE -> connect() + CheckDAppResult.UNSAFE -> showSecurityRiskAlert() + CheckDAppResult.FAILED_TO_VERIFY -> showUnknownDomainAlert() + } + } + + private fun connect() { val enabledAvailableNetworks = proposalNetwork.available.filter { network -> network.id.rawId in additionallyEnabledNetworks } wcPairUseCase.approve( @@ -131,6 +152,79 @@ internal class WcPairModel @Inject constructor( ) } + private fun connectFromAlert() { + stackNavigation.pop() + connect() + } + + private fun showUnknownDomainAlert() { + val message = messageBottomSheetUM { + infoBlock { + icon(R.drawable.img_knight_shield_32) { + type = MessageBottomSheetUMV2.Icon.Type.Attention + backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.SameAsTint + } + title = resourceReference(R.string.security_alert_title) + body = resourceReference(R.string.wc_alert_domain_issues_description) + chip(resourceReference(R.string.wc_alert_audit_unknown_domain)) { + type = MessageBottomSheetUMV2.Chip.Type.Unspecified + } + } + primaryButton { + text = resourceReference(R.string.common_cancel) + onClick { closeBs() } + } + secondaryButton { + text = resourceReference(R.string.wc_alert_connect_anyway) + onClick { connectFromAlert() } + } + } + stackNavigation.pushNew(WcAppInfoRoutes.Alert(elements = message.elements)) + } + + private fun showSecurityRiskAlert() { + val message = messageBottomSheetUM { + infoBlock { + icon(R.drawable.img_knight_shield_32) { + type = MessageBottomSheetUMV2.Icon.Type.Warning + backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.SameAsTint + } + title = resourceReference(R.string.security_alert_title) + body = resourceReference(R.string.wc_alert_domain_issues_description) + chip(resourceReference(R.string.wc_alert_audit_unknown_domain)) { + type = MessageBottomSheetUMV2.Chip.Type.Warning + } + } + primaryButton { + text = resourceReference(R.string.common_cancel) + onClick { closeBs() } + } + secondaryButton { + text = resourceReference(R.string.wc_alert_connect_anyway) + onClick { connectFromAlert() } + } + } + stackNavigation.pushNew(WcAppInfoRoutes.Alert(elements = message.elements)) + } + + private fun showVerifiedAlert(appName: String) { + val message = messageBottomSheetUM { + infoBlock { + icon(R.drawable.img_approvale2_20) { + type = MessageBottomSheetUMV2.Icon.Type.Accent + backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.Unspecified + } + title = resourceReference(R.string.wc_alert_verified_domain_title) + body = resourceReference(R.string.wc_alert_verified_domain_description, wrappedList(appName)) + } + secondaryButton { + text = resourceReference(R.string.common_done) + onClick { closeBs() } + } + } + stackNavigation.pushNew(WcAppInfoRoutes.Alert(elements = message.elements)) + } + override fun onWalletSelected(userWalletId: UserWalletId) { val selectedUserWallet = sessionProposal.proposalNetwork.keys.first { it.walletId == userWalletId } proposalNetwork = sessionProposal.proposalNetwork.getValue(selectedUserWallet) @@ -147,7 +241,7 @@ internal class WcPairModel @Inject constructor( private fun createLoadingState(): WcAppInfoUM.Loading { return WcAppInfoUM.Loading( onDismiss = ::rejectPairing, - connectButtonConfig = WcPrimaryButtonConfig(showProgress = false, enabled = false, onClick = ::onConnect), + connectButtonConfig = WcPrimaryButtonConfig(showProgress = false, enabled = false, onClick = {}), ) } } \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/transformers/WcAppInfoTransformer.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/transformers/WcAppInfoTransformer.kt index e83438a16b..2989e66730 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/transformers/WcAppInfoTransformer.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/transformers/WcAppInfoTransformer.kt @@ -3,14 +3,17 @@ package com.tangem.features.walletconnect.connections.model.transformers import com.domain.blockaid.models.dapp.CheckDAppResult import com.tangem.domain.walletconnect.model.WcSessionProposal import com.tangem.domain.wallets.models.UserWallet -import com.tangem.features.walletconnect.connections.entity.* +import com.tangem.features.walletconnect.connections.entity.WcAppInfoSecurityNotification +import com.tangem.features.walletconnect.connections.entity.WcAppInfoUM +import com.tangem.features.walletconnect.connections.entity.WcPrimaryButtonConfig import com.tangem.utils.transformer.Transformer @Suppress("LongParameterList") internal class WcAppInfoTransformer( private val dAppSession: WcSessionProposal, + private val dAppVerifiedStateConverter: WcDAppVerifiedStateConverter, private val onDismiss: () -> Unit, - private val onConnect: () -> Unit, + private val onConnect: (securityStatus: CheckDAppResult) -> Unit, private val onWalletClick: () -> Unit, private val onNetworksClick: () -> Unit, private val userWallet: UserWallet, @@ -21,7 +24,9 @@ internal class WcAppInfoTransformer( return WcAppInfoUM.Content( appName = dAppSession.dAppMetaData.name, appIcon = dAppSession.dAppMetaData.icons.firstOrNull().orEmpty(), - isVerified = dAppSession.securityStatus == CheckDAppResult.SAFE, + verifiedDAppState = dAppVerifiedStateConverter.convert( + dAppSession.securityStatus to dAppSession.dAppMetaData.name, + ), appSubtitle = dAppSession.dAppMetaData.description, notification = createNotification(dAppSession.securityStatus), walletName = userWallet.name, @@ -31,7 +36,7 @@ internal class WcAppInfoTransformer( connectButtonConfig = WcPrimaryButtonConfig( showProgress = false, enabled = proposalNetwork.missingRequired.isEmpty(), - onClick = onConnect, + onClick = { onConnect(dAppSession.securityStatus) }, ), onDismiss = onDismiss, ) diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/transformers/WcDAppVerifiedStateConverter.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/transformers/WcDAppVerifiedStateConverter.kt new file mode 100644 index 0000000000..59e3acd2c5 --- /dev/null +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/transformers/WcDAppVerifiedStateConverter.kt @@ -0,0 +1,21 @@ +package com.tangem.features.walletconnect.connections.model.transformers + +import com.domain.blockaid.models.dapp.CheckDAppResult +import com.tangem.features.walletconnect.connections.entity.VerifiedDAppState +import com.tangem.utils.converter.Converter + +internal class WcDAppVerifiedStateConverter( + private val onVerifiedClick: (String) -> Unit, +) : Converter, VerifiedDAppState> { + + override fun convert(value: Pair): VerifiedDAppState { + val (checkDAppResult, appName) = value + return if (checkDAppResult == CheckDAppResult.SAFE) { + VerifiedDAppState.Verified( + onVerifiedClick = { onVerifiedClick.invoke(appName) }, + ) + } else { + VerifiedDAppState.Unknown + } + } +} \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/routes/WcAppInfoRoutes.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/routes/WcAppInfoRoutes.kt index dfebe98a93..3b46d9f662 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/routes/WcAppInfoRoutes.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/routes/WcAppInfoRoutes.kt @@ -3,9 +3,10 @@ package com.tangem.features.walletconnect.connections.routes import androidx.compose.runtime.Immutable import com.tangem.core.decompose.navigation.Route import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent +import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetUMV2 import com.tangem.domain.models.network.Network import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.features.walletconnect.connections.components.AlertsComponent +import kotlinx.collections.immutable.ImmutableList import kotlinx.serialization.Serializable @Serializable @@ -27,5 +28,5 @@ internal sealed class WcAppInfoRoutes : TangemBottomSheetConfigContent, Route { ) : WcAppInfoRoutes() @Serializable - data class Alert(val alertType: AlertsComponent.AlertType) : WcAppInfoRoutes() + data class Alert(val elements: ImmutableList) : WcAppInfoRoutes() } \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/InternalComponents.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/InternalComponents.kt index 960e4b2e74..da3205081b 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/InternalComponents.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/InternalComponents.kt @@ -14,7 +14,10 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import coil.compose.AsyncImage +import com.tangem.core.ui.extensions.clickableSingle +import com.tangem.core.ui.extensions.conditional import com.tangem.core.ui.res.TangemTheme +import com.tangem.features.walletconnect.connections.entity.VerifiedDAppState import com.tangem.features.walletconnect.impl.R @Composable @@ -22,11 +25,18 @@ internal fun WcAppInfoItem( iconUrl: String, title: String, subtitle: String, - isVerified: Boolean, + verifiedDAppState: VerifiedDAppState, modifier: Modifier = Modifier, ) { Row( - modifier = modifier.padding(TangemTheme.dimens.spacing12), + modifier = modifier + .conditional( + condition = verifiedDAppState is VerifiedDAppState.Verified, + modifier = { + clickableSingle { (verifiedDAppState as VerifiedDAppState.Verified).onVerifiedClick() } + }, + ) + .padding(TangemTheme.dimens.spacing12), horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing16), ) { AsyncImage( @@ -50,7 +60,7 @@ internal fun WcAppInfoItem( color = TangemTheme.colors.text.primary1, style = TangemTheme.typography.h3, ) - if (isVerified) { + if (verifiedDAppState is VerifiedDAppState.Verified) { Icon( modifier = Modifier.size(20.dp), painter = painterResource(R.drawable.img_approvale2_20), @@ -81,7 +91,9 @@ internal fun WcNetworkInfoItem(@DrawableRes icon: Int, name: String, symbol: Str tint = Color.Unspecified, ) Text( - modifier = Modifier.padding(start = 12.dp).weight(1f, fill = false), + modifier = Modifier + .padding(start = 12.dp) + .weight(1f, fill = false), text = name, style = TangemTheme.typography.body1, color = TangemTheme.colors.text.primary1, diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/WcAppInfoBS.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/WcAppInfoBS.kt index 2c2a3b4c51..29f564d601 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/WcAppInfoBS.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/WcAppInfoBS.kt @@ -127,11 +127,12 @@ private fun WcAppInfoModalBottomSheetContent(state: WcAppInfoUM.Content, modifie private fun WcAppInfoFirstBlock(state: WcAppInfoUM.Content, modifier: Modifier = Modifier) { var connectionRequestExpanded by remember { mutableStateOf(false) } Column(modifier = modifier) { + val verifiedDAppState = state.verifiedDAppState WcAppInfoItem( iconUrl = state.appIcon, title = state.appName, subtitle = state.appSubtitle, - isVerified = state.isVerified, + verifiedDAppState = state.verifiedDAppState, ) HorizontalDivider(thickness = 1.dp, color = TangemTheme.colors.stroke.primary) ConnectionRequestBlock( @@ -594,7 +595,7 @@ private class WcAppInfoStateProvider : CollectionPreviewParameterProvider Unit, val onDismiss: () -> Unit, val onSign: () -> Unit, val onCopy: () -> Unit, diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/common/WcTransactionAppInfoContentUM.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/common/WcTransactionAppInfoContentUM.kt index 3d84687afa..0fe3f892d4 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/common/WcTransactionAppInfoContentUM.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/common/WcTransactionAppInfoContentUM.kt @@ -1,8 +1,10 @@ package com.tangem.features.walletconnect.transaction.entity.common +import com.tangem.features.walletconnect.connections.entity.VerifiedDAppState + internal data class WcTransactionAppInfoContentUM( val appName: String, val appIcon: String, - val isVerified: Boolean, + val verifiedState: VerifiedDAppState, val appSubtitle: String, ) \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSignTransactionModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSignTransactionModel.kt index fbba3635eb..8385ffb643 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSignTransactionModel.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSignTransactionModel.kt @@ -5,7 +5,16 @@ import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.decompose.navigation.Router +import com.tangem.core.ui.R import com.tangem.core.ui.clipboard.ClipboardManager +import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetUMV2 +import com.tangem.core.ui.components.bottomsheets.message.icon +import com.tangem.core.ui.components.bottomsheets.message.infoBlock +import com.tangem.core.ui.components.bottomsheets.message.messageBottomSheetUM +import com.tangem.core.ui.components.bottomsheets.message.onClick +import com.tangem.core.ui.components.bottomsheets.message.secondaryButton +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.wrappedList import com.tangem.domain.walletconnect.WcRequestUseCaseFactory import com.tangem.domain.walletconnect.usecase.method.WcSignState import com.tangem.domain.walletconnect.usecase.method.WcSignStep @@ -46,6 +55,7 @@ internal class WcSignTransactionModel @Inject constructor( val signTransactionUM = useCase.toUM( signState = signState, actions = WcTransactionActionsUM( + onShowVerifiedAlert = ::showVerifiedAlert, onDismiss = { cancel(useCase) }, onSign = useCase::sign, onCopy = { copyData(useCase.rawSdkRequest.request.params) }, @@ -61,6 +71,25 @@ internal class WcSignTransactionModel @Inject constructor( _uiState.value?.transaction?.onDismiss?.invoke() ?: router.pop() } + private fun showVerifiedAlert(appName: String) { + val message = messageBottomSheetUM { + infoBlock { + icon(R.drawable.img_approvale2_20) { + type = MessageBottomSheetUMV2.Icon.Type.Accent + backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.Unspecified + } + title = resourceReference(R.string.wc_alert_verified_domain_title) + body = resourceReference(R.string.wc_alert_verified_domain_description, wrappedList(appName)) + } + secondaryButton { + text = resourceReference(R.string.common_done) + onClick { closeBs() } + } + } + // TODO(wc): Nastya [REDACTED_JIRA] + // router.push(WcAppInfoRoutes.Alert(elements = message.elements)) + } + private fun signingIsDone(signState: WcSignState<*>): Boolean { (signState.domainStep as? WcSignStep.Result)?.result?.let { router.pop() diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/approve/WcApproveTransactionModalBottomSheet.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/approve/WcApproveTransactionModalBottomSheet.kt index 93ed940925..6e1ffd76e9 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/approve/WcApproveTransactionModalBottomSheet.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/approve/WcApproveTransactionModalBottomSheet.kt @@ -21,6 +21,7 @@ import com.tangem.core.ui.components.divider.DividerWithPadding import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.features.walletconnect.connections.entity.VerifiedDAppState import com.tangem.features.walletconnect.connections.ui.WcAppInfoItem import com.tangem.features.walletconnect.impl.R import com.tangem.features.walletconnect.transaction.entity.approve.WcApproveTransactionItemUM @@ -50,7 +51,7 @@ internal fun WcApproveTransactionModalBottomSheetContent( iconUrl = state.appInfo.appIcon, title = state.appInfo.appName, subtitle = state.appInfo.appSubtitle, - isVerified = state.appInfo.isVerified, + verifiedDAppState = state.appInfo.verifiedState, ) DividerWithPadding(start = 0.dp, end = 0.dp) WcTransactionRequestItem( @@ -120,7 +121,7 @@ private class WcApproveTransactionStateProvider : CollectionPreviewParameterProv appInfo = WcTransactionAppInfoContentUM( appName = "React App", appIcon = "", - isVerified = true, + verifiedState = VerifiedDAppState.Verified {}, appSubtitle = "react-app.walletconnect.com", ), spendAllowance = WcSpendAllowanceUM(amountText = "Unlimited USDT", tokenImageUrl = ""), diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/chain/WcAddEthereumChainModalBottomSheetContent.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/chain/WcAddEthereumChainModalBottomSheetContent.kt index 1bcae5ad7d..eb9b7c0945 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/chain/WcAddEthereumChainModalBottomSheetContent.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/chain/WcAddEthereumChainModalBottomSheetContent.kt @@ -4,7 +4,9 @@ import android.content.res.Configuration import androidx.compose.animation.animateContentSize import androidx.compose.foundation.background import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -21,11 +23,15 @@ import com.tangem.core.ui.components.divider.DividerWithPadding import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.features.walletconnect.connections.entity.VerifiedDAppState import com.tangem.features.walletconnect.connections.ui.WcAppInfoItem import com.tangem.features.walletconnect.impl.R import com.tangem.features.walletconnect.transaction.entity.chain.WcAddEthereumChainItemUM import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionAppInfoContentUM -import com.tangem.features.walletconnect.transaction.ui.common.* +import com.tangem.features.walletconnect.transaction.ui.common.WcSmallTitleItem +import com.tangem.features.walletconnect.transaction.ui.common.WcTransactionRequestButtons +import com.tangem.features.walletconnect.transaction.ui.common.WcTransactionRequestItem +import com.tangem.features.walletconnect.transaction.ui.common.WcWalletItem @Composable internal fun WcAddEthereumChainModalBottomSheetContent( @@ -48,7 +54,7 @@ internal fun WcAddEthereumChainModalBottomSheetContent( iconUrl = state.appInfo.appIcon, title = state.appInfo.appName, subtitle = state.appInfo.appSubtitle, - isVerified = state.appInfo.isVerified, + verifiedDAppState = state.appInfo.verifiedState, ) DividerWithPadding(start = 0.dp, end = 0.dp) WcTransactionRequestItem( @@ -130,7 +136,7 @@ private class WcAddEthereumChainStateProvider : CollectionPreviewParameterProvid appInfo = WcTransactionAppInfoContentUM( appName = "React App", appIcon = "", - isVerified = true, + verifiedState = VerifiedDAppState.Verified {}, appSubtitle = "react-app.walletconnect.com", ), walletName = "Tangem 2.0", diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/sign/WcSignTransactionModalBottomSheetContent.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/sign/WcSignTransactionModalBottomSheetContent.kt index 395b577374..7041668bae 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/sign/WcSignTransactionModalBottomSheetContent.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/sign/WcSignTransactionModalBottomSheetContent.kt @@ -23,6 +23,7 @@ import com.tangem.core.ui.components.divider.DividerWithPadding import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.features.walletconnect.connections.entity.VerifiedDAppState import com.tangem.features.walletconnect.connections.ui.WcAppInfoItem import com.tangem.features.walletconnect.impl.R import com.tangem.features.walletconnect.transaction.entity.common.WcNetworkInfoUM @@ -51,7 +52,7 @@ internal fun WcSignTransactionModalBottomSheetContent( iconUrl = state.appInfo.appIcon, title = state.appInfo.appName, subtitle = state.appInfo.appSubtitle, - isVerified = state.appInfo.isVerified, + verifiedDAppState = state.appInfo.verifiedState, ) DividerWithPadding(start = 0.dp, end = 0.dp) WcTransactionRequestItem( @@ -146,7 +147,7 @@ private class WcSignTransactionStateProvider : CollectionPreviewParameterProvide appInfo = WcTransactionAppInfoContentUM( appName = "React App", appIcon = "", - isVerified = true, + verifiedState = VerifiedDAppState.Verified {}, appSubtitle = "react-app.walletconnect.com", ), walletName = "Tangem 2.0", @@ -158,7 +159,7 @@ private class WcSignTransactionStateProvider : CollectionPreviewParameterProvide appInfo = WcTransactionAppInfoContentUM( appName = "React App", appIcon = "", - isVerified = true, + verifiedState = VerifiedDAppState.Verified {}, appSubtitle = "react-app.walletconnect.com", ), walletName = "Tangem 2.0", diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/utils/WcTransactionUtils.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/utils/WcTransactionUtils.kt index fdd34e1967..e031ea0a21 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/utils/WcTransactionUtils.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/utils/WcTransactionUtils.kt @@ -1,11 +1,16 @@ package com.tangem.features.walletconnect.transaction.utils +import com.domain.blockaid.models.dapp.CheckDAppResult import com.tangem.core.ui.extensions.getActiveIconRes import com.tangem.core.ui.extensions.resourceReference import com.tangem.domain.walletconnect.model.WcEthMethod import com.tangem.domain.walletconnect.model.WcSolanaMethod import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest -import com.tangem.domain.walletconnect.usecase.method.* +import com.tangem.domain.walletconnect.usecase.method.WcMessageSignUseCase +import com.tangem.domain.walletconnect.usecase.method.WcSignState +import com.tangem.domain.walletconnect.usecase.method.WcSignStep +import com.tangem.domain.walletconnect.usecase.method.WcSignUseCase +import com.tangem.features.walletconnect.connections.model.transformers.WcDAppVerifiedStateConverter import com.tangem.features.walletconnect.impl.R import com.tangem.features.walletconnect.transaction.entity.chain.WcAddEthereumChainItemUM import com.tangem.features.walletconnect.transaction.entity.chain.WcAddEthereumChainUM @@ -148,7 +153,9 @@ private fun WcMethodContext.basicTransactionRequestInfoBlocks() = persistentList private fun WcMethodContext.appInfo() = WcTransactionAppInfoContentUM( appName = session.sdkModel.appMetaData.name, appIcon = session.sdkModel.appMetaData.url, - isVerified = session.securityStatus == com.domain.blockaid.models.dapp.CheckDAppResult.SAFE, + verifiedState = WcDAppVerifiedStateConverter(onVerifiedClick = actions.onShowVerifiedAlert).convert( + session.securityStatus to session.sdkModel.appMetaData.name, + ), appSubtitle = session.sdkModel.appMetaData.description, )