diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index 440dac2a29..7a0bd7ff8e 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -1236,6 +1236,21 @@
This is a Testnet card. It cannot process transactions and should only be used for testing and development purposes.
For testing purposes only
Balance may be outdated. Refresh the page.
+ Unknown domain
+ Connect anyway
+ Timeout error. Please, try again later.
+ Failed to establish WalletConnect
+ This domain cannot be verified. Check the request carefully approving.
+ Go back to your browser and connect via WalletConnect again.
+ WalletConnect session was disconnected
+ Error code: %s. If the problem persists — feel free to contact our support.
+ We\'ve encountered unknown error
+ Tangem does not currently support a required network by %s.
+ Unsuported networks
+ Tangem support a required network by %s.
+ Verified domain
+ Wrong card or ring selected in Tangem App
+ We\'ve got some kind of problem
Connections
Disconnect all
Text about discnected all dApps
diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt
index 7698a8067d..1d5172f597 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt
@@ -95,6 +95,7 @@ data class TangemDimens internal constructor(
val size142: Dp = 142.dp,
val size158: Dp = 158.dp,
val size164: Dp = 164.dp,
+ val size180: Dp = 180.dp,
val size200: Dp = 200.dp,
val size248: Dp = 248.dp,
val size350: Dp = 350.dp,
diff --git a/core/ui/src/main/res/drawable/ic_network_new_24.xml b/core/ui/src/main/res/drawable/ic_network_new_24.xml
new file mode 100644
index 0000000000..2e2f3e36ed
--- /dev/null
+++ b/core/ui/src/main/res/drawable/ic_network_new_24.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
diff --git a/core/ui/src/main/res/drawable/img_approvale2_20.xml b/core/ui/src/main/res/drawable/img_approvale2_20.xml
new file mode 100644
index 0000000000..d5e74adaf9
--- /dev/null
+++ b/core/ui/src/main/res/drawable/img_approvale2_20.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/core/ui/src/main/res/drawable/img_knight_shield_32.xml b/core/ui/src/main/res/drawable/img_knight_shield_32.xml
new file mode 100644
index 0000000000..31dd7900c0
--- /dev/null
+++ b/core/ui/src/main/res/drawable/img_knight_shield_32.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/AlertsComponent.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/AlertsComponent.kt
new file mode 100644
index 0000000000..a03ae3a26d
--- /dev/null
+++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/AlertsComponent.kt
@@ -0,0 +1,51 @@
+package com.tangem.features.walletconnect.connections.components
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Immutable
+import com.tangem.core.decompose.context.AppComponentContext
+import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
+import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
+import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
+import com.tangem.features.walletconnect.connections.ui.AlertsModalBottomSheet
+
+internal class AlertsComponent(
+ appComponentContext: AppComponentContext,
+ private val params: Params,
+) : AppComponentContext by appComponentContext, ComposableBottomSheetComponent {
+
+ override fun dismiss() {
+ params.alertType.onDismiss()
+ }
+
+ @Composable
+ override fun BottomSheet() {
+ AlertsModalBottomSheet(
+ config = TangemBottomSheetConfig(
+ isShown = true,
+ onDismissRequest = params.alertType.onDismiss,
+ content = params.alertType,
+ ),
+ )
+ }
+
+ internal data class Params(val alertType: AlertType)
+
+ @Immutable
+ internal sealed class AlertType : TangemBottomSheetConfigContent {
+ abstract val onDismiss: () -> Unit
+
+ data class VerifiedDomain(val appName: String, override val onDismiss: () -> Unit) : AlertType()
+ data class UnknownDomain(val onConnect: () -> Unit, override val onDismiss: () -> Unit) : AlertType()
+ data class UnsafeDomain(val onConnect: () -> Unit, override val onDismiss: () -> Unit) : AlertType()
+ data class UnsupportedNetworks(val appName: String, override val onDismiss: () -> Unit) : AlertType()
+ data class WcDisconnected(override val onDismiss: () -> Unit) : AlertType()
+ data class UnknownError(val errorCode: Int, override val onDismiss: () -> Unit) : AlertType()
+ data class WrongCardSelected(override val onDismiss: () -> Unit) : AlertType()
+ data class ConnectionTimeout(val onTryAgain: () -> Unit, override val onDismiss: () -> Unit) : AlertType()
+ data class MaliciousTransaction(
+ val descriptionMessage: String,
+ val onConnect: () -> Unit,
+ override val onDismiss: () -> Unit,
+ ) : AlertType()
+ }
+}
\ No newline at end of file
diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/AlertsModalBottomSheet.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/AlertsModalBottomSheet.kt
new file mode 100644
index 0000000000..ae14e28008
--- /dev/null
+++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/AlertsModalBottomSheet.kt
@@ -0,0 +1,355 @@
+package com.tangem.features.walletconnect.connections.ui
+
+import android.content.res.Configuration
+import androidx.annotation.DrawableRes
+import androidx.annotation.StringRes
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.style.TextAlign
+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 com.tangem.core.ui.R
+import com.tangem.core.ui.components.PrimaryButton
+import com.tangem.core.ui.components.SecondaryButton
+import com.tangem.core.ui.components.SecondaryButtonIconEnd
+import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
+import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet
+import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
+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.connections.components.AlertsComponent
+
+@Composable
+internal fun AlertsModalBottomSheet(config: TangemBottomSheetConfig) {
+ TangemModalBottomSheet(
+ config = config,
+ title = { alert ->
+ TangemModalBottomSheetTitle(
+ endIconRes = R.drawable.ic_close_24,
+ onEndClick = alert.onDismiss,
+ )
+ },
+ content = { alert -> Content(alert = alert) },
+ )
+}
+
+@Composable
+private fun Content(alert: AlertsComponent.AlertType, modifier: Modifier = Modifier) {
+ Column(modifier = modifier) {
+ ContentContainer(
+ modifier = Modifier
+ .heightIn(min = TangemTheme.dimens.size180)
+ .fillMaxWidth()
+ .padding(horizontal = TangemTheme.dimens.spacing16)
+ .padding(bottom = 32.dp),
+ alert = alert,
+ )
+ ButtonsContainer(modifier = Modifier.fillMaxWidth(), alert = alert)
+ }
+}
+
+@Composable
+private fun ContentContainer(alert: AlertsComponent.AlertType, modifier: Modifier = Modifier) {
+ Column(modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally) {
+ AlertIcon(alert = alert)
+ AlertContentTitle(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(top = TangemTheme.dimens.spacing24),
+ alert = alert,
+ )
+ AlertContentDescription(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(top = TangemTheme.dimens.spacing8),
+ alert = alert,
+ )
+ AlertAudit(alert, modifier = Modifier.padding(top = TangemTheme.dimens.spacing16))
+ }
+}
+
+@Suppress("LongMethod")
+@Composable
+private fun ButtonsContainer(alert: AlertsComponent.AlertType, modifier: Modifier = Modifier) {
+ Column(
+ modifier = modifier.padding(all = TangemTheme.dimens.spacing16),
+ verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
+ ) {
+ val buttonsModifier = Modifier.fillMaxWidth()
+ when (alert) {
+ is AlertsComponent.AlertType.UnsafeDomain -> {
+ PrimaryButton(
+ modifier = buttonsModifier,
+ text = stringResourceSafe(R.string.common_cancel),
+ onClick = alert.onDismiss,
+ )
+ SecondaryButton(
+ modifier = buttonsModifier,
+ onClick = alert.onConnect,
+ text = stringResourceSafe(R.string.wc_alert_connect_anyway),
+ )
+ }
+ is AlertsComponent.AlertType.MaliciousTransaction -> {
+ PrimaryButton(
+ modifier = buttonsModifier,
+ text = stringResourceSafe(R.string.common_cancel),
+ onClick = alert.onDismiss,
+ )
+ SecondaryButtonIconEnd(
+ modifier = buttonsModifier,
+ onClick = alert.onConnect,
+ iconResId = R.drawable.ic_tangem_24,
+ text = stringResourceSafe(R.string.wc_alert_connect_anyway),
+ )
+ }
+ is AlertsComponent.AlertType.UnknownDomain -> {
+ PrimaryButton(
+ modifier = buttonsModifier,
+ text = stringResourceSafe(R.string.common_cancel),
+ onClick = alert.onDismiss,
+ )
+ SecondaryButton(
+ modifier = buttonsModifier,
+ onClick = alert.onConnect,
+ text = stringResourceSafe(R.string.wc_alert_connect_anyway),
+ )
+ }
+ is AlertsComponent.AlertType.VerifiedDomain -> SecondaryButton(
+ modifier = buttonsModifier,
+ text = stringResourceSafe(R.string.common_done),
+ onClick = alert.onDismiss,
+ )
+ is AlertsComponent.AlertType.ConnectionTimeout -> {
+ PrimaryButton(
+ modifier = buttonsModifier,
+ onClick = alert.onTryAgain,
+ text = stringResourceSafe(R.string.alert_button_try_again),
+ )
+ SecondaryButton(
+ modifier = buttonsModifier,
+ onClick = alert.onDismiss,
+ text = stringResourceSafe(R.string.balance_hidden_got_it_button),
+ )
+ }
+ is AlertsComponent.AlertType.WrongCardSelected,
+ is AlertsComponent.AlertType.UnknownError,
+ -> SecondaryButton(
+ modifier = buttonsModifier,
+ onClick = alert.onDismiss,
+ text = stringResourceSafe(R.string.balance_hidden_got_it_button),
+ )
+ is AlertsComponent.AlertType.WcDisconnected,
+ is AlertsComponent.AlertType.UnsupportedNetworks,
+ -> PrimaryButton(
+ modifier = buttonsModifier,
+ onClick = alert.onDismiss,
+ text = stringResourceSafe(R.string.balance_hidden_got_it_button),
+ )
+ }
+ }
+}
+
+@Composable
+private fun AlertIcon(alert: AlertsComponent.AlertType, modifier: Modifier = Modifier) {
+ val color = when (alert) {
+ is AlertsComponent.AlertType.VerifiedDomain -> TangemTheme.colors.icon.accent
+ is AlertsComponent.AlertType.WcDisconnected,
+ is AlertsComponent.AlertType.ConnectionTimeout,
+ is AlertsComponent.AlertType.UnsupportedNetworks,
+ -> TangemTheme.colors.icon.informative
+ is AlertsComponent.AlertType.WrongCardSelected,
+ is AlertsComponent.AlertType.UnknownDomain,
+ is AlertsComponent.AlertType.UnknownError,
+ -> TangemTheme.colors.icon.attention
+ is AlertsComponent.AlertType.MaliciousTransaction,
+ is AlertsComponent.AlertType.UnsafeDomain,
+ -> TangemTheme.colors.icon.warning
+ }
+
+ @DrawableRes val drawableId = when (alert) {
+ is AlertsComponent.AlertType.WcDisconnected,
+ is AlertsComponent.AlertType.ConnectionTimeout,
+ -> R.drawable.ic_wallet_connect_24
+ is AlertsComponent.AlertType.MaliciousTransaction -> R.drawable.img_knight_shield_32
+ is AlertsComponent.AlertType.UnknownDomain,
+ is AlertsComponent.AlertType.UnsafeDomain,
+ -> R.drawable.img_knight_shield_32
+ is AlertsComponent.AlertType.UnsupportedNetworks -> R.drawable.ic_network_new_24
+ is AlertsComponent.AlertType.WrongCardSelected,
+ is AlertsComponent.AlertType.UnknownError,
+ -> R.drawable.img_attention_20
+ is AlertsComponent.AlertType.VerifiedDomain -> R.drawable.img_approvale2_20
+ }
+ val iconTint = when (alert) {
+ is AlertsComponent.AlertType.UnknownDomain,
+ is AlertsComponent.AlertType.UnsafeDomain,
+ is AlertsComponent.AlertType.MaliciousTransaction,
+ is AlertsComponent.AlertType.UnsupportedNetworks,
+ is AlertsComponent.AlertType.ConnectionTimeout,
+ is AlertsComponent.AlertType.WcDisconnected,
+ -> color
+ is AlertsComponent.AlertType.VerifiedDomain,
+ is AlertsComponent.AlertType.UnknownError,
+ is AlertsComponent.AlertType.WrongCardSelected,
+ -> Color.Unspecified
+ }
+ Box(
+ modifier = modifier
+ .size(TangemTheme.dimens.size56)
+ .clip(CircleShape)
+ .background(color.copy(alpha = 0.1F)),
+ contentAlignment = Alignment.Center,
+ content = {
+ Icon(
+ modifier = Modifier.size(TangemTheme.dimens.size32),
+ painter = painterResource(drawableId),
+ contentDescription = null,
+ tint = iconTint,
+ )
+ },
+ )
+}
+
+@Composable
+private fun AlertContentTitle(alert: AlertsComponent.AlertType, modifier: Modifier = Modifier) {
+ @StringRes val titleRes: Int = when (alert) {
+ is AlertsComponent.AlertType.UnknownDomain,
+ is AlertsComponent.AlertType.UnsafeDomain,
+ is AlertsComponent.AlertType.MaliciousTransaction,
+ -> R.string.security_alert_title
+ is AlertsComponent.AlertType.ConnectionTimeout -> R.string.wc_alert_connection_timeout_title
+ is AlertsComponent.AlertType.UnknownError -> R.string.wc_alert_unknown_error_title
+ is AlertsComponent.AlertType.UnsupportedNetworks -> R.string.wc_alert_unsupported_networks_title
+ is AlertsComponent.AlertType.VerifiedDomain -> R.string.wc_alert_verified_domain_title
+ is AlertsComponent.AlertType.WcDisconnected -> R.string.wc_alert_session_disconnected_title
+ is AlertsComponent.AlertType.WrongCardSelected -> R.string.wc_alert_wrong_card_title
+ }
+ Text(
+ modifier = modifier,
+ text = stringResourceSafe(titleRes),
+ style = TangemTheme.typography.h3,
+ color = TangemTheme.colors.text.primary1,
+ textAlign = TextAlign.Center,
+ )
+}
+
+@Composable
+private fun AlertContentDescription(alert: AlertsComponent.AlertType, modifier: Modifier = Modifier) {
+ val message = when (alert) {
+ is AlertsComponent.AlertType.ConnectionTimeout -> stringResourceSafe(
+ R.string.wc_alert_connection_timeout_description,
+ )
+ is AlertsComponent.AlertType.MaliciousTransaction -> alert.descriptionMessage
+ is AlertsComponent.AlertType.UnknownDomain,
+ is AlertsComponent.AlertType.UnsafeDomain,
+ -> stringResourceSafe(R.string.wc_alert_domain_issues_description)
+ is AlertsComponent.AlertType.UnknownError -> stringResourceSafe(
+ R.string.wc_alert_unknown_error_description,
+ alert.errorCode,
+ )
+ is AlertsComponent.AlertType.UnsupportedNetworks -> stringResourceSafe(
+ R.string.wc_alert_unsupported_networks_description,
+ alert.appName,
+ )
+ is AlertsComponent.AlertType.VerifiedDomain -> stringResourceSafe(
+ R.string.wc_alert_verified_domain_description,
+ alert.appName,
+ )
+ is AlertsComponent.AlertType.WcDisconnected -> stringResourceSafe(
+ R.string.wc_alert_session_disconnected_description,
+ )
+ is AlertsComponent.AlertType.WrongCardSelected -> stringResourceSafe(R.string.wc_alert_wrong_card_description)
+ }
+ Text(
+ modifier = modifier,
+ text = message,
+ style = TangemTheme.typography.body2,
+ color = TangemTheme.colors.text.secondary,
+ textAlign = TextAlign.Center,
+ )
+}
+
+@Composable
+private fun AlertAudit(alert: AlertsComponent.AlertType, modifier: Modifier = Modifier) {
+ when (alert) {
+ is AlertsComponent.AlertType.UnknownDomain -> Text(
+ modifier = modifier
+ .background(
+ shape = RoundedCornerShape(TangemTheme.dimens.radius16),
+ color = TangemTheme.colors.text.primary1.copy(alpha = 0.1F),
+ )
+ .padding(vertical = TangemTheme.dimens.spacing4, horizontal = TangemTheme.dimens.spacing12),
+ text = stringResourceSafe(R.string.wc_alert_audit_unknown_domain),
+ style = TangemTheme.typography.caption1,
+ color = TangemTheme.colors.text.primary1,
+ )
+ is AlertsComponent.AlertType.UnsafeDomain -> Text(
+ modifier = Modifier
+ .padding(top = TangemTheme.dimens.spacing16)
+ .background(
+ shape = RoundedCornerShape(TangemTheme.dimens.radius16),
+ color = TangemTheme.colors.text.warning.copy(alpha = 0.1F),
+ )
+ .padding(vertical = TangemTheme.dimens.spacing4, horizontal = TangemTheme.dimens.spacing12),
+ text = stringResourceSafe(R.string.wc_alert_audit_unknown_domain),
+ style = TangemTheme.typography.caption1,
+ color = TangemTheme.colors.text.warning,
+ )
+ is AlertsComponent.AlertType.ConnectionTimeout,
+ is AlertsComponent.AlertType.MaliciousTransaction,
+ is AlertsComponent.AlertType.UnknownError,
+ is AlertsComponent.AlertType.UnsupportedNetworks,
+ is AlertsComponent.AlertType.VerifiedDomain,
+ is AlertsComponent.AlertType.WcDisconnected,
+ is AlertsComponent.AlertType.WrongCardSelected,
+ -> Unit
+ }
+}
+
+@Preview(showBackground = true)
+@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+private fun AlertsModalBottomSheet_Preview(
+ @PreviewParameter(AlertTypesProvider::class) alert: AlertsComponent.AlertType,
+) {
+ TangemThemePreview {
+ AlertsModalBottomSheet(
+ config = TangemBottomSheetConfig(
+ isShown = true,
+ onDismissRequest = {},
+ content = alert,
+ ),
+ )
+ }
+}
+
+private class AlertTypesProvider : CollectionPreviewParameterProvider(
+ collection = listOf(
+ AlertsComponent.AlertType.VerifiedDomain(appName = "React App", onDismiss = {}),
+ AlertsComponent.AlertType.UnknownDomain(onConnect = {}, onDismiss = {}),
+ AlertsComponent.AlertType.UnsafeDomain(onConnect = {}, onDismiss = {}),
+ AlertsComponent.AlertType.MaliciousTransaction(
+ descriptionMessage = "The transaction approves erc20 tokens to a known malicious address",
+ onConnect = {},
+ onDismiss = {},
+ ),
+ AlertsComponent.AlertType.UnsupportedNetworks(appName = "React App", onDismiss = {}),
+ AlertsComponent.AlertType.WcDisconnected(onDismiss = {}),
+ AlertsComponent.AlertType.UnknownError(errorCode = 8005, onDismiss = {}),
+ AlertsComponent.AlertType.WrongCardSelected(onDismiss = {}),
+ AlertsComponent.AlertType.ConnectionTimeout(onTryAgain = {}, onDismiss = {}),
+ ),
+)
\ No newline at end of file