From ecd02950fb50bc0d7f044203e4e9446393aec61b Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 29 Apr 2025 12:48:02 +0400 Subject: [PATCH] Updated on 2026-08-14 --- core/res/src/main/res/values/strings.xml | 7 + .../src/main/res/drawable/ic_doc_new_24.xml | 19 +++ .../di/WalletConnectModelModule.kt | 6 + .../components/WcSignTransactionComponent.kt | 37 +++++ .../transaction/entity/WcSignTransactionUM.kt | 60 +++++++ .../model/WcSignTransactionModel.kt | 86 ++++++++++ .../common/TransactionRequestInfoContent.kt | 64 ++++++++ .../transaction/ui/common/WcNetworkItem.kt | 60 +++++++ .../ui/common/WcRequestFromItem.kt | 22 +++ .../ui/common/WcTransactionRequestButtons.kt | 39 +++++ .../ui/common/WcTransactionRequestItem.kt | 43 +++++ .../transaction/ui/common/WcWalletItem.kt | 44 +++++ .../sign/WcSignTransactionModalBottomSheet.kt | 154 ++++++++++++++++++ ...cSignTransactionModalBottomSheetContent.kt | 89 ++++++++++ .../utils/WcSignTransactionUtils.kt | 66 ++++++++ 15 files changed, 796 insertions(+) create mode 100644 core/ui/src/main/res/drawable/ic_doc_new_24.xml create mode 100644 features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/components/WcSignTransactionComponent.kt create mode 100644 features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/WcSignTransactionUM.kt create mode 100644 features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSignTransactionModel.kt create mode 100644 features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/TransactionRequestInfoContent.kt create mode 100644 features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcNetworkItem.kt create mode 100644 features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcRequestFromItem.kt create mode 100644 features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcTransactionRequestButtons.kt create mode 100644 features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcTransactionRequestItem.kt create mode 100644 features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcWalletItem.kt create mode 100644 features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/sign/WcSignTransactionModalBottomSheet.kt create mode 100644 features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/sign/WcSignTransactionModalBottomSheetContent.kt create mode 100644 features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/utils/WcSignTransactionUtils.kt diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 02d8bd7799..81f172e609 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -1299,6 +1299,7 @@ Will not be able to Would like to Connection request + Transaction request Connections Disconnect all Text about discnected all dApps @@ -1311,6 +1312,12 @@ This domain is flagged as unsafe by multiple security providers. Leave immediately to protect your assets Known security risk Wallet connect + Request from + Sign + Copy data + Transaction request + Signature Type + Contents Discard You have an interrupted backup. Do you want to resume? Yes, resume diff --git a/core/ui/src/main/res/drawable/ic_doc_new_24.xml b/core/ui/src/main/res/drawable/ic_doc_new_24.xml new file mode 100644 index 0000000000..9f506aaa9d --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_doc_new_24.xml @@ -0,0 +1,19 @@ + + + + + + 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 00039ca420..a9f3afb61d 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 @@ -5,6 +5,7 @@ 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 com.tangem.features.walletconnect.transaction.model.WcSignTransactionModel import dagger.Binds import dagger.Module import dagger.hilt.InstallIn @@ -29,4 +30,9 @@ internal interface WalletConnectModelModule { @IntoMap @ClassKey(WcConnectedAppInfoModel::class) fun bindWcConnectedAppInfoModel(model: WcConnectedAppInfoModel): Model + + @Binds + @IntoMap + @ClassKey(WcSignTransactionModel::class) + fun bindWcSignTransactionModel(model: WcSignTransactionModel): Model } \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/components/WcSignTransactionComponent.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/components/WcSignTransactionComponent.kt new file mode 100644 index 0000000000..9ec9f43b8f --- /dev/null +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/components/WcSignTransactionComponent.kt @@ -0,0 +1,37 @@ +package com.tangem.features.walletconnect.transaction.components + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.decompose.model.getOrCreateModel +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig +import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest +import com.tangem.features.walletconnect.transaction.model.WcSignTransactionModel +import com.tangem.features.walletconnect.transaction.ui.sign.WcSignTransactionModalBottomSheet + +internal class WcSignTransactionComponent( + appComponentContext: AppComponentContext, + params: Params, +) : AppComponentContext by appComponentContext, ComposableContentComponent { + + private val model: WcSignTransactionModel = getOrCreateModel(params) + + @Composable + override fun Content(modifier: Modifier) { + val content by model.uiState.collectAsStateWithLifecycle() + content?.let { + WcSignTransactionModalBottomSheet( + config = TangemBottomSheetConfig( + isShown = true, + onDismissRequest = it.actions.onDismiss, + content = it, + ), + ) + } + } + + data class Params(val rawRequest: WcSdkSessionRequest) +} \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/WcSignTransactionUM.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/WcSignTransactionUM.kt new file mode 100644 index 0000000000..3f796ce6b1 --- /dev/null +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/WcSignTransactionUM.kt @@ -0,0 +1,60 @@ +package com.tangem.features.walletconnect.transaction.entity + +import androidx.annotation.DrawableRes +import androidx.compose.runtime.Immutable +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent +import com.tangem.core.ui.extensions.TextReference +import kotlinx.collections.immutable.ImmutableList + +@Immutable +internal data class WcSignTransactionUM( + @DrawableRes val startIconRes: Int, + @DrawableRes val endIconRes: Int, + @DrawableRes val transactionIconRes: Int, + val actions: WcTransactionActionsUM, + val state: State = State.TRANSACTION, + val transaction: WcTransactionUM, + val transactionRequestInfo: WcTransactionRequestInfoUM, +) : TangemBottomSheetConfigContent { + + enum class State { + TRANSACTION, TRANSACTION_REQUEST_INFO + } +} + +@Immutable +internal data class WcTransactionUM( + val appName: String, + val appIcon: String, + val isVerified: Boolean, + val appSubtitle: String, + val walletName: String, + val networkInfo: WcNetworkInfoUM, + val isLoading: Boolean = false, +) + +@Immutable +internal data class WcTransactionRequestInfoUM( + val info: ImmutableList, +) + +@Immutable +internal data class WcTransactionRequestInfoItemUM( + val title: TextReference, + val description: String, +) + +@Immutable +internal data class WcTransactionActionsUM( + val transactionRequestOnClick: () -> Unit, + val onDismiss: () -> Unit, + val onSign: () -> Unit, + val onBack: () -> Unit, + val onCopy: () -> Unit, +) + +@Immutable +internal data class WcNetworkInfoUM( + val name: String, + @DrawableRes val iconRes: Int, +) \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSignTransactionModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSignTransactionModel.kt new file mode 100644 index 0000000000..9a8eabf961 --- /dev/null +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/model/WcSignTransactionModel.kt @@ -0,0 +1,86 @@ +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.core.ui.clipboard.ClipboardManager +import com.tangem.domain.walletconnect.WcRequestUseCaseFactory +import com.tangem.domain.walletconnect.usecase.sign.WcSignState +import com.tangem.domain.walletconnect.usecase.sign.WcSignStep +import com.tangem.domain.walletconnect.usecase.sign.WcSignUseCase +import com.tangem.features.walletconnect.transaction.components.WcSignTransactionComponent +import com.tangem.features.walletconnect.transaction.entity.WcSignTransactionUM +import com.tangem.features.walletconnect.transaction.entity.WcTransactionActionsUM +import com.tangem.features.walletconnect.transaction.utils.toUM +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import javax.inject.Inject + +@Stable +@ModelScoped +internal class WcSignTransactionModel @Inject constructor( + paramsContainer: ParamsContainer, + override val dispatchers: CoroutineDispatcherProvider, + private val router: Router, + private val clipboardManager: ClipboardManager, + private val useCaseFactory: WcRequestUseCaseFactory, +) : Model() { + + private val _uiState = MutableStateFlow(null) + val uiState: StateFlow = _uiState + + private val params = paramsContainer.require() + + init { + modelScope.launch { + val useCase: WcSignUseCase.SimpleRun<*> = useCaseFactory.createUseCase(params.rawRequest) + useCase.invoke() + .onEach { signState -> + if (signingIsDone(signState)) return@onEach + val signTransactionUM = (useCase as? WcSignUseCase)?.toUM( + signState = signState, + actions = WcTransactionActionsUM( + onDismiss = { cancel(useCase) }, + onBack = ::showTransactionState, + onSign = useCase::sign, + onCopy = { copyData(useCase.rawSdkRequest.request.params) }, + transactionRequestOnClick = ::showTransactionRequestState, + ), + ) + _uiState.emit(signTransactionUM) + } + .launchIn(this) + } + } + + private fun signingIsDone(signState: WcSignState<*>): Boolean { + (signState.domainStep as? WcSignStep.Result)?.result?.let { + router.pop() + return true + } + return false + } + + private fun cancel(useCase: WcSignUseCase) { + useCase.cancel() + router.pop() + } + + private fun copyData(text: String) { + clipboardManager.setText(text = text, isSensitive = true) + } + + private fun showTransactionRequestState() { + _uiState.value = _uiState.value?.copy(state = WcSignTransactionUM.State.TRANSACTION_REQUEST_INFO) + } + + private fun showTransactionState() { + _uiState.value = _uiState.value?.copy(state = WcSignTransactionUM.State.TRANSACTION) + } +} \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/TransactionRequestInfoContent.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/TransactionRequestInfoContent.kt new file mode 100644 index 0000000000..d7c61d3af8 --- /dev/null +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/TransactionRequestInfoContent.kt @@ -0,0 +1,64 @@ +package com.tangem.features.walletconnect.transaction.ui.common + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.unit.dp +import com.tangem.core.ui.components.SecondaryButtonIconEnd +import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.extensions.stringResourceSafe +import com.tangem.core.ui.res.TangemTheme +import com.tangem.features.walletconnect.impl.R +import com.tangem.features.walletconnect.transaction.entity.WcSignTransactionUM + +@Composable +internal fun TransactionRequestInfoContent(state: WcSignTransactionUM) { + Box( + modifier = Modifier + .fillMaxWidth() + .heightIn(min = 310.dp) + .padding(horizontal = TangemTheme.dimens.spacing16), + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(TangemTheme.dimens.radius16)) + .background(TangemTheme.colors.background.action) + .padding( + start = TangemTheme.dimens.spacing16, + end = TangemTheme.dimens.spacing16, + bottom = TangemTheme.dimens.spacing20, + ), + ) { + state.transactionRequestInfo.info.forEach { item -> + Text( + text = item.title.resolveReference(), + modifier = Modifier.padding(top = TangemTheme.dimens.spacing20), + style = TangemTheme.typography.subtitle2, + color = TangemTheme.colors.text.tertiary, + ) + Text( + text = item.description, + modifier = Modifier.padding(top = TangemTheme.dimens.spacing4), + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.primary1, + ) + } + } + + SecondaryButtonIconEnd( + modifier = Modifier + .align(Alignment.BottomCenter) + .padding(bottom = TangemTheme.dimens.spacing20) + .fillMaxWidth(), + text = stringResourceSafe(R.string.wc_copy_data_button_text), + onClick = state.actions.onCopy, + iconResId = R.drawable.ic_copy_24, + ) + } +} \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcNetworkItem.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcNetworkItem.kt new file mode 100644 index 0000000000..1f337688aa --- /dev/null +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcNetworkItem.kt @@ -0,0 +1,60 @@ +package com.tangem.features.walletconnect.transaction.ui.common + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import com.tangem.core.ui.extensions.stringResourceSafe +import com.tangem.core.ui.res.TangemTheme +import com.tangem.features.walletconnect.impl.R +import com.tangem.features.walletconnect.transaction.entity.WcNetworkInfoUM + +@Composable +internal fun WcNetworkItem(networkInfo: WcNetworkInfoUM, modifier: Modifier = Modifier) { + Row( + modifier = modifier, + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + modifier = Modifier.size(TangemTheme.dimens.size24), + painter = painterResource(R.drawable.ic_network_new_24), + contentDescription = null, + tint = TangemTheme.colors.icon.accent, + ) + Text( + modifier = Modifier + .padding(start = TangemTheme.dimens.spacing4) + .weight(1f), + text = stringResourceSafe(R.string.wc_common_network), + style = TangemTheme.typography.body1, + color = TangemTheme.colors.text.primary1, + ) + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.End, + modifier = Modifier.weight(1f), + ) { + Text( + text = networkInfo.name, + style = TangemTheme.typography.body1, + color = TangemTheme.colors.text.tertiary, + ) + Spacer(modifier = Modifier.width(TangemTheme.dimens.spacing6)) + Image( + painter = painterResource(id = networkInfo.iconRes), + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = Modifier + .clip(CircleShape) + .size(TangemTheme.dimens.size20), + ) + } + } +} \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcRequestFromItem.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcRequestFromItem.kt new file mode 100644 index 0000000000..a36d7b2365 --- /dev/null +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcRequestFromItem.kt @@ -0,0 +1,22 @@ +package com.tangem.features.walletconnect.transaction.ui.common + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.tangem.core.ui.extensions.stringResourceSafe +import com.tangem.core.ui.res.TangemTheme +import com.tangem.features.walletconnect.impl.R + +@Composable +fun RequestFromItem(modifier: Modifier = Modifier) { + Text( + modifier = modifier + .fillMaxWidth() + .padding(top = TangemTheme.dimens.spacing12, start = TangemTheme.dimens.spacing12), + text = stringResourceSafe(R.string.wc_request_from), + color = TangemTheme.colors.text.tertiary, + style = TangemTheme.typography.subtitle2, + ) +} \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcTransactionRequestButtons.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcTransactionRequestButtons.kt new file mode 100644 index 0000000000..27eec41927 --- /dev/null +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcTransactionRequestButtons.kt @@ -0,0 +1,39 @@ +package com.tangem.features.walletconnect.transaction.ui.common + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.tangem.core.ui.components.PrimaryButtonIconEnd +import com.tangem.core.ui.components.SecondaryButton +import com.tangem.core.ui.extensions.stringResourceSafe +import com.tangem.core.ui.res.TangemTheme +import com.tangem.features.walletconnect.impl.R + +@Composable +internal fun WcTransactionRequestButtons( + isLoading: Boolean, + onDismiss: () -> Unit, + onSign: () -> Unit, + modifier: Modifier = Modifier, +) { + Row(modifier = modifier, horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) { + SecondaryButton( + modifier = Modifier + .fillMaxWidth() + .weight(1f), + text = stringResourceSafe(R.string.common_cancel), + onClick = onDismiss, + ) + PrimaryButtonIconEnd( + modifier = Modifier + .fillMaxWidth() + .weight(1f), + text = stringResourceSafe(R.string.wc_sign_message_button_text), + onClick = onSign, + iconResId = R.drawable.ic_tangem_24, + showProgress = isLoading, + ) + } +} \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcTransactionRequestItem.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcTransactionRequestItem.kt new file mode 100644 index 0000000000..00215b757a --- /dev/null +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcTransactionRequestItem.kt @@ -0,0 +1,43 @@ +package com.tangem.features.walletconnect.transaction.ui.common + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import com.tangem.core.ui.extensions.stringResourceSafe +import com.tangem.core.ui.res.TangemTheme +import com.tangem.features.walletconnect.impl.R + +@Composable +internal fun WcTransactionRequestItem(@DrawableRes iconRes: Int, modifier: Modifier = Modifier) { + Row(modifier = modifier, verticalAlignment = Alignment.CenterVertically) { + Icon( + modifier = Modifier.size(TangemTheme.dimens.size24), + painter = painterResource(iconRes), + contentDescription = null, + tint = TangemTheme.colors.icon.accent, + ) + Text( + modifier = Modifier + .padding(start = TangemTheme.dimens.spacing4) + .weight(1f), + text = stringResourceSafe(R.string.wc_transaction_request), + style = TangemTheme.typography.body1, + color = TangemTheme.colors.text.primary1, + ) + Icon( + modifier = Modifier + .padding(start = TangemTheme.dimens.spacing12) + .size(width = TangemTheme.dimens.size18, height = TangemTheme.dimens.size24), + painter = painterResource(R.drawable.ic_chevron_right_24), + contentDescription = null, + tint = TangemTheme.colors.icon.informative, + ) + } +} \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcWalletItem.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcWalletItem.kt new file mode 100644 index 0000000000..63633a22c1 --- /dev/null +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcWalletItem.kt @@ -0,0 +1,44 @@ +package com.tangem.features.walletconnect.transaction.ui.common + +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextAlign +import com.tangem.core.ui.extensions.stringResourceSafe +import com.tangem.core.ui.res.TangemTheme +import com.tangem.features.walletconnect.impl.R + +@Composable +internal fun WcWalletItem(walletName: String, modifier: Modifier = Modifier) { + Row(modifier = modifier, verticalAlignment = Alignment.CenterVertically) { + Icon( + modifier = Modifier.size(TangemTheme.dimens.size24), + 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 = walletName, + textAlign = TextAlign.End, + 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/transaction/ui/sign/WcSignTransactionModalBottomSheet.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/sign/WcSignTransactionModalBottomSheet.kt new file mode 100644 index 0000000000..665eb497b3 --- /dev/null +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/sign/WcSignTransactionModalBottomSheet.kt @@ -0,0 +1,154 @@ +package com.tangem.features.walletconnect.transaction.ui.sign + +import android.content.res.Configuration +import androidx.compose.animation.animateContentSize +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Devices +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider +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.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.features.walletconnect.impl.R +import com.tangem.features.walletconnect.transaction.entity.* +import com.tangem.features.walletconnect.transaction.ui.common.TransactionRequestInfoContent +import kotlinx.collections.immutable.persistentListOf + +@Composable +internal fun WcSignTransactionModalBottomSheet(config: TangemBottomSheetConfig) { + TangemModalBottomSheet( + config = config, + containerColor = TangemTheme.colors.background.tertiary, + title = { state -> + TangemModalBottomSheetTitle( + title = when (state.state) { + WcSignTransactionUM.State.TRANSACTION -> { + resourceReference(R.string.wallet_connect_title) + } + WcSignTransactionUM.State.TRANSACTION_REQUEST_INFO -> { + resourceReference(R.string.wc_transaction_request_title) + } + }, + endIconRes = state.endIconRes.takeIf { + state.state == WcSignTransactionUM.State.TRANSACTION + }, + onEndClick = state.actions.onDismiss, + startIconRes = state.startIconRes.takeIf { + state.state == WcSignTransactionUM.State.TRANSACTION_REQUEST_INFO + }, + onStartClick = state.actions.onBack, + ) + }, + content = { state -> + Box( + modifier = Modifier + .fillMaxWidth() + .animateContentSize(), + ) { + when (state.state) { + WcSignTransactionUM.State.TRANSACTION -> { + WcSignTransactionModalBottomSheetContent(state) + } + WcSignTransactionUM.State.TRANSACTION_REQUEST_INFO -> { + TransactionRequestInfoContent(state) + } + } + } + }, + ) +} + +@Composable +@Preview(showBackground = true, device = Devices.PIXEL_7_PRO) +@Preview(showBackground = true, device = Devices.PIXEL_7_PRO, uiMode = Configuration.UI_MODE_NIGHT_YES) +private fun WcSignTransactionBottomSheetPreview( + @PreviewParameter(WcSignTransactionStateProvider::class) state: WcSignTransactionUM, +) { + TangemThemePreview { + WcSignTransactionModalBottomSheet( + config = TangemBottomSheetConfig( + isShown = true, + onDismissRequest = {}, + content = state, + ), + ) + } +} + +private class WcSignTransactionStateProvider : CollectionPreviewParameterProvider( + listOf( + WcSignTransactionUM( + startIconRes = R.drawable.ic_back_24, + endIconRes = R.drawable.ic_close_24, + transactionIconRes = R.drawable.ic_doc_new_24, + state = WcSignTransactionUM.State.TRANSACTION, + transaction = WcTransactionUM( + appName = "React App", + appIcon = "", + isVerified = true, + appSubtitle = "react-app.walletconnect.com", + walletName = "Tangem 2.0", + networkInfo = WcNetworkInfoUM(name = "Ethereum", iconRes = R.drawable.img_eth_22), + ), + transactionRequestInfo = WcTransactionRequestInfoUM( + persistentListOf( + WcTransactionRequestInfoItemUM( + title = resourceReference(R.string.wc_signature_type), + description = "personal_sign", + ), + WcTransactionRequestInfoItemUM( + title = resourceReference(R.string.wc_contents), + description = "Hello! My name is John Dow. test@tange.com", + ), + ), + ), + actions = WcTransactionActionsUM( + onDismiss = {}, + onBack = {}, + onSign = {}, + onCopy = {}, + transactionRequestOnClick = {}, + ), + ), + WcSignTransactionUM( + startIconRes = R.drawable.ic_back_24, + endIconRes = R.drawable.ic_close_24, + transactionIconRes = R.drawable.ic_doc_new_24, + state = WcSignTransactionUM.State.TRANSACTION_REQUEST_INFO, + transaction = WcTransactionUM( + appName = "React App", + appIcon = "", + isVerified = true, + appSubtitle = "react-app.walletconnect.com", + walletName = "Tangem 2.0", + networkInfo = WcNetworkInfoUM(name = "Ethereum", iconRes = R.drawable.img_eth_22), + ), + transactionRequestInfo = WcTransactionRequestInfoUM( + persistentListOf( + WcTransactionRequestInfoItemUM( + title = resourceReference(R.string.wc_signature_type), + description = "personal_sign", + ), + WcTransactionRequestInfoItemUM( + title = resourceReference(R.string.wc_contents), + description = "Hello! My name is John Dow. test@tange.com", + ), + ), + ), + actions = WcTransactionActionsUM( + onDismiss = {}, + onBack = {}, + onSign = {}, + onCopy = {}, + transactionRequestOnClick = {}, + ), + ), + ), +) \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/sign/WcSignTransactionModalBottomSheetContent.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/sign/WcSignTransactionModalBottomSheetContent.kt new file mode 100644 index 0000000000..7548486b13 --- /dev/null +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/sign/WcSignTransactionModalBottomSheetContent.kt @@ -0,0 +1,89 @@ +package com.tangem.features.walletconnect.transaction.ui.sign + +import androidx.compose.animation.animateContentSize +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.HorizontalDivider +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import com.tangem.core.ui.res.TangemTheme +import com.tangem.features.walletconnect.connections.ui.WcAppInfoItem +import com.tangem.features.walletconnect.transaction.entity.WcSignTransactionUM +import com.tangem.features.walletconnect.transaction.ui.common.* +import com.tangem.features.walletconnect.transaction.ui.common.WcNetworkItem +import com.tangem.features.walletconnect.transaction.ui.common.WcTransactionRequestButtons +import com.tangem.features.walletconnect.transaction.ui.common.WcTransactionRequestItem +import com.tangem.features.walletconnect.transaction.ui.common.WcWalletItem + +@Composable +internal fun WcSignTransactionModalBottomSheetContent(state: WcSignTransactionUM) { + Column(modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16)) { + Column( + modifier = Modifier + .clip(RoundedCornerShape(TangemTheme.dimens.radius14)) + .background(color = TangemTheme.colors.background.action) + .fillMaxWidth() + .animateContentSize(), + ) { + RequestFromItem() + WcAppInfoItem( + iconUrl = state.transaction.appIcon, + title = state.transaction.appName, + subtitle = state.transaction.appSubtitle, + isVerified = state.transaction.isVerified, + ) + HorizontalDivider( + thickness = TangemTheme.dimens.size1, + color = TangemTheme.colors.stroke.primary, + ) + WcTransactionRequestItem( + iconRes = state.transactionIconRes, + modifier = Modifier + .fillMaxWidth() + .clickable { state.actions.transactionRequestOnClick() } + .padding(TangemTheme.dimens.spacing12), + ) + } + Column(modifier = Modifier.padding(top = TangemTheme.dimens.spacing16)) { + Column( + modifier = Modifier + .clip(RoundedCornerShape(TangemTheme.dimens.radius14)) + .background(color = TangemTheme.colors.background.action) + .fillMaxWidth() + .animateContentSize(), + ) { + val itemsModifier = Modifier + .fillMaxWidth() + .padding(TangemTheme.dimens.spacing12) + + HorizontalDivider( + thickness = TangemTheme.dimens.size1, + color = TangemTheme.colors.stroke.primary, + ) + WcWalletItem( + modifier = itemsModifier, + walletName = state.transaction.walletName, + ) + HorizontalDivider( + thickness = TangemTheme.dimens.size1, + color = TangemTheme.colors.stroke.primary, + ) + WcNetworkItem( + modifier = itemsModifier, + networkInfo = state.transaction.networkInfo, + ) + } + WcTransactionRequestButtons( + modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing16), + onDismiss = state.actions.onDismiss, + onSign = state.actions.onSign, + isLoading = state.transaction.isLoading, + ) + } + } +} \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/utils/WcSignTransactionUtils.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/utils/WcSignTransactionUtils.kt new file mode 100644 index 0000000000..563dbda856 --- /dev/null +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/utils/WcSignTransactionUtils.kt @@ -0,0 +1,66 @@ +package com.tangem.features.walletconnect.transaction.utils + +import com.domain.blockaid.models.dapp.CheckDAppResult +import com.tangem.core.ui.extensions.getActiveIconRes +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.domain.walletconnect.usecase.ethereum.WcEthMessageSignUseCase +import com.tangem.domain.walletconnect.usecase.sign.WcSignState +import com.tangem.domain.walletconnect.usecase.sign.WcSignStep +import com.tangem.domain.walletconnect.usecase.sign.WcSignUseCase +import com.tangem.features.walletconnect.impl.R +import com.tangem.features.walletconnect.transaction.entity.WcNetworkInfoUM +import com.tangem.features.walletconnect.transaction.entity.WcSignTransactionUM +import com.tangem.features.walletconnect.transaction.entity.WcTransactionActionsUM +import com.tangem.features.walletconnect.transaction.entity.WcTransactionRequestInfoItemUM +import com.tangem.features.walletconnect.transaction.entity.WcTransactionRequestInfoUM +import com.tangem.features.walletconnect.transaction.entity.WcTransactionUM +import kotlinx.collections.immutable.persistentListOf + +internal fun WcSignUseCase.toUM(signState: WcSignState<*>, actions: WcTransactionActionsUM): WcSignTransactionUM? { + return when (this) { + is WcEthMessageSignUseCase -> { + ethMessageSignToUM( + signState = signState, + signModel = signState.signModel as WcEthMessageSignUseCase.SignModel, + actions = actions, + ) + } + else -> null + } +} + +private fun WcEthMessageSignUseCase.ethMessageSignToUM( + signState: WcSignState<*>, + signModel: WcEthMessageSignUseCase.SignModel, + actions: WcTransactionActionsUM, +) = WcSignTransactionUM( + startIconRes = R.drawable.ic_back_24, + endIconRes = R.drawable.ic_close_24, + transactionIconRes = R.drawable.ic_doc_new_24, + state = WcSignTransactionUM.State.TRANSACTION, + actions = actions, + transaction = WcTransactionUM( + appName = session.sdkModel.appMetaData.name, + appIcon = session.sdkModel.appMetaData.url, + isVerified = session.securityStatus == CheckDAppResult.SAFE, + appSubtitle = session.sdkModel.appMetaData.description, + walletName = session.wallet.name, + networkInfo = WcNetworkInfoUM( + name = network.name, + iconRes = getActiveIconRes(network.id.value), + ), + isLoading = signState.domainStep == WcSignStep.Signing, + ), + transactionRequestInfo = WcTransactionRequestInfoUM( + persistentListOf( + WcTransactionRequestInfoItemUM( + title = resourceReference(R.string.wc_signature_type), + description = rawSdkRequest.request.method, + ), + WcTransactionRequestInfoItemUM( + title = resourceReference(R.string.wc_contents), + description = signModel.humanMsg, + ), + ), + ), +) \ No newline at end of file