Updated on 2026-08-14

This commit is contained in:
Tangem 2025-09-10 15:37:16 +03:00
parent 83ab0146ec
commit ac121ce008
14 changed files with 458 additions and 16 deletions

View file

@ -62,6 +62,8 @@ internal abstract class WcCommonTransactionComponentDelegate(
is WcTransactionRoutes.Alert,
is WcTransactionRoutes.CustomAllowance,
is WcTransactionRoutes.SelectFee,
is WcTransactionRoutes.MultipleTransactions,
WcTransactionRoutes.TransactionProcess,
-> stackNavigation?.pop()
null -> Unit
}

View file

@ -4,10 +4,13 @@ import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.domain.walletconnect.WcAnalyticEvents
import com.tangem.features.send.v2.api.FeeSelectorComponent
import com.tangem.features.send.v2.api.params.FeeSelectorParams
import com.tangem.features.send.v2.api.params.FeeSelectorParams.FeeDisplaySource
import com.tangem.features.send.v2.api.params.FeeSelectorParams.FeeSelectorDetailsParams
import com.tangem.features.walletconnect.connections.components.AlertsComponentV2
import com.tangem.features.walletconnect.connections.utils.WcAlertsFactory.createCommonTransactionAppInfoAlertUM
import com.tangem.features.walletconnect.transaction.components.send.WcCustomAllowanceComponent
import com.tangem.features.walletconnect.transaction.components.send.WcSendingProcessComponent
import com.tangem.features.walletconnect.transaction.components.send.WcSendMultipleTransactionsComponent
import com.tangem.features.walletconnect.transaction.entity.common.WcCommonTransactionModel
import com.tangem.features.walletconnect.transaction.model.WcSendTransactionModel
import com.tangem.features.walletconnect.transaction.routes.WcTransactionRoutes
@ -43,17 +46,32 @@ internal fun getWcCommonScreen(
feeSelectorComponentFactory.create(
context = appComponentContext,
onDismiss = model::popBack,
params = FeeSelectorParams.FeeSelectorDetailsParams(
params = FeeSelectorDetailsParams(
state = state.feeSelectorUM,
onLoadFee = model::loadFee,
feeCryptoCurrencyStatus = model.cryptoCurrencyStatus,
cryptoCurrencyStatus = model.cryptoCurrencyStatus,
callback = model,
feeStateConfiguration = model.feeStateConfiguration,
feeDisplaySource = FeeSelectorParams.FeeDisplaySource.BottomSheet,
feeDisplaySource = FeeDisplaySource.BottomSheet,
analyticsCategoryName = WcAnalyticEvents.WC_CATEGORY_NAME,
),
)
}
is WcTransactionRoutes.MultipleTransactions -> {
val model = model as? WcSendTransactionModel ?: error("model must be WcSendTransactionModel")
WcSendMultipleTransactionsComponent(
appComponentContext = appComponentContext,
model = model,
onConfirm = config.onConfirm,
)
}
WcTransactionRoutes.TransactionProcess -> {
val model = model as? WcSendTransactionModel ?: error("model must be WcSendTransactionModel")
WcSendingProcessComponent(
appComponentContext = appComponentContext,
model = model,
)
}
}
}

View file

@ -0,0 +1,33 @@
package com.tangem.features.walletconnect.transaction.components.send
import androidx.compose.runtime.Composable
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.features.walletconnect.transaction.model.WcSendTransactionModel
import com.tangem.features.walletconnect.transaction.ui.send.WcSendMultipleTransactionsModalBottomSheet
internal class WcSendMultipleTransactionsComponent(
private val appComponentContext: AppComponentContext,
private val model: WcSendTransactionModel,
private val onConfirm: () -> Unit,
) : AppComponentContext by appComponentContext, ComposableBottomSheetComponent {
override fun dismiss() {
model.dismiss()
}
@Composable
override fun BottomSheet() {
WcSendMultipleTransactionsModalBottomSheet(
config = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = { model.popBack() },
content = TangemBottomSheetConfigContent.Empty,
),
onConfirm = onConfirm,
onBack = { model.popBack() },
)
}
}

View file

@ -0,0 +1,30 @@
package com.tangem.features.walletconnect.transaction.components.send
import androidx.compose.runtime.Composable
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.features.walletconnect.transaction.model.WcSendTransactionModel
import com.tangem.features.walletconnect.transaction.ui.send.WcSendingProcessModalBottomSheet
internal class WcSendingProcessComponent(
private val appComponentContext: AppComponentContext,
private val model: WcSendTransactionModel,
) : AppComponentContext by appComponentContext, ComposableBottomSheetComponent {
override fun dismiss() {
model.dismiss()
}
@Composable
override fun BottomSheet() {
WcSendingProcessModalBottomSheet(
config = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = ::dismiss,
content = TangemBottomSheetConfigContent.Empty,
),
)
}
}

View file

@ -141,7 +141,13 @@ internal class WcSendTransactionModel @Inject constructor(
val isApprovalMethod = isSecurityCheckContent &&
securityCheck.content is BlockAidTransactionCheck.Result.Approval
wcApproval = useCase as? WcApproval
sign = { useCase.sign() }
sign = {
if (isMultipleSignRequired(useCase)) {
openMultipleTransaction(useCase)
} else {
useCase.sign()
}
}
buildUiState(securityCheck, useCase, signState, isApprovalMethod)
if (feeReloadState.value) {
triggerFeeReload()
@ -162,6 +168,17 @@ internal class WcSendTransactionModel @Inject constructor(
}
}
private fun openMultipleTransaction(useCase: WcSignUseCase<*>) {
stackNavigation.pushNew(
WcTransactionRoutes.MultipleTransactions(
onConfirm = {
useCase.sign()
stackNavigation.pushNew(WcTransactionRoutes.TransactionProcess)
},
),
)
}
/**
* Handles callback with updated [feeSelectorUM] from click FeeSelectorComponent.
* We need to trigger navigation to dismiss fee selector component
@ -272,6 +289,14 @@ internal class WcSendTransactionModel @Inject constructor(
stackNavigation.pop()
}
private fun isMultipleSignRequired(useCase: WcSignUseCase<*>): Boolean {
return if (useCase is SignRequirements) {
useCase.isMultipleSignRequired()
} else {
false
}
}
fun showTransactionRequest() {
analytics.send(
WcAnalyticEvents.TransactionDetailsOpened(

View file

@ -34,6 +34,7 @@ internal sealed class WcTransactionRoutes : TangemBottomSheetConfigContent, Rout
val iconType: MessageBottomSheetUMV2.Icon.Type,
val iconBgType: MessageBottomSheetUMV2.Icon.BackgroundType,
) : Type()
data class UnknownError(
val errorMessage: String?,
val onDismiss: () -> Unit,
@ -41,4 +42,12 @@ internal sealed class WcTransactionRoutes : TangemBottomSheetConfigContent, Rout
) : Type()
}
}
@Serializable
data class MultipleTransactions(
val onConfirm: () -> Unit,
) : WcTransactionRoutes()
@Serializable
data object TransactionProcess : WcTransactionRoutes()
}

View file

@ -0,0 +1,104 @@
package com.tangem.features.walletconnect.transaction.ui.send
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
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 androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Devices
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.R
import com.tangem.core.ui.components.*
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
@Composable
internal fun WcSendMultipleTransactionsModalBottomSheet(
config: TangemBottomSheetConfig,
onConfirm: () -> Unit,
onBack: () -> Unit,
) {
TangemModalBottomSheet<TangemBottomSheetConfigContent>(
config = config,
content = {
Column(
modifier = Modifier.padding(start = 16.dp, end = 16.dp, bottom = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
SpacerH24()
Icon(
modifier = Modifier
.size(56.dp)
.clip(RoundedCornerShape(percent = 100))
.background(TangemTheme.colors.icon.informative.copy(alpha = 0.1f))
.padding(12.dp),
painter = rememberVectorPainter(
ImageVector.vectorResource(com.tangem.core.ui.R.drawable.ic_alert_24),
),
tint = TangemTheme.colors.icon.attention,
contentDescription = null,
)
SpacerH24()
Text(
text = "Multiple Transactions",
style = TangemTheme.typography.h3,
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.Center,
)
SpacerH8()
Text(
text = "Youll need to tap your Tangem device a few times to complete this process.",
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.secondary,
textAlign = TextAlign.Center,
)
SpacerH(48.dp)
SecondaryButton(
modifier = Modifier.fillMaxWidth(),
text = "Return",
onClick = onBack,
)
SpacerH8()
PrimaryButtonIconEnd(
modifier = Modifier.fillMaxWidth(),
iconResId = R.drawable.ic_tangem_24,
text = "Send",
onClick = onConfirm,
)
}
},
)
}
@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 WcSendTransactionBottomSheetPreview() {
TangemThemePreview {
WcSendMultipleTransactionsModalBottomSheet(
config = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = {},
content = TangemBottomSheetConfigContent.Empty,
),
onConfirm = {},
onBack = {},
)
}
}

View file

@ -0,0 +1,77 @@
package com.tangem.features.walletconnect.transaction.ui.send
import android.content.res.Configuration
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Devices
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.SpacerH
import com.tangem.core.ui.components.SpacerH8
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
@Composable
internal fun WcSendingProcessModalBottomSheet(config: TangemBottomSheetConfig) {
TangemModalBottomSheet<TangemBottomSheetConfigContent>(
config = config,
onBack = {
// empty to disable back handling
},
dismissOnClickOutside = false,
content = {
Column(
modifier = Modifier.padding(start = 16.dp, end = 16.dp, bottom = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
SpacerH(64.dp)
CircularProgressIndicator(
modifier = Modifier
.size(TangemTheme.dimens.size32),
color = TangemTheme.colors.text.accent,
strokeWidth = TangemTheme.dimens.size2,
)
SpacerH(32.dp)
Text(
text = "Sending your funds",
style = TangemTheme.typography.h3,
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.Center,
)
SpacerH8()
Text(
text = "Were processing the transaction — itll be complete in moments.",
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.secondary,
textAlign = TextAlign.Center,
)
SpacerH(56.dp)
}
},
)
}
@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 WcSendTransactionBottomSheetPreview() {
TangemThemePreview {
WcSendingProcessModalBottomSheet(
config = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = {},
content = TangemBottomSheetConfigContent.Empty,
),
)
}
}