Updated on 2026-08-14

This commit is contained in:
Tangem 2025-07-04 13:16:23 +04:00
parent 7b862a2494
commit 3aaa27ea2e
7 changed files with 134 additions and 18 deletions

View file

@ -4,18 +4,15 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.features.walletconnect.connections.model.WcConnectedAppInfoModel import com.tangem.features.walletconnect.connections.model.WcConnectedAppInfoModel
import com.tangem.features.walletconnect.connections.ui.WcConnectedAppInfoBS import com.tangem.features.walletconnect.connections.ui.WcConnectedAppInfoBS
internal class WcConnectedAppInfoComponent( internal class WcConnectedAppInfoComponent(
params: Params,
appComponentContext: AppComponentContext, appComponentContext: AppComponentContext,
private val model: WcConnectedAppInfoModel,
) : AppComponentContext by appComponentContext, ComposableBottomSheetComponent { ) : AppComponentContext by appComponentContext, ComposableBottomSheetComponent {
private val model: WcConnectedAppInfoModel = getOrCreateModel(params = params)
override fun dismiss() { override fun dismiss() {
model.dismiss() model.dismiss()
} }
@ -23,8 +20,9 @@ internal class WcConnectedAppInfoComponent(
@Composable @Composable
override fun BottomSheet() { override fun BottomSheet() {
val state by model.uiState.collectAsStateWithLifecycle() val state by model.uiState.collectAsStateWithLifecycle()
state?.let { WcConnectedAppInfoBS(it) } val appInfoUM = state
if (appInfoUM != null) {
WcConnectedAppInfoBS(appInfoUM)
}
} }
data class Params(val topic: String, val onDismiss: () -> Unit)
} }

View file

@ -0,0 +1,78 @@
package com.tangem.features.walletconnect.connections.components
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import com.arkivanov.decompose.ComponentContext
import com.arkivanov.decompose.extensions.compose.subscribeAsState
import com.arkivanov.decompose.router.stack.childStack
import com.arkivanov.decompose.router.stack.pop
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.context.childByContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.decompose.navigation.inner.InnerRouter
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.features.walletconnect.connections.model.WcConnectedAppInfoModel
import com.tangem.features.walletconnect.connections.routes.ConnectedAppInfoRoutes
import com.tangem.features.walletconnect.connections.utils.WcAlertsFactory
internal class WcConnectedAppInfoContainerComponent(
params: Params,
appComponentContext: AppComponentContext,
) : AppComponentContext by appComponentContext, ComposableBottomSheetComponent {
private val model: WcConnectedAppInfoModel = getOrCreateModel(params = params)
private val innerRouter = InnerRouter<ConnectedAppInfoRoutes>(
stackNavigation = model.stackNavigation,
popCallback = { onChildBack() },
)
private val contentStack = childStack(
key = "WcConnectedAppInfoStack",
source = model.stackNavigation,
serializer = ConnectedAppInfoRoutes.serializer(),
initialConfiguration = ConnectedAppInfoRoutes.AppInfo,
handleBackButton = true,
childFactory = ::screenChild,
)
@Composable
override fun BottomSheet() {
val content by contentStack.subscribeAsState()
BackHandler(onBack = ::onChildBack)
content.active.instance.BottomSheet()
}
override fun dismiss() {
router.pop()
}
private fun onChildBack() {
when (contentStack.value.active.configuration) {
is ConnectedAppInfoRoutes.AppInfo -> dismiss()
is ConnectedAppInfoRoutes.VerifiedAlert -> model.stackNavigation.pop()
}
}
private fun screenChild(
config: ConnectedAppInfoRoutes,
componentContext: ComponentContext,
): ComposableBottomSheetComponent {
val appComponentContext = childByContext(
componentContext = componentContext,
router = innerRouter,
)
return when (config) {
is ConnectedAppInfoRoutes.AppInfo -> WcConnectedAppInfoComponent(
appComponentContext = appComponentContext,
model = model,
)
is ConnectedAppInfoRoutes.VerifiedAlert -> AlertsComponentV2(
appComponentContext = appComponentContext,
messageUM = WcAlertsFactory.createVerifiedDomainAlert(config.appName),
)
}
}
data class Params(val topic: String, val onDismiss: () -> Unit)
}

View file

@ -54,9 +54,9 @@ internal class WcConnectionsComponent(
): ComposableBottomSheetComponent { ): ComposableBottomSheetComponent {
val context = childByContext(componentContext = componentContext, messageHandler = messageHandler) val context = childByContext(componentContext = componentContext, messageHandler = messageHandler)
return when (config) { return when (config) {
is WcConnectionsBottomSheetConfig.ConnectedApp -> WcConnectedAppInfoComponent( is WcConnectionsBottomSheetConfig.ConnectedApp -> WcConnectedAppInfoContainerComponent(
appComponentContext = context, appComponentContext = context,
params = WcConnectedAppInfoComponent.Params( params = WcConnectedAppInfoContainerComponent.Params(
topic = config.topic, topic = config.topic,
onDismiss = model.bottomSheetNavigation::dismiss, onDismiss = model.bottomSheetNavigation::dismiss,
), ),

View file

@ -1,10 +1,13 @@
package com.tangem.features.walletconnect.connections.model package com.tangem.features.walletconnect.connections.model
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import com.arkivanov.decompose.router.stack.StackNavigation
import com.arkivanov.decompose.router.stack.pushNew
import com.domain.blockaid.models.dapp.CheckDAppResult import com.domain.blockaid.models.dapp.CheckDAppResult
import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.decompose.ui.UiMessageSender import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.extensions.iconResId import com.tangem.core.ui.extensions.iconResId
import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.stringReference
@ -12,13 +15,14 @@ import com.tangem.core.ui.message.SnackbarMessage
import com.tangem.domain.walletconnect.model.WcSession import com.tangem.domain.walletconnect.model.WcSession
import com.tangem.domain.walletconnect.usecase.WcSessionsUseCase import com.tangem.domain.walletconnect.usecase.WcSessionsUseCase
import com.tangem.domain.walletconnect.usecase.disconnect.WcDisconnectUseCase import com.tangem.domain.walletconnect.usecase.disconnect.WcDisconnectUseCase
import com.tangem.features.walletconnect.connections.components.WcConnectedAppInfoComponent import com.tangem.features.walletconnect.connections.components.WcConnectedAppInfoContainerComponent
import com.tangem.features.walletconnect.connections.entity.VerifiedDAppState import com.tangem.features.walletconnect.connections.entity.VerifiedDAppState
import com.tangem.features.walletconnect.connections.entity.WcConnectedAppInfoUM import com.tangem.features.walletconnect.connections.entity.WcConnectedAppInfoUM
import com.tangem.features.walletconnect.connections.entity.WcNetworkInfoItem import com.tangem.features.walletconnect.connections.entity.WcNetworkInfoItem
import com.tangem.features.walletconnect.connections.entity.WcPrimaryButtonConfig import com.tangem.features.walletconnect.connections.entity.WcPrimaryButtonConfig
import com.tangem.features.walletconnect.connections.entity.WcAppInfoSecurityNotification import com.tangem.features.walletconnect.connections.entity.WcAppInfoSecurityNotification
import com.tangem.features.walletconnect.connections.model.transformers.WcAppSubtitleConverter import com.tangem.features.walletconnect.connections.model.transformers.WcAppSubtitleConverter
import com.tangem.features.walletconnect.connections.routes.ConnectedAppInfoRoutes
import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@ -31,6 +35,7 @@ import javax.inject.Inject
@Stable @Stable
@ModelScoped @ModelScoped
internal class WcConnectedAppInfoModel @Inject constructor( internal class WcConnectedAppInfoModel @Inject constructor(
private val router: Router,
private val wcSessionsUseCase: WcSessionsUseCase, private val wcSessionsUseCase: WcSessionsUseCase,
private val messageSender: UiMessageSender, private val messageSender: UiMessageSender,
private val disconnectUseCase: WcDisconnectUseCase, private val disconnectUseCase: WcDisconnectUseCase,
@ -38,10 +43,12 @@ internal class WcConnectedAppInfoModel @Inject constructor(
paramsContainer: ParamsContainer, paramsContainer: ParamsContainer,
) : Model() { ) : Model() {
private val params = paramsContainer.require<WcConnectedAppInfoComponent.Params>() private val params = paramsContainer.require<WcConnectedAppInfoContainerComponent.Params>()
val uiState: StateFlow<WcConnectedAppInfoUM?> val uiState: StateFlow<WcConnectedAppInfoUM?>
field = MutableStateFlow<WcConnectedAppInfoUM?>(null) field = MutableStateFlow<WcConnectedAppInfoUM?>(null)
val stackNavigation = StackNavigation<ConnectedAppInfoRoutes>()
init { init {
loadDAppInfo(params.topic) loadDAppInfo(params.topic)
} }
@ -94,7 +101,11 @@ internal class WcConnectedAppInfoModel @Inject constructor(
private fun extractVerifiedState(session: WcSession): VerifiedDAppState { private fun extractVerifiedState(session: WcSession): VerifiedDAppState {
return if (session.securityStatus == CheckDAppResult.SAFE) { return if (session.securityStatus == CheckDAppResult.SAFE) {
VerifiedDAppState.Verified(onVerifiedClick = {}) VerifiedDAppState.Verified(
onVerifiedClick = {
stackNavigation.pushNew(ConnectedAppInfoRoutes.VerifiedAlert(session.sdkModel.appMetaData.name))
},
)
} else { } else {
VerifiedDAppState.Unknown VerifiedDAppState.Unknown
} }

View file

@ -0,0 +1,14 @@
package com.tangem.features.walletconnect.connections.routes
import com.tangem.core.decompose.navigation.Route
import kotlinx.serialization.Serializable
@Serializable
internal sealed class ConnectedAppInfoRoutes : Route {
@Serializable
data object AppInfo : ConnectedAppInfoRoutes()
@Serializable
data class VerifiedAlert(val appName: String) : ConnectedAppInfoRoutes()
}

View file

@ -15,6 +15,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
@ -33,6 +34,7 @@ import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.walletconnect.connections.entity.*
import com.tangem.features.walletconnect.connections.entity.WcConnectedAppInfo import com.tangem.features.walletconnect.connections.entity.WcConnectedAppInfo
import com.tangem.features.walletconnect.connections.entity.WcConnectionsState import com.tangem.features.walletconnect.connections.entity.WcConnectionsState
import com.tangem.features.walletconnect.connections.entity.WcConnectionsTopAppBarConfig import com.tangem.features.walletconnect.connections.entity.WcConnectionsTopAppBarConfig
@ -206,11 +208,24 @@ private fun AppInfoItem(appInfo: WcConnectedAppInfo, modifier: Modifier = Modifi
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing2), verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing2),
) { ) {
Text( Row(
text = appInfo.name, horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4),
color = TangemTheme.colors.text.primary1, verticalAlignment = Alignment.CenterVertically,
style = TangemTheme.typography.subtitle2, ) {
) Text(
text = appInfo.name,
color = TangemTheme.colors.text.primary1,
style = TangemTheme.typography.subtitle2,
)
if (appInfo.verifiedState is VerifiedDAppState.Verified) {
Icon(
modifier = Modifier.size(20.dp),
painter = painterResource(R.drawable.img_approvale2_20),
contentDescription = null,
tint = Color.Unspecified,
)
}
}
Text( Text(
text = appInfo.subtitle.resolveReference(), text = appInfo.subtitle.resolveReference(),
color = TangemTheme.colors.text.tertiary, color = TangemTheme.colors.text.tertiary,

View file

@ -45,7 +45,7 @@ internal class WcSendAndReceiveBlockAidUiConverter @Inject constructor(
is AmountInfo.FungibleTokens -> estimatedWalletChangeUMConverter.convert( is AmountInfo.FungibleTokens -> estimatedWalletChangeUMConverter.convert(
WcEstimatedWalletChangeUMConverter.Input( WcEstimatedWalletChangeUMConverter.Input(
amountInfo = it, amountInfo = it,
iconRes = R.drawable.ic_receive_new_24, iconRes = R.drawable.ic_send_new_24,
titleRes = R.string.common_send, titleRes = R.string.common_send,
sign = StringsSigns.MINUS, sign = StringsSigns.MINUS,
), ),