Updated on 2026-08-14

This commit is contained in:
Tangem 2025-05-06 12:51:54 +04:00
parent a6d0b50f43
commit d17f0c4f01
20 changed files with 770 additions and 269 deletions

View file

@ -1063,7 +1063,7 @@
<string name="transaction_history_transaction_validator">validator: %s</string>
<string name="transfer_min_amount_error">Minimum %s</string>
<string name="transfer_notification_invalid_minimum_transaction_amount_text">The minimum transaction amount is %1$s.</string>
<string name="tron_will_be_send_token_fee_description">Tron network fees for popular tokens are higher. Stake some TRX for cheaper or free transactions.</string>
<string name="tron_will_be_send_token_fee_description">Tron network fees for popular tokens can be higher. Staking TRX may help reduce transaction costs.</string>
<string name="tron_will_be_send_token_fee_title">Save on Tron network fees</string>
<string name="try_to_load_data_again_button_title">Try again</string>
<string name="twin_error_same_card">You\'ve scanned the same card. To create a twin wallet you need to scan the card with number %d</string>
@ -1334,6 +1334,8 @@
<string name="wc_transaction_request">Transaction request</string>
<string name="wc_transaction_request_title">Transaction request</string>
<string name="wc_wallet_connect">Wallet connect</string>
<string name="wc_custom_allowance_title">Custom allowance</string>
<string name="wc_allow_to_spend">Allow to spend</string>
<string name="welcome_interrupted_backup_alert_discard">Discard</string>
<string name="welcome_interrupted_backup_alert_message">You have an interrupted backup. Do you want to resume?</string>
<string name="welcome_interrupted_backup_alert_resume">Yes, resume</string>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="13dp"
android:height="13dp"
android:viewportWidth="13"
android:viewportHeight="13">
<path
android:pathData="M11.149,0.969C10.775,0.595 10.169,0.595 9.795,0.969L8.567,2.197L10.803,4.433L12.031,3.205C12.405,2.831 12.405,2.225 12.031,1.851L11.149,0.969Z"
android:fillColor="#919191"/>
<path
android:pathData="M9.893,5.342L7.657,3.106L1.047,9.716C0.899,9.865 0.803,10.058 0.775,10.267L0.524,12.153C0.499,12.34 0.659,12.5 0.847,12.476L2.733,12.224C2.941,12.196 3.135,12.101 3.283,11.952L9.893,5.342Z"
android:fillColor="#919191"/>
</vector>

View file

@ -1,60 +0,0 @@
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<WcTransactionRequestInfoItemUM>,
)
@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,
)

View file

@ -0,0 +1,6 @@
package com.tangem.features.walletconnect.transaction.entity.approve
internal data class SpendAllowanceUM(
val amountText: String,
val tokenImageUrl: String,
)

View file

@ -0,0 +1,20 @@
package com.tangem.features.walletconnect.transaction.entity.approve
import androidx.compose.runtime.Immutable
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionActionsUM
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionRequestInfoUM
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionUM
@Immutable
internal data class WcApproveTransactionUM(
val actions: WcTransactionActionsUM,
val state: State = State.TRANSACTION,
val transaction: WcTransactionUM,
val transactionRequestInfo: WcTransactionRequestInfoUM,
) : TangemBottomSheetConfigContent {
enum class State {
TRANSACTION, CUSTOM_ALLOWANCE, TRANSACTION_REQUEST_INFO
}
}

View file

@ -0,0 +1,21 @@
package com.tangem.features.walletconnect.transaction.entity.common
import androidx.compose.runtime.Immutable
import com.tangem.core.ui.extensions.TextReference
import kotlinx.collections.immutable.ImmutableList
@Immutable
internal data class WcTransactionRequestInfoUM(
val blocks: ImmutableList<WcTransactionRequestBlockUM>,
)
@Immutable
internal data class WcTransactionRequestBlockUM(
val info: ImmutableList<WcTransactionRequestInfoItemUM>,
)
@Immutable
internal data class WcTransactionRequestInfoItemUM(
val title: TextReference,
val description: String = "",
)

View file

@ -0,0 +1,36 @@
package com.tangem.features.walletconnect.transaction.entity.common
import androidx.annotation.DrawableRes
import androidx.compose.runtime.Immutable
import com.tangem.core.ui.extensions.TextReference
import com.tangem.features.walletconnect.transaction.entity.approve.SpendAllowanceUM
@Immutable
internal data class WcTransactionUM(
val appName: String,
val appIcon: String,
val isVerified: Boolean,
val appSubtitle: String,
val walletName: String,
val networkInfo: WcNetworkInfoUM,
val activeButtonText: TextReference,
val addressText: String? = null,
val networkFee: String? = null,
val spendAllowance: SpendAllowanceUM? = null,
val isLoading: Boolean = false,
)
@Immutable
internal data class WcTransactionActionsUM(
val transactionRequestOnClick: () -> Unit,
val onDismiss: () -> Unit,
val activeButtonOnClick: () -> Unit,
val onBack: () -> Unit,
val onCopy: () -> Unit,
)
@Immutable
internal data class WcNetworkInfoUM(
val name: String,
@DrawableRes val iconRes: Int,
)

View file

@ -0,0 +1,20 @@
package com.tangem.features.walletconnect.transaction.entity.sign
import androidx.compose.runtime.Immutable
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionActionsUM
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionRequestInfoUM
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionUM
@Immutable
internal data class WcSignTransactionUM(
val actions: WcTransactionActionsUM,
val state: State = State.TRANSACTION,
val transaction: WcTransactionUM,
val transactionRequestInfo: WcTransactionRequestInfoUM,
) : TangemBottomSheetConfigContent {
enum class State {
TRANSACTION, TRANSACTION_REQUEST_INFO
}
}

View file

@ -11,8 +11,8 @@ import com.tangem.domain.walletconnect.usecase.method.WcSignState
import com.tangem.domain.walletconnect.usecase.method.WcSignStep
import com.tangem.domain.walletconnect.usecase.method.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.entity.sign.WcSignTransactionUM
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionActionsUM
import com.tangem.features.walletconnect.transaction.utils.toUM
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.MutableStateFlow
@ -48,7 +48,7 @@ internal class WcSignTransactionModel @Inject constructor(
actions = WcTransactionActionsUM(
onDismiss = { cancel(useCase) },
onBack = ::showTransactionState,
onSign = useCase::sign,
activeButtonOnClick = useCase::sign,
onCopy = { copyData(useCase.rawSdkRequest.request.params) },
transactionRequestOnClick = ::showTransactionRequestState,
),

View file

@ -0,0 +1,82 @@
package com.tangem.features.walletconnect.transaction.ui.approve
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
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 com.tangem.features.walletconnect.impl.R
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import coil.compose.AsyncImage
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.walletconnect.transaction.entity.approve.SpendAllowanceUM
import com.tangem.features.walletconnect.transaction.ui.common.WcSmallTitleItem
@Composable
internal fun SpendAllowanceItem(spendAllowance: SpendAllowanceUM, modifier: Modifier = Modifier) {
Column(
modifier = modifier
.clip(RoundedCornerShape(TangemTheme.dimens.radius14))
.background(TangemTheme.colors.background.action)
.fillMaxWidth()
.padding(
end = TangemTheme.dimens.spacing12,
bottom = TangemTheme.dimens.spacing12,
),
) {
WcSmallTitleItem(R.string.wc_allow_to_spend)
Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing8))
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth(),
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.weight(1f),
) {
AsyncImage(
model = spendAllowance.tokenImageUrl,
modifier = Modifier
.size(TangemTheme.dimens.size24)
.clip(CircleShape),
contentDescription = null,
)
Spacer(modifier = Modifier.width(TangemTheme.dimens.spacing12))
Text(
text = spendAllowance.amountText,
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.primary1,
)
}
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End,
) {
Text(
text = stringResource(R.string.manage_tokens_edit),
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.tertiary,
)
Spacer(modifier = Modifier.width(TangemTheme.dimens.spacing8))
Icon(
painter = painterResource(id = R.drawable.ic_edit_new_24),
contentDescription = null,
tint = TangemTheme.colors.icon.informative,
)
}
}
}
}

View file

@ -0,0 +1,122 @@
package com.tangem.features.walletconnect.transaction.ui.approve
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.approve.SpendAllowanceUM
import com.tangem.features.walletconnect.transaction.entity.approve.WcApproveTransactionUM
import com.tangem.features.walletconnect.transaction.entity.common.WcNetworkInfoUM
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionActionsUM
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionRequestInfoUM
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionUM
import com.tangem.features.walletconnect.transaction.ui.common.WcTransactionRequestInfoContent
import com.tangem.features.walletconnect.transaction.ui.sign.WcTransactionModalBottomSheetContent
import kotlinx.collections.immutable.persistentListOf
@Composable
internal fun WcApproveTransactionModalBottomSheet(config: TangemBottomSheetConfig) {
TangemModalBottomSheet<WcApproveTransactionUM>(
config = config,
containerColor = TangemTheme.colors.background.tertiary,
title = { state ->
TangemModalBottomSheetTitle(
title = when (state.state) {
WcApproveTransactionUM.State.TRANSACTION -> {
resourceReference(R.string.wallet_connect_title)
}
WcApproveTransactionUM.State.CUSTOM_ALLOWANCE -> {
resourceReference(R.string.wc_custom_allowance_title)
}
WcApproveTransactionUM.State.TRANSACTION_REQUEST_INFO -> {
resourceReference(R.string.wc_transaction_request_title)
}
},
endIconRes = R.drawable.ic_close_24.takeIf {
state.state == WcApproveTransactionUM.State.TRANSACTION
},
onEndClick = state.actions.onDismiss,
startIconRes = R.drawable.ic_back_24.takeIf {
state.state != WcApproveTransactionUM.State.TRANSACTION
},
onStartClick = state.actions.onBack,
)
},
content = { state ->
Box(
modifier = Modifier
.fillMaxWidth()
.animateContentSize(),
) {
when (state.state) {
WcApproveTransactionUM.State.TRANSACTION -> {
WcTransactionModalBottomSheetContent(state.transaction, state.actions)
}
WcApproveTransactionUM.State.CUSTOM_ALLOWANCE -> {
TODO("Will be done in the second part of the PR")
}
WcApproveTransactionUM.State.TRANSACTION_REQUEST_INFO -> {
WcTransactionRequestInfoContent(state.transactionRequestInfo, state.actions)
}
}
}
},
)
}
@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 WcApproveTransactionBottomSheetPreview(
@PreviewParameter(WcApproveTransactionStateProvider::class) state: WcApproveTransactionUM,
) {
TangemThemePreview {
WcApproveTransactionModalBottomSheet(
config = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = {},
content = state,
),
)
}
}
private class WcApproveTransactionStateProvider : CollectionPreviewParameterProvider<WcApproveTransactionUM>(
listOf(
WcApproveTransactionUM(
state = WcApproveTransactionUM.State.TRANSACTION,
transaction = WcTransactionUM(
appName = "React App",
appIcon = "",
isVerified = true,
appSubtitle = "react-app.walletconnect.com",
walletName = "Tangem 2.0",
activeButtonText = resourceReference(R.string.common_send),
networkInfo = WcNetworkInfoUM(name = "Ethereum", iconRes = R.drawable.img_eth_22),
networkFee = "~ 0.22 $",
spendAllowance = SpendAllowanceUM(amountText = "Unlimited USDT", tokenImageUrl = ""),
),
transactionRequestInfo = WcTransactionRequestInfoUM(persistentListOf()),
actions = WcTransactionActionsUM(
onDismiss = {},
onBack = {},
activeButtonOnClick = {},
onCopy = {},
transactionRequestOnClick = {},
),
),
),
)

View file

@ -1,64 +0,0 @@
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,
)
}
}

View file

@ -14,7 +14,7 @@ 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
import com.tangem.features.walletconnect.transaction.entity.common.WcNetworkInfoUM
@Composable
internal fun WcNetworkItem(networkInfo: WcNetworkInfoUM, modifier: Modifier = Modifier) {

View file

@ -1,5 +1,6 @@
package com.tangem.features.walletconnect.transaction.ui.common
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
@ -7,15 +8,14 @@ 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) {
fun WcSmallTitleItem(@StringRes textRex: Int, modifier: Modifier = Modifier) {
Text(
modifier = modifier
.fillMaxWidth()
.padding(top = TangemTheme.dimens.spacing12, start = TangemTheme.dimens.spacing12),
text = stringResourceSafe(R.string.wc_request_from),
text = stringResourceSafe(textRex),
color = TangemTheme.colors.text.tertiary,
style = TangemTheme.typography.subtitle2,
)

View file

@ -7,12 +7,15 @@ 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.TextReference
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
@Composable
internal fun WcTransactionRequestButtons(
activeButtonText: TextReference,
isLoading: Boolean,
onDismiss: () -> Unit,
onSign: () -> Unit,
@ -30,7 +33,7 @@ internal fun WcTransactionRequestButtons(
modifier = Modifier
.fillMaxWidth()
.weight(1f),
text = stringResourceSafe(R.string.common_sign),
text = activeButtonText.resolveReference(),
onClick = onSign,
iconResId = R.drawable.ic_tangem_24,
showProgress = isLoading,

View file

@ -0,0 +1,90 @@
package com.tangem.features.walletconnect.transaction.ui.common
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
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.platform.LocalConfiguration
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.common.WcTransactionActionsUM
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionRequestInfoUM
private const val MIN_HEIGHT_SCREEN_PERCENT = 0.35f
private const val MAX_HEIGHT_SCREEN_PERCENT = 0.75f
@Composable
internal fun WcTransactionRequestInfoContent(info: WcTransactionRequestInfoUM, actions: WcTransactionActionsUM) {
val screenHeight = LocalConfiguration.current.screenHeightDp
val minHeight = (screenHeight * MIN_HEIGHT_SCREEN_PERCENT).dp
val maxHeight = (screenHeight * MAX_HEIGHT_SCREEN_PERCENT).dp
Box(
modifier = Modifier
.fillMaxWidth()
.heightIn(min = minHeight, max = maxHeight)
.padding(horizontal = TangemTheme.dimens.spacing16),
) {
LazyColumn(
modifier = Modifier
.fillMaxWidth(),
contentPadding = PaddingValues(top = TangemTheme.dimens.spacing8, bottom = TangemTheme.dimens.spacing70),
) {
items(info.blocks) { block ->
Column(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(TangemTheme.dimens.radius16))
.background(TangemTheme.colors.background.action)
.padding(
start = TangemTheme.dimens.spacing16,
end = TangemTheme.dimens.spacing16,
top = TangemTheme.dimens.spacing20,
),
) {
block.info.forEach { item ->
Text(
text = item.title.resolveReference(),
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.tertiary,
)
if (item.description.isNotEmpty()) {
Text(
text = item.description,
modifier = Modifier.padding(
top = TangemTheme.dimens.spacing4,
bottom = TangemTheme.dimens.spacing20,
),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.primary1,
)
} else {
Spacer(modifier = Modifier.size(TangemTheme.dimens.size12))
}
}
}
Spacer(modifier = Modifier.size(TangemTheme.dimens.size20))
}
}
SecondaryButtonIconEnd(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(bottom = TangemTheme.dimens.spacing20)
.fillMaxWidth(),
text = stringResourceSafe(R.string.wc_copy_data_button_text),
onClick = actions.onCopy,
iconResId = R.drawable.ic_copy_24,
)
}
}

View file

@ -17,8 +17,14 @@ 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 com.tangem.features.walletconnect.transaction.entity.common.WcNetworkInfoUM
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionActionsUM
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionRequestBlockUM
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionRequestInfoItemUM
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionRequestInfoUM
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionUM
import com.tangem.features.walletconnect.transaction.entity.sign.WcSignTransactionUM
import com.tangem.features.walletconnect.transaction.ui.common.WcTransactionRequestInfoContent
import kotlinx.collections.immutable.persistentListOf
@Composable
@ -36,11 +42,11 @@ internal fun WcSignTransactionModalBottomSheet(config: TangemBottomSheetConfig)
resourceReference(R.string.wc_transaction_request_title)
}
},
endIconRes = state.endIconRes.takeIf {
endIconRes = R.drawable.ic_close_24.takeIf {
state.state == WcSignTransactionUM.State.TRANSACTION
},
onEndClick = state.actions.onDismiss,
startIconRes = state.startIconRes.takeIf {
startIconRes = R.drawable.ic_back_24.takeIf {
state.state == WcSignTransactionUM.State.TRANSACTION_REQUEST_INFO
},
onStartClick = state.actions.onBack,
@ -54,10 +60,10 @@ internal fun WcSignTransactionModalBottomSheet(config: TangemBottomSheetConfig)
) {
when (state.state) {
WcSignTransactionUM.State.TRANSACTION -> {
WcSignTransactionModalBottomSheetContent(state)
WcTransactionModalBottomSheetContent(state.transaction, state.actions)
}
WcSignTransactionUM.State.TRANSACTION_REQUEST_INFO -> {
TransactionRequestInfoContent(state)
WcTransactionRequestInfoContent(state.transactionRequestInfo, state.actions)
}
}
}
@ -85,9 +91,6 @@ private fun WcSignTransactionBottomSheetPreview(
private class WcSignTransactionStateProvider : CollectionPreviewParameterProvider<WcSignTransactionUM>(
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",
@ -95,32 +98,34 @@ private class WcSignTransactionStateProvider : CollectionPreviewParameterProvide
isVerified = true,
appSubtitle = "react-app.walletconnect.com",
walletName = "Tangem 2.0",
activeButtonText = resourceReference(R.string.common_sign),
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",
blocks = persistentListOf(
WcTransactionRequestBlockUM(
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 = {},
activeButtonOnClick = {},
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",
@ -128,24 +133,133 @@ private class WcSignTransactionStateProvider : CollectionPreviewParameterProvide
isVerified = true,
appSubtitle = "react-app.walletconnect.com",
walletName = "Tangem 2.0",
activeButtonText = resourceReference(R.string.common_sign),
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",
blocks = persistentListOf(
WcTransactionRequestBlockUM(
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 = {},
activeButtonOnClick = {},
onCopy = {},
transactionRequestOnClick = {},
),
),
WcSignTransactionUM(
state = WcSignTransactionUM.State.TRANSACTION,
transaction = WcTransactionUM(
appName = "React App",
appIcon = "",
isVerified = true,
appSubtitle = "react-app.walletconnect.com",
walletName = "Tangem 2.0",
activeButtonText = resourceReference(R.string.common_sign),
networkInfo = WcNetworkInfoUM(name = "Ethereum", iconRes = R.drawable.img_eth_22),
addressText = "0x345FF...34FA",
networkFee = "~ 0.22 $",
),
transactionRequestInfo = WcTransactionRequestInfoUM(
blocks = persistentListOf(
WcTransactionRequestBlockUM(
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",
),
),
),
WcTransactionRequestBlockUM(
persistentListOf(
WcTransactionRequestInfoItemUM(
title = resourceReference(R.string.wc_transaction_info_to_title),
),
WcTransactionRequestInfoItemUM(
title = resourceReference(R.string.settings_wallet_name_title),
description = "Bob",
),
WcTransactionRequestInfoItemUM(
title = resourceReference(R.string.wc_common_wallet),
description = "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826",
),
),
),
),
),
actions = WcTransactionActionsUM(
onDismiss = {},
onBack = {},
activeButtonOnClick = {},
onCopy = {},
transactionRequestOnClick = {},
),
),
WcSignTransactionUM(
state = WcSignTransactionUM.State.TRANSACTION_REQUEST_INFO,
transaction = WcTransactionUM(
appName = "React App",
appIcon = "",
isVerified = true,
appSubtitle = "react-app.walletconnect.com",
walletName = "Tangem 2.0",
activeButtonText = resourceReference(R.string.common_sign),
networkInfo = WcNetworkInfoUM(name = "Ethereum", iconRes = R.drawable.img_eth_22),
addressText = "0x345FF...34FA",
networkFee = "~ 0.22 $",
),
transactionRequestInfo = WcTransactionRequestInfoUM(
blocks = persistentListOf(
WcTransactionRequestBlockUM(
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",
),
),
),
WcTransactionRequestBlockUM(
persistentListOf(
WcTransactionRequestInfoItemUM(
title = resourceReference(R.string.wc_transaction_info_to_title),
),
WcTransactionRequestInfoItemUM(
title = resourceReference(R.string.settings_wallet_name_title),
description = "Bob",
),
WcTransactionRequestInfoItemUM(
title = resourceReference(R.string.wc_common_wallet),
description = "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826",
),
),
),
),
),
actions = WcTransactionActionsUM(
onDismiss = {},
onBack = {},
activeButtonOnClick = {},
onCopy = {},
transactionRequestOnClick = {},
),

View file

@ -1,89 +0,0 @@
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,
)
}
}
}

View file

@ -0,0 +1,119 @@
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.*
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 androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.walletconnect.connections.ui.WcAppInfoItem
import com.tangem.features.walletconnect.impl.R
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionActionsUM
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionUM
import com.tangem.features.walletconnect.transaction.ui.approve.SpendAllowanceItem
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 WcTransactionModalBottomSheetContent(transaction: WcTransactionUM, actions: WcTransactionActionsUM) {
Column(modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16)) {
Column(
modifier = Modifier
.clip(RoundedCornerShape(TangemTheme.dimens.radius14))
.background(color = TangemTheme.colors.background.action)
.fillMaxWidth()
.animateContentSize(),
) {
WcSmallTitleItem(R.string.wc_request_from)
WcAppInfoItem(
iconUrl = transaction.appIcon,
title = transaction.appName,
subtitle = transaction.appSubtitle,
isVerified = transaction.isVerified,
)
DividerWithPadding(start = 0.dp, end = 0.dp)
WcTransactionRequestItem(
iconRes = R.drawable.ic_doc_new_24,
modifier = Modifier
.fillMaxWidth()
.clickable { actions.transactionRequestOnClick() }
.padding(TangemTheme.dimens.spacing12),
)
}
Column(modifier = Modifier.padding(top = TangemTheme.dimens.spacing16)) {
if (transaction.spendAllowance != null) {
SpendAllowanceItem(transaction.spendAllowance)
Spacer(Modifier.height(TangemTheme.dimens.spacing16))
}
WcSignTransactionItems(transaction)
WcTransactionRequestButtons(
modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing16),
onDismiss = actions.onDismiss,
onSign = actions.activeButtonOnClick,
activeButtonText = transaction.activeButtonText,
isLoading = transaction.isLoading,
)
}
}
}
@Composable
private fun WcSignTransactionItems(transaction: WcTransactionUM) {
Column(
modifier = Modifier
.clip(RoundedCornerShape(TangemTheme.dimens.radius14))
.background(color = TangemTheme.colors.background.action)
.fillMaxWidth()
.animateContentSize(),
) {
val itemsModifier = Modifier
.fillMaxWidth()
.padding(TangemTheme.dimens.spacing12)
DividerWithPadding(start = 0.dp, end = 0.dp)
WcWalletItem(
modifier = itemsModifier,
walletName = transaction.walletName,
)
DividerWithPadding(start = TangemTheme.dimens.spacing40, end = TangemTheme.dimens.spacing12)
WcNetworkItem(
modifier = itemsModifier,
networkInfo = transaction.networkInfo,
)
if (!transaction.addressText.isNullOrEmpty()) {
DividerWithPadding(start = TangemTheme.dimens.spacing40, end = TangemTheme.dimens.spacing12)
WcAddressItem(
modifier = itemsModifier,
addressText = transaction.addressText,
)
}
if (!transaction.networkFee.isNullOrEmpty()) {
DividerWithPadding(start = TangemTheme.dimens.spacing40, end = TangemTheme.dimens.spacing12)
WcNetworkFeeItem(
modifier = itemsModifier,
networkFeeText = transaction.networkFee,
)
}
}
}
@Composable
private fun DividerWithPadding(start: Dp, end: Dp) {
HorizontalDivider(
modifier = Modifier.padding(
start = start,
end = end,
),
thickness = TangemTheme.dimens.size1,
color = TangemTheme.colors.stroke.primary,
)
}

View file

@ -3,40 +3,50 @@ 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.model.WcEthMethod
import com.tangem.domain.walletconnect.model.WcSolanaMethod
import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest
import com.tangem.domain.walletconnect.usecase.method.WcMessageSignUseCase
import com.tangem.domain.walletconnect.usecase.method.WcSignState
import com.tangem.domain.walletconnect.usecase.method.WcSignStep
import com.tangem.domain.walletconnect.usecase.method.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 com.tangem.features.walletconnect.transaction.entity.common.WcNetworkInfoUM
import com.tangem.features.walletconnect.transaction.entity.sign.WcSignTransactionUM
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionActionsUM
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionRequestBlockUM
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionRequestInfoItemUM
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionRequestInfoUM
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionUM
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
internal fun WcSignUseCase.toUM(signState: WcSignState<*>, actions: WcTransactionActionsUM): WcSignTransactionUM? {
return when (this) {
is WcMessageSignUseCase -> {
ethMessageSignToUM(
signState = signState,
signModel = signState.signModel as WcMessageSignUseCase.SignModel,
actions = actions,
)
when (method) {
is WcEthMethod.SignTypedData -> signTypedDataToUM(
signState = signState,
signModel = signState.signModel as WcMessageSignUseCase.SignModel,
actions = actions,
)
is WcEthMethod.MessageSign, is WcSolanaMethod.SignMessage -> messageSignToUM(
signState = signState,
signModel = signState.signModel as WcMessageSignUseCase.SignModel,
actions = actions,
)
else -> null
}
}
else -> null
}
}
private fun WcMessageSignUseCase.ethMessageSignToUM(
private fun WcMessageSignUseCase.signTypedDataToUM(
signState: WcSignState<*>,
signModel: WcMessageSignUseCase.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(
@ -49,9 +59,66 @@ private fun WcMessageSignUseCase.ethMessageSignToUM(
name = network.name,
iconRes = getActiveIconRes(network.id.value),
),
addressText = walletAddress.toShortAddressText(),
activeButtonText = resourceReference(R.string.common_sign),
isLoading = signState.domainStep == WcSignStep.Signing,
),
transactionRequestInfo = WcTransactionRequestInfoUM(
buildList {
add(createInfoBlockUM(rawSdkRequest, signModel))
(method as? WcEthMethod.SignTypedData)?.params?.message?.to?.let { to ->
add(
WcTransactionRequestBlockUM(
persistentListOf(
WcTransactionRequestInfoItemUM(
title = resourceReference(R.string.wc_transaction_info_to_title),
),
WcTransactionRequestInfoItemUM(
title = resourceReference(R.string.settings_wallet_name_title),
description = to.name,
),
WcTransactionRequestInfoItemUM(
title = resourceReference(R.string.wc_common_wallet),
description = to.wallet,
),
),
),
)
}
}.toImmutableList(),
),
)
private fun WcMessageSignUseCase.messageSignToUM(
signState: WcSignState<*>,
signModel: WcMessageSignUseCase.SignModel,
actions: WcTransactionActionsUM,
) = WcSignTransactionUM(
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),
),
activeButtonText = resourceReference(R.string.common_sign),
isLoading = signState.domainStep == WcSignStep.Signing,
),
transactionRequestInfo = WcTransactionRequestInfoUM(
persistentListOf(createInfoBlockUM(rawSdkRequest, signModel)),
),
)
private fun createInfoBlockUM(
rawSdkRequest: WcSdkSessionRequest,
signModel: WcMessageSignUseCase.SignModel,
): WcTransactionRequestBlockUM {
return WcTransactionRequestBlockUM(
persistentListOf(
WcTransactionRequestInfoItemUM(
title = resourceReference(R.string.wc_signature_type),
@ -62,5 +129,5 @@ private fun WcMessageSignUseCase.ethMessageSignToUM(
description = signModel.humanMsg,
),
),
),
)
)
}