Updated on 2026-08-14

This commit is contained in:
Tangem 2025-08-19 14:15:30 +07:00
parent c8a163fd0a
commit 65ace6c5f2
21 changed files with 349 additions and 39 deletions

View file

@ -38,5 +38,23 @@ internal class AlertsComponent(
@Serializable
data class WcDisconnected(override val onDismiss: () -> Unit) : AlertType()
@Serializable
data class TangemUnsupportedNetwork(
val network: String,
override val onDismiss: () -> Unit,
) : AlertType()
@Serializable
data class RequiredAddNetwork(
val network: String,
override val onDismiss: () -> Unit,
) : AlertType()
@Serializable
data class RequiredReconnectWithNetwork(
val network: String,
override val onDismiss: () -> Unit,
) : AlertType()
}
}

View file

@ -18,8 +18,10 @@ import com.tangem.features.send.v2.api.FeeSelectorBlockComponent
import com.tangem.features.send.v2.api.FeeSelectorComponent
import com.tangem.features.walletconnect.components.WcRoutingComponent
import com.tangem.features.walletconnect.connections.components.AlertsComponent
import com.tangem.features.walletconnect.connections.components.AlertsComponent.AlertType.*
import com.tangem.features.walletconnect.connections.components.WcPairComponent
import com.tangem.features.walletconnect.transaction.components.chain.WcAddNetworkContainerComponent
import com.tangem.features.walletconnect.transaction.components.chain.WcSwitchNetworkComponent
import com.tangem.features.walletconnect.transaction.components.common.WcTransactionModelParams
import com.tangem.features.walletconnect.transaction.components.send.WcSendTransactionContainerComponent
import com.tangem.features.walletconnect.transaction.components.sign.WcSignTransactionContainerComponent
@ -71,6 +73,10 @@ internal class DefaultWcRoutingComponent @AssistedInject constructor(
appComponentContext = childContext,
params = WcTransactionModelParams(config.rawRequest),
)
is WcInnerRoute.SwitchNetwork -> WcSwitchNetworkComponent(
appComponentContext = childContext,
params = WcTransactionModelParams(config.rawRequest),
)
is WcInnerRoute.Send -> WcSendTransactionContainerComponent(
appComponentContext = childContext,
params = WcTransactionModelParams(config.rawRequest),
@ -88,13 +94,31 @@ internal class DefaultWcRoutingComponent @AssistedInject constructor(
is WcInnerRoute.UnsupportedMethodAlert -> AlertsComponent(
childContext,
AlertsComponent.Params(
alertType = AlertsComponent.AlertType.UnsupportedMethod { model.innerRouter.pop() },
alertType = UnsupportedMethod { model.innerRouter.pop() },
),
)
is WcInnerRoute.WcDappDisconnected -> AlertsComponent(
childContext,
AlertsComponent.Params(
alertType = AlertsComponent.AlertType.WcDisconnected { model.innerRouter.pop() },
alertType = WcDisconnected { model.innerRouter.pop() },
),
)
is WcInnerRoute.TangemUnsupportedNetwork -> AlertsComponent(
childContext,
AlertsComponent.Params(
alertType = TangemUnsupportedNetwork(config.networkName) { model.innerRouter.pop() },
),
)
is WcInnerRoute.RequiredAddNetwork -> AlertsComponent(
childContext,
AlertsComponent.Params(
alertType = RequiredAddNetwork(config.networkName) { model.innerRouter.pop() },
),
)
is WcInnerRoute.RequiredReconnectWithNetwork -> AlertsComponent(
childContext,
AlertsComponent.Params(
alertType = RequiredReconnectWithNetwork(config.networkName) { model.innerRouter.pop() },
),
)
}

View file

@ -22,6 +22,9 @@ internal sealed interface WcInnerRoute : Route {
@Serializable
data class AddNetwork(override val rawRequest: WcSdkSessionRequest) : Method
@Serializable
data class SwitchNetwork(override val rawRequest: WcSdkSessionRequest) : Method
@Serializable
data class Pair(val request: WcPairRequest) : WcInnerRoute
@ -30,4 +33,13 @@ internal sealed interface WcInnerRoute : Route {
@Serializable
data object WcDappDisconnected : WcInnerRoute
@Serializable
data class TangemUnsupportedNetwork(val networkName: String) : WcInnerRoute
@Serializable
data class RequiredAddNetwork(val networkName: String) : WcInnerRoute
@Serializable
data class RequiredReconnectWithNetwork(val networkName: String) : WcInnerRoute
}

View file

@ -51,6 +51,8 @@ internal class WcRoutingModel @Inject constructor(
-> WcInnerRoute.SignMessage(rawRequest)
WcEthMethodName.AddEthereumChain,
-> WcInnerRoute.AddNetwork(rawRequest)
WcEthMethodName.SwitchEthereumChain,
-> WcInnerRoute.SwitchNetwork(rawRequest)
WcEthMethodName.SignTransaction,
WcEthMethodName.SendTransaction,
WcSolanaMethodName.SignTransaction,

View file

@ -87,7 +87,11 @@ private fun ButtonsContainer(alert: AlertsComponent.AlertType, modifier: Modifie
) {
val buttonsModifier = Modifier.fillMaxWidth()
when (alert) {
is AlertsComponent.AlertType.UnsupportedMethod -> SecondaryButton(
is AlertsComponent.AlertType.UnsupportedMethod,
is AlertsComponent.AlertType.RequiredAddNetwork,
is AlertsComponent.AlertType.RequiredReconnectWithNetwork,
is AlertsComponent.AlertType.TangemUnsupportedNetwork,
-> SecondaryButton(
modifier = buttonsModifier,
onClick = alert.onDismiss,
text = stringResourceSafe(R.string.balance_hidden_got_it_button),
@ -105,16 +109,28 @@ private fun ButtonsContainer(alert: AlertsComponent.AlertType, modifier: Modifie
private fun AlertIcon(alert: AlertsComponent.AlertType, modifier: Modifier = Modifier) {
val color = when (alert) {
is AlertsComponent.AlertType.WcDisconnected -> TangemTheme.colors.icon.informative
is AlertsComponent.AlertType.UnsupportedMethod -> TangemTheme.colors.icon.attention
is AlertsComponent.AlertType.UnsupportedMethod,
is AlertsComponent.AlertType.RequiredAddNetwork,
is AlertsComponent.AlertType.RequiredReconnectWithNetwork,
is AlertsComponent.AlertType.TangemUnsupportedNetwork,
-> TangemTheme.colors.icon.attention
}
@DrawableRes val drawableId = when (alert) {
is AlertsComponent.AlertType.WcDisconnected -> R.drawable.ic_wallet_connect_24
is AlertsComponent.AlertType.UnsupportedMethod -> R.drawable.img_attention_20
is AlertsComponent.AlertType.UnsupportedMethod,
is AlertsComponent.AlertType.RequiredAddNetwork,
is AlertsComponent.AlertType.RequiredReconnectWithNetwork,
is AlertsComponent.AlertType.TangemUnsupportedNetwork,
-> R.drawable.img_attention_20
}
val iconTint = when (alert) {
is AlertsComponent.AlertType.WcDisconnected -> color
is AlertsComponent.AlertType.UnsupportedMethod -> Color.Unspecified
is AlertsComponent.AlertType.UnsupportedMethod,
is AlertsComponent.AlertType.RequiredAddNetwork,
is AlertsComponent.AlertType.RequiredReconnectWithNetwork,
is AlertsComponent.AlertType.TangemUnsupportedNetwork,
-> Color.Unspecified
}
Box(
modifier = modifier
@ -138,6 +154,9 @@ private fun AlertContentTitle(alert: AlertsComponent.AlertType, modifier: Modifi
@StringRes val titleRes: Int = when (alert) {
is AlertsComponent.AlertType.WcDisconnected -> R.string.wc_alert_session_disconnected_title
is AlertsComponent.AlertType.UnsupportedMethod -> R.string.wc_alert_unsupported_method_title
is AlertsComponent.AlertType.RequiredAddNetwork -> R.string.wc_alert_add_network_to_portfolio_title
is AlertsComponent.AlertType.RequiredReconnectWithNetwork -> R.string.wc_alert_network_not_connected_title
is AlertsComponent.AlertType.TangemUnsupportedNetwork -> R.string.wc_alert_unsupported_network_title
}
Text(
modifier = modifier,
@ -157,6 +176,18 @@ private fun AlertContentDescription(alert: AlertsComponent.AlertType, modifier:
is AlertsComponent.AlertType.UnsupportedMethod -> stringResourceSafe(
R.string.wc_alert_unsupported_method_description,
)
is AlertsComponent.AlertType.RequiredAddNetwork -> stringResourceSafe(
R.string.wc_alert_unsupported_method_description,
alert.network,
)
is AlertsComponent.AlertType.RequiredReconnectWithNetwork -> stringResourceSafe(
R.string.wc_alert_network_not_connected_description,
alert.network,
)
is AlertsComponent.AlertType.TangemUnsupportedNetwork -> stringResourceSafe(
R.string.wc_alert_unsupported_network_description,
alert.network,
)
}
Text(
modifier = modifier,
@ -188,5 +219,8 @@ private class AlertTypesProvider : CollectionPreviewParameterProvider<AlertsComp
collection = listOf(
AlertsComponent.AlertType.WcDisconnected(onDismiss = {}),
AlertsComponent.AlertType.UnsupportedMethod(onDismiss = {}),
AlertsComponent.AlertType.TangemUnsupportedNetwork(network = "Solana", onDismiss = {}),
AlertsComponent.AlertType.RequiredAddNetwork(network = "Solana", onDismiss = {}),
AlertsComponent.AlertType.RequiredReconnectWithNetwork(network = "Solana", onDismiss = {}),
),
)

View file

@ -7,6 +7,7 @@ import com.tangem.features.walletconnect.connections.routing.WcRoutingModel
import com.tangem.features.walletconnect.transaction.model.WcAddNetworkModel
import com.tangem.features.walletconnect.transaction.model.WcSendTransactionModel
import com.tangem.features.walletconnect.transaction.model.WcSignTransactionModel
import com.tangem.features.walletconnect.transaction.model.WcSwitchNetworkModel
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
@ -57,6 +58,11 @@ internal interface WalletConnectModelModule {
@ClassKey(WcAddNetworkModel::class)
fun bindWcAddNetworkModel(model: WcAddNetworkModel): Model
@Binds
@IntoMap
@ClassKey(WcSwitchNetworkModel::class)
fun bindWcSwitchNetworkModel(model: WcSwitchNetworkModel): Model
@Binds
@IntoMap
@ClassKey(WcSendTransactionModel::class)

View file

@ -0,0 +1,21 @@
package com.tangem.features.walletconnect.transaction.components.chain
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.features.walletconnect.transaction.components.common.WcTransactionModelParams
import com.tangem.features.walletconnect.transaction.model.WcSwitchNetworkModel
internal class WcSwitchNetworkComponent(
appComponentContext: AppComponentContext,
params: WcTransactionModelParams,
) : AppComponentContext by appComponentContext, ComposableContentComponent {
private val model: WcSwitchNetworkModel = getOrCreateModel(params = params)
@Composable
override fun Content(modifier: Modifier) {
}
}

View file

@ -12,5 +12,9 @@ internal object WcHandleMethodErrorConverter :
is HandleMethodError.UnknownError,
-> WcInnerRoute.UnsupportedMethodAlert
HandleMethodError.UnknownSession -> WcInnerRoute.WcDappDisconnected
is HandleMethodError.NotAddedNetwork -> WcInnerRoute.RequiredAddNetwork(value.networkName)
is HandleMethodError.RequiredNetwork -> WcInnerRoute.RequiredReconnectWithNetwork(value.networkName)
is HandleMethodError.TangemUnsupportedNetwork -> WcInnerRoute.TangemUnsupportedNetwork(value.unsupportedNetwork)
}
}

View file

@ -57,22 +57,27 @@ internal class WcAddNetworkModel @Inject constructor(
.onLeft { router.push(WcHandleMethodErrorConverter.convert(it)) }
.getOrNull() ?: return@launch
sendSignatureReceivedAnalytics(useCase)
_uiState.emit(
wcAddEthereumChainUMConverter.convert(
WcAddEthereumChainUMConverter.Input(
useCase = useCase,
actions = WcTransactionActionsUM(
onShowVerifiedAlert = ::showVerifiedAlert,
onDismiss = { cancel(useCase) },
onSign = { sign(useCase) },
onCopy = { copyData(useCase.rawSdkRequest.request.params) },
),
),
),
)
val either = useCase.invoke()
either
.onLeft { router.push(WcHandleMethodErrorConverter.convert(it)) }
.map { showUI() }
}
}
private fun showUI() {
_uiState.value = wcAddEthereumChainUMConverter.convert(
WcAddEthereumChainUMConverter.Input(
useCase = useCase,
actions = WcTransactionActionsUM(
onShowVerifiedAlert = ::showVerifiedAlert,
onDismiss = { cancel(useCase) },
onSign = { sign(useCase) },
onCopy = { copyData(useCase.rawSdkRequest.request.params) },
),
),
)
}
override fun dismiss() {
_uiState.value?.transaction?.onDismiss?.invoke() ?: router.pop()
}

View file

@ -0,0 +1,45 @@
package com.tangem.features.walletconnect.transaction.model
import androidx.compose.runtime.Stable
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.domain.walletconnect.WcRequestUseCaseFactory
import com.tangem.domain.walletconnect.model.HandleMethodError
import com.tangem.domain.walletconnect.usecase.method.WcSwitchNetworkUseCase
import com.tangem.features.walletconnect.transaction.components.common.WcTransactionModelParams
import com.tangem.features.walletconnect.transaction.converter.WcHandleMethodErrorConverter
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.launch
import javax.inject.Inject
@Suppress("LongParameterList")
@Stable
@ModelScoped
internal class WcSwitchNetworkModel @Inject constructor(
paramsContainer: ParamsContainer,
override val dispatchers: CoroutineDispatcherProvider,
private val router: Router,
private val useCaseFactory: WcRequestUseCaseFactory,
) : Model() {
private val params = paramsContainer.require<WcTransactionModelParams>()
init {
modelScope.launch {
val useCase = useCaseFactory.createUseCase<WcSwitchNetworkUseCase>(params.rawRequest)
.onLeft { showErrorDialog(it) }
.getOrNull() ?: return@launch
val either = useCase.invoke()
useCase.reject()
either
.onLeft { showErrorDialog(it) }
.map { showErrorDialog(HandleMethodError.RequiredNetwork(it.network.name)) }
}
}
private fun showErrorDialog(error: HandleMethodError) {
router.push(WcHandleMethodErrorConverter.convert(error))
}
}