Updated on 2026-08-14
This commit is contained in:
commit
4df207e832
59 changed files with 492 additions and 234 deletions
|
|
@ -45,7 +45,7 @@ class DetailsPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) :
|
|||
|
||||
val contactSupportButton: KNode = child {
|
||||
hasTestTag(DetailsScreenTestTags.SCREEN_ITEM)
|
||||
hasText(getResourceString(R.string.details_row_title_contact_to_support))
|
||||
hasText(getResourceString(R.string.common_contact_support))
|
||||
}
|
||||
val toSButton: KNode = child {
|
||||
hasTestTag(DetailsScreenTestTags.SCREEN_ITEM)
|
||||
|
|
|
|||
|
|
@ -18,8 +18,12 @@ import com.tangem.tap.domain.userWalletList.utils.toUserWallets
|
|||
import com.tangem.tap.domain.userWalletList.utils.updateWith
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import timber.log.Timber
|
||||
|
||||
@Suppress("LargeClass")
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
internal class BiometricUserWalletsListManager(
|
||||
private val keysRepository: UserWalletsKeysRepository,
|
||||
|
|
@ -29,6 +33,9 @@ internal class BiometricUserWalletsListManager(
|
|||
) : UserWalletsListManager.Lockable {
|
||||
private val state = MutableStateFlow(State())
|
||||
|
||||
private var hasSavedWallets: Boolean? = null
|
||||
private val savedWalletMutex = Mutex()
|
||||
|
||||
override val isLockable: Boolean = true
|
||||
|
||||
override val userWallets: Flow<List<UserWallet>>
|
||||
|
|
@ -61,7 +68,21 @@ internal class BiometricUserWalletsListManager(
|
|||
get() = state.value.isLocked
|
||||
|
||||
override val hasUserWallets: Boolean
|
||||
get() = keysRepository.hasSavedEncryptionKeys()
|
||||
get() {
|
||||
return runBlocking {
|
||||
// workaround to avoid calling hasSavedEncryptionKeys many times because of performance
|
||||
savedWalletMutex.withLock {
|
||||
val hasSavedWalletsLocal = hasSavedWallets
|
||||
if (hasSavedWalletsLocal == null || !hasSavedWalletsLocal) {
|
||||
val hasKeys = keysRepository.hasSavedEncryptionKeys()
|
||||
hasSavedWallets = hasKeys
|
||||
hasKeys
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val walletsCount: Int
|
||||
get() = state.value.userWallets.size
|
||||
|
|
@ -194,6 +215,9 @@ internal class BiometricUserWalletsListManager(
|
|||
}
|
||||
|
||||
override suspend fun clear(): CompletionResult<Unit> {
|
||||
savedWalletMutex.withLock {
|
||||
hasSavedWallets = null
|
||||
}
|
||||
return sensitiveInformationRepository.clear()
|
||||
.flatMap { publicInformationRepository.clear() }
|
||||
.map {
|
||||
|
|
|
|||
|
|
@ -35,8 +35,8 @@ import com.tangem.core.ui.extensions.isNullOrEmpty
|
|||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.utils.singleEvent
|
||||
import com.tangem.core.ui.test.SendScreenTestTags
|
||||
import com.tangem.core.ui.utils.singleEvent
|
||||
|
||||
@Composable
|
||||
fun NavigationButtonsBlock(
|
||||
|
|
@ -78,7 +78,9 @@ fun NavigationButtonsBlockV2(
|
|||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
PreviousButton(navigationUM?.prevButton)
|
||||
NavigationPrimaryButton(navigationUM?.primaryButton, modifier = Modifier.weight(1f))
|
||||
key(navigationUM?.source) {
|
||||
NavigationPrimaryButton(navigationUM?.primaryButton, modifier = Modifier.weight(1f))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import com.tangem.core.ui.extensions.TextReference
|
|||
@Immutable
|
||||
sealed class NavigationUM {
|
||||
data class Content(
|
||||
val source: String,
|
||||
val title: TextReference,
|
||||
val subtitle: TextReference?,
|
||||
@DrawableRes val backIconRes: Int,
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
},
|
||||
{
|
||||
"name": "zklink",
|
||||
"version": "5.31"
|
||||
"version": "undefined"
|
||||
},
|
||||
{
|
||||
"name": "plasma",
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@
|
|||
},
|
||||
{
|
||||
"name": "NEW_ONRAMP_MAIN_ENABLED",
|
||||
"version": "undefined"
|
||||
"version": "5.31.0"
|
||||
},
|
||||
{
|
||||
"name": "ACCOUNTS_FEATURE_ENABLED",
|
||||
|
|
|
|||
|
|
@ -248,6 +248,7 @@
|
|||
<string name="common_coming_soon">Coming soon</string>
|
||||
<string name="common_confirm">Confirm</string>
|
||||
<string name="common_connecting">Connecting</string>
|
||||
<string name="common_contact_support">Contact support</string>
|
||||
<string name="common_contact_tangem_support">Contact Tangem Support</string>
|
||||
<string name="common_contact_visa_support">Contact Visa Support</string>
|
||||
<string name="common_continue">Continue</string>
|
||||
|
|
@ -1390,6 +1391,8 @@
|
|||
<string name="swapping_to_title">You receive</string>
|
||||
<string name="swapping_token_list_title">Choose token</string>
|
||||
<string name="swapping_token_not_available">not available</string>
|
||||
<string name="tangem_pay_beta_notification_subtitle">We would be happy to receive your feedback</string>
|
||||
<string name="tangem_pay_beta_notification_title">Tangem Pay is now in beta</string>
|
||||
<string name="tangem_pay_card_frozen">Card frozen</string>
|
||||
<string name="tangem_pay_card_payment">Card payment</string>
|
||||
<string name="tangem_pay_deposit">Deposit</string>
|
||||
|
|
|
|||
|
|
@ -61,7 +61,6 @@ fun NetworkTitle(
|
|||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.weight(weight = 1f)
|
||||
.heightIn(min = TangemTheme.dimens.size20),
|
||||
contentAlignment = Alignment.CenterStart,
|
||||
content = title,
|
||||
|
|
@ -99,4 +98,4 @@ private fun NetworkTitlePreview(@PreviewParameter(NetworkTitleIconVisibilityProv
|
|||
}
|
||||
}
|
||||
|
||||
private object NetworkTitleIconVisibilityProvider : CollectionPreviewParameterProvider<Boolean>(listOf(true, false))
|
||||
private class NetworkTitleIconVisibilityProvider : CollectionPreviewParameterProvider<Boolean>(listOf(true, false))
|
||||
|
|
@ -124,4 +124,4 @@ private fun NetworkTitleItemPreview(@PreviewParameter(GroupTitleItemProvider::cl
|
|||
}
|
||||
}
|
||||
|
||||
private object GroupTitleItemProvider : CollectionPreviewParameterProvider<Boolean>(collection = listOf(true, false))
|
||||
private class GroupTitleItemProvider : CollectionPreviewParameterProvider<Boolean>(collection = listOf(true, false))
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="26dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="26"
|
||||
android:viewportHeight="16">
|
||||
<path
|
||||
android:pathData="M3,0L23,0A3,3 0,0 1,26 3L26,13A3,3 0,0 1,23 16L3,16A3,3 0,0 1,0 13L0,3A3,3 0,0 1,3 0z"
|
||||
android:strokeAlpha="0.5"
|
||||
android:fillColor="#474E71"
|
||||
android:fillAlpha="0.4"/>
|
||||
<path
|
||||
android:pathData="M7.521,11.139H5.788L4.488,6.193C4.427,5.966 4.296,5.765 4.103,5.67C3.622,5.431 3.093,5.242 2.515,5.146V4.956H5.306C5.692,4.956 5.981,5.242 6.029,5.574L6.703,9.142L8.435,4.956H10.12L7.521,11.139ZM11.082,11.139H9.445L10.793,4.956H12.429L11.082,11.139ZM14.549,6.668C14.597,6.335 14.886,6.145 15.223,6.145C15.753,6.097 16.33,6.192 16.811,6.43L17.1,5.099C16.619,4.908 16.089,4.813 15.608,4.813C14.02,4.813 12.864,5.669 12.864,6.858C12.864,7.762 13.683,8.237 14.261,8.523C14.886,8.808 15.127,8.998 15.078,9.284C15.078,9.712 14.597,9.902 14.116,9.902C13.538,9.902 12.96,9.759 12.431,9.521L12.142,10.853C12.72,11.091 13.345,11.186 13.924,11.186C15.705,11.233 16.811,10.378 16.811,9.093C16.811,7.476 14.549,7.381 14.549,6.668ZM22.54,11.139L21.241,4.956H19.845C19.556,4.956 19.267,5.146 19.171,5.431L16.764,11.139H18.449L18.785,10.236H20.855L21.048,11.139H22.54ZM20.083,6.621L20.564,8.951H19.216L20.083,6.621Z"
|
||||
android:strokeAlpha="0.5"
|
||||
android:fillColor="#ffffff"
|
||||
android:fillType="evenOdd"
|
||||
android:fillAlpha="0.5"/>
|
||||
</vector>
|
||||
BIN
core/ui/src/main/res/drawable/img_visa_notification.webp
Normal file
BIN
core/ui/src/main/res/drawable/img_visa_notification.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
|
|
@ -155,9 +155,10 @@ internal class DefaultCurrencyChecksRepository(
|
|||
|
||||
override suspend fun getRentExemptionError(
|
||||
userWalletId: UserWalletId,
|
||||
currencyStatus: CryptoCurrencyStatus,
|
||||
currencyStatus: CryptoCurrencyStatus?,
|
||||
balanceAfterTransaction: BigDecimal,
|
||||
): CryptoCurrencyWarning.Rent? {
|
||||
if (currencyStatus == null) return null
|
||||
val rentData = walletManagersFacade.getRentInfo(userWalletId, currencyStatus.currency.network) ?: return null
|
||||
return when {
|
||||
balanceAfterTransaction.isZero() -> null
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
package com.tangem.data.pay
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.Either.Companion.catch
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.blockchain.blockchains.ethereum.Chain
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.core.error.UniversalError
|
||||
import com.tangem.data.common.currency.CryptoCurrencyFactory
|
||||
import com.tangem.data.common.network.NetworkFactory
|
||||
import com.tangem.data.pay.util.TangemPayErrorConverter
|
||||
import com.tangem.datasource.di.NetworkMoshi
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.pay.TangemPayCryptoCurrencyFactory
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val TAG = "TangemPay: DefaultTangemPayCryptoCurrencyFactory"
|
||||
/**
|
||||
* Custom token parameters. Will be used only for F&F.
|
||||
*/
|
||||
private const val TOKEN_ID = "usd-coin"
|
||||
private const val TOKEN_NAME = "USDC"
|
||||
private const val TOKEN_CONTRACT_ADDRESS = "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359"
|
||||
private const val TOKEN_DECIMALS = 6
|
||||
|
||||
internal class DefaultTangemPayCryptoCurrencyFactory @Inject constructor(
|
||||
@NetworkMoshi moshi: Moshi,
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
) : TangemPayCryptoCurrencyFactory {
|
||||
|
||||
private val cryptoCurrencyFactory by lazy(mode = LazyThreadSafetyMode.NONE) {
|
||||
CryptoCurrencyFactory(excludedBlockchains)
|
||||
}
|
||||
private val networkFactory by lazy(mode = LazyThreadSafetyMode.NONE) {
|
||||
NetworkFactory(excludedBlockchains)
|
||||
}
|
||||
|
||||
private val errorConverter by lazy(mode = LazyThreadSafetyMode.NONE) { TangemPayErrorConverter(moshi) }
|
||||
|
||||
override fun create(userWallet: UserWallet, chainId: Int): Either<UniversalError, CryptoCurrency> {
|
||||
return catch {
|
||||
val chain = requireNotNull(Chain.entries.find { it.id == chainId }) { "Can not find chain with $chainId" }
|
||||
val blockchain = requireNotNull(chain.blockchain)
|
||||
val network = networkFactory.create(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = null,
|
||||
userWallet = userWallet,
|
||||
)
|
||||
cryptoCurrencyFactory.createToken(
|
||||
network = requireNotNull(network),
|
||||
rawId = CryptoCurrency.RawID(TOKEN_ID),
|
||||
name = TOKEN_NAME,
|
||||
symbol = TOKEN_NAME,
|
||||
contractAddress = TOKEN_CONTRACT_ADDRESS,
|
||||
decimals = TOKEN_DECIMALS,
|
||||
)
|
||||
}.mapLeft { exception ->
|
||||
Timber.tag(TAG).e(exception)
|
||||
errorConverter.convert(exception)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
package com.tangem.data.pay
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.Either.Companion.catch
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.blockchain.blockchains.ethereum.Chain
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.core.error.UniversalError
|
||||
import com.tangem.data.common.currency.CryptoCurrencyFactory
|
||||
import com.tangem.data.common.network.NetworkFactory
|
||||
import com.tangem.data.pay.util.TangemPayErrorConverter
|
||||
import com.tangem.datasource.di.NetworkMoshi
|
||||
import com.tangem.domain.models.ReceiveAddressModel
|
||||
import com.tangem.domain.models.ReceiveAddressModel.NameService
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.pay.TangemPayTopUpData
|
||||
import com.tangem.domain.pay.TangemPaySwapDataFactory
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val TAG = "TangemPay: DefaultDataForTopUpFactory"
|
||||
/**
|
||||
* Custom token parameters. Will be used only for F&F.
|
||||
*/
|
||||
private const val TOKEN_ID = "usd-coin"
|
||||
private const val TOKEN_NAME = "USDC"
|
||||
private const val TOKEN_CONTRACT_ADDRESS = "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359"
|
||||
private const val TOKEN_DECIMALS = 6
|
||||
|
||||
internal class DefaultTangemPaySwapDataFactory @Inject constructor(
|
||||
@NetworkMoshi moshi: Moshi,
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
) : TangemPaySwapDataFactory {
|
||||
|
||||
private val cryptoCurrencyFactory by lazy(mode = LazyThreadSafetyMode.NONE) {
|
||||
CryptoCurrencyFactory(excludedBlockchains)
|
||||
}
|
||||
private val networkFactory by lazy(mode = LazyThreadSafetyMode.NONE) {
|
||||
NetworkFactory(excludedBlockchains)
|
||||
}
|
||||
private val errorConverter by lazy(mode = LazyThreadSafetyMode.NONE) { TangemPayErrorConverter(moshi) }
|
||||
|
||||
private fun getCurrency(userWallet: UserWallet, chainId: Int): CryptoCurrency {
|
||||
val chain = requireNotNull(Chain.entries.find { it.id == chainId }) { "Can not find chain with $chainId" }
|
||||
val blockchain = requireNotNull(chain.blockchain)
|
||||
val network = networkFactory.create(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = null,
|
||||
userWallet = userWallet,
|
||||
)
|
||||
return cryptoCurrencyFactory.createToken(
|
||||
network = requireNotNull(network),
|
||||
rawId = CryptoCurrency.RawID(TOKEN_ID),
|
||||
name = TOKEN_NAME,
|
||||
symbol = TOKEN_NAME,
|
||||
contractAddress = TOKEN_CONTRACT_ADDRESS,
|
||||
decimals = TOKEN_DECIMALS,
|
||||
)
|
||||
}
|
||||
|
||||
override fun create(
|
||||
userWallet: UserWallet,
|
||||
depositAddress: String,
|
||||
chainId: Int,
|
||||
cryptoBalance: BigDecimal,
|
||||
fiatBalance: BigDecimal,
|
||||
): Either<UniversalError, TangemPayTopUpData> {
|
||||
return catch {
|
||||
val currency = getCurrency(userWallet, chainId)
|
||||
TangemPayTopUpData(
|
||||
currency = currency,
|
||||
walletId = userWallet.walletId,
|
||||
cryptoBalance = cryptoBalance,
|
||||
fiatBalance = fiatBalance,
|
||||
depositAddress = depositAddress,
|
||||
receiveAddress = listOf(ReceiveAddressModel(nameService = NameService.Default, value = depositAddress)),
|
||||
)
|
||||
}.mapLeft { exception ->
|
||||
Timber.tag(TAG).e(exception)
|
||||
errorConverter.convert(exception)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
package com.tangem.data.pay.di
|
||||
|
||||
import com.tangem.data.pay.DefaultTangemPaySwapDataFactory
|
||||
import com.tangem.data.pay.DefaultTangemPayCryptoCurrencyFactory
|
||||
import com.tangem.data.pay.repository.*
|
||||
import com.tangem.data.pay.usecase.DefaultGetTangemPayCurrencyStatusUseCase
|
||||
import com.tangem.data.pay.usecase.DefaultTangemPayWithdrawUseCase
|
||||
import com.tangem.domain.pay.TangemPaySwapDataFactory
|
||||
import com.tangem.domain.pay.TangemPayCryptoCurrencyFactory
|
||||
import com.tangem.domain.pay.repository.*
|
||||
import com.tangem.domain.pay.usecase.ProduceTangemPayInitialDataUseCase
|
||||
import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase
|
||||
|
|
@ -48,7 +48,9 @@ internal interface TangemPayDataModule {
|
|||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindTangemPaySwapDataFactory(factory: DefaultTangemPaySwapDataFactory): TangemPaySwapDataFactory
|
||||
fun bindTangemPayCryptoCurrencyFactory(
|
||||
factory: DefaultTangemPayCryptoCurrencyFactory,
|
||||
): TangemPayCryptoCurrencyFactory
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ internal class DefaultTangemPayCardDetailsRepository @Inject constructor(
|
|||
fiatBalance = result.fiat.availableBalance,
|
||||
currencyCode = result.fiat.currency,
|
||||
cryptoBalance = result.crypto.balance,
|
||||
availableForWithdrawal = result.availableForWithdrawal.amount,
|
||||
chainId = result.crypto.chainId,
|
||||
depositAddress = result.crypto.depositAddress,
|
||||
contractAddress = result.crypto.tokenContractAddress,
|
||||
|
|
|
|||
|
|
@ -78,5 +78,7 @@ sealed interface FeedbackEmailType {
|
|||
val providerName: String,
|
||||
val txId: String,
|
||||
) : Visa()
|
||||
|
||||
data class FeatureIsBeta(override val walletMetaInfo: WalletMetaInfo) : Visa()
|
||||
}
|
||||
}
|
||||
|
|
@ -49,6 +49,10 @@ internal class FeedbackDataBuilder {
|
|||
builder.appendKeyValue("Total saved wallets", userWalletsInfo.totalUserWallets.toString())
|
||||
}
|
||||
|
||||
fun addUserWalletId(userWalletId: String) {
|
||||
builder.appendKeyValue("User Wallet ID", userWalletId)
|
||||
}
|
||||
|
||||
fun addUserWalletMetaInfo(walletMetaInfo: WalletMetaInfo) {
|
||||
builder.appendKeyValue("Mobile Wallet is backed up", walletMetaInfo.hotWalletIsBackedUp?.toString())
|
||||
builder.appendKeyValue("Card ID", walletMetaInfo.cardId)
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ class SendFeedbackEmailUseCase(
|
|||
is FeedbackEmailType.CardAttestationFailed,
|
||||
is FeedbackEmailType.Visa.Dispute,
|
||||
is FeedbackEmailType.Visa.DisputeV2,
|
||||
is FeedbackEmailType.Visa.FeatureIsBeta,
|
||||
-> this
|
||||
is FeedbackEmailType.DirectUserRequest,
|
||||
is FeedbackEmailType.RateCanBeBetter,
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ internal class EmailMessageBodyResolver(
|
|||
is FeedbackEmailType.Visa.Dispute -> addVisaRequestBody(type.walletMetaInfo, type.visaTxDetails)
|
||||
is FeedbackEmailType.Visa.DisputeV2 -> addTangemPayRequestBody(type.walletMetaInfo, type.item)
|
||||
is FeedbackEmailType.Visa.Withdrawal -> addTangemPayWithdrawalRequestBody(type)
|
||||
is FeedbackEmailType.Visa.FeatureIsBeta -> addTangemPayBetaRequestBody(type.walletMetaInfo)
|
||||
}
|
||||
|
||||
return build()
|
||||
|
|
@ -52,6 +53,15 @@ internal class EmailMessageBodyResolver(
|
|||
addTangemPayTxInfo(item)
|
||||
}
|
||||
|
||||
private fun FeedbackDataBuilder.addTangemPayBetaRequestBody(walletMetaInfo: WalletMetaInfo) {
|
||||
addPhoneInfoBody()
|
||||
addDelimiter()
|
||||
walletMetaInfo.userWalletId?.let { userWalletId ->
|
||||
addUserWalletId(userWalletId = userWalletId.stringValue)
|
||||
addDelimiter()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun FeedbackDataBuilder.addTangemPayWithdrawalRequestBody(
|
||||
type: FeedbackEmailType.Visa.Withdrawal,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ internal class EmailMessageTitleResolver(private val resources: Resources) {
|
|||
is FeedbackEmailType.Visa.DisputeV2,
|
||||
is FeedbackEmailType.Visa.FailedIssueCard,
|
||||
is FeedbackEmailType.Visa.Withdrawal,
|
||||
is FeedbackEmailType.Visa.FeatureIsBeta,
|
||||
is FeedbackEmailType.PreActivatedWallet,
|
||||
-> R.string.feedback_preface_support
|
||||
is FeedbackEmailType.RateCanBeBetter -> R.string.feedback_preface_rate_negative
|
||||
is FeedbackEmailType.ScanningProblem -> R.string.feedback_preface_scan_failed
|
||||
|
|
@ -33,7 +35,6 @@ internal class EmailMessageTitleResolver(private val resources: Resources) {
|
|||
is FeedbackEmailType.StakingProblem,
|
||||
is FeedbackEmailType.SwapProblem,
|
||||
-> R.string.feedback_preface_tx_failed
|
||||
is FeedbackEmailType.PreActivatedWallet -> R.string.feedback_preface_support
|
||||
}
|
||||
|
||||
return resources.getStringSafe(resId)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import com.tangem.domain.feedback.models.FeedbackEmailType
|
|||
internal class EmailSubjectResolver(private val resources: Resources) {
|
||||
|
||||
/** Resolve email message body by [type] */
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
fun resolve(type: FeedbackEmailType): String {
|
||||
return when (type) {
|
||||
is FeedbackEmailType.DirectUserRequest -> {
|
||||
|
|
@ -43,8 +44,8 @@ internal class EmailSubjectResolver(private val resources: Resources) {
|
|||
is FeedbackEmailType.Visa.Dispute,
|
||||
is FeedbackEmailType.Visa.DisputeV2,
|
||||
-> "[Visa] [DISPUTE] {auto-filled subject}"
|
||||
is FeedbackEmailType.Visa.Withdrawal,
|
||||
-> "[Visa] [WITHDRAWAL] {auto-filled subject}"
|
||||
is FeedbackEmailType.Visa.Withdrawal -> "[Visa] [WITHDRAWAL] {auto-filled subject}"
|
||||
is FeedbackEmailType.Visa.FeatureIsBeta -> "[VISA] [FEEDBACK]"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,9 +14,11 @@ class GetCurrencyCheckUseCase(
|
|||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) {
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
suspend operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
currencyStatus: CryptoCurrencyStatus,
|
||||
feeCurrencyStatus: CryptoCurrencyStatus?,
|
||||
amount: BigDecimal?,
|
||||
fee: BigDecimal?,
|
||||
feeCurrencyBalanceAfterTransaction: BigDecimal?,
|
||||
|
|
@ -31,7 +33,7 @@ class GetCurrencyCheckUseCase(
|
|||
val existentialDeposit = currencyChecksRepository.getExistentialDeposit(userWalletId, network)
|
||||
val rentWarning = currencyChecksRepository.getRentExemptionError(
|
||||
userWalletId = userWalletId,
|
||||
currencyStatus = currencyStatus,
|
||||
currencyStatus = feeCurrencyStatus,
|
||||
balanceAfterTransaction = feeCurrencyBalanceAfterTransaction ?: BigDecimal.ZERO,
|
||||
)
|
||||
val isAccountFunded = recipientAddress?.let {
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ interface CurrencyChecksRepository {
|
|||
*/
|
||||
suspend fun getRentExemptionError(
|
||||
userWalletId: UserWalletId,
|
||||
currencyStatus: CryptoCurrencyStatus,
|
||||
currencyStatus: CryptoCurrencyStatus?,
|
||||
balanceAfterTransaction: BigDecimal,
|
||||
): CryptoCurrencyWarning.Rent?
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.domain.pay
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.core.error.UniversalError
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
|
||||
interface TangemPayCryptoCurrencyFactory {
|
||||
|
||||
fun create(userWallet: UserWallet, chainId: Int): Either<UniversalError, CryptoCurrency>
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ data class TangemPayCardBalance(
|
|||
val fiatBalance: BigDecimal,
|
||||
val currencyCode: String,
|
||||
val cryptoBalance: BigDecimal,
|
||||
val availableForWithdrawal: BigDecimal,
|
||||
val chainId: Int,
|
||||
val depositAddress: String?,
|
||||
val contractAddress: String,
|
||||
|
|
|
|||
|
|
@ -1,24 +1,10 @@
|
|||
package com.tangem.domain.pay
|
||||
package com.tangem.domain.pay.model
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.core.error.UniversalError
|
||||
import com.tangem.domain.models.ReceiveAddressModel
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import java.math.BigDecimal
|
||||
|
||||
interface TangemPaySwapDataFactory {
|
||||
|
||||
fun create(
|
||||
userWallet: UserWallet,
|
||||
depositAddress: String,
|
||||
chainId: Int,
|
||||
cryptoBalance: BigDecimal,
|
||||
fiatBalance: BigDecimal,
|
||||
): Either<UniversalError, TangemPayTopUpData>
|
||||
}
|
||||
|
||||
data class TangemPayTopUpData(
|
||||
val walletId: UserWalletId,
|
||||
val currency: CryptoCurrency,
|
||||
|
|
@ -87,7 +87,7 @@ internal class ItemsBuilder @Inject constructor(private val router: Router) {
|
|||
DetailsItemUM.Basic.Item(
|
||||
id = "support_email",
|
||||
block = BlockUM(
|
||||
text = resourceReference(R.string.details_row_title_contact_to_support),
|
||||
text = resourceReference(R.string.common_contact_support),
|
||||
iconRes = R.drawable.ic_comment_24,
|
||||
onClick = onSupportEmailClick,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -20,28 +20,26 @@ import androidx.compose.ui.graphics.Color
|
|||
import androidx.compose.ui.layout.onGloballyPositioned
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import com.tangem.common.ui.charts.state.MarketChartDataProducer
|
||||
import com.tangem.core.ui.components.*
|
||||
import com.tangem.core.ui.components.appbar.TangemTopAppBar
|
||||
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.components.buttons.segmentedbutton.SegmentedButtons
|
||||
import com.tangem.core.ui.components.currency.icon.CoinIcon
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeInPercent
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.event.EventEffect
|
||||
import com.tangem.core.ui.event.StateEvent
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.domain.markets.PriceChangeInterval
|
||||
import com.tangem.features.markets.details.impl.ui.components.*
|
||||
import com.tangem.features.markets.details.impl.ui.preview.MarketsTokenDetailsPreview
|
||||
import com.tangem.features.markets.details.impl.ui.state.ExchangesBottomSheetContent
|
||||
import com.tangem.features.markets.details.impl.ui.state.InfoBottomSheetContent
|
||||
import com.tangem.features.markets.details.impl.ui.state.MarketsTokenDetailsUM
|
||||
|
|
@ -317,36 +315,16 @@ fun PriceChangeInterval.getText(): TextReference {
|
|||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
// region Preview
|
||||
@Composable
|
||||
private fun Preview() {
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
private fun MarketsTokenDetailsContent_Preview(
|
||||
@PreviewParameter(MarketsTokenDetailsContentPreviewProvider::class) params: MarketsTokenDetailsUM,
|
||||
) {
|
||||
TangemThemePreview {
|
||||
MarketsTokenDetailsContent(
|
||||
state = MarketsTokenDetailsUM(
|
||||
tokenName = "Token Name",
|
||||
priceText = "$0.00000000324",
|
||||
dateTimeText = stringReference("Today"),
|
||||
priceChangePercentText = "52.00%",
|
||||
iconUrl = "",
|
||||
priceChangeType = PriceChangeType.UP,
|
||||
chartState = MarketsTokenDetailsUM.ChartState(
|
||||
dataProducer = MarketChartDataProducer.build { },
|
||||
onLoadRetryClick = {},
|
||||
status = MarketsTokenDetailsUM.ChartState.Status.LOADING,
|
||||
onMarkerPointSelected = { _, _ -> },
|
||||
),
|
||||
selectedInterval = PriceChangeInterval.H24,
|
||||
onSelectedIntervalChange = { },
|
||||
body = MarketsTokenDetailsUM.Body.Loading,
|
||||
bottomSheetConfig = TangemBottomSheetConfig(
|
||||
isShown = false,
|
||||
onDismissRequest = {},
|
||||
content = TangemBottomSheetConfigContent.Empty,
|
||||
),
|
||||
markerSet = false,
|
||||
triggerPriceChange = consumedEvent(),
|
||||
),
|
||||
state = params,
|
||||
onHeaderSizeChange = {},
|
||||
onBackClick = {},
|
||||
backgroundColor = TangemTheme.colors.background.tertiary,
|
||||
|
|
@ -356,4 +334,13 @@ private fun Preview() {
|
|||
addTopBarStatusBarPadding = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class MarketsTokenDetailsContentPreviewProvider : PreviewParameterProvider<MarketsTokenDetailsUM> {
|
||||
override val values: Sequence<MarketsTokenDetailsUM>
|
||||
get() = sequenceOf(
|
||||
MarketsTokenDetailsPreview.loadingState,
|
||||
MarketsTokenDetailsPreview.contentState,
|
||||
)
|
||||
}
|
||||
// endregion
|
||||
|
|
@ -8,6 +8,7 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.block.information.GridItems
|
||||
import com.tangem.core.ui.components.block.information.InformationBlock
|
||||
|
|
@ -51,12 +52,16 @@ internal fun InsightsBlock(state: InsightsUM, modifier: Modifier = Modifier) {
|
|||
currentInterval = it
|
||||
state.onIntervalChanged(it)
|
||||
},
|
||||
modifier = Modifier.width(IntrinsicSize.Min),
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.align(Alignment.Center)
|
||||
.padding(vertical = TangemTheme.dimens.spacing4),
|
||||
.padding(
|
||||
horizontal = 14.dp,
|
||||
vertical = 4.dp,
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
|
|
|
|||
|
|
@ -57,12 +57,16 @@ internal fun PricePerformanceBlock(state: PricePerformanceUM, modifier: Modifier
|
|||
currentInterval = it
|
||||
state.onIntervalChanged(it)
|
||||
},
|
||||
modifier = Modifier.width(IntrinsicSize.Min),
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.align(Alignment.Center)
|
||||
.padding(vertical = TangemTheme.dimens.spacing4),
|
||||
.padding(
|
||||
horizontal = 14.dp,
|
||||
vertical = TangemTheme.dimens.spacing4,
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,129 @@
|
|||
package com.tangem.features.markets.details.impl.ui.preview
|
||||
|
||||
import com.tangem.common.ui.charts.state.MarketChartDataProducer
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.domain.markets.PriceChangeInterval
|
||||
import com.tangem.features.markets.details.impl.ui.state.*
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
internal object MarketsTokenDetailsPreview {
|
||||
private val infoPoint = InfoPointUM(
|
||||
title = stringReference("1"),
|
||||
value = "2",
|
||||
change = InfoPointUM.ChangeType.DOWN,
|
||||
onInfoClick = {},
|
||||
)
|
||||
|
||||
val loadingState = MarketsTokenDetailsUM(
|
||||
tokenName = "Token Name",
|
||||
priceText = "$0.00000000324",
|
||||
dateTimeText = stringReference("Today"),
|
||||
priceChangePercentText = "52.00%",
|
||||
iconUrl = "",
|
||||
priceChangeType = PriceChangeType.UP,
|
||||
chartState = MarketsTokenDetailsUM.ChartState(
|
||||
dataProducer = MarketChartDataProducer.build { },
|
||||
onLoadRetryClick = {},
|
||||
status = MarketsTokenDetailsUM.ChartState.Status.LOADING,
|
||||
onMarkerPointSelected = { _, _ -> },
|
||||
),
|
||||
selectedInterval = PriceChangeInterval.H24,
|
||||
onSelectedIntervalChange = { },
|
||||
body = MarketsTokenDetailsUM.Body.Loading,
|
||||
bottomSheetConfig = TangemBottomSheetConfig(
|
||||
isShown = false,
|
||||
onDismissRequest = {},
|
||||
content = TangemBottomSheetConfigContent.Empty,
|
||||
),
|
||||
markerSet = false,
|
||||
triggerPriceChange = consumedEvent(),
|
||||
)
|
||||
|
||||
val contentState = MarketsTokenDetailsUM(
|
||||
tokenName = "Token Name",
|
||||
priceText = "$0.00000000324",
|
||||
dateTimeText = stringReference("Today"),
|
||||
priceChangePercentText = "52.00%",
|
||||
iconUrl = "",
|
||||
priceChangeType = PriceChangeType.UP,
|
||||
chartState = MarketsTokenDetailsUM.ChartState(
|
||||
dataProducer = MarketChartDataProducer.build { },
|
||||
onLoadRetryClick = {},
|
||||
status = MarketsTokenDetailsUM.ChartState.Status.LOADING,
|
||||
onMarkerPointSelected = { _, _ -> },
|
||||
),
|
||||
selectedInterval = PriceChangeInterval.H24,
|
||||
onSelectedIntervalChange = { },
|
||||
body = MarketsTokenDetailsUM.Body.Content(
|
||||
description = MarketsTokenDetailsUM.Description(
|
||||
shortDescription = stringReference("markets_token_details_description_short"),
|
||||
fullDescription = stringReference("markets_token_details_description_full"),
|
||||
onReadMoreClick = {},
|
||||
),
|
||||
infoBlocks = MarketsTokenDetailsUM.InformationBlocks(
|
||||
insights = InsightsUM(
|
||||
h24Info = persistentListOf(
|
||||
infoPoint,
|
||||
infoPoint,
|
||||
infoPoint,
|
||||
),
|
||||
weekInfo = persistentListOf(
|
||||
infoPoint,
|
||||
infoPoint,
|
||||
infoPoint,
|
||||
),
|
||||
monthInfo = persistentListOf(
|
||||
infoPoint,
|
||||
infoPoint,
|
||||
infoPoint,
|
||||
),
|
||||
onInfoClick = {},
|
||||
onIntervalChanged = {},
|
||||
),
|
||||
securityScore = SecurityScoreUM(
|
||||
score = 2.3f,
|
||||
description = stringReference("markets_token_details_security_score_description"),
|
||||
onInfoClick = {},
|
||||
),
|
||||
metrics = MetricsUM(
|
||||
metrics = persistentListOf(
|
||||
infoPoint,
|
||||
infoPoint,
|
||||
infoPoint,
|
||||
),
|
||||
),
|
||||
pricePerformance = PricePerformanceUM(
|
||||
h24 = PricePerformanceUM.Value(
|
||||
low = "1",
|
||||
high = "2",
|
||||
indicatorFraction = 0.3f,
|
||||
),
|
||||
month = PricePerformanceUM.Value(
|
||||
low = "1",
|
||||
high = "2",
|
||||
indicatorFraction = 0.3f,
|
||||
),
|
||||
all = PricePerformanceUM.Value(
|
||||
low = "1",
|
||||
high = "2",
|
||||
indicatorFraction = 0.3f,
|
||||
),
|
||||
onIntervalChanged = {},
|
||||
),
|
||||
listedOn = ListedOnUM.Empty,
|
||||
links = null,
|
||||
),
|
||||
),
|
||||
bottomSheetConfig = TangemBottomSheetConfig(
|
||||
isShown = false,
|
||||
onDismissRequest = {},
|
||||
content = TangemBottomSheetConfigContent.Empty,
|
||||
),
|
||||
markerSet = true,
|
||||
triggerPriceChange = consumedEvent(),
|
||||
)
|
||||
}
|
||||
|
|
@ -17,14 +17,15 @@ internal class OnrampOffersStateFactory(
|
|||
fun getOffersState(offers: List<OnrampOffersBlock>): OnrampV2MainComponentUM {
|
||||
val currentState = currentStateProvider.invoke()
|
||||
return when (currentState) {
|
||||
is OnrampV2MainComponentUM.InitialLoading -> currentState
|
||||
is OnrampV2MainComponentUM.Content -> {
|
||||
if (currentState.offersBlockState is OnrampOffersBlockUM.Loading && offers.isEmpty()) {
|
||||
return currentState
|
||||
}
|
||||
currentState.copy(
|
||||
offersBlockState = mapOnrampOffersBlockToUM(offersBlocks = offers),
|
||||
)
|
||||
}
|
||||
is OnrampV2MainComponentUM.InitialLoading -> {
|
||||
currentState
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ internal class OnrampV2AmountStateFactory(
|
|||
currencySymbol = currency.unit,
|
||||
onAmountValueChanged = onrampIntents::onAmountValueChanged,
|
||||
),
|
||||
offersBlockState = OnrampOffersBlockUM.Loading,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -523,6 +523,7 @@ internal class SendConfirmModel @Inject constructor(
|
|||
params.callback.onResult(
|
||||
state.copy(
|
||||
navigationUM = NavigationUM.Content(
|
||||
source = CommonSendRoute.Confirm.javaClass.simpleName,
|
||||
title = resourceReference(id = R.string.common_send),
|
||||
subtitle = null,
|
||||
backIconRes = when (confirmUM) {
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ internal class SendConfirmSuccessModel @Inject constructor(
|
|||
params.callback.onResult(
|
||||
state.copy(
|
||||
navigationUM = NavigationUM.Content(
|
||||
source = CommonSendRoute.ConfirmSuccess.javaClass.simpleName,
|
||||
title = stringReference(""),
|
||||
subtitle = null,
|
||||
backIconRes = R.drawable.ic_close_24,
|
||||
|
|
|
|||
|
|
@ -371,11 +371,12 @@ internal class NFTSendConfirmModel @Inject constructor(
|
|||
flow = uiState,
|
||||
flow2 = params.currentRoute,
|
||||
transform = { state, route -> state to route },
|
||||
).onEach { (state, _) ->
|
||||
).onEach { (state, route) ->
|
||||
val confirmUM = state.confirmUM
|
||||
params.callback.onResult(
|
||||
state.copy(
|
||||
navigationUM = NavigationUM.Content(
|
||||
source = CommonSendRoute.Confirm.javaClass.simpleName,
|
||||
title = resourceReference(R.string.nft_send),
|
||||
subtitle = null,
|
||||
backIconRes = when (confirmUM) {
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ internal class NFTSendSuccessModel @Inject constructor(
|
|||
params.callback.onResult(
|
||||
state.copy(
|
||||
navigationUM = NavigationUM.Content(
|
||||
source = CommonSendRoute.ConfirmSuccess.javaClass.simpleName,
|
||||
title = stringReference(""),
|
||||
subtitle = null,
|
||||
backIconRes = R.drawable.ic_close_24,
|
||||
|
|
|
|||
|
|
@ -378,6 +378,7 @@ internal class SendAmountModel @Inject constructor(
|
|||
setSendWithSwapAvailability()
|
||||
params.callback.onNavigationResult(
|
||||
NavigationUM.Content(
|
||||
source = CommonSendRoute.Amount::class.java.simpleName,
|
||||
title = resourceReference(R.string.send_amount_label),
|
||||
subtitle = null,
|
||||
backIconRes = if (route.isEditMode) {
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import com.tangem.features.send.v2.api.entity.PredefinedValues
|
|||
import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationComponentParams
|
||||
import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationComponentParams.DestinationBlockParams
|
||||
import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM
|
||||
import com.tangem.features.send.v2.common.CommonSendRoute
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.subcomponents.destination.analytics.EnterAddressSource
|
||||
import com.tangem.features.send.v2.subcomponents.destination.analytics.SendDestinationAnalyticEvents
|
||||
|
|
@ -359,6 +360,7 @@ internal class SendDestinationModel @Inject constructor(
|
|||
).onEach { (state, route) ->
|
||||
params.callback.onNavigationResult(
|
||||
NavigationUM.Content(
|
||||
source = CommonSendRoute.Destination::class.java.simpleName,
|
||||
title = params.title,
|
||||
subtitle = null,
|
||||
backIconRes = if (route.isEditMode) {
|
||||
|
|
|
|||
|
|
@ -136,6 +136,7 @@ internal class NotificationsModel @Inject constructor(
|
|||
val currencyCheck = getCurrencyCheckUseCase(
|
||||
userWalletId = userWalletId,
|
||||
currencyStatus = cryptoCurrencyStatus,
|
||||
feeCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
amount = sendingAmount,
|
||||
fee = feeValue,
|
||||
recipientAddress = destinationAddress,
|
||||
|
|
|
|||
|
|
@ -772,6 +772,7 @@ internal class StakingModel @Inject constructor(
|
|||
val currencyStatus = getCurrencyCheckUseCase(
|
||||
userWalletId = userWalletId,
|
||||
currencyStatus = cryptoCurrencyStatus,
|
||||
feeCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
amount = amount,
|
||||
fee = fee,
|
||||
feeCurrencyBalanceAfterTransaction = balanceAfterTransaction,
|
||||
|
|
|
|||
|
|
@ -720,6 +720,7 @@ internal class SwapAmountModel @Inject constructor(
|
|||
).filter { (_, route) -> route is SendWithSwapRoute.Amount }.onEach { (state, route) ->
|
||||
params.callback.onNavigationResult(
|
||||
NavigationUM.Content(
|
||||
source = SendWithSwapRoute.Amount::class.java.simpleName,
|
||||
title = resourceReference(R.string.common_amount),
|
||||
subtitle = null,
|
||||
backIconRes = if (route.isEditMode) {
|
||||
|
|
|
|||
|
|
@ -428,6 +428,7 @@ internal class SendWithSwapConfirmModel @Inject constructor(
|
|||
route = SendWithSwapRoute.Confirm,
|
||||
sendWithSwapUM = state.copy(
|
||||
navigationUM = NavigationUM.Content(
|
||||
source = SendWithSwapRoute.Confirm.javaClass.simpleName,
|
||||
title = resourceReference(id = R.string.send_with_swap_confirm_title),
|
||||
subtitle = null,
|
||||
backIconRes = R.drawable.ic_back_24,
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import com.tangem.core.ui.extensions.TextReference
|
|||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.features.swap.v2.impl.R
|
||||
import com.tangem.features.swap.v2.impl.common.entity.ConfirmUM
|
||||
import com.tangem.features.swap.v2.impl.sendviaswap.SendWithSwapRoute
|
||||
import com.tangem.features.swap.v2.impl.sendviaswap.entity.SendWithSwapUM
|
||||
import com.tangem.features.swap.v2.impl.sendviaswap.success.SendWithSwapSuccessComponent
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
|
@ -40,6 +41,7 @@ internal class SendWithSwapSuccessModel @Inject constructor(
|
|||
private fun configConfirmSuccessNavigation() {
|
||||
params.callback.onNavigationResult(
|
||||
NavigationUM.Content(
|
||||
source = SendWithSwapRoute.Success.javaClass.simpleName,
|
||||
title = TextReference.EMPTY,
|
||||
subtitle = null,
|
||||
backIconRes = R.drawable.ic_close_24,
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM
|
|||
import com.tangem.features.swap.v2.impl.amount.ui.preview.SwapAmountContentPreview
|
||||
import com.tangem.features.swap.v2.impl.common.entity.ConfirmUM
|
||||
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
|
||||
import com.tangem.features.swap.v2.impl.sendviaswap.SendWithSwapRoute
|
||||
import com.tangem.features.swap.v2.impl.sendviaswap.entity.SendWithSwapUM
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -368,6 +369,7 @@ private fun SendWithSwapSuccessContent_Preview() {
|
|||
),
|
||||
),
|
||||
navigationUM = NavigationUM.Content(
|
||||
source = SendWithSwapRoute.Success.javaClass.simpleName,
|
||||
title = TextReference.EMPTY,
|
||||
subtitle = null,
|
||||
backIconRes = R.drawable.ic_close_24,
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ internal class SwapInteractorImpl @AssistedInject constructor(
|
|||
private val amountFormatter: AmountFormatter,
|
||||
private val rampStateManager: RampStateManager,
|
||||
private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
|
||||
private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase,
|
||||
private val accountsFeatureToggles: AccountsFeatureToggles,
|
||||
@Assisted private val userWalletId: UserWalletId,
|
||||
) : SwapInteractor {
|
||||
|
|
@ -631,9 +632,14 @@ internal class SwapInteractorImpl @AssistedInject constructor(
|
|||
} else {
|
||||
amount
|
||||
}
|
||||
val feePaidCurrencyStatus = getFeePaidCryptoCurrencyStatusSyncUseCase(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencyStatus = fromTokenStatus,
|
||||
).getOrNull()
|
||||
val currencyCheck = getCurrencyCheckUseCase(
|
||||
userWalletId = userWalletId,
|
||||
currencyStatus = fromTokenStatus,
|
||||
feeCurrencyStatus = feePaidCurrencyStatus,
|
||||
amount = amountToRequest.value,
|
||||
fee = fee,
|
||||
feeCurrencyBalanceAfterTransaction = balanceAfterTransaction,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import com.tangem.core.decompose.context.AppComponentContext
|
|||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.pay.TangemPayTopUpData
|
||||
import com.tangem.domain.pay.model.TangemPayTopUpData
|
||||
import com.tangem.features.tangempay.model.TangemPayAddFundsModel
|
||||
import com.tangem.features.tangempay.ui.TangemPayAddFundsContent
|
||||
import java.math.BigDecimal
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
package com.tangem.features.tangempay.entity
|
||||
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
|
||||
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
|
||||
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenuItem
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.themedColor
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
|
@ -21,6 +23,7 @@ internal class TangemPayDetailsStateFactory(
|
|||
private val converter: TangemPayCardFrozenStateConverter,
|
||||
) {
|
||||
|
||||
@Suppress("LongMethod")
|
||||
fun getInitialState(): TangemPayDetailsUM {
|
||||
val cardFrozenStateItem = when (cardFrozenState) {
|
||||
is TangemPayCardFrozenState.Pending -> null
|
||||
|
|
@ -88,6 +91,16 @@ internal class TangemPayDetailsStateFactory(
|
|||
isBalanceHidden = false,
|
||||
addFundsEnabled = true,
|
||||
cardFrozenState = converter.convert(cardFrozenState),
|
||||
betaNotificationConfig = NotificationConfig(
|
||||
title = resourceReference(R.string.tangem_pay_beta_notification_title),
|
||||
subtitle = resourceReference(R.string.tangem_pay_beta_notification_subtitle),
|
||||
iconResId = R.drawable.img_visa_notification,
|
||||
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(R.string.common_contact_support),
|
||||
onClick = intents::onContactSupportClicked,
|
||||
),
|
||||
iconSize = 36.dp,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.features.tangempay.entity
|
|||
|
||||
import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
|
||||
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.visa.model.TangemPayCardFrozenState
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
|
@ -14,6 +15,7 @@ internal data class TangemPayDetailsUM(
|
|||
val isBalanceHidden: Boolean,
|
||||
val addFundsEnabled: Boolean,
|
||||
val cardFrozenState: CardFrozenState,
|
||||
val betaNotificationConfig: NotificationConfig,
|
||||
)
|
||||
|
||||
internal data class TangemPayCardDetailsUM(
|
||||
|
|
|
|||
|
|
@ -4,7 +4,10 @@ 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.domain.pay.TangemPaySwapDataFactory
|
||||
import com.tangem.domain.models.ReceiveAddressModel
|
||||
import com.tangem.domain.models.ReceiveAddressModel.NameService
|
||||
import com.tangem.domain.pay.TangemPayCryptoCurrencyFactory
|
||||
import com.tangem.domain.pay.model.TangemPayTopUpData
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.features.tangempay.components.TangemPayAddFundsComponent
|
||||
import com.tangem.features.tangempay.entity.TangemPayAddFundsUM
|
||||
|
|
@ -17,7 +20,7 @@ import javax.inject.Inject
|
|||
internal class TangemPayAddFundsModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val tangemPaySwapDataFactory: TangemPaySwapDataFactory,
|
||||
private val tangemPayCryptoCurrencyFactory: TangemPayCryptoCurrencyFactory,
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||
) : Model() {
|
||||
|
||||
|
|
@ -26,17 +29,25 @@ internal class TangemPayAddFundsModel @Inject constructor(
|
|||
val uiState: TangemPayAddFundsUM = getInitialState()
|
||||
|
||||
private fun getInitialState(): TangemPayAddFundsUM {
|
||||
val userWallet = requireNotNull(
|
||||
getUserWalletUseCase(params.walletId).getOrNull(),
|
||||
) { "User wallet not found for id: ${params.walletId}" }
|
||||
val data = tangemPaySwapDataFactory.create(
|
||||
userWallet = userWallet,
|
||||
depositAddress = params.depositAddress,
|
||||
chainId = params.chainId,
|
||||
cryptoBalance = params.cryptoBalance,
|
||||
fiatBalance = params.fiatBalance,
|
||||
).getOrNull()
|
||||
|
||||
val userWallet = getUserWalletUseCase(params.walletId).getOrNull()
|
||||
val currency = userWallet?.let {
|
||||
tangemPayCryptoCurrencyFactory.create(userWallet = userWallet, chainId = params.chainId).getOrNull()
|
||||
}
|
||||
val data = currency?.let {
|
||||
TangemPayTopUpData(
|
||||
currency = currency,
|
||||
walletId = params.walletId,
|
||||
cryptoBalance = params.cryptoBalance,
|
||||
fiatBalance = params.fiatBalance,
|
||||
depositAddress = params.depositAddress,
|
||||
receiveAddress = listOf(
|
||||
ReceiveAddressModel(
|
||||
nameService = NameService.Default,
|
||||
value = params.depositAddress,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
return TangemPayAddFundsUMConverter(listener = params.listener).convert(data)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,10 +17,13 @@ import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfi
|
|||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.message.SnackbarMessage
|
||||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
||||
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||
import com.tangem.domain.feedback.models.WalletMetaInfo
|
||||
import com.tangem.domain.models.TokenReceiveConfig
|
||||
import com.tangem.domain.pay.TangemPaySwapDataFactory
|
||||
import com.tangem.domain.pay.TangemPayTopUpData
|
||||
import com.tangem.domain.pay.TangemPayCryptoCurrencyFactory
|
||||
import com.tangem.domain.pay.model.TangemPayCardBalance
|
||||
import com.tangem.domain.pay.model.TangemPayTopUpData
|
||||
import com.tangem.domain.pay.repository.CustomerOrderRepository
|
||||
import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository
|
||||
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
|
||||
|
|
@ -70,9 +73,10 @@ internal class TangemPayDetailsModel @Inject constructor(
|
|||
private val uiMessageSender: UiMessageSender,
|
||||
private val cardDetailsEventListener: CardDetailsEventListener,
|
||||
private val txHistoryUpdateListener: TangemPayTxHistoryUpdateListener,
|
||||
private val tangemPaySwapDataFactory: TangemPaySwapDataFactory,
|
||||
private val tangemPayCryptoCurrencyFactory: TangemPayCryptoCurrencyFactory,
|
||||
private val orderRepository: CustomerOrderRepository,
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
|
||||
) : Model(), TangemPayTxHistoryUiActions, TangemPayDetailIntents, AddFundsListener {
|
||||
|
||||
private val params: TangemPayDetailsContainerComponent.Params = paramsContainer.require()
|
||||
|
|
@ -236,27 +240,22 @@ internal class TangemPayDetailsModel @Inject constructor(
|
|||
if (hasActiveWithdrawal) {
|
||||
showBottomSheetError(TangemPayDetailsErrorType.WithdrawInProgress)
|
||||
} else {
|
||||
val userWallet = requireNotNull(
|
||||
getUserWalletUseCase(params.userWalletId).getOrNull(),
|
||||
) { "User wallet not found: ${params.userWalletId}" }
|
||||
val data = tangemPaySwapDataFactory.create(
|
||||
userWallet = userWallet,
|
||||
depositAddress = depositAddress,
|
||||
chainId = params.config.chainId,
|
||||
cryptoBalance = currentBalance.cryptoBalance,
|
||||
fiatBalance = currentBalance.fiatBalance,
|
||||
).getOrNull()
|
||||
if (data != null) {
|
||||
val userWallet = getUserWalletUseCase(params.userWalletId).getOrNull()
|
||||
val currency = userWallet?.let {
|
||||
tangemPayCryptoCurrencyFactory.create(userWallet = userWallet, chainId = params.config.chainId)
|
||||
.getOrNull()
|
||||
}
|
||||
if (currency != null) {
|
||||
router.push(
|
||||
AppRoute.Swap(
|
||||
currencyFrom = data.currency,
|
||||
userWalletId = data.walletId,
|
||||
currencyFrom = currency,
|
||||
userWalletId = params.userWalletId,
|
||||
isInitialReverseOrder = false,
|
||||
screenSource = AnalyticsParam.ScreensSources.TangemPay.value,
|
||||
tangemPayInput = AppRoute.Swap.TangemPayInput(
|
||||
cryptoAmount = data.cryptoBalance,
|
||||
fiatAmount = data.fiatBalance,
|
||||
depositAddress = data.depositAddress,
|
||||
cryptoAmount = currentBalance.availableForWithdrawal,
|
||||
fiatAmount = currentBalance.availableForWithdrawal,
|
||||
depositAddress = depositAddress,
|
||||
isWithdrawal = true,
|
||||
),
|
||||
),
|
||||
|
|
@ -277,7 +276,13 @@ internal class TangemPayDetailsModel @Inject constructor(
|
|||
Timber.e(e)
|
||||
return@launch
|
||||
}
|
||||
uiState.update(DetailsBalanceTransformer(balance = result))
|
||||
uiState.update(
|
||||
transformer = DetailsBalanceTransformer(
|
||||
balance = result,
|
||||
userWallet = getUserWalletUseCase(params.userWalletId).getOrNull(),
|
||||
cryptoCurrencyFactory = tangemPayCryptoCurrencyFactory,
|
||||
),
|
||||
)
|
||||
}.saveIn(fetchBalanceJobHolder)
|
||||
}
|
||||
|
||||
|
|
@ -305,6 +310,16 @@ internal class TangemPayDetailsModel @Inject constructor(
|
|||
}.saveIn(addToWalletBannerJobHolder)
|
||||
}
|
||||
|
||||
override fun onContactSupportClicked() {
|
||||
modelScope.launch {
|
||||
sendFeedbackEmailUseCase.invoke(
|
||||
type = FeedbackEmailType.Visa.FeatureIsBeta(
|
||||
walletMetaInfo = WalletMetaInfo(userWalletId = params.userWalletId),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRefreshSwipe(refreshState: ShowRefreshState) {
|
||||
modelScope.launch {
|
||||
uiState.update(TangemPayDetailsRefreshTransformer(isRefreshing = refreshState.value))
|
||||
|
|
|
|||
|
|
@ -2,17 +2,24 @@ package com.tangem.features.tangempay.model.transformers
|
|||
|
||||
import arrow.core.Either
|
||||
import com.tangem.core.error.UniversalError
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.pay.TangemPayCryptoCurrencyFactory
|
||||
import com.tangem.domain.pay.model.TangemPayCardBalance
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsBalanceBlockState
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsUM
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import java.math.BigDecimal
|
||||
import java.util.Currency
|
||||
|
||||
internal class DetailsBalanceTransformer(
|
||||
private val balance: Either<UniversalError, TangemPayCardBalance>,
|
||||
private val cryptoCurrencyFactory: TangemPayCryptoCurrencyFactory,
|
||||
private val userWallet: UserWallet?,
|
||||
) : Transformer<TangemPayDetailsUM> {
|
||||
|
||||
override fun transform(prevState: TangemPayDetailsUM): TangemPayDetailsUM {
|
||||
|
|
@ -21,22 +28,32 @@ internal class DetailsBalanceTransformer(
|
|||
TangemPayDetailsBalanceBlockState.Error(actionButtons = persistentListOf())
|
||||
}
|
||||
is Either.Right<TangemPayCardBalance> -> {
|
||||
TangemPayDetailsBalanceBlockState.Content(
|
||||
isBalanceFlickering = false,
|
||||
fiatBalance = getBalanceText(balance.value),
|
||||
// TODO [REDACTED_TASK_KEY]: Add crypto balance when the BFF is ready
|
||||
cryptoBalance = "",
|
||||
actionButtons = prevState.balanceBlockState.actionButtons,
|
||||
)
|
||||
val cryptoCurrency = userWallet?.let {
|
||||
cryptoCurrencyFactory.create(userWallet, balance.value.chainId).getOrNull()
|
||||
}
|
||||
if (cryptoCurrency == null) {
|
||||
TangemPayDetailsBalanceBlockState.Error(actionButtons = persistentListOf())
|
||||
} else {
|
||||
TangemPayDetailsBalanceBlockState.Content(
|
||||
isBalanceFlickering = false,
|
||||
fiatBalance = getFiatBalanceText(balance.value),
|
||||
cryptoBalance = getCryptoBalanceText(balance.value.cryptoBalance, cryptoCurrency),
|
||||
actionButtons = prevState.balanceBlockState.actionButtons,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return prevState.copy(balanceBlockState = balance)
|
||||
}
|
||||
|
||||
private fun getBalanceText(balance: TangemPayCardBalance): String {
|
||||
private fun getFiatBalanceText(balance: TangemPayCardBalance): String {
|
||||
val currency = Currency.getInstance(balance.currencyCode)
|
||||
return balance.fiatBalance.format {
|
||||
fiat(fiatCurrencyCode = currency.currencyCode, fiatCurrencySymbol = currency.symbol)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCryptoBalanceText(cryptoBalance: BigDecimal, cryptoCurrency: CryptoCurrency): String {
|
||||
return cryptoBalance.format { crypto(cryptoCurrency = cryptoCurrency) }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.features.tangempay.model.transformers
|
||||
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.pay.TangemPayTopUpData
|
||||
import com.tangem.domain.pay.model.TangemPayTopUpData
|
||||
import com.tangem.features.tangempay.components.AddFundsListener
|
||||
import com.tangem.features.tangempay.details.impl.R
|
||||
import com.tangem.features.tangempay.entity.TangemPayAddFundsItemUM
|
||||
|
|
|
|||
|
|
@ -31,11 +31,10 @@ import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfi
|
|||
import com.tangem.core.ui.components.containers.pullToRefresh.TangemPullToRefreshContainer
|
||||
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownItem
|
||||
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenu
|
||||
import com.tangem.core.ui.components.notifications.Notification
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig
|
||||
import com.tangem.core.ui.components.text.applyBladeBrush
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.orMaskWithStars
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.TokenDetailsTopBarTestTags
|
||||
|
|
@ -81,7 +80,9 @@ internal fun TangemPayDetailsScreen(
|
|||
) {
|
||||
item(TangemPayCardDetailsUM::class.java) {
|
||||
cardDetailsBlockComponent.CardDetailsBlockContent(
|
||||
modifier = Modifier.padding(horizontal = 16.dp).padding(top = 8.dp),
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.padding(top = 8.dp),
|
||||
state = cardDetailsState,
|
||||
)
|
||||
}
|
||||
|
|
@ -116,7 +117,7 @@ internal fun TangemPayDetailsScreen(
|
|||
key = TangemPayDetailsBalanceBlockState::class.java,
|
||||
content = {
|
||||
TangemPayDetailsBalanceBlock(
|
||||
modifier = modifier
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16)
|
||||
.padding(top = 12.dp)
|
||||
.fillMaxWidth(),
|
||||
|
|
@ -125,15 +126,32 @@ internal fun TangemPayDetailsScreen(
|
|||
)
|
||||
},
|
||||
)
|
||||
item(
|
||||
key = "TANGEM_PAY_IS_IN_BETA",
|
||||
content = {
|
||||
TangemPayBetaBlock(
|
||||
modifier = modifier
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16)
|
||||
.padding(top = 12.dp)
|
||||
.fillMaxWidth(),
|
||||
config = state.betaNotificationConfig,
|
||||
)
|
||||
},
|
||||
)
|
||||
with(txHistoryComponent) { txHistoryContent(listState = listState, state = txHistoryState) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TangemPayBetaBlock(config: NotificationConfig, modifier: Modifier = Modifier) {
|
||||
Notification(modifier = modifier, config = config)
|
||||
}
|
||||
|
||||
// region Balance block
|
||||
@Composable
|
||||
internal fun TangemPayDetailsBalanceBlock(
|
||||
private fun TangemPayDetailsBalanceBlock(
|
||||
state: TangemPayDetailsBalanceBlockState,
|
||||
isBalanceHidden: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
|
|
@ -157,12 +175,11 @@ internal fun TangemPayDetailsBalanceBlock(
|
|||
state = state,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
)
|
||||
// TODO [REDACTED_TASK_KEY]: Uncomment after adding crypto balance when the BFF is ready
|
||||
// CryptoBalance(
|
||||
// modifier = Modifier.padding(start = 12.dp, top = 4.dp),
|
||||
// state = state,
|
||||
// isBalanceHidden = isBalanceHidden,
|
||||
// )
|
||||
CryptoBalance(
|
||||
modifier = Modifier.padding(start = 12.dp, top = 4.dp),
|
||||
state = state,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
)
|
||||
if (state.actionButtons.isNotEmpty()) {
|
||||
HorizontalActionChips(
|
||||
modifier = Modifier.padding(top = 12.dp),
|
||||
|
|
@ -336,6 +353,16 @@ private class TangemPayDetailsUMProvider : CollectionPreviewParameterProvider<Ta
|
|||
isBalanceHidden = false,
|
||||
addFundsEnabled = true,
|
||||
cardFrozenState = CardFrozenState.Frozen(onUnfreeze = {}),
|
||||
betaNotificationConfig = NotificationConfig(
|
||||
title = resourceReference(R.string.tangem_pay_beta_notification_title),
|
||||
subtitle = resourceReference(R.string.tangem_pay_beta_notification_subtitle),
|
||||
iconResId = R.drawable.img_visa_notification,
|
||||
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(R.string.common_contact_support),
|
||||
onClick = {},
|
||||
),
|
||||
iconSize = 36.dp,
|
||||
),
|
||||
),
|
||||
TangemPayDetailsUM(
|
||||
topBarConfig = TangemPayDetailsTopBarConfig(onBackClick = {}, items = null),
|
||||
|
|
@ -345,6 +372,16 @@ private class TangemPayDetailsUMProvider : CollectionPreviewParameterProvider<Ta
|
|||
isBalanceHidden = false,
|
||||
addFundsEnabled = true,
|
||||
cardFrozenState = CardFrozenState.Unfrozen,
|
||||
betaNotificationConfig = NotificationConfig(
|
||||
title = resourceReference(R.string.tangem_pay_beta_notification_title),
|
||||
subtitle = resourceReference(R.string.tangem_pay_beta_notification_subtitle),
|
||||
iconResId = R.drawable.img_visa_notification,
|
||||
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(R.string.common_contact_support),
|
||||
onClick = {},
|
||||
),
|
||||
iconSize = 36.dp,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.features.tangempay.utils
|
|||
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig.ShowRefreshState
|
||||
|
||||
internal interface TangemPayDetailIntents {
|
||||
fun onContactSupportClicked()
|
||||
fun onRefreshSwipe(refreshState: ShowRefreshState)
|
||||
fun onClickAddFunds()
|
||||
fun onClickWithdraw()
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ internal class TangemPayOnboardingModel @Inject constructor(
|
|||
}
|
||||
is TangemPayOnboardingComponent.Params.Deeplink -> {
|
||||
repository.validateDeeplink(params.deeplink)
|
||||
.onRight { isValid -> if (isValid) showOnboarding() }
|
||||
.onRight { isValid -> if (isValid) showOnboarding() else back() }
|
||||
.onLeft { back() }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ internal class TangemPayClickIntentsImplementor @Inject constructor(
|
|||
body = resourceReference(R.string.tangempay_failed_to_issue_card_support_description)
|
||||
}
|
||||
secondaryButton {
|
||||
text = resourceReference(R.string.details_row_title_contact_to_support)
|
||||
text = resourceReference(R.string.common_contact_support)
|
||||
onClick {
|
||||
onPaySupportClick()
|
||||
closeBs()
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ sealed class WalletNotification(val config: NotificationConfig) {
|
|||
title = resourceReference(R.string.warning_backup_errors_title),
|
||||
subtitle = resourceReference(R.string.warning_backup_errors_message),
|
||||
buttonsState = NotificationConfig.ButtonsState.PrimaryButtonConfig(
|
||||
text = resourceReference(id = R.string.details_row_title_contact_to_support),
|
||||
text = resourceReference(id = R.string.common_contact_support),
|
||||
onClick = onSupportClick,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue