Updated on 2026-08-14
This commit is contained in:
parent
83ab0146ec
commit
ac121ce008
14 changed files with 458 additions and 16 deletions
|
|
@ -235,4 +235,13 @@ internal object TransactionDomainModule {
|
|||
): GetReverseResolvedEnsAddressUseCase {
|
||||
return GetReverseResolvedEnsAddressUseCase(walletAddressServiceRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun sendLargeSolanaTransactionUseCase(
|
||||
cardSdkConfigRepository: CardSdkConfigRepository,
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
): SendLargeSolanaTransactionUseCase {
|
||||
return SendLargeSolanaTransactionUseCase(cardSdkConfigRepository, walletManagersFacade)
|
||||
}
|
||||
}
|
||||
|
|
@ -49,6 +49,7 @@ inline fun <reified T : TangemBottomSheetConfigContent> TangemModalBottomSheet(
|
|||
config: TangemBottomSheetConfig,
|
||||
containerColor: Color = TangemTheme.colors.background.primary,
|
||||
skipPartiallyExpanded: Boolean = true,
|
||||
dismissOnClickOutside: Boolean = true,
|
||||
noinline onBack: (() -> Unit)? = null,
|
||||
crossinline title: @Composable BoxScope.(T) -> Unit = {},
|
||||
crossinline content: @Composable ColumnScope.(T) -> Unit,
|
||||
|
|
@ -69,6 +70,7 @@ inline fun <reified T : TangemBottomSheetConfigContent> TangemModalBottomSheet(
|
|||
containerColor = containerColor,
|
||||
title = title,
|
||||
content = content,
|
||||
dismissOnClickOutside = dismissOnClickOutside,
|
||||
onBack = onBack,
|
||||
skipPartiallyExpanded = skipPartiallyExpanded,
|
||||
)
|
||||
|
|
@ -81,12 +83,22 @@ inline fun <reified T : TangemBottomSheetConfigContent> DefaultModalBottomSheet(
|
|||
config: TangemBottomSheetConfig,
|
||||
containerColor: Color,
|
||||
skipPartiallyExpanded: Boolean = true,
|
||||
dismissOnClickOutside: Boolean = true,
|
||||
noinline onBack: (() -> Unit)? = null,
|
||||
crossinline title: @Composable (BoxScope.(T) -> Unit),
|
||||
crossinline content: @Composable (ColumnScope.(T) -> Unit),
|
||||
) {
|
||||
var isVisible by remember { mutableStateOf(value = config.isShown) }
|
||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = skipPartiallyExpanded)
|
||||
val sheetState = rememberModalBottomSheetState(
|
||||
skipPartiallyExpanded = skipPartiallyExpanded,
|
||||
confirmValueChange = {
|
||||
if (!dismissOnClickOutside) {
|
||||
it != SheetValue.Hidden // Ignore transitions to hidden (prevents dismiss on outside click/back press)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
if (isVisible && config.content is T) {
|
||||
BasicModalBottomSheet<T>(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.data.walletconnect.network.solana
|
||||
|
||||
import arrow.core.left
|
||||
import com.tangem.blockchain.blockchains.solana.SolanaTransactionHelper
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.extensions.encodeBase58
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
|
|
@ -10,10 +11,13 @@ import com.tangem.data.walletconnect.sign.SignCollector
|
|||
import com.tangem.data.walletconnect.sign.SignStateConverter.toResult
|
||||
import com.tangem.data.walletconnect.sign.WcMethodUseCaseContext
|
||||
import com.tangem.data.walletconnect.utils.BlockAidVerificationDelegate
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.transaction.usecase.PrepareAndSignUseCase
|
||||
import com.tangem.domain.transaction.usecase.SendLargeSolanaTransactionUseCase
|
||||
import com.tangem.domain.walletconnect.error.parseSendError
|
||||
import com.tangem.domain.walletconnect.model.WcSolanaMethod
|
||||
import com.tangem.domain.walletconnect.usecase.method.BlockAidTransactionCheck
|
||||
import com.tangem.domain.walletconnect.usecase.method.SignRequirements
|
||||
import com.tangem.domain.walletconnect.usecase.method.WcSignState
|
||||
import com.tangem.domain.walletconnect.usecase.method.WcTransactionUseCase
|
||||
import dagger.assisted.Assisted
|
||||
|
|
@ -22,18 +26,21 @@ import dagger.assisted.AssistedInject
|
|||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import okio.ByteString.Companion.decodeBase64
|
||||
import timber.log.Timber
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class WcSolanaSignTransactionUseCase @AssistedInject constructor(
|
||||
override val respondService: WcRespondService,
|
||||
override val analytics: AnalyticsEventHandler,
|
||||
private val prepareAndSign: PrepareAndSignUseCase,
|
||||
private val sendLargeSolanaTransactionUseCase: SendLargeSolanaTransactionUseCase,
|
||||
@Assisted override val context: WcMethodUseCaseContext,
|
||||
@Assisted override val method: WcSolanaMethod.SignTransaction,
|
||||
blockAidDelegate: BlockAidVerificationDelegate,
|
||||
addressConverter: SolanaBlockAidAddressConverter,
|
||||
) : BaseWcSignUseCase<Nothing, TransactionData>(),
|
||||
WcTransactionUseCase {
|
||||
WcTransactionUseCase,
|
||||
SignRequirements {
|
||||
|
||||
override val securityStatus = blockAidDelegate.getSecurityStatus(
|
||||
network = network,
|
||||
|
|
@ -44,15 +51,35 @@ internal class WcSolanaSignTransactionUseCase @AssistedInject constructor(
|
|||
).map { lce -> lce.map { result -> BlockAidTransactionCheck.Result.Plain(result) } }
|
||||
|
||||
override suspend fun SignCollector<TransactionData>.onSign(state: WcSignState<TransactionData>) {
|
||||
val hash = prepareAndSign.invoke(transactionData = state.signModel, userWallet = wallet, network = network)
|
||||
.onLeft { error ->
|
||||
emit(state.toResult(parseSendError(error).left()))
|
||||
}
|
||||
.getOrNull()
|
||||
?: return
|
||||
val respond = "{ signature: \"${hash.encodeBase58()}\" }"
|
||||
val respondResult = respondService.respond(rawSdkRequest, respond)
|
||||
emit(state.toResult(respondResult))
|
||||
val hash = state.signModel.getTxHashFromCompiled()
|
||||
val formattedHash = getFormattedHash(hash) // uses for flow sendLargeSolanaTransaction
|
||||
if (context.session.wallet is UserWallet.Cold && isLargeHash(formattedHash)) {
|
||||
// workaround for large transactions that cannot be signed directly by card
|
||||
Timber.w("The transaction hash is too large to be signed directly: ${formattedHash.size} bytes")
|
||||
sendLargeSolanaTransactionUseCase(context.session.wallet as UserWallet.Cold, context.network, formattedHash)
|
||||
.fold(
|
||||
ifLeft = {
|
||||
Timber.e(it.toString())
|
||||
emit(state.toResult(parseSendError(it).left()))
|
||||
},
|
||||
ifRight = {
|
||||
val emptyRespond = ByteArray(0).formatAsSolanaSignature()
|
||||
val respondResult = respondService.respond(rawSdkRequest, emptyRespond)
|
||||
emit(state.toResult(respondResult))
|
||||
},
|
||||
)
|
||||
} else {
|
||||
val signedHash =
|
||||
prepareAndSign.invoke(transactionData = state.signModel, userWallet = wallet, network = network)
|
||||
.onLeft { error ->
|
||||
emit(state.toResult(parseSendError(error).left()))
|
||||
}
|
||||
.getOrNull()
|
||||
?: return
|
||||
val respond = signedHash.formatAsSolanaSignature()
|
||||
val respondResult = respondService.respond(rawSdkRequest, respond)
|
||||
emit(state.toResult(respondResult))
|
||||
}
|
||||
}
|
||||
|
||||
override fun invoke(): Flow<WcSignState<TransactionData>> {
|
||||
|
|
@ -64,6 +91,40 @@ internal class WcSolanaSignTransactionUseCase @AssistedInject constructor(
|
|||
return delegate.invoke(transactionData)
|
||||
}
|
||||
|
||||
private fun ByteArray.formatAsSolanaSignature(): String {
|
||||
return "{ signature: \"${this.encodeBase58()}\" }"
|
||||
}
|
||||
|
||||
private fun TransactionData.getTxHashFromCompiled(): ByteArray {
|
||||
return when (this) {
|
||||
is TransactionData.Compiled -> (value as? TransactionData.Compiled.Data.Bytes)?.data
|
||||
?: error("Invalid transaction data")
|
||||
is TransactionData.Uncompiled -> error("Transaction must be compiled")
|
||||
}
|
||||
}
|
||||
|
||||
private fun isLargeHash(hash: ByteArray): Boolean {
|
||||
return hash.size > LARGE_HASH_SIZE
|
||||
}
|
||||
|
||||
private fun getFormattedHash(hash: ByteArray): ByteArray {
|
||||
return try {
|
||||
SolanaTransactionHelper.removeSignaturesPlaceholders(hash)
|
||||
} catch (e: Exception) {
|
||||
Timber.e("Failed to format the hash: ${e.message}")
|
||||
hash
|
||||
}
|
||||
}
|
||||
|
||||
override fun isMultipleSignRequired(): Boolean {
|
||||
val data = method.transaction.decodeBase64()?.toByteArray() ?: ByteArray(0)
|
||||
return isLargeHash(data)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private const val LARGE_HASH_SIZE = 930 // bytes
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
package com.tangem.domain.transaction.usecase
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.left
|
||||
import arrow.core.right
|
||||
import com.tangem.blockchain.blockchains.solana.SolanaWalletManager
|
||||
import com.tangem.blockchain.extensions.Result
|
||||
import com.tangem.domain.card.common.TapWorkarounds.isTangemTwins
|
||||
import com.tangem.domain.card.models.TwinKey
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.transaction.error.SendTransactionError
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
|
||||
/**
|
||||
* Use case for sending large Solana transactions that cannot be signed directly by the card.
|
||||
* It handles the transaction creating alt tables and sending the transaction through the Solana network.
|
||||
*
|
||||
* @property cardSdkConfigRepository Repository to access card SDK configurations and signers.
|
||||
* @property walletManagersFacade Facade to manage and retrieve wallet managers.
|
||||
*/
|
||||
class SendLargeSolanaTransactionUseCase(
|
||||
private val cardSdkConfigRepository: CardSdkConfigRepository,
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(
|
||||
userWallet: UserWallet.Cold,
|
||||
network: Network,
|
||||
txHash: ByteArray,
|
||||
): Either<SendTransactionError, Unit> {
|
||||
val card = userWallet.scanResponse.card
|
||||
val isCardNotBackedUp = card.backupStatus?.isActive != true && !card.isTangemTwins
|
||||
|
||||
val signer = cardSdkConfigRepository.getCommonSigner(
|
||||
cardId = card.cardId.takeIf { isCardNotBackedUp },
|
||||
twinKey = TwinKey.getOrNull(scanResponse = userWallet.scanResponse),
|
||||
)
|
||||
|
||||
val walletManager = walletManagersFacade
|
||||
.getOrCreateWalletManager(userWallet.walletId, network)
|
||||
?: error("WalletManager is null")
|
||||
|
||||
if (walletManager !is SolanaWalletManager) return SendTransactionError.UnknownError().left()
|
||||
val result = walletManager.handleLargeLegacyTransaction(signer, txHash)
|
||||
return when (result) {
|
||||
is Result.Failure -> SendTransactionError.BlockchainSdkError(
|
||||
code = result.error.code,
|
||||
message = result.error.customMessage,
|
||||
).left()
|
||||
is Result.Success -> Unit.right()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -46,4 +46,11 @@ interface WcMutableFee {
|
|||
interface WcApproval {
|
||||
fun getAmount(): WcApprovedAmount?
|
||||
fun updateAmount(amount: WcApprovedAmount?)
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines if multiple signatures are required for the transaction
|
||||
*/
|
||||
interface SignRequirements {
|
||||
fun isMultipleSignRequired(): Boolean
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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() },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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 = "You’ll 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 = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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 = "We’re processing the transaction — it’ll 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,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
# https://github.com/tangem/tangem-sdk-android/
|
||||
# https://github.com/tangem/vico
|
||||
|
||||
tangemBlockchainSdk = "develop-1216"
|
||||
tangemBlockchainSdk = "develop-1222"
|
||||
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
|
||||
tangemCardSdk = "develop-561"
|
||||
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue