Updated on 2026-08-14
This commit is contained in:
parent
c1c3b52715
commit
b280cd9e5e
20 changed files with 491 additions and 44 deletions
|
|
@ -1292,6 +1292,7 @@
|
|||
<string name="wc_common_network">Network</string>
|
||||
<string name="wc_common_networks">Networks</string>
|
||||
<string name="wc_common_wallet">Wallet</string>
|
||||
<string name="wc_connected_networks">Connected networks</string>
|
||||
<string name="wc_connection_reqeust_can_view_balance">View your wallet balance and activity</string>
|
||||
<string name="wc_connection_reqeust_cant_sign">Sign transactions without your notice</string>
|
||||
<string name="wc_connection_reqeust_request_approval">Request approval for transactions</string>
|
||||
|
|
|
|||
|
|
@ -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<WcNamespaceConverter>,
|
||||
val handlers: Set<WcRequestToUseCaseConverter>,
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -14,4 +14,8 @@ class WcSessionsUseCase(private val sessionsManager: WcSessionsManager) {
|
|||
suspend fun invokeSync(): Map<UserWallet, List<WcSession>> {
|
||||
return sessionsManager.sessions.first()
|
||||
}
|
||||
|
||||
suspend fun findByTopic(topic: String): WcSession? {
|
||||
return sessionsManager.findSessionByTopic(topic)
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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<Int>) : WcNetworksInfo()
|
||||
data class ContainsAllRequiredNetworks(val items: ImmutableList<WcNetworkInfoItem>) : WcNetworksInfo()
|
||||
}
|
||||
|
|
@ -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<WcNetworkInfoItem>,
|
||||
val disconnectButtonConfig: WcPrimaryButtonConfig,
|
||||
val onDismiss: () -> Unit,
|
||||
) : TangemBottomSheetConfigContent
|
||||
|
|
@ -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,
|
||||
)
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<WcConnectedAppInfoComponent.Params>()
|
||||
val uiState: StateFlow<WcConnectedAppInfoUM?>
|
||||
field = MutableStateFlow<WcConnectedAppInfoUM?>(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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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<WcConnectionsState>
|
||||
field = MutableStateFlow<WcConnectionsState>(getInitialState())
|
||||
val bottomSheetNavigation: SlotNavigation<WcConnectionsBottomSheetRoutes> = SlotNavigation()
|
||||
val bottomSheetNavigation: SlotNavigation<WcConnectionsBottomSheetConfig> = 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(),
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Int>, modifier: Modifier = Modifier) {
|
||||
val (iconsToShow, remainingCount) = remember(icons) {
|
||||
private fun NetworkIcons(items: ImmutableList<WcNetworkInfoItem>, 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<WcAppI
|
|||
notification = WcAppInfoSecurityNotification.SecurityRisk,
|
||||
walletName = "Tangem 2.0",
|
||||
networksInfo = WcNetworksInfo.ContainsAllRequiredNetworks(
|
||||
icons = persistentListOf(
|
||||
R.drawable.img_optimism_22,
|
||||
R.drawable.img_bsc_22,
|
||||
R.drawable.img_avalanche_22,
|
||||
R.drawable.img_solana_22,
|
||||
R.drawable.img_avalanche_22,
|
||||
items = persistentListOf(
|
||||
WcNetworkInfoItem(
|
||||
id = "1",
|
||||
icon = R.drawable.img_optimism_22,
|
||||
name = "img_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_avalanche_22,
|
||||
name = "img_avalanche_22",
|
||||
symbol = "avalanche",
|
||||
),
|
||||
WcNetworkInfoItem(
|
||||
id = "4",
|
||||
icon = R.drawable.img_solana_22,
|
||||
name = "img_solana_22",
|
||||
symbol = "solana",
|
||||
),
|
||||
WcNetworkInfoItem(
|
||||
id = "5",
|
||||
icon = R.drawable.img_avalanche_22,
|
||||
name = "img_avalanche_22",
|
||||
symbol = "avalanche",
|
||||
),
|
||||
),
|
||||
),
|
||||
onDismiss = {},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,185 @@
|
|||
package com.tangem.features.walletconnect.connections.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import com.tangem.core.ui.components.SecondaryButton
|
||||
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.resourceReference
|
||||
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.entity.WcConnectedAppInfoUM
|
||||
import com.tangem.features.walletconnect.connections.entity.WcNetworkInfoItem
|
||||
import com.tangem.features.walletconnect.connections.entity.WcPrimaryButtonConfig
|
||||
import com.tangem.features.walletconnect.impl.R
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
@Composable
|
||||
internal fun WcConnectedAppInfoBS(state: WcConnectedAppInfoUM) {
|
||||
TangemModalBottomSheet<WcConnectedAppInfoUM>(
|
||||
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<WcNetworkInfoItem>, 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 = {},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue