Updated on 2026-08-14

This commit is contained in:
Tangem 2022-09-15 20:37:05 +03:00
commit e1ea317688
18 changed files with 170 additions and 112 deletions

View file

@ -5,19 +5,23 @@ import androidx.annotation.ColorInt
import androidx.core.graphics.luminance import androidx.core.graphics.luminance
import androidx.core.graphics.toColorInt import androidx.core.graphics.toColorInt
import com.tangem.blockchain.common.Token import com.tangem.blockchain.common.Token
import com.tangem.wallet.R
@ColorInt @ColorInt
fun Token.getColor(): Int { fun Token.getColor(isTestnet: Boolean = false): Int {
return try { val defaultColor = "#C7C7CC".toColorInt() // equivalent to R.color.lightGray4
("#" + this.contractAddress.subSequence(2..7).toString())
.toColorInt() return if (isTestnet)
defaultColor
else try {
("#" + this.contractAddress.subSequence(2..7).toString()).toColorInt()
} catch (exception: Exception) { } catch (exception: Exception) {
R.color.lightGray4 defaultColor
} }
} }
@ColorInt @ColorInt
fun Token.getTextColor(): Int { fun Token.getTextColor(isTestnet: Boolean = false): Int = when {
return if (this.getColor().luminance > 0.5) Color.BLACK else Color.WHITE isTestnet -> Color.WHITE
this.getColor().luminance > 0.5 -> Color.BLACK
else -> Color.WHITE
} }

View file

@ -113,15 +113,11 @@ sealed class FeeActionUi : SendScreenActionUi {
} }
sealed class FeeAction : SendScreenAction { sealed class FeeAction : SendScreenAction {
enum class Error {
ADDRESS_OR_AMOUNT_IS_EMPTY,
REQUEST_FAILED
}
object RequestFee : FeeAction() object RequestFee : FeeAction()
sealed class FeeCalculation : FeeAction() { sealed class FeeCalculation : FeeAction() {
data class SetFeeResult(val fee: List<Amount>) : FeeCalculation() data class SetFeeResult(val fee: List<Amount>) : FeeCalculation()
data class SetFeeError(val error: Error) : FeeCalculation() object ClearResult : FeeCalculation()
} }
data class ChangeLayoutVisibility( data class ChangeLayoutVisibility(
@ -160,6 +156,12 @@ sealed class SendAction : SendScreenAction {
data class CardSdkError(val error: TangemSdkError): Dialog() data class CardSdkError(val error: TangemSdkError): Dialog()
data class BlockchainSdkError(val error: com.tangem.blockchain.common.BlockchainSdkError): Dialog() data class BlockchainSdkError(val error: com.tangem.blockchain.common.BlockchainSdkError): Dialog()
} }
data class RequestFeeError(
val error: com.tangem.blockchain.common.BlockchainSdkError,
val onRetry: () -> Unit,
) : Dialog()
object Hide : Dialog() object Hide : Dialog()
} }

View file

@ -2,6 +2,7 @@ package com.tangem.tap.features.send.redux.middlewares
import com.tangem.blockchain.common.Amount import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.BlockchainSdkError
import com.tangem.blockchain.common.TransactionSender import com.tangem.blockchain.common.TransactionSender
import com.tangem.blockchain.extensions.Result import com.tangem.blockchain.extensions.Result
import com.tangem.common.extensions.isZero import com.tangem.common.extensions.isZero
@ -31,8 +32,7 @@ class RequestFeeMiddleware {
val scanResponse = appState.globalState.scanResponse ?: return val scanResponse = appState.globalState.scanResponse ?: return
if (!SendState.isReadyToRequestFee()) { if (!SendState.isReadyToRequestFee()) {
dispatch(FeeAction.FeeCalculation.SetFeeError(FeeAction.Error.ADDRESS_OR_AMOUNT_IS_EMPTY)) dispatch(FeeAction.FeeCalculation.ClearResult)
// dispatch(FeeAction.ChangeLayoutVisibility(main = false, chipGroup = true))
dispatch(ReceiptAction.RefreshReceipt) dispatch(ReceiptAction.RefreshReceipt)
dispatch(SendAction.ChangeSendButtonState(sendState.getButtonState())) dispatch(SendAction.ChangeSendButtonState(sendState.getButtonState()))
return return
@ -66,14 +66,21 @@ class RequestFeeMiddleware {
} }
} }
is Result.Failure -> { is Result.Failure -> {
dispatch(FeeAction.FeeCalculation.SetFeeError(FeeAction.Error.REQUEST_FAILED)) dispatch(FeeAction.FeeCalculation.ClearResult)
dispatch(FeeAction.ChangeLayoutVisibility(main = false)) dispatch(FeeAction.ChangeLayoutVisibility(main = false))
val blockchainSdkError = feeResult.error as? BlockchainSdkError ?: return@withContext
dispatch(
SendAction.Dialog.RequestFeeError(
error = blockchainSdkError,
onRetry = { dispatch(FeeAction.RequestFee) },
),
)
} }
} }
dispatch(AmountActionUi.CheckAmountToSend) dispatch(AmountActionUi.CheckAmountToSend)
} }
} }
} }
} }
@ -93,13 +100,13 @@ class FeeMock {
suspend fun feeStandard(blockchain: Blockchain): List<Amount> = listOf( suspend fun feeStandard(blockchain: Blockchain): List<Amount> = listOf(
Amount(0.001500.toBigDecimal(), blockchain), Amount(0.001500.toBigDecimal(), blockchain),
Amount(0.0030.toBigDecimal(), blockchain), Amount(0.0030.toBigDecimal(), blockchain),
Amount(0.0045001.toBigDecimal(), blockchain) Amount(0.0045001.toBigDecimal(), blockchain),
) )
suspend fun feeStandardBig(blockchain: Blockchain): List<Amount> = listOf( suspend fun feeStandardBig(blockchain: Blockchain): List<Amount> = listOf(
Amount(1.76.toBigDecimal(), blockchain), Amount(1.76.toBigDecimal(), blockchain),
Amount(2.30.toBigDecimal(), blockchain), Amount(2.30.toBigDecimal(), blockchain),
Amount(3.45.toBigDecimal(), blockchain) Amount(3.45.toBigDecimal(), blockchain),
) )
suspend fun feeSingle(blockchain: Blockchain): List<Amount> = listOf(Amount(0.0015.toBigDecimal(), blockchain)) suspend fun feeSingle(blockchain: Blockchain): List<Amount> = listOf(Amount(0.0015.toBigDecimal(), blockchain))

View file

@ -36,7 +36,7 @@ class FeeReducer : SendInternalReducer {
private fun handleAction(action: FeeAction, sendState: SendState, state: FeeState): SendState { private fun handleAction(action: FeeAction, sendState: SendState, state: FeeState): SendState {
val result = when (action) { val result = when (action) {
is FeeAction.RequestFee -> { is FeeAction.RequestFee -> {
state.copy(error = null) state
} }
is FeeAction.ChangeLayoutVisibility -> { is FeeAction.ChangeLayoutVisibility -> {
fun getVisibility(current: Boolean, proposed: Boolean?): Boolean = proposed ?: current fun getVisibility(current: Boolean, proposed: Boolean?): Boolean = proposed ?: current
@ -56,7 +56,6 @@ class FeeReducer : SendInternalReducer {
selectedFeeType = feeType, selectedFeeType = feeType,
feeList = fees, feeList = fees,
currentFee = currentFee, currentFee = currentFee,
error = null,
feeIsApproximate = isFeeApproximate(sendState), feeIsApproximate = isFeeApproximate(sendState),
) )
} else { } else {
@ -67,16 +66,14 @@ class FeeReducer : SendInternalReducer {
selectedFeeType = feeType, selectedFeeType = feeType,
feeList = fees, feeList = fees,
currentFee = currentFee, currentFee = currentFee,
error = null,
feeIsApproximate = isFeeApproximate(sendState), feeIsApproximate = isFeeApproximate(sendState),
) )
} }
} }
is FeeAction.FeeCalculation.SetFeeError -> { FeeAction.FeeCalculation.ClearResult -> {
state.copy( state.copy(
feeList = null, feeList = null,
currentFee = null, currentFee = null,
error = action.error,
) )
} }
} }

View file

@ -59,10 +59,10 @@ private class SendReducer : SendInternalReducer {
is SendAction.Dialog.TezosWarningDialog -> sendState.copy(dialog = action) is SendAction.Dialog.TezosWarningDialog -> sendState.copy(dialog = action)
is SendAction.Dialog.SendTransactionFails.CardSdkError -> sendState.copy(dialog = action) is SendAction.Dialog.SendTransactionFails.CardSdkError -> sendState.copy(dialog = action)
is SendAction.Dialog.SendTransactionFails.BlockchainSdkError -> sendState.copy(dialog = action) is SendAction.Dialog.SendTransactionFails.BlockchainSdkError -> sendState.copy(dialog = action)
is SendAction.Dialog.RequestFeeError -> sendState.copy(dialog = action)
is SendAction.Dialog.Hide -> sendState.copy(dialog = null) is SendAction.Dialog.Hide -> sendState.copy(dialog = null)
is SendAction.Warnings.Set -> sendState.copy(sendWarningsList = action.warningList) is SendAction.Warnings.Set -> sendState.copy(sendWarningsList = action.warningList)
is SendAction.SendSpecificTransaction -> is SendAction.SendSpecificTransaction -> handleSendSpecificTransactionAction(action, sendState)
handleSendSpecificTransactionAction(action, sendState)
else -> return sendState else -> return sendState
} }

View file

@ -1,7 +1,6 @@
package com.tangem.tap.features.send.redux.states package com.tangem.tap.features.send.redux.states
import com.tangem.blockchain.common.Amount import com.tangem.blockchain.common.Amount
import com.tangem.tap.features.send.redux.FeeAction
import java.math.BigDecimal import java.math.BigDecimal
/** /**
@ -21,12 +20,11 @@ data class FeeState(
val controlsLayoutIsVisible: Boolean = false, val controlsLayoutIsVisible: Boolean = false,
val feeChipGroupIsVisible: Boolean = true, val feeChipGroupIsVisible: Boolean = true,
val includeFeeSwitcherIsEnabled: Boolean = true, val includeFeeSwitcherIsEnabled: Boolean = true,
val error: FeeAction.Error? = null,
) : SendScreenState { ) : SendScreenState {
override val stateId: StateId = StateId.FEE override val stateId: StateId = StateId.FEE
fun isReady(): Boolean = error == null && currentFee != null fun isReady(): Boolean = currentFee != null
fun getCurrentFeeValue(): BigDecimal = currentFee?.value ?: BigDecimal.ZERO fun getCurrentFeeValue(): BigDecimal = currentFee?.value ?: BigDecimal.ZERO
} }

View file

@ -0,0 +1,32 @@
package com.tangem.tap.features.send.ui.dialogs
import android.content.Context
import androidx.appcompat.app.AlertDialog
import com.tangem.tap.common.feedback.SendTransactionFailedEmail
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.features.send.redux.SendAction
import com.tangem.tap.store
import com.tangem.wallet.R
/**
[REDACTED_AUTHOR]
*/
class RequestFeeErrorDialog {
companion object {
fun create(context: Context, dialog: SendAction.Dialog.RequestFeeError): AlertDialog {
val errorMessage = dialog.error.customMessage
return AlertDialog.Builder(context).apply {
setTitle(R.string.send_error_fee_request_failed)
setMessage(context.getString(R.string.alert_failed_to_send_transaction_message, errorMessage))
setNegativeButton(R.string.alert_button_send_feedback) { _, _ ->
store.dispatch(GlobalAction.SendEmail(SendTransactionFailedEmail(errorMessage)))
}
setPositiveButton(R.string.common_retry) { _, _ -> dialog.onRetry() }
setNeutralButton(R.string.common_cancel) { _, _ -> }
setOnDismissListener { store.dispatch(SendAction.Dialog.Hide) }
}.create()
}
}
}

View file

@ -20,7 +20,6 @@ import com.tangem.tap.domain.MultiMessageError
import com.tangem.tap.domain.assembleErrors import com.tangem.tap.domain.assembleErrors
import com.tangem.tap.features.BaseStoreFragment import com.tangem.tap.features.BaseStoreFragment
import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.Error import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.Error
import com.tangem.tap.features.send.redux.FeeAction
import com.tangem.tap.features.send.redux.SendAction import com.tangem.tap.features.send.redux.SendAction
import com.tangem.tap.features.send.redux.states.AddressPayIdState import com.tangem.tap.features.send.redux.states.AddressPayIdState
import com.tangem.tap.features.send.redux.states.AmountState import com.tangem.tap.features.send.redux.states.AmountState
@ -35,12 +34,12 @@ import com.tangem.tap.features.send.redux.states.TransactionExtrasState
import com.tangem.tap.features.send.redux.states.XlmMemoType import com.tangem.tap.features.send.redux.states.XlmMemoType
import com.tangem.tap.features.send.ui.FeeUiHelper import com.tangem.tap.features.send.ui.FeeUiHelper
import com.tangem.tap.features.send.ui.SendFragment import com.tangem.tap.features.send.ui.SendFragment
import com.tangem.tap.features.send.ui.dialogs.RequestFeeErrorDialog
import com.tangem.tap.features.send.ui.dialogs.SendTransactionFailsDialog import com.tangem.tap.features.send.ui.dialogs.SendTransactionFailsDialog
import com.tangem.tap.features.send.ui.dialogs.TezosWarningDialog import com.tangem.tap.features.send.ui.dialogs.TezosWarningDialog
import com.tangem.tap.features.wallet.redux.WalletState.Companion.ROUGH_SIGN import com.tangem.tap.features.wallet.redux.WalletState.Companion.ROUGH_SIGN
import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN
import com.tangem.tap.features.wallet.ui.adapters.WarningMessagesAdapter import com.tangem.tap.features.wallet.ui.adapters.WarningMessagesAdapter
import com.tangem.tap.store
import com.tangem.wallet.R import com.tangem.wallet.R
/** /**
@ -62,10 +61,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
when (it) { when (it) {
StateId.SEND_SCREEN -> handleSendScreen(fg, state) StateId.SEND_SCREEN -> handleSendScreen(fg, state)
StateId.ADDRESS_PAY_ID -> handleAddressPayIdState(fg, state.addressPayIdState) StateId.ADDRESS_PAY_ID -> handleAddressPayIdState(fg, state.addressPayIdState)
StateId.TRANSACTION_EXTRAS -> handleTransactionExtrasState( StateId.TRANSACTION_EXTRAS -> handleTransactionExtrasState(fg, state.transactionExtrasState)
fg,
state.transactionExtrasState
)
StateId.AMOUNT -> handleAmountState(fg, state.amountState) StateId.AMOUNT -> handleAmountState(fg, state.amountState)
StateId.FEE -> handleFeeState(fg, state.feeState) StateId.FEE -> handleFeeState(fg, state.feeState)
StateId.RECEIPT -> handleReceiptState(fg, state.receiptState) StateId.RECEIPT -> handleReceiptState(fg, state.receiptState)
@ -144,6 +140,12 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
dialog?.show() dialog?.show()
} }
} }
is SendAction.Dialog.RequestFeeError -> {
if (dialog == null) {
dialog = RequestFeeErrorDialog.create(fg.requireContext(), state.dialog)
dialog?.show()
}
}
else -> { else -> {
dialog?.dismiss() dialog?.dismiss()
dialog = null dialog = null
@ -161,7 +163,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
toolbar.title = fg.getString( toolbar.title = fg.getString(
R.string.send_title_currency_format, R.string.send_title_currency_format,
state.amountState.mainCurrency.currencySymbol state.amountState.mainCurrency.currencySymbol,
) )
} }
@ -233,11 +235,11 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
val balanceText = when (state.mainCurrency.type) { val balanceText = when (state.mainCurrency.type) {
MainCurrencyType.FIAT -> fg.getString( MainCurrencyType.FIAT -> fg.getString(
R.string.send_balance_subtitle_format, R.string.send_balance_subtitle_format,
state.viewBalanceValue, state.mainCurrency.currencySymbol state.viewBalanceValue, state.mainCurrency.currencySymbol,
).remove(":") ).remove(":")
MainCurrencyType.CRYPTO -> fg.getString( MainCurrencyType.CRYPTO -> fg.getString(
R.string.send_balance_subtitle_format, R.string.send_balance_subtitle_format,
state.mainCurrency.currencySymbol, state.viewBalanceValue state.mainCurrency.currencySymbol, state.viewBalanceValue,
) )
} }
@ -263,14 +265,6 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
swIncludeFee.isChecked = state.feeIsIncluded swIncludeFee.isChecked = state.feeIsIncluded
} }
if (state.error == FeeAction.Error.REQUEST_FAILED) {
fg.showRetrySnackbar(
fg.requireContext().getString(R.string.send_error_fee_request_failed)
) {
store.dispatch(FeeAction.RequestFee)
}
}
val chipId = FeeUiHelper.toId(state.selectedFeeType) val chipId = FeeUiHelper.toId(state.selectedFeeType)
if (chipGroup.checkedChipId != chipId && chipId != View.NO_ID) chipGroup.check(chipId) if (chipGroup.checkedChipId != chipId && chipId != View.NO_ID) chipGroup.check(chipId)
} }
@ -299,7 +293,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
val willSent = getString( val willSent = getString(
R.string.send_total_subtitle_format, R.string.send_total_subtitle_format,
receipt.willSentCrypto, receipt.symbols.crypto receipt.willSentCrypto, receipt.symbols.crypto,
) )
llTotalContainer.tvWillBeSentValue.update(willSent) llTotalContainer.tvWillBeSentValue.update(willSent)
@ -334,7 +328,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
val willSent = getString( val willSent = getString(
R.string.send_total_subtitle_asset_format, R.string.send_total_subtitle_asset_format,
receipt.symbols.token ?: "", receipt.willSentToken, receipt.symbols.token ?: "", receipt.willSentToken,
receipt.symbols.crypto, receipt.willSentFeeCoin receipt.symbols.crypto, receipt.willSentFeeCoin,
) )
llTotalContainer.tvWillBeSentValue.update(willSent) llTotalContainer.tvWillBeSentValue.update(willSent)
} }

View file

@ -41,6 +41,7 @@ import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN
import com.tangem.tap.features.wallet.ui.adapters.PendingTransactionsAdapter import com.tangem.tap.features.wallet.ui.adapters.PendingTransactionsAdapter
import com.tangem.tap.features.wallet.ui.adapters.WalletDetailWarningMessagesAdapter import com.tangem.tap.features.wallet.ui.adapters.WalletDetailWarningMessagesAdapter
import com.tangem.tap.features.wallet.ui.images.load
import com.tangem.tap.features.wallet.ui.test.TestWalletDetails import com.tangem.tap.features.wallet.ui.test.TestWalletDetails
import com.tangem.tap.store import com.tangem.tap.store
import com.tangem.wallet.R import com.tangem.wallet.R

View file

@ -15,6 +15,7 @@ import com.tangem.tap.common.extensions.show
import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletData import com.tangem.tap.features.wallet.redux.WalletData
import com.tangem.tap.features.wallet.ui.BalanceStatus import com.tangem.tap.features.wallet.ui.BalanceStatus
import com.tangem.tap.features.wallet.ui.images.load
import com.tangem.tap.store import com.tangem.tap.store
import com.tangem.wallet.R import com.tangem.wallet.R
import com.tangem.wallet.databinding.ItemCurrencyWalletBinding import com.tangem.wallet.databinding.ItemCurrencyWalletBinding
@ -62,7 +63,7 @@ class WalletAdapter
fun bind(wallet: WalletData) = with(binding) { fun bind(wallet: WalletData) = with(binding) {
val status = wallet.currencyData.status val status = wallet.currencyData.status
// Skip changes when on refreshing status // Skip changes when on refreshing status
if (status == BalanceStatus.Refreshing || wallet.fiatRateString == null) return@with if (status == BalanceStatus.Refreshing) return@with
val statusMessage = when (status) { val statusMessage = when (status) {
BalanceStatus.TransactionInProgress -> { BalanceStatus.TransactionInProgress -> {

View file

@ -21,7 +21,7 @@ private const val VOYR = "VOYRME"
class CurrencyIconRequest( class CurrencyIconRequest(
private val currencyImageView: ImageFilterView, private val currencyImageView: ImageFilterView,
private val currencyTextView: TextView, private val currencyTextView: TextView?,
private val token: Token?, private val token: Token?,
private val blockchain: Blockchain, private val blockchain: Blockchain,
) { ) {
@ -38,7 +38,7 @@ class CurrencyIconRequest(
loadBlockchainIconBase( loadBlockchainIconBase(
onStart = { onStart = {
currencyImageView.colorFilter = null currencyImageView.colorFilter = null
} },
) )
} }
@ -46,7 +46,7 @@ class CurrencyIconRequest(
loadBlockchainIconBase( loadBlockchainIconBase(
onStart = { onStart = {
currencyImageView.saturation = 0f currencyImageView.saturation = 0f
} },
) )
} }
@ -57,7 +57,11 @@ class CurrencyIconRequest(
}, },
onSuccess = { onSuccess = {
currencyImageView.colorFilter = null currencyImageView.colorFilter = null
} },
onError = {
currencyImageView.setColorFilter(it.getColor())
currencyTextView?.setTextColor(it.getTextColor())
},
) )
} }
@ -65,7 +69,12 @@ class CurrencyIconRequest(
loadTokenIconBase( loadTokenIconBase(
onStart = { onStart = {
currencyImageView.saturation = 0f currencyImageView.saturation = 0f
} },
onError = {
currencyImageView.saturation = 0f
currencyImageView.setColorFilter(it.getColor(true))
currencyTextView?.setTextColor(it.getTextColor(true))
},
) )
} }
@ -94,15 +103,19 @@ class CurrencyIconRequest(
data = getTokenIcon(token, blockchain), data = getTokenIcon(token, blockchain),
placeholderRes = R.drawable.shape_circle, placeholderRes = R.drawable.shape_circle,
onStart = { onStart = {
currencyTextView.text = token.symbol.take(1) currencyTextView?.text = token.name.take(1)
currencyTextView.setTextColor(token.getTextColor()) currencyTextView?.setTextColor(token.getTextColor())
onStart(token) onStart(token)
}, },
onSuccess = { onSuccess = {
currencyTextView.text = null currencyTextView?.text = null
onSuccess(token) onSuccess(token)
}, },
onError = { onError(token) }, onError = {
// for some reason the onStart doesn't call if an error occurs
currencyTextView?.text = token.name.take(1)
onError(token)
},
) )
} }
} }
@ -122,7 +135,7 @@ private inline fun ImageView.loadIcon(
.listener( .listener(
onStart = { onStart() }, onStart = { onStart() },
onSuccess = { _, _ -> onSuccess() }, onSuccess = { _, _ -> onSuccess() },
onError = { _, _ -> onError() } onError = { _, _ -> onError() },
) )
.target(imageView = this) .target(imageView = this)
.build() .build()

View file

@ -4,13 +4,11 @@ import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import android.view.LayoutInflater import android.view.LayoutInflater
import android.widget.TextView import android.widget.TextView
import androidx.annotation.DrawableRes
import androidx.constraintlayout.utils.widget.ImageFilterView import androidx.constraintlayout.utils.widget.ImageFilterView
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.tangem.blockchain.common.DerivationStyle import com.tangem.blockchain.common.DerivationStyle
import com.tangem.tangem_sdk_new.extensions.dpToPx import com.tangem.tangem_sdk_new.extensions.dpToPx
import com.tangem.tap.common.extensions.getRoundIconRes
import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.features.wallet.models.Currency
import com.tangem.wallet.databinding.ViewCurrencyIconBinding import com.tangem.wallet.databinding.ViewCurrencyIconBinding
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -25,46 +23,50 @@ class CurrencyIconView @JvmOverloads constructor(
this, this,
) )
private val currencyImageView: ImageFilterView val currencyImageView: ImageFilterView
get() = binding.ivCurrency get() = binding.ivCurrency
private val currencyTextView: TextView val currencyTextView: TextView
get() = binding.tvTokenLetter get() = binding.tvTokenLetter
private var isBlockchainIconVisible: Boolean val blockchainBadge: ImageFilterView
get() = binding.ivBlockchain.isVisible get() = binding.ivBlockchainBadge
set(value) = binding.ivBlockchain::isVisible.set(value)
private var isBadgeVisible: Boolean var isBlockchainBadgeVisible: Boolean
get() = binding.badge.isVisible get() = binding.ivBlockchainBadge.isVisible
set(value) = binding.badge::isVisible.set(value) set(value) = binding.ivBlockchainBadge::isVisible.set(value)
@DrawableRes var isCustomCurrencyBadgeVisible: Boolean
private var blockchainIconRes: Int? = null get() = binding.customBadge.isVisible
set(value) { set(value) = binding.customBadge::isVisible.set(value)
if (value != null && value != field && isBlockchainIconVisible) {
binding.ivBlockchain.setImageResource(value)
field = value
}
}
init { init {
minWidth = dpToPx(48f).roundToInt() minWidth = dpToPx(48f).roundToInt()
minHeight = dpToPx(48f).roundToInt() minHeight = dpToPx(48f).roundToInt()
} }
}
fun load( fun CurrencyIconView.load(
currency: Currency, currency: Currency,
derivationStyle: DerivationStyle?, derivationStyle: DerivationStyle?,
) { ) {
isBlockchainIconVisible = currency.isToken() isCustomCurrencyBadgeVisible = currency.isCustomCurrency(derivationStyle)
isBadgeVisible = currency.isCustomCurrency(derivationStyle)
blockchainIconRes = currency.blockchain.getRoundIconRes() CurrencyIconRequest(
currencyImageView = currencyImageView,
currencyTextView = currencyTextView,
token = (currency as? Currency.Token)?.token,
blockchain = currency.blockchain,
).load()
if (currency.isToken()) {
// load a blockchain icon into the blockchain badge
isBlockchainBadgeVisible = true
CurrencyIconRequest( CurrencyIconRequest(
currencyImageView = currencyImageView, currencyImageView = blockchainBadge,
currencyTextView = currencyTextView, currencyTextView = null,
token = (currency as? Currency.Token)?.token, token = null,
blockchain = currency.blockchain, blockchain = currency.blockchain,
).load() ).load()
} }

View file

@ -35,12 +35,13 @@
android:textColor="@android:color/white" android:textColor="@android:color/white"
android:textSize="25sp" android:textSize="25sp"
android:textStyle="bold" android:textStyle="bold"
android:translationY="-1dp"
tools:text="J" /> tools:text="J" />
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>
<ImageView <androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/iv_blockchain" android:id="@+id/iv_blockchain_badge"
android:layout_width="18dp" android:layout_width="18dp"
android:layout_height="18dp" android:layout_height="18dp"
android:background="@drawable/shape_circle" android:background="@drawable/shape_circle"
@ -54,7 +55,7 @@
tools:visibility="visible" /> tools:visibility="visible" />
<View <View
android:id="@+id/badge" android:id="@+id/custom_badge"
android:layout_width="10dp" android:layout_width="10dp"
android:layout_height="10dp" android:layout_height="10dp"
android:background="@drawable/shape_badge_custom_currency" android:background="@drawable/shape_badge_custom_currency"

View file

@ -2,7 +2,7 @@ ext.versions = [
kotlin : '1.6.10', kotlin : '1.6.10',
build_gradle : '7.1.3', build_gradle : '7.1.3',
tangem_card_sdk : 'develop-159', tangem_card_sdk : 'develop-159',
tangem_blockchain_sdk: 'develop-107', tangem_blockchain_sdk: 'develop-111',
// tangem_blockchain_sdk: '0.0.1', // tangem_blockchain_sdk: '0.0.1',
] ]

View file

@ -5,7 +5,7 @@ import com.tangem.blockchain.common.Blockchain
fun Blockchain.Companion.fromNetworkId(networkId: String): Blockchain? { fun Blockchain.Companion.fromNetworkId(networkId: String): Blockchain? {
return when (networkId) { return when (networkId) {
"arbitrum-one" -> Blockchain.Arbitrum "arbitrum-one" -> Blockchain.Arbitrum
"arbitrum-one/test" -> Blockchain.ArbitrumTestnet "arbitrum/test" -> Blockchain.ArbitrumTestnet
"avalanche", "avalanche-2" -> Blockchain.Avalanche "avalanche", "avalanche-2" -> Blockchain.Avalanche
"avalanche/test", "avalanche-2/test" -> Blockchain.AvalancheTestnet "avalanche/test", "avalanche-2/test" -> Blockchain.AvalancheTestnet
"binancecoin" -> Blockchain.Binance "binancecoin" -> Blockchain.Binance
@ -40,6 +40,7 @@ fun Blockchain.Companion.fromNetworkId(networkId: String): Blockchain? {
"xdai" -> Blockchain.Gnosis "xdai" -> Blockchain.Gnosis
"polkadot" -> Blockchain.Polkadot "polkadot" -> Blockchain.Polkadot
"polkadot/test" -> Blockchain.PolkadotTestnet "polkadot/test" -> Blockchain.PolkadotTestnet
"kusama" -> Blockchain.Kusama
else -> null else -> null
} }
} }
@ -84,6 +85,7 @@ fun Blockchain.toNetworkId(): String {
Blockchain.Gnosis -> "xdai" Blockchain.Gnosis -> "xdai"
Blockchain.Polkadot -> "polkadot" Blockchain.Polkadot -> "polkadot"
Blockchain.PolkadotTestnet -> "polkadot/test" Blockchain.PolkadotTestnet -> "polkadot/test"
Blockchain.Kusama -> "kusama"
} }
} }
@ -101,15 +103,16 @@ fun Blockchain.toCoinId(): String {
Blockchain.Avalanche, Blockchain.AvalancheTestnet -> "avalanche-2" Blockchain.Avalanche, Blockchain.AvalancheTestnet -> "avalanche-2"
Blockchain.Solana, Blockchain.SolanaTestnet -> "solana" Blockchain.Solana, Blockchain.SolanaTestnet -> "solana"
Blockchain.Fantom, Blockchain.FantomTestnet -> "fantom" Blockchain.Fantom, Blockchain.FantomTestnet -> "fantom"
Blockchain.Tron, Blockchain.TronTestnet -> "tron"
Blockchain.Polkadot, Blockchain.PolkadotTestnet -> "polkadot"
Blockchain.Ducatus -> "ducatus" Blockchain.Ducatus -> "ducatus"
Blockchain.Litecoin -> "litecoin" Blockchain.Litecoin -> "litecoin"
Blockchain.RSK -> "rootstock" Blockchain.RSK -> "rootstock"
Blockchain.Tezos -> "tezos" Blockchain.Tezos -> "tezos"
Blockchain.XRP -> "ripple" Blockchain.XRP -> "ripple"
Blockchain.Dogecoin -> "dogecoin" Blockchain.Dogecoin -> "dogecoin"
Blockchain.Tron, Blockchain.TronTestnet -> "tron"
Blockchain.Gnosis -> "xdai" Blockchain.Gnosis -> "xdai"
Blockchain.Polkadot, Blockchain.PolkadotTestnet -> "polkadot" Blockchain.Kusama -> "kusama"
Blockchain.Unknown -> "unknown" Blockchain.Unknown -> "unknown"
} }
} }

View file

@ -18,8 +18,7 @@ fun Card.supportedBlockchains(): List<Blockchain> {
Blockchain.fromCurve(EllipticCurve.Secp256k1) Blockchain.fromCurve(EllipticCurve.Secp256k1)
} }
else -> { else -> {
Blockchain.fromCurve(EllipticCurve.Secp256k1) + (Blockchain.fromCurve(EllipticCurve.Secp256k1) + Blockchain.fromCurve(EllipticCurve.Ed25519)).distinct()
Blockchain.fromCurve(EllipticCurve.Ed25519)
} }
} }
val filtered = supportedBlockchains.filter { isTestCard == it.isTestnet() } val filtered = supportedBlockchains.filter { isTestCard == it.isTestnet() }

View file

@ -0,0 +1,20 @@
package com.tangem.domain.features
import com.google.common.truth.Truth
import com.tangem.blockchain.common.Blockchain
import com.tangem.domain.common.extensions.fromNetworkId
import com.tangem.domain.common.extensions.toNetworkId
import org.junit.Test
class BlockchainTests {
@Test
fun allNetworkIdsAreImplemented() {
val unimplementedIds = Blockchain.values()
.toMutableList()
.apply { remove(Blockchain.Unknown) }
.map { it to Blockchain.fromNetworkId(it.toNetworkId()) }
.mapNotNull { if(it.second == null) it.first else null }
Truth.assertThat(unimplementedIds).isEmpty()
}
}

View file

@ -1,16 +0,0 @@
package com.tangem.domain.features
import org.junit.Assert.assertEquals
import org.junit.Test
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}