diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index 90a9ac6c99..02d8bd7799 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -1292,6 +1292,7 @@
Network
Networks
Wallet
+ Connected networks
View your wallet balance and activity
Sign transactions without your notice
Request approval for transactions
diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/di/WalletConnectDataModule.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/di/WalletConnectDataModule.kt
index 77752b86b6..16f2cde7ad 100644
--- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/di/WalletConnectDataModule.kt
+++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/di/WalletConnectDataModule.kt
@@ -7,11 +7,6 @@ import com.tangem.data.walletconnect.DefaultWalletConnectRepository
import com.tangem.data.walletconnect.initialize.DefaultWcInitializeUseCase
import com.tangem.data.walletconnect.network.ethereum.WcEthNetwork
import com.tangem.data.walletconnect.network.solana.WcSolanaNetwork
-import com.tangem.data.walletconnect.pair.AssociateNetworksDelegate
-import com.tangem.data.walletconnect.pair.CaipNamespaceDelegate
-import com.tangem.data.walletconnect.pair.DefaultWcPairService
-import com.tangem.data.walletconnect.pair.DefaultWcPairUseCase
-import com.tangem.data.walletconnect.pair.WcPairSdkDelegate
import com.tangem.data.walletconnect.pair.*
import com.tangem.data.walletconnect.request.DefaultWcRequestService
import com.tangem.data.walletconnect.request.DefaultWcRequestUseCaseFactory
@@ -31,6 +26,7 @@ import com.tangem.domain.walletconnect.WcRequestUseCaseFactory
import com.tangem.domain.walletconnect.model.legacy.WalletConnectSessionsRepository
import com.tangem.domain.walletconnect.repository.WalletConnectRepository
import com.tangem.domain.walletconnect.repository.WcSessionsManager
+import com.tangem.domain.walletconnect.usecase.disconnect.WcDisconnectUseCase
import com.tangem.domain.walletconnect.usecase.initialize.WcInitializeUseCase
import com.tangem.domain.walletconnect.usecase.pair.WcPairUseCase
import com.tangem.domain.walletmanager.WalletManagersFacade
@@ -197,6 +193,12 @@ internal object WalletConnectDataModule {
return DefaultWcRequestUseCaseFactory(diHelperBox.handlers)
}
+ @Provides
+ @Singleton
+ fun providesWcDisconnectUseCase(sessionsManager: WcSessionsManager): WcDisconnectUseCase {
+ return WcDisconnectUseCase(sessionsManager)
+ }
+
internal class DiHelperBox(
val converters: Set,
val handlers: Set,
diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/CaipNamespaceDelegate.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/CaipNamespaceDelegate.kt
index 597bdecefb..cdc248d8b7 100644
--- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/CaipNamespaceDelegate.kt
+++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/CaipNamespaceDelegate.kt
@@ -34,9 +34,14 @@ internal class CaipNamespaceDelegate constructor(
requiredNamespaces[namespaceKey]?.methods?.let { addAll(it) }
optionalNamespaces[namespaceKey]?.methods?.let { addAll(it) }
}
+ val events = buildSet {
+ requiredNamespaces[namespaceKey]?.events?.let { addAll(it) }
+ optionalNamespaces[namespaceKey]?.events?.let { addAll(it) }
+ }
session.chains.add(account.chainId.raw)
session.accounts.add(account.raw)
session.methods.addAll(methods)
+ session.events.addAll(events)
}
return result.mapValues { (namespaceKey, session) ->
Wallet.Model.Namespace.Session(
diff --git a/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/usecase/WcSessionsUseCase.kt b/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/usecase/WcSessionsUseCase.kt
index 138c4a419f..d15047f8e9 100644
--- a/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/usecase/WcSessionsUseCase.kt
+++ b/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/usecase/WcSessionsUseCase.kt
@@ -14,4 +14,8 @@ class WcSessionsUseCase(private val sessionsManager: WcSessionsManager) {
suspend fun invokeSync(): Map> {
return sessionsManager.sessions.first()
}
+
+ suspend fun findByTopic(topic: String): WcSession? {
+ return sessionsManager.findSessionByTopic(topic)
+ }
}
\ No newline at end of file
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
index a03ae3a26d..5ba2668002 100644
--- 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
@@ -7,6 +7,7 @@ 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
+import kotlinx.serialization.Serializable
internal class AlertsComponent(
appComponentContext: AppComponentContext,
@@ -31,17 +32,35 @@ internal class AlertsComponent(
internal data class Params(val alertType: AlertType)
@Immutable
+ @Serializable
internal sealed class AlertType : TangemBottomSheetConfigContent {
abstract val onDismiss: () -> Unit
+ @Serializable
data class VerifiedDomain(val appName: String, override val onDismiss: () -> Unit) : AlertType()
+
+ @Serializable
data class UnknownDomain(val onConnect: () -> Unit, override val onDismiss: () -> Unit) : AlertType()
+
+ @Serializable
data class UnsafeDomain(val onConnect: () -> Unit, override val onDismiss: () -> Unit) : AlertType()
+
+ @Serializable
data class UnsupportedNetworks(val appName: String, override val onDismiss: () -> Unit) : AlertType()
+
+ @Serializable
data class WcDisconnected(override val onDismiss: () -> Unit) : AlertType()
+
+ @Serializable
data class UnknownError(val errorCode: Int, override val onDismiss: () -> Unit) : AlertType()
+
+ @Serializable
data class WrongCardSelected(override val onDismiss: () -> Unit) : AlertType()
+
+ @Serializable
data class ConnectionTimeout(val onTryAgain: () -> Unit, override val onDismiss: () -> Unit) : AlertType()
+
+ @Serializable
data class MaliciousTransaction(
val descriptionMessage: String,
val onConnect: () -> Unit,
diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/ConnectionsComponent.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/ConnectionsComponent.kt
index 0c311f6f4a..0fd5c6d2d5 100644
--- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/ConnectionsComponent.kt
+++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/ConnectionsComponent.kt
@@ -15,7 +15,7 @@ import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.walletconnect.model.WcPairRequest
import com.tangem.features.walletconnect.connections.model.WcConnectionsModel
-import com.tangem.features.walletconnect.connections.routes.WcConnectionsBottomSheetRoutes
+import com.tangem.features.walletconnect.connections.routes.WcConnectionsBottomSheetConfig
import com.tangem.features.walletconnect.connections.ui.WcConnectionsContent
internal class ConnectionsComponent(
@@ -40,11 +40,11 @@ internal class ConnectionsComponent(
}
private fun bottomSheetChild(
- config: WcConnectionsBottomSheetRoutes,
+ config: WcConnectionsBottomSheetConfig,
componentContext: ComponentContext,
): ComposableBottomSheetComponent {
return when (config) {
- is WcConnectionsBottomSheetRoutes.AppInfo -> WcAppInfoContainerComponent(
+ is WcConnectionsBottomSheetConfig.AppInfo -> WcAppInfoContainerComponent(
appComponentContext = childByContext(componentContext),
params = WcAppInfoContainerComponent.Params(
wcUrl = config.wcUrl,
@@ -52,6 +52,13 @@ internal class ConnectionsComponent(
onDismiss = model.bottomSheetNavigation::dismiss,
),
)
+ is WcConnectionsBottomSheetConfig.ConnectedApp -> WcConnectedAppInfoComponent(
+ appComponentContext = childByContext(componentContext),
+ params = WcConnectedAppInfoComponent.Params(
+ topic = config.topic,
+ onDismiss = model.bottomSheetNavigation::dismiss,
+ ),
+ )
}
}
}
\ No newline at end of file
diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcConnectedAppInfoComponent.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcConnectedAppInfoComponent.kt
new file mode 100644
index 0000000000..cfdaa99d34
--- /dev/null
+++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcConnectedAppInfoComponent.kt
@@ -0,0 +1,30 @@
+package com.tangem.features.walletconnect.connections.components
+
+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.decompose.model.getOrCreateModel
+import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
+import com.tangem.features.walletconnect.connections.model.WcConnectedAppInfoModel
+import com.tangem.features.walletconnect.connections.ui.WcConnectedAppInfoBS
+
+internal class WcConnectedAppInfoComponent(
+ params: Params,
+ appComponentContext: AppComponentContext,
+) : AppComponentContext by appComponentContext, ComposableBottomSheetComponent {
+
+ private val model: WcConnectedAppInfoModel = getOrCreateModel(params = params)
+
+ override fun dismiss() {
+ model.dismiss()
+ }
+
+ @Composable
+ override fun BottomSheet() {
+ val state by model.uiState.collectAsStateWithLifecycle()
+ state?.let { WcConnectedAppInfoBS(it) }
+ }
+
+ data class Params(val topic: String, val onDismiss: () -> Unit)
+}
\ No newline at end of file
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 413724be60..f7d17a2af5 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
@@ -50,5 +50,5 @@ sealed class WcAppInfoSecurityNotification(
@Immutable
internal sealed class WcNetworksInfo {
data class MissingRequiredNetworkInfo(val networks: String) : WcNetworksInfo()
- data class ContainsAllRequiredNetworks(val icons: ImmutableList) : WcNetworksInfo()
+ data class ContainsAllRequiredNetworks(val items: ImmutableList) : WcNetworksInfo()
}
\ No newline at end of file
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
new file mode 100644
index 0000000000..5fe46765ca
--- /dev/null
+++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcConnectedAppInfoUM.kt
@@ -0,0 +1,15 @@
+package com.tangem.features.walletconnect.connections.entity
+
+import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
+import kotlinx.collections.immutable.ImmutableList
+
+data class WcConnectedAppInfoUM(
+ val appName: String,
+ val appIcon: String,
+ val isVerified: Boolean,
+ val appSubtitle: String,
+ val walletName: String,
+ val networks: ImmutableList,
+ val disconnectButtonConfig: WcPrimaryButtonConfig,
+ val onDismiss: () -> Unit,
+) : TangemBottomSheetConfigContent
\ No newline at end of file
diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcNetworkInfoItem.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcNetworkInfoItem.kt
new file mode 100644
index 0000000000..498b1b759c
--- /dev/null
+++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcNetworkInfoItem.kt
@@ -0,0 +1,10 @@
+package com.tangem.features.walletconnect.connections.entity
+
+import androidx.annotation.DrawableRes
+
+data class WcNetworkInfoItem(
+ val id: String,
+ @DrawableRes val icon: Int,
+ val name: String,
+ val symbol: String,
+)
\ No newline at end of file
diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcAppInfoModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcAppInfoModel.kt
index d2c9722fbc..1d36aed1f4 100644
--- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcAppInfoModel.kt
+++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcAppInfoModel.kt
@@ -53,7 +53,7 @@ internal class WcAppInfoModel @Inject constructor(
appInfoUiState.transformerUpdate(
WcConnectButtonProgressTransformer(showProgress = false),
)
- // TODO: wc dismiss and reload sessions
+ params.onDismiss()
}
is WcPairState.Error -> {
// TODO: wc show toast/snackbar/alert?
@@ -78,6 +78,7 @@ internal class WcAppInfoModel @Inject constructor(
}
fun dismiss() {
+ wcPairUseCase.reject()
params.onDismiss()
}
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
new file mode 100644
index 0000000000..6126b5f404
--- /dev/null
+++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcConnectedAppInfoModel.kt
@@ -0,0 +1,88 @@
+package com.tangem.features.walletconnect.connections.model
+
+import androidx.compose.runtime.Stable
+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.ui.UiMessageSender
+import com.tangem.core.ui.extensions.stringReference
+import com.tangem.core.ui.message.SnackbarMessage
+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.WcConnectedAppInfoUM
+import com.tangem.features.walletconnect.connections.entity.WcPrimaryButtonConfig
+import com.tangem.utils.coroutines.CoroutineDispatcherProvider
+import kotlinx.collections.immutable.persistentListOf
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
+import timber.log.Timber
+import javax.inject.Inject
+
+@Stable
+@ModelScoped
+internal class WcConnectedAppInfoModel @Inject constructor(
+ private val wcSessionsUseCase: WcSessionsUseCase,
+ private val messageSender: UiMessageSender,
+ private val disconnectUseCase: WcDisconnectUseCase,
+ override val dispatchers: CoroutineDispatcherProvider,
+ paramsContainer: ParamsContainer,
+) : Model() {
+
+ private val params = paramsContainer.require()
+ val uiState: StateFlow
+ field = MutableStateFlow(null)
+
+ init {
+ loadDAppInfo(params.topic)
+ }
+
+ private fun loadDAppInfo(topic: String) {
+ modelScope.launch {
+ val session = wcSessionsUseCase.findByTopic(topic = topic)
+ if (session == null) {
+ Timber.e("Can not find WcSession by topic: $topic")
+ messageSender.send(SnackbarMessage(stringReference("Can not find WcSession by topic: $topic")))
+ dismiss()
+ } else {
+ uiState.update { state ->
+ WcConnectedAppInfoUM(
+ appName = session.sdkModel.appMetaData.name,
+ appIcon = session.sdkModel.appMetaData.icons.firstOrNull().orEmpty(),
+ isVerified = session.securityStatus == CheckDAppResult.SAFE,
+ appSubtitle = session.sdkModel.appMetaData.description,
+ walletName = session.wallet.name,
+ networks = persistentListOf(), // TODO(wc): Nikolai & Doston: Where to find networks???
+ disconnectButtonConfig = WcPrimaryButtonConfig(
+ showProgress = false,
+ enabled = true,
+ onClick = { disconnect(session) },
+ ),
+ onDismiss = ::dismiss,
+ )
+ }
+ }
+ }
+ }
+
+ private fun disconnect(session: WcSession) {
+ uiState.update { state ->
+ state?.copy(
+ disconnectButtonConfig = state.disconnectButtonConfig.copy(showProgress = true),
+ )
+ }
+ modelScope.launch {
+ disconnectUseCase.disconnect(session)
+ messageSender.send(SnackbarMessage(message = stringReference("dApp disconnected")))
+ dismiss()
+ }
+ }
+
+ fun dismiss() {
+ params.onDismiss()
+ }
+}
\ No newline at end of file
diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcConnectionsModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcConnectionsModel.kt
index 69216f06c3..11a6d2a74c 100644
--- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcConnectionsModel.kt
+++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcConnectionsModel.kt
@@ -16,16 +16,19 @@ import com.tangem.core.ui.message.EventMessageAction
import com.tangem.core.ui.res.TangemTheme
import com.tangem.domain.qrscanning.models.SourceType
import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase
+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.entity.WcConnectionsState
import com.tangem.features.walletconnect.connections.entity.WcConnectionsTopAppBarConfig
import com.tangem.features.walletconnect.connections.model.transformers.WcSessionsTransformer
-import com.tangem.features.walletconnect.connections.routes.WcConnectionsBottomSheetRoutes
+import com.tangem.features.walletconnect.connections.routes.WcConnectionsBottomSheetConfig
import com.tangem.features.walletconnect.impl.R
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.transformer.update
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.flow.*
+import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@@ -35,12 +38,13 @@ internal class WcConnectionsModel @Inject constructor(
private val uiMessageSender: UiMessageSender,
private val listenToQrScanningUseCase: ListenToQrScanningUseCase,
private val wcSessionsUseCase: WcSessionsUseCase,
+ private val wcDisconnectUseCase: WcDisconnectUseCase,
override val dispatchers: CoroutineDispatcherProvider,
) : Model() {
val uiState: StateFlow
field = MutableStateFlow(getInitialState())
- val bottomSheetNavigation: SlotNavigation = SlotNavigation()
+ val bottomSheetNavigation: SlotNavigation = SlotNavigation()
init {
listenQrUpdates()
@@ -50,39 +54,43 @@ internal class WcConnectionsModel @Inject constructor(
private fun listenQrUpdates() {
listenToQrScanningUseCase(SourceType.WALLET_CONNECT)
.getOrElse { emptyFlow() }
- .onEach { wcUrl -> bottomSheetNavigation.activate(WcConnectionsBottomSheetRoutes.AppInfo(wcUrl)) }
+ .onEach { wcUrl -> bottomSheetNavigation.activate(WcConnectionsBottomSheetConfig.AppInfo(wcUrl)) }
.launchIn(modelScope)
}
private fun listenWcSessions() {
wcSessionsUseCase.invoke()
.conflate()
+ .onEach { Timber.tag("ddk9499").d("listenWcSessions before: ${it.size}") }
.distinctUntilChanged()
.onEach { sessionsMap ->
+ Timber.tag("ddk9499").d("listenWcSessions after: ${sessionsMap.size}")
uiState.update(
WcSessionsTransformer(
sessionsMap = sessionsMap,
- openAppInfoModal = { Timber.d("Open app info for session: $it") },
+ openAppInfoModal = ::openAppInfoModal,
),
)
}
.launchIn(modelScope)
}
+ private fun openAppInfoModal(session: WcSession) {
+ bottomSheetNavigation.activate(WcConnectionsBottomSheetConfig.ConnectedApp(session.sdkModel.topic))
+ }
+
private fun openQrScanScreen() {
router.push(AppRoute.QrScanning(source = AppRoute.QrScanning.Source.WalletConnect))
}
- private fun disconnectAll() {
+ private fun showDisconnectAllDialog() {
val message = DialogMessage(
title = resourceReference(R.string.wc_disconnect_all_alert_title),
message = resourceReference(R.string.wc_disconnect_all_alert_desc),
firstActionBuilder = {
EventMessageAction(
title = resourceReference(R.string.common_disconnect),
- onClick = {
- // TODO(wc): disconnect all
- },
+ onClick = ::disconnectAllSessions,
)
},
secondActionBuilder = { cancelAction() },
@@ -90,6 +98,10 @@ internal class WcConnectionsModel @Inject constructor(
uiMessageSender.send(message)
}
+ private fun disconnectAllSessions() {
+ modelScope.launch { wcDisconnectUseCase.disconnectAll() }
+ }
+
private fun getInitialState(): WcConnectionsState {
return WcConnectionsState(
topAppBarConfig = WcConnectionsTopAppBarConfig(
@@ -101,7 +113,7 @@ internal class WcConnectionsModel @Inject constructor(
disconnectAllItem = TangemDropdownMenuItem(
title = resourceReference(R.string.wc_disconnect_all),
textColorProvider = { TangemTheme.colors.text.warning },
- onClick = ::disconnectAll,
+ onClick = ::showDisconnectAllDialog,
),
),
connections = persistentListOf(),
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 d64ddbd43b..134338c410 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
@@ -4,10 +4,7 @@ import com.domain.blockaid.models.dapp.CheckDAppResult
import com.tangem.core.ui.extensions.iconResId
import com.tangem.domain.walletconnect.model.WcSessionProposal
import com.tangem.domain.wallets.models.UserWallet
-import com.tangem.features.walletconnect.connections.entity.WcAppInfoSecurityNotification
-import com.tangem.features.walletconnect.connections.entity.WcAppInfoUM
-import com.tangem.features.walletconnect.connections.entity.WcNetworksInfo
-import com.tangem.features.walletconnect.connections.entity.WcPrimaryButtonConfig
+import com.tangem.features.walletconnect.connections.entity.*
import com.tangem.utils.transformer.Transformer
import kotlinx.collections.immutable.toImmutableList
@@ -53,8 +50,15 @@ internal class WcAppInfoTransformer(
)
} else {
WcNetworksInfo.ContainsAllRequiredNetworks(
- icons = (proposalNetwork.required + proposalNetwork.available)
- .map { it.iconResId }
+ items = (proposalNetwork.required + proposalNetwork.available)
+ .map {
+ WcNetworkInfoItem(
+ id = it.id.value,
+ icon = it.iconResId,
+ name = it.name,
+ symbol = it.currencySymbol,
+ )
+ }
.toImmutableList(),
)
}
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 8c0d1fcc2c..74a5c349f2 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
@@ -8,8 +8,15 @@ import kotlinx.serialization.Serializable
@Serializable
@Immutable
internal sealed class WcAppInfoRoutes : TangemBottomSheetConfigContent {
+ @Serializable
data object AppInfo : WcAppInfoRoutes()
+
+ @Serializable
data object SelectWallet : WcAppInfoRoutes()
+
+ @Serializable
data object SelectNetworks : WcAppInfoRoutes()
+
+ @Serializable
data class Alert(val alertType: AlertsComponent.AlertType) : WcAppInfoRoutes()
}
\ No newline at end of file
diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/routes/WcConnectionsBottomSheetRoutes.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/routes/WcConnectionsBottomSheetConfig.kt
similarity index 54%
rename from features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/routes/WcConnectionsBottomSheetRoutes.kt
rename to features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/routes/WcConnectionsBottomSheetConfig.kt
index 66f2dc4f1e..b83e610fd8 100644
--- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/routes/WcConnectionsBottomSheetRoutes.kt
+++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/routes/WcConnectionsBottomSheetConfig.kt
@@ -3,7 +3,10 @@ package com.tangem.features.walletconnect.connections.routes
import kotlinx.serialization.Serializable
@Serializable
-internal sealed class WcConnectionsBottomSheetRoutes {
+internal sealed class WcConnectionsBottomSheetConfig {
@Serializable
- data class AppInfo(val wcUrl: String) : WcConnectionsBottomSheetRoutes()
+ data class AppInfo(val wcUrl: String) : WcConnectionsBottomSheetConfig()
+
+ @Serializable
+ data class ConnectedApp(val topic: String) : WcConnectionsBottomSheetConfig()
}
\ 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 2f583ee287..960e4b2e74 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
@@ -1,5 +1,6 @@
package com.tangem.features.walletconnect.connections.ui
+import androidx.annotation.DrawableRes
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
@@ -10,6 +11,7 @@ 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.TextOverflow
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import com.tangem.core.ui.res.TangemTheme
@@ -64,4 +66,33 @@ internal fun WcAppInfoItem(
)
}
}
+}
+
+@Composable
+internal fun WcNetworkInfoItem(@DrawableRes icon: Int, name: String, symbol: String, modifier: Modifier = Modifier) {
+ Row(
+ modifier = modifier.padding(vertical = 14.dp, horizontal = 12.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Icon(
+ modifier = Modifier.size(24.dp),
+ painter = painterResource(icon),
+ contentDescription = null,
+ tint = Color.Unspecified,
+ )
+ Text(
+ modifier = Modifier.padding(start = 12.dp).weight(1f, fill = false),
+ text = name,
+ style = TangemTheme.typography.body1,
+ color = TangemTheme.colors.text.primary1,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ )
+ Text(
+ modifier = Modifier.padding(start = 4.dp),
+ text = symbol,
+ style = TangemTheme.typography.body1,
+ color = TangemTheme.colors.text.tertiary,
+ )
+ }
}
\ No newline at end of file
diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/WcAppInfoContent.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/WcAppInfoContent.kt
index eef9435ca0..f248d6323c 100644
--- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/WcAppInfoContent.kt
+++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/WcAppInfoContent.kt
@@ -37,10 +37,7 @@ import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.res.TangemColorPalette
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
-import com.tangem.features.walletconnect.connections.entity.WcAppInfoSecurityNotification
-import com.tangem.features.walletconnect.connections.entity.WcAppInfoUM
-import com.tangem.features.walletconnect.connections.entity.WcNetworksInfo
-import com.tangem.features.walletconnect.connections.entity.WcPrimaryButtonConfig
+import com.tangem.features.walletconnect.connections.entity.*
import com.tangem.features.walletconnect.impl.R
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
@@ -308,7 +305,7 @@ private fun SelectNetworksBlock(networksInfo: WcNetworksInfo, modifier: Modifier
color = TangemTheme.colors.text.primary1,
)
when (networksInfo) {
- is WcNetworksInfo.ContainsAllRequiredNetworks -> NetworkIcons(icons = networksInfo.icons)
+ is WcNetworksInfo.ContainsAllRequiredNetworks -> NetworkIcons(items = networksInfo.items)
is WcNetworksInfo.MissingRequiredNetworkInfo -> Unit
}
Icon(
@@ -327,17 +324,17 @@ private fun SelectNetworksBlock(networksInfo: WcNetworksInfo, modifier: Modifier
@Suppress("MagicNumber")
@Composable
-private fun NetworkIcons(icons: ImmutableList, modifier: Modifier = Modifier) {
- val (iconsToShow, remainingCount) = remember(icons) {
+private fun NetworkIcons(items: ImmutableList, modifier: Modifier = Modifier) {
+ val (iconsToShow, remainingCount) = remember(items) {
when {
- icons.size <= 4 -> icons to 0
- else -> icons.take(3) to icons.size - 3
+ items.size <= 4 -> items to 0
+ else -> items.take(3) to items.size - 3
}
}
Box(modifier = modifier.wrapContentWidth()) {
- iconsToShow.forEachIndexed { index, iconRes ->
+ iconsToShow.forEachIndexed { index, item ->
Image(
- painter = painterResource(id = iconRes),
+ painter = painterResource(id = item.icon),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier
@@ -569,12 +566,32 @@ private class WcAppInfoStateProvider : CollectionPreviewParameterProvider(
+ config = TangemBottomSheetConfig(
+ isShown = true,
+ onDismissRequest = state.onDismiss,
+ content = state,
+ ),
+ containerColor = TangemTheme.colors.background.tertiary,
+ title = {
+ TangemModalBottomSheetTitle(
+ title = resourceReference(R.string.wc_wallet_connect),
+ endIconRes = R.drawable.ic_close_24,
+ onEndClick = state.onDismiss,
+ )
+ },
+ content = {
+ WcConnectedAppInfoBSContent(
+ state,
+ modifier = Modifier
+ .padding(horizontal = 16.dp)
+ .fillMaxSize(),
+ )
+ },
+ )
+}
+
+@Composable
+private fun WcConnectedAppInfoBSContent(state: WcConnectedAppInfoUM, modifier: Modifier = Modifier) {
+ Column(modifier = modifier) {
+ val blocksModifier = Modifier
+ .clip(RoundedCornerShape(TangemTheme.dimens.radius14))
+ .background(color = TangemTheme.colors.background.action)
+ .fillMaxWidth()
+ AppInfoFirstBlock(
+ state = state,
+ modifier = Modifier
+ .padding(top = 8.dp)
+ .then(blocksModifier),
+ )
+ NetworksBlock(
+ networks = state.networks,
+ modifier = Modifier
+ .padding(top = 14.dp)
+ .then(blocksModifier),
+ )
+ SecondaryButton(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(vertical = 16.dp),
+ text = stringResourceSafe(R.string.common_disconnect),
+ enabled = state.disconnectButtonConfig.enabled,
+ showProgress = state.disconnectButtonConfig.showProgress,
+ onClick = state.disconnectButtonConfig.onClick,
+ )
+ }
+}
+
+@Composable
+private fun AppInfoFirstBlock(state: WcConnectedAppInfoUM, modifier: Modifier = Modifier) {
+ Column(modifier = modifier) {
+ WcAppInfoItem(
+ iconUrl = state.appIcon,
+ title = state.appName,
+ subtitle = state.appSubtitle,
+ isVerified = state.isVerified,
+ )
+ HorizontalDivider(thickness = 1.dp, color = TangemTheme.colors.stroke.primary)
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(TangemTheme.dimens.spacing12),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Icon(
+ modifier = Modifier.size(24.dp),
+ painter = painterResource(R.drawable.ic_wallet_new_24),
+ contentDescription = null,
+ tint = TangemTheme.colors.icon.accent,
+ )
+ Text(
+ modifier = Modifier
+ .padding(start = TangemTheme.dimens.spacing4)
+ .weight(1f),
+ text = stringResourceSafe(R.string.manage_tokens_network_selector_wallet),
+ style = TangemTheme.typography.body1,
+ color = TangemTheme.colors.text.primary1,
+ )
+ Text(
+ modifier = Modifier
+ .padding(start = TangemTheme.dimens.spacing16)
+ .weight(1f),
+ text = state.walletName,
+ textAlign = TextAlign.End,
+ style = TangemTheme.typography.body1,
+ color = TangemTheme.colors.text.tertiary,
+ )
+ }
+ }
+}
+
+@Composable
+private fun NetworksBlock(networks: ImmutableList, modifier: Modifier = Modifier) {
+ Column(modifier = modifier) {
+ Text(
+ modifier = Modifier
+ .padding(top = 12.dp, bottom = 4.dp)
+ .padding(horizontal = 12.dp),
+ text = stringResourceSafe(R.string.wc_connected_networks),
+ style = TangemTheme.typography.subtitle2,
+ color = TangemTheme.colors.text.tertiary,
+ )
+ networks.fastForEach { network ->
+ key(network.id) { WcNetworkInfoItem(icon = network.icon, name = network.name, symbol = network.symbol) }
+ }
+ }
+}
+
+@Preview(showBackground = true)
+@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+private fun WcConnectedAppInfoBS_Preview() {
+ TangemThemePreview {
+ WcConnectedAppInfoBS(
+ state = WcConnectedAppInfoUM(
+ appName = "React App",
+ appIcon = "",
+ isVerified = true,
+ appSubtitle = "react-app.walletconnect.com",
+ walletName = "Tangem 2.0",
+ networks = persistentListOf(
+ WcNetworkInfoItem(
+ id = "1",
+ icon = R.drawable.img_optimism_22,
+ name = "img_optimism_22img_optimism_22",
+ symbol = "optimism",
+ ),
+ WcNetworkInfoItem(id = "2", icon = R.drawable.img_bsc_22, name = "img_bsc_22", symbol = "bsc"),
+ WcNetworkInfoItem(
+ id = "3",
+ icon = R.drawable.img_solana_22,
+ name = "img_solana_22",
+ symbol = "solana",
+ ),
+ ),
+ disconnectButtonConfig = WcPrimaryButtonConfig(showProgress = false, enabled = true, onClick = {}),
+ onDismiss = {},
+ ),
+ )
+ }
+}
\ No newline at end of file
diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/di/WalletConnectModelModule.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/di/WalletConnectModelModule.kt
index bb589438d6..00039ca420 100644
--- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/di/WalletConnectModelModule.kt
+++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/di/WalletConnectModelModule.kt
@@ -3,6 +3,7 @@ package com.tangem.features.walletconnect.di
import com.tangem.core.decompose.di.ModelComponent
import com.tangem.core.decompose.model.Model
import com.tangem.features.walletconnect.connections.model.WcAppInfoModel
+import com.tangem.features.walletconnect.connections.model.WcConnectedAppInfoModel
import com.tangem.features.walletconnect.connections.model.WcConnectionsModel
import dagger.Binds
import dagger.Module
@@ -23,4 +24,9 @@ internal interface WalletConnectModelModule {
@IntoMap
@ClassKey(WcAppInfoModel::class)
fun bindWcAppInfoModel(model: WcAppInfoModel): Model
+
+ @Binds
+ @IntoMap
+ @ClassKey(WcConnectedAppInfoModel::class)
+ fun bindWcConnectedAppInfoModel(model: WcConnectedAppInfoModel): Model
}
\ No newline at end of file