Updated on 2026-08-14
This commit is contained in:
commit
1ffa785bc0
26 changed files with 288 additions and 144 deletions
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.tap.common.feedback
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.tap.common.extensions.breakLine
|
||||
|
||||
class FeedbackDataBuilder(
|
||||
|
|
@ -33,9 +32,10 @@ class FeedbackDataBuilder(
|
|||
builder.appendKeyValue("Blockchain", walletInfo.blockchain.fullName)
|
||||
builder.appendKeyValue("Derivation path", walletInfo.derivationPath)
|
||||
|
||||
if (walletInfo.blockchain == Blockchain.Bitcoin) {
|
||||
builder.appendKeyValue("XPUB", infoHolder.extendedPublicKey)
|
||||
}
|
||||
// enable later
|
||||
// if (walletInfo.blockchain == Blockchain.Bitcoin) {
|
||||
// builder.appendKeyValue("XPUB", infoHolder.extendedPublicKey)
|
||||
// }
|
||||
|
||||
builder.appendKeyValue("Outputs count", walletInfo.outputsCount)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import com.tangem.domain.common.TwinsHelper
|
|||
import com.tangem.domain.common.configs.CardConfig
|
||||
import com.tangem.domain.common.util.derivationStyleProvider
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.CardDTO.Companion.RING_BATCH_IDS
|
||||
import com.tangem.domain.models.scan.ProductType
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.operations.ScanTask
|
||||
|
|
@ -247,7 +248,7 @@ private class ScanWalletProcessor(
|
|||
}
|
||||
|
||||
private fun getWalletProductType(card: CardDTO): ProductType {
|
||||
if (card.batchId == CardDTO.RING_BATCH_ID) {
|
||||
if (RING_BATCH_IDS.contains(card.batchId)) {
|
||||
return ProductType.Ring
|
||||
}
|
||||
return if (card.firmwareVersion >= FirmwareVersion.Ed25519Slip0010Available &&
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ internal object CoinsResponseConverter : Converter<CoinsResponse, List<Token>> {
|
|||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}.filter { it.networks.isNotEmpty() }
|
||||
}
|
||||
|
||||
fun getIconUrl(id: String, imageHost: String? = null): String {
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ internal class MercuryoService(private val environment: MercuryoEnvironment) : E
|
|||
.appendQueryParameter("address", walletAddress)
|
||||
.appendQueryParameter("signature", signature(walletAddress))
|
||||
.appendQueryParameter("fix_currency", "true")
|
||||
.appendQueryParameter("return_url", ExchangeUrlBuilder.SUCCESS_URL)
|
||||
.appendQueryParameter("redirect_url", ExchangeUrlBuilder.SUCCESS_URL)
|
||||
if (isDarkTheme) builder.appendQueryParameter("theme", "1inch")
|
||||
|
||||
blockchain.mercuryoNetwork()?.let {
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ class MoonPayService(
|
|||
|
||||
override fun availableForSell(currency: Currency): Boolean {
|
||||
val availableForSell = status?.availableForSell ?: return false
|
||||
val metadata = status?.responseCurrencies?.map { it.metadata }
|
||||
val metadata = status?.responseCurrencies?.filter { it.isSellSupported }?.map { it.metadata }
|
||||
if (!isSellAllowed()) return false
|
||||
|
||||
return when (currency) {
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@ import com.squareup.moshi.JsonClass
|
|||
@JsonClass(generateAdapter = true)
|
||||
data class CreateUserNetworkAccountBody(
|
||||
@Json(name = "networkId") val networkId: String,
|
||||
@Json(name = "walletPublicKey") val publicWalletKey: String,
|
||||
@Json(name = "walletPublicKey") val walletPublicKey: String,
|
||||
)
|
||||
|
|
@ -12,6 +12,6 @@ data class CreateUserNetworkAccountResponse(
|
|||
@JsonClass(generateAdapter = true)
|
||||
data class AccountCreated(
|
||||
@Json(name = "accountId") val accountId: String,
|
||||
@Json(name = "publicWalletKey") val publicWalletKey: String,
|
||||
@Json(name = "walletPublicKey") val walletPublicKey: String,
|
||||
)
|
||||
}
|
||||
|
|
@ -53,6 +53,7 @@ fun getActiveIconRes(blockchainId: String): Int {
|
|||
"algorand", "algorand/test" -> R.drawable.img_algorand_22
|
||||
"hedera", "hedera/test" -> R.drawable.img_hedera_22
|
||||
"playa3ull" -> R.drawable.img_playa3ull_22
|
||||
"DUC" -> R.drawable.img_ducatus_22
|
||||
else -> R.drawable.ic_alert_24
|
||||
}
|
||||
}
|
||||
|
|
@ -107,6 +108,7 @@ fun getActiveIconResByNetworkId(networkId: String): Int {
|
|||
"algorand", "algorand/test" -> R.drawable.img_algorand_22
|
||||
"hedera-hashgraph", "hedera/test" -> R.drawable.img_hedera_22
|
||||
"playa3ull-games" -> R.drawable.img_playa3ull_22
|
||||
"ducatus" -> R.drawable.img_ducatus_22
|
||||
else -> R.drawable.ic_alert_24
|
||||
}
|
||||
}
|
||||
|
|
@ -158,6 +160,7 @@ fun getActiveIconResByCoinId(coinId: String): Int {
|
|||
"algorand" -> R.drawable.img_algorand_22
|
||||
"hedera-hashgraph" -> R.drawable.img_hedera_22
|
||||
"playa3ull-games-2" -> R.drawable.img_playa3ull_22
|
||||
"ducatus" -> R.drawable.img_ducatus_22
|
||||
else -> R.drawable.ic_alert_24
|
||||
}
|
||||
}
|
||||
|
|
@ -212,6 +215,7 @@ fun getGreyedOutIconRes(blockchainId: String): Int {
|
|||
"algorand", "algorand/test" -> R.drawable.ic_algorand_22
|
||||
"hedera", "hedera/test" -> R.drawable.ic_hedera_22
|
||||
"playa3ull" -> R.drawable.ic_playa3ull_22
|
||||
"DUC" -> R.drawable.ic_ducatus_22
|
||||
else -> R.drawable.ic_alert_24
|
||||
}
|
||||
}
|
||||
|
|
@ -266,6 +270,7 @@ fun getGreyedOutIconResByNetworkId(networkId: String): Int {
|
|||
"algorand", "algorand/test" -> R.drawable.ic_algorand_22
|
||||
"hedera-hashgraph", "hedera/test" -> R.drawable.ic_hedera_22
|
||||
"playa3ull-games" -> R.drawable.ic_playa3ull_22
|
||||
"ducatus" -> R.drawable.ic_ducatus_22
|
||||
else -> R.drawable.ic_alert_24
|
||||
}
|
||||
}
|
||||
|
|
@ -14,12 +14,18 @@ object BigDecimalFormatter {
|
|||
|
||||
private const val TEMP_CURRENCY_CODE = "USD"
|
||||
|
||||
fun formatCryptoAmount(cryptoAmount: BigDecimal?, cryptoCurrency: String, decimals: Int): String {
|
||||
fun formatCryptoAmount(
|
||||
cryptoAmount: BigDecimal?,
|
||||
cryptoCurrency: String,
|
||||
decimals: Int,
|
||||
locale: Locale = Locale.getDefault(),
|
||||
): String {
|
||||
if (cryptoAmount == null) return EMPTY_BALANCE_SIGN
|
||||
|
||||
val formatter = NumberFormat.getNumberInstance().apply {
|
||||
val formatter = NumberFormat.getNumberInstance(locale).apply {
|
||||
maximumFractionDigits = decimals.coerceAtMost(maximumValue = 8)
|
||||
minimumFractionDigits = 2
|
||||
isGroupingUsed = true
|
||||
roundingMode = RoundingMode.DOWN
|
||||
}
|
||||
|
||||
|
|
@ -32,8 +38,12 @@ object BigDecimalFormatter {
|
|||
}
|
||||
}
|
||||
|
||||
fun formatCryptoAmount(cryptoAmount: BigDecimal?, cryptoCurrency: CryptoCurrency): String {
|
||||
return formatCryptoAmount(cryptoAmount, cryptoCurrency.symbol, cryptoCurrency.decimals)
|
||||
fun formatCryptoAmount(
|
||||
cryptoAmount: BigDecimal?,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
locale: Locale = Locale.getDefault(),
|
||||
): String {
|
||||
return formatCryptoAmount(cryptoAmount, cryptoCurrency.symbol, cryptoCurrency.decimals, locale)
|
||||
}
|
||||
|
||||
fun formatFiatAmount(
|
||||
|
|
|
|||
13
core/ui/src/main/res/drawable/ic_ducatus_22.xml
Normal file
13
core/ui/src/main/res/drawable/ic_ducatus_22.xml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="22dp"
|
||||
android:height="22dp"
|
||||
android:viewportWidth="22"
|
||||
android:viewportHeight="22">
|
||||
<group>
|
||||
<clip-path android:pathData="M11,0L11,0A11,11 0,0 1,22 11L22,11A11,11 0,0 1,11 22L11,22A11,11 0,0 1,0 11L0,11A11,11 0,0 1,11 0z" />
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M11.026,3.5L11.428,3.768L12.235,3.792L12.65,4.376L15.042,4.588L15.737,6.742L17.5,7.709L18.236,11.706L17.647,12.295L17.785,13.814L16.566,14.894L16.183,15.938L13.214,17.354L13.032,17.68H10.335L7.129,17.474L7.128,17.473L4.268,17.336L4.246,17.203L5.823,16.566V13.338L5.823,13.337L5.857,10.684V6.039L4,5.128L4.03,4.997H5.677L10.516,3.864L11.026,3.5ZM10.28,4.061L5.974,5.069L8.356,5.68L10.28,4.061ZM8.217,5.786L7.488,6.332L6.131,9.944L6.946,5.46L8.217,5.786ZM6.812,5.426L5.781,5.162L5.995,5.988V9.919L6.812,5.426ZM5.993,10.811L5.963,13.146L7.237,11.629L5.993,10.811ZM7.224,11.859L6.154,13.132L7.071,12.683L7.224,11.859ZM7.225,12.608L7.499,12.474L7.352,11.926L7.225,12.608ZM7.343,11.535L8.187,11.099L6.227,10.8L7.343,11.535ZM6.415,10.689L7.729,10.515L8.2,10.962L6.415,10.689ZM8.232,11.231L7.42,11.65L7.603,12.331L8.232,11.231ZM7.894,12.1L7.63,12.563L6.01,13.356L6.97,14.287L7.894,12.1ZM6.935,14.445L6.035,13.573L7.172,17.3L7.841,16.893L6.935,14.445ZM7.99,16.869L10.019,17.449L8.366,16.15L8.365,16.149L7.903,15.796L7.99,16.869ZM7.893,15.615L8.059,13.594L8.486,13.434L8.614,14.679L8.361,15.973L7.893,15.615ZM8.507,16.085L10.162,17.386L9.216,15.258L8.507,16.085ZM9.502,15.56L10.382,17.543H11.06L10.008,16.172L9.502,15.56ZM10.17,16.157L11.127,17.404L11.098,15.752L10.17,16.157ZM11.238,15.86L11.267,17.531L12.351,17.36L11.238,15.86ZM12.505,17.336L13,17.257L12.458,16.818L12.177,16.894L12.505,17.336ZM12.108,16.769L12.326,16.711L12.017,16.46L12.108,16.769ZM12.064,16.321L12.492,16.668L15.158,16.044L12.064,16.321ZM15.522,16.101L12.627,16.778L13.174,17.22L15.522,16.101ZM13.035,17.392L12.078,17.543H12.951L13.035,17.392ZM16.101,15.761L16.448,14.814L17.226,14.125L15.448,15.079L16.101,15.761ZM15.601,14.84L17.641,13.747L17.522,12.446L15.601,14.84ZM15.31,15.135L15.979,15.832L12.097,16.18L12.884,15.505L15.31,15.135ZM13.001,15.348L15.146,15.02L13.24,13.78L13.303,14.908L13.001,15.348ZM13.269,13.634L15.127,14.843L14.596,13.941L13.523,13.355L13.269,13.634ZM14.706,13.856L15.241,14.764L14.976,11.689L14.253,12.994L14.706,13.856ZM14.177,12.848L14.885,11.568L13.678,11.899L14.177,12.848ZM13.693,11.752L13.97,11.059L17.229,10.782L13.693,11.752ZM17.824,10.761L15.099,11.509L15.386,14.842L17.824,10.761ZM15.907,14.238L17.524,12.223L18.087,11.66L17.936,10.842L15.907,14.238ZM17.867,10.465L17.61,9.071L15.769,7.85L15.783,8.619L17.867,10.465ZM15.643,8.517L15.613,6.921L14.49,7.717L15.643,8.517ZM14.37,7.634L15.474,6.851L14.692,6.824L13.725,7.187L14.37,7.634ZM13.477,7.145L11.108,3.816L11.332,8.33L13.477,7.145ZM11.193,8.322L10.972,3.869L9.393,7.312L11.193,8.322ZM9.296,7.192L10.856,3.79L10.587,3.983L8.462,5.77L9.296,7.192ZM8.304,5.893L7.627,6.401L8.304,8.519V5.893ZM8.279,8.892L7.541,6.585L6.674,8.892H8.279ZM6.596,9.099L6.031,10.6L7.623,10.39L6.596,9.099ZM7.817,10.409L8.476,11.033L8.511,12.875L8.166,13.406L8.605,13.242L8.749,14.646L9.284,15.081L10.082,16.045L11.177,15.566L11.889,16.177L12.802,15.394L13.163,14.869L13.094,13.623L13.494,13.182L14.479,13.72L13.502,11.86L13.865,10.953L14.578,10.341L14.613,9.443L15.341,8.861L14.65,9.114L13.552,7.261L11.268,8.522L9.254,7.392L8.442,6.009V8.994L7.689,9.576L7.817,10.409ZM7.609,9.464L8.171,9.029H6.837L7.609,9.464ZM6.847,9.193L7.645,10.196L7.552,9.59L6.847,9.193ZM13.603,7.085L11.3,3.849L13.461,5.289L14.569,6.723L13.603,7.085ZM14.715,6.687L15.584,6.717L14.948,4.743L13.619,5.269L14.715,6.687ZM13.517,5.161L14.694,4.695L12.531,4.503L13.517,5.161ZM12.291,4.344L12.47,4.36L12.163,3.927L11.644,3.912L12.291,4.344ZM15.751,6.907L15.765,7.652L17.159,7.68L15.751,6.907ZM17.38,7.822L15.933,7.793L17.576,8.882L17.38,7.822ZM13.798,7.405L14.71,8.945L15.566,8.631L13.798,7.405ZM15.445,8.954L14.725,10.116L14.748,9.512L15.445,8.954ZM14.764,10.314L15.73,8.756L17.77,10.564L14.764,10.314ZM17.148,10.65L14.669,10.445L14.129,10.907L17.148,10.65ZM11.828,16.306L11.587,16.099L11.886,16.502L11.828,16.306ZM8.729,14.808L8.525,15.852L9.136,15.139L8.729,14.808ZM8.116,13.23L8.373,12.835L8.353,11.81L8.116,13.23ZM8.243,11.628L7.926,13.536L7.769,15.444L7.072,14.399L8.243,11.628ZM7.263,14.933L7.816,16.428L7.754,15.67L7.263,14.933ZM9.701,17.501L7.355,17.35L7.935,16.997L9.701,17.501ZM7.012,17.246L5.96,13.8V16.574L7.012,17.246ZM5.884,16.689L4.589,17.213L6.877,17.323L5.884,16.689ZM5.631,5.135L5.822,5.868L4.327,5.135H5.631ZM9.442,10.098L8.442,9.34H9.752L10.165,9.822V10.374L9.855,9.994H9.717L9.442,10.098ZM12.095,11.201V10.098L12.647,9.34H13.957L13.061,10.029H12.509L12.302,10.305L12.095,11.201ZM10.234,11.476L10.096,11.683L11.13,12.166V12.717L10.131,13.338H9.821V13.786L10.751,13.131H11.647L12.578,13.82V13.338H12.233L11.268,12.717V12.166L12.302,11.683L12.164,11.476H10.234ZM11.13,14.441L10.717,14.854H11.647L11.234,14.441H11.13Z" />
|
||||
</group>
|
||||
</vector>
|
||||
20
core/ui/src/main/res/drawable/img_ducatus_22.xml
Normal file
20
core/ui/src/main/res/drawable/img_ducatus_22.xml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="22dp"
|
||||
android:height="22dp"
|
||||
android:viewportWidth="22"
|
||||
android:viewportHeight="22">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M11,0L11,0A11,11 0,0 1,22 11L22,11A11,11 0,0 1,11 22L11,22A11,11 0,0 1,0 11L0,11A11,11 0,0 1,11 0z"/>
|
||||
<path
|
||||
android:pathData="M11,0L11,0A11,11 0,0 1,22 11L22,11A11,11 0,0 1,11 22L11,22A11,11 0,0 1,0 11L0,11A11,11 0,0 1,11 0z"
|
||||
android:fillColor="#E8E8E8"/>
|
||||
<path
|
||||
android:pathData="M5.754,5.072H4.134L5.892,6.007V10.476L6.615,8.882H8.201L8.27,6.769L9.235,7.323L11.234,8.432L13.095,7.462L13.922,7.878L14.715,9.09H15.301L14.198,9.644V10.372L13.612,10.995L13.095,11.757L13.715,12.589L13.509,12.935L14.094,13.489L13.509,13.212L13.095,13.731V14.805L11.992,16.399L11.061,15.741L10.165,16.052L9.166,15.221L8.27,15.394L8.408,13.489H8.028L8.477,12.866L8.408,11.099L7.615,12.519L5.892,13.316V16.641L4.41,17.403L7.408,17.542L11.234,17.715L12.44,17.403L12.199,16.884L16.025,15.914L16.404,14.979L17.61,13.731L17.231,12.104L17.92,10.995L17.231,9.471L17.403,8.778L17.231,7.982L16.68,7.704L16.438,6.873L15.611,6.769L15.542,6.596L14.991,5.799L14.715,5.072L12.819,4.76L12.095,4.31L10.889,3.825L10.545,4.414L5.754,5.072Z"
|
||||
android:fillColor="#911413"/>
|
||||
<path
|
||||
android:pathData="M11.026,3.5L11.428,3.768L12.235,3.792L12.65,4.376L15.042,4.588L15.737,6.742L17.5,7.709L18.236,11.706L17.647,12.295L17.785,13.814L16.566,14.894L16.183,15.938L13.214,17.354L13.032,17.68H10.335L7.129,17.474L7.128,17.473L4.268,17.336L4.246,17.203L5.823,16.566V13.338L5.823,13.337L5.857,10.684V6.039L4,5.128L4.03,4.997H5.677L10.516,3.864L11.026,3.5ZM10.28,4.061L5.974,5.069L8.356,5.68L10.28,4.061ZM8.217,5.786L7.488,6.332L6.131,9.944L6.946,5.46L8.217,5.786ZM6.812,5.426L5.781,5.162L5.995,5.988V9.919L6.812,5.426ZM5.993,10.811L5.963,13.146L7.237,11.629L5.993,10.811ZM7.224,11.859L6.154,13.132L7.071,12.683L7.224,11.859ZM7.225,12.608L7.499,12.474L7.352,11.926L7.225,12.608ZM7.343,11.535L8.187,11.099L6.227,10.8L7.343,11.535ZM6.415,10.689L7.729,10.515L8.2,10.962L6.415,10.689ZM8.232,11.231L7.42,11.65L7.603,12.331L8.232,11.231ZM7.894,12.1L7.63,12.563L6.01,13.356L6.97,14.287L7.894,12.1ZM6.935,14.445L6.035,13.573L7.172,17.3L7.841,16.893L6.935,14.445ZM7.99,16.869L10.019,17.449L8.366,16.15L8.365,16.149L7.903,15.796L7.99,16.869ZM7.893,15.615L8.059,13.594L8.486,13.434L8.614,14.679L8.361,15.973L7.893,15.615ZM8.507,16.085L10.162,17.386L9.216,15.258L8.507,16.085ZM9.502,15.56L10.382,17.543H11.06L10.008,16.172L9.502,15.56ZM10.17,16.157L11.127,17.404L11.098,15.752L10.17,16.157ZM11.238,15.86L11.267,17.531L12.351,17.36L11.238,15.86ZM12.505,17.336L13,17.257L12.458,16.818L12.177,16.894L12.505,17.336ZM12.108,16.769L12.326,16.711L12.017,16.46L12.108,16.769ZM12.064,16.321L12.492,16.668L15.158,16.044L12.064,16.321ZM15.522,16.101L12.627,16.778L13.174,17.22L15.522,16.101ZM13.035,17.392L12.078,17.543H12.951L13.035,17.392ZM16.101,15.761L16.448,14.814L17.226,14.125L15.448,15.079L16.101,15.761ZM15.601,14.84L17.641,13.747L17.522,12.446L15.601,14.84ZM15.31,15.135L15.979,15.832L12.097,16.18L12.884,15.505L15.31,15.135ZM13.001,15.348L15.146,15.02L13.24,13.78L13.303,14.908L13.001,15.348ZM13.269,13.634L15.127,14.843L14.596,13.941L13.523,13.355L13.269,13.634ZM14.706,13.856L15.241,14.764L14.976,11.689L14.253,12.994L14.706,13.856ZM14.177,12.848L14.885,11.568L13.678,11.899L14.177,12.848ZM13.693,11.752L13.97,11.059L17.229,10.782L13.693,11.752ZM17.824,10.761L15.099,11.509L15.386,14.842L17.824,10.761ZM15.907,14.238L17.524,12.223L18.087,11.66L17.936,10.842L15.907,14.238ZM17.867,10.465L17.61,9.071L15.769,7.85L15.783,8.619L17.867,10.465ZM15.643,8.517L15.613,6.921L14.49,7.717L15.643,8.517ZM14.37,7.634L15.474,6.851L14.692,6.824L13.725,7.187L14.37,7.634ZM13.477,7.145L11.108,3.816L11.332,8.33L13.477,7.145ZM11.193,8.322L10.972,3.869L9.393,7.312L11.193,8.322ZM9.296,7.192L10.856,3.79L10.587,3.983L8.462,5.77L9.296,7.192ZM8.304,5.893L7.627,6.401L8.304,8.519V5.893ZM8.279,8.892L7.541,6.585L6.674,8.892H8.279ZM6.596,9.099L6.031,10.6L7.623,10.39L6.596,9.099ZM7.817,10.409L8.476,11.033L8.511,12.875L8.166,13.406L8.605,13.242L8.749,14.646L9.284,15.081L10.082,16.045L11.177,15.566L11.889,16.177L12.802,15.394L13.163,14.869L13.094,13.623L13.494,13.182L14.479,13.72L13.502,11.86L13.865,10.953L14.578,10.341L14.613,9.443L15.341,8.861L14.65,9.114L13.552,7.261L11.268,8.522L9.254,7.392L8.442,6.009V8.994L7.689,9.576L7.817,10.409ZM7.609,9.464L8.171,9.029H6.837L7.609,9.464ZM6.847,9.193L7.645,10.196L7.552,9.59L6.847,9.193ZM13.603,7.085L11.3,3.849L13.461,5.289L14.569,6.723L13.603,7.085ZM14.715,6.687L15.584,6.717L14.948,4.743L13.619,5.269L14.715,6.687ZM13.517,5.161L14.694,4.695L12.531,4.503L13.517,5.161ZM12.291,4.344L12.47,4.36L12.163,3.927L11.644,3.912L12.291,4.344ZM15.751,6.907L15.765,7.652L17.159,7.68L15.751,6.907ZM17.38,7.822L15.933,7.793L17.576,8.882L17.38,7.822ZM13.798,7.405L14.71,8.945L15.566,8.631L13.798,7.405ZM15.445,8.954L14.725,10.116L14.748,9.512L15.445,8.954ZM14.764,10.314L15.73,8.756L17.77,10.564L14.764,10.314ZM17.148,10.65L14.669,10.445L14.129,10.907L17.148,10.65ZM11.828,16.306L11.587,16.099L11.886,16.502L11.828,16.306ZM8.729,14.808L8.525,15.852L9.136,15.139L8.729,14.808ZM8.116,13.23L8.373,12.835L8.353,11.81L8.116,13.23ZM8.243,11.628L7.926,13.536L7.769,15.444L7.072,14.399L8.243,11.628ZM7.263,14.933L7.816,16.428L7.754,15.67L7.263,14.933ZM9.701,17.501L7.355,17.35L7.935,16.997L9.701,17.501ZM7.012,17.246L5.96,13.8V16.574L7.012,17.246ZM5.884,16.689L4.589,17.213L6.877,17.323L5.884,16.689ZM5.631,5.135L5.822,5.868L4.327,5.135H5.631ZM9.442,10.098L8.442,9.34H9.752L10.165,9.822V10.374L9.855,9.994H9.717L9.442,10.098ZM12.095,11.201V10.098L12.647,9.34H13.957L13.061,10.029H12.509L12.302,10.305L12.095,11.201ZM10.234,11.476L10.096,11.683L11.13,12.166V12.717L10.131,13.338H9.821V13.786L10.751,13.131H11.647L12.578,13.82V13.338H12.233L11.268,12.717V12.166L12.302,11.683L12.164,11.476H10.234ZM11.13,14.441L10.717,14.854H11.647L11.234,14.441H11.13Z"
|
||||
android:fillColor="#C79960"
|
||||
android:fillType="evenOdd"/>
|
||||
</group>
|
||||
</vector>
|
||||
|
|
@ -138,7 +138,13 @@ internal class DefaultCurrenciesRepository(
|
|||
storeAndPushTokens(
|
||||
userWalletId = userWalletId,
|
||||
response = savedCurrencies.copy(
|
||||
tokens = savedCurrencies.tokens.filter { it != token },
|
||||
tokens = savedCurrencies.tokens.filterNot {
|
||||
// it's better to compare by fields, to support renaming and etc
|
||||
it.contractAddress == token.contractAddress &&
|
||||
it.networkId == token.networkId &&
|
||||
it.derivationPath == token.derivationPath &&
|
||||
it.decimals == token.decimals
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -260,5 +260,5 @@ private const val NODL_AMOUNT_TO_CREATE_ACCOUNT = 1.5
|
|||
|
||||
private val excludedBlockchains = listOf(
|
||||
Blockchain.Unknown,
|
||||
Blockchain.Ducatus,
|
||||
Blockchain.Playa3ull,
|
||||
)
|
||||
|
|
@ -321,6 +321,6 @@ data class CardDTO(
|
|||
}
|
||||
|
||||
companion object {
|
||||
const val RING_BATCH_ID = "AC17"
|
||||
val RING_BATCH_IDS = listOf("AC17", "BA01")
|
||||
}
|
||||
}
|
||||
|
|
@ -12,7 +12,9 @@ import com.tangem.domain.tokens.model.CryptoCurrency
|
|||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlin.collections.set
|
||||
|
||||
class ApplyTokenListSortingUseCase(
|
||||
private val currenciesRepository: CurrenciesRepository,
|
||||
|
|
@ -25,11 +27,17 @@ class ApplyTokenListSortingUseCase(
|
|||
isGroupedByNetwork: Boolean,
|
||||
isSortedByBalance: Boolean,
|
||||
): Either<TokenListSortingError, Unit> {
|
||||
return withContext(dispatchers.default) {
|
||||
either {
|
||||
return either {
|
||||
val storedCurrencies = getCurrencies(userWalletId)
|
||||
val isSortingTypeChanged = checkIsCurrenciesSortedByBalance(userWalletId) != isSortedByBalance
|
||||
val isGroupingTypeChanged = checkIsCurrenciesGroupedByNetwork(userWalletId) != isGroupedByNetwork
|
||||
|
||||
val sortedCurrencies = sortTokens(sortedTokensIds, storedCurrencies)
|
||||
|
||||
if (storedCurrencies != sortedCurrencies || isSortingTypeChanged || isGroupingTypeChanged) {
|
||||
applySorting(
|
||||
userWalletId = userWalletId,
|
||||
tokens = sortTokens(sortedTokensIds, getCurrencies(userWalletId)),
|
||||
currencies = sortedCurrencies,
|
||||
isGrouped = isGroupedByNetwork,
|
||||
isSortedByBalance = isSortedByBalance,
|
||||
)
|
||||
|
|
@ -37,17 +45,29 @@ class ApplyTokenListSortingUseCase(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun Raise<TokenListSortingError>.checkIsCurrenciesSortedByBalance(userWalletId: UserWalletId) =
|
||||
catch(
|
||||
block = { currenciesRepository.isTokensSortedByBalance(userWalletId).firstOrNull() ?: false },
|
||||
catch = { raise(TokenListSortingError.DataError(it)) },
|
||||
)
|
||||
|
||||
private suspend fun Raise<TokenListSortingError>.checkIsCurrenciesGroupedByNetwork(userWalletId: UserWalletId) =
|
||||
catch(
|
||||
block = { currenciesRepository.isTokensGrouped(userWalletId).firstOrNull() ?: false },
|
||||
catch = { raise(TokenListSortingError.DataError(it)) },
|
||||
)
|
||||
|
||||
private suspend fun Raise<TokenListSortingError>.sortTokens(
|
||||
sortedTokensIds: List<CryptoCurrency.ID>,
|
||||
unsortedTokens: List<CryptoCurrency>,
|
||||
sortedCurrenciesIds: List<CryptoCurrency.ID>,
|
||||
unsortedCurrencies: List<CryptoCurrency>,
|
||||
): List<CryptoCurrency> = withContext(dispatchers.default) {
|
||||
val nonEmptySortedTokensIds = ensureNotNull(sortedTokensIds.toNonEmptySetOrNull()) {
|
||||
val nonEmptySortedTokensIds = ensureNotNull(sortedCurrenciesIds.toNonEmptySetOrNull()) {
|
||||
TokenListSortingError.TokenListIsEmpty
|
||||
}
|
||||
|
||||
val sortedTokens = sortedMapOf<Int, CryptoCurrency>()
|
||||
|
||||
unsortedTokens.distinct().forEach { currency ->
|
||||
unsortedCurrencies.distinct().forEach { currency ->
|
||||
val index = nonEmptySortedTokensIds.indexOfFirst { currencyId ->
|
||||
currencyId == currency.id
|
||||
}
|
||||
|
|
@ -79,12 +99,12 @@ class ApplyTokenListSortingUseCase(
|
|||
|
||||
private suspend fun Raise<TokenListSortingError>.applySorting(
|
||||
userWalletId: UserWalletId,
|
||||
tokens: List<CryptoCurrency>,
|
||||
currencies: List<CryptoCurrency>,
|
||||
isGrouped: Boolean,
|
||||
isSortedByBalance: Boolean,
|
||||
) = withContext(dispatchers.io) {
|
||||
catch(
|
||||
block = { currenciesRepository.saveTokens(userWalletId, tokens, isGrouped, isSortedByBalance) },
|
||||
block = { currenciesRepository.saveTokens(userWalletId, currencies, isGrouped, isSortedByBalance) },
|
||||
catch = { raise(TokenListSortingError.DataError(it)) },
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ internal class ApplyTokenListSortingUseCaseTest {
|
|||
// When
|
||||
val result = useCase(
|
||||
userWalletId = userWalletId,
|
||||
sortedTokensIds = MockTokens.tokens.map { it.id },
|
||||
sortedTokensIds = MockTokens.tokens.map { it.id }.sortedByDescending { it.value },
|
||||
isGroupedByNetwork = false,
|
||||
isSortedByBalance = false,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,20 +6,24 @@ import android.net.Uri
|
|||
import android.os.Bundle
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleEventObserver
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import com.google.mlkit.vision.common.InputImage
|
||||
import com.tangem.core.ui.components.SystemBarsEffect
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.screen.ComposeFragment
|
||||
import com.tangem.core.ui.theme.AppThemeModeHolder
|
||||
import com.tangem.feature.qrscanning.inner.MLKitBarcodeAnalyzer
|
||||
import com.tangem.feature.qrscanning.navigation.QrScanningInnerRouter
|
||||
import com.tangem.feature.qrscanning.presentation.QrScanningContent
|
||||
import com.tangem.feature.qrscanning.viewmodel.QrScanningViewModel
|
||||
import com.tangem.feature.qrscanning.inner.MLKitBarcodeAnalyzer
|
||||
import com.tangem.feature.qrscanning.navigation.QrScanningInnerRouter
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
|
|
@ -67,22 +71,18 @@ internal class QrScanningFragment : ComposeFragment() {
|
|||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
setFitSystemWindows(fit = true)
|
||||
cameraPermissionLauncher.unregister()
|
||||
cameraExecutor.shutdown()
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun ScreenContent(modifier: Modifier) {
|
||||
SystemBarsEffect {
|
||||
setSystemBarsColor(Color.Transparent, darkIcons = false)
|
||||
}
|
||||
StatusBarTransparencyDisposable()
|
||||
QrScanningContent(
|
||||
executor = { cameraExecutor },
|
||||
analyzer = { analyzer },
|
||||
uiState = viewModel.uiState.collectAsStateWithLifecycle().value,
|
||||
)
|
||||
setFitSystemWindows(fit = false)
|
||||
}
|
||||
|
||||
private fun requestCameraPermission() {
|
||||
|
|
@ -96,14 +96,33 @@ internal class QrScanningFragment : ComposeFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun setFitSystemWindows(fit: Boolean) {
|
||||
activity?.window?.let {
|
||||
WindowCompat.setDecorFitsSystemWindows(it, fit)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun create() = QrScanningFragment()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun StatusBarTransparencyDisposable() {
|
||||
val systemUiController = rememberSystemUiController()
|
||||
val systemBarsColor = TangemTheme.colors.background.secondary
|
||||
val lifecycleOwner = LocalLifecycleOwner.current
|
||||
DisposableEffect(Unit) {
|
||||
val observer = LifecycleEventObserver { _, event ->
|
||||
if (event == Lifecycle.Event.ON_START) {
|
||||
systemUiController.setSystemBarsColor(
|
||||
color = Color.Transparent,
|
||||
darkIcons = false,
|
||||
)
|
||||
}
|
||||
if (event == Lifecycle.Event.ON_STOP) {
|
||||
systemUiController.setSystemBarsColor(systemBarsColor)
|
||||
}
|
||||
}
|
||||
lifecycleOwner.lifecycle.addObserver(observer)
|
||||
|
||||
onDispose {
|
||||
lifecycleOwner.lifecycle.removeObserver(observer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -665,7 +665,9 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
blockchain == Blockchain.Aptos -> {
|
||||
Fee.Aptos(
|
||||
amount = feeAmount,
|
||||
gasUnitPrice = fee.feeValue.toLong() / fee.gasLimit,
|
||||
gasUnitPrice = fee.feeValue.divide(BigDecimal(fee.gasLimit))
|
||||
.movePointRight(Blockchain.Aptos.decimals())
|
||||
.toLong(),
|
||||
gasLimit = fee.gasLimit.toLong(),
|
||||
)
|
||||
}
|
||||
|
|
@ -1358,7 +1360,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
is Fee.Common -> 0
|
||||
is Fee.Ethereum -> gasLimit.toInt()
|
||||
is Fee.VeChain -> gasLimit.toInt()
|
||||
is Fee.Aptos -> amount.longValue?.div(gasUnitPrice)?.toInt() ?: 0
|
||||
is Fee.Aptos -> gasLimit.toInt()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
package com.tangem.feature.swap.models.states
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
||||
@Immutable
|
||||
sealed class ProviderState {
|
||||
|
||||
abstract val onProviderClick: ((String) -> Unit)?
|
||||
|
|
@ -26,6 +28,7 @@ sealed class ProviderState {
|
|||
val selectionType: SelectionType,
|
||||
val additionalBadge: AdditionalBadge,
|
||||
val percentLowerThenBest: PercentDifference = PercentDifference.Empty,
|
||||
val namePrefix: PrefixType,
|
||||
override val onProviderClick: (String) -> Unit,
|
||||
) : ProviderState()
|
||||
|
||||
|
|
@ -39,17 +42,25 @@ sealed class ProviderState {
|
|||
override val onProviderClick: ((String) -> Unit)? = null,
|
||||
) : ProviderState()
|
||||
|
||||
@Immutable
|
||||
sealed class AdditionalBadge {
|
||||
object BestTrade : AdditionalBadge()
|
||||
object Empty : AdditionalBadge()
|
||||
object PermissionRequired : AdditionalBadge()
|
||||
}
|
||||
|
||||
@Immutable
|
||||
enum class SelectionType {
|
||||
NONE, CLICK, SELECT
|
||||
}
|
||||
|
||||
@Immutable
|
||||
enum class PrefixType {
|
||||
NONE, PROVIDED_BY
|
||||
}
|
||||
}
|
||||
|
||||
@Immutable
|
||||
sealed class PercentDifference {
|
||||
data class Value(val value: Float) : PercentDifference()
|
||||
object Empty : PercentDifference()
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ private fun ChooseProviderBottomSheet_Preview() {
|
|||
additionalBadge = ProviderState.AdditionalBadge.BestTrade,
|
||||
percentLowerThenBest = PercentDifference.Value(-1.0f),
|
||||
selectionType = ProviderState.SelectionType.SELECT,
|
||||
namePrefix = ProviderState.PrefixType.NONE,
|
||||
onProviderClick = {},
|
||||
),
|
||||
ProviderState.Unavailable(
|
||||
|
|
|
|||
|
|
@ -17,11 +17,12 @@ import androidx.compose.ui.res.painterResource
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import coil.compose.SubcomposeAsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.SpacerH24
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
|
@ -110,12 +111,14 @@ private fun ProviderContentState(
|
|||
modifier = Modifier.padding(start = TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
Row {
|
||||
Text(
|
||||
text = stringResource(id = R.string.express_by_provider),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
modifier = Modifier.padding(end = TangemTheme.dimens.spacing4),
|
||||
)
|
||||
if (state.namePrefix == ProviderState.PrefixType.PROVIDED_BY) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.express_by_provider),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
modifier = Modifier.padding(end = TangemTheme.dimens.spacing4),
|
||||
)
|
||||
}
|
||||
AnimatedContent(targetState = state.name, label = "") {
|
||||
Text(
|
||||
text = it,
|
||||
|
|
@ -390,75 +393,76 @@ private fun PermissionBadgeItem(modifier: Modifier = Modifier) {
|
|||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
// region Preview
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun ProviderItem_Loading_Preview() {
|
||||
Column {
|
||||
TangemTheme(isDark = false) {
|
||||
ProviderItemBlock(state = ProviderState.Loading())
|
||||
}
|
||||
|
||||
SpacerH24()
|
||||
|
||||
TangemTheme(isDark = true) {
|
||||
ProviderItemBlock(state = ProviderState.Loading())
|
||||
}
|
||||
private fun ProviderItemPreview_Light(
|
||||
@PreviewParameter(ProviderItemParameterProvider::class) state: Pair<ProviderState, Boolean>,
|
||||
) {
|
||||
TangemTheme {
|
||||
ProviderItem(
|
||||
modifier = Modifier.background(TangemTheme.colors.background.action),
|
||||
state = state.first,
|
||||
isSelected = state.second,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun ProviderItem_Content_Preview() {
|
||||
val state = ProviderState.Content(
|
||||
id = "1",
|
||||
name = "1inch",
|
||||
type = "DEX",
|
||||
iconUrl = "",
|
||||
subtitle = stringReference("1 000 000"),
|
||||
additionalBadge = ProviderState.AdditionalBadge.PermissionRequired,
|
||||
percentLowerThenBest = PercentDifference.Value(12.0f),
|
||||
selectionType = ProviderState.SelectionType.SELECT,
|
||||
onProviderClick = {},
|
||||
)
|
||||
Column {
|
||||
TangemTheme(isDark = false) {
|
||||
ProviderItemBlock(state = state)
|
||||
}
|
||||
|
||||
SpacerH24()
|
||||
|
||||
TangemTheme(isDark = true) {
|
||||
ProviderItemBlock(state = state)
|
||||
}
|
||||
private fun ProviderItemPreview_Dark(
|
||||
@PreviewParameter(ProviderItemParameterProvider::class) state: Pair<ProviderState, Boolean>,
|
||||
) {
|
||||
TangemTheme(isDark = true) {
|
||||
ProviderItem(
|
||||
modifier = Modifier.background(TangemTheme.colors.background.action),
|
||||
state = state.first,
|
||||
isSelected = state.second,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun ProviderItem_Unavailable_Preview() {
|
||||
val state = ProviderState.Unavailable(
|
||||
id = "1",
|
||||
name = "1inch",
|
||||
type = "DEX",
|
||||
iconUrl = "",
|
||||
selectionType = ProviderState.SelectionType.SELECT,
|
||||
alertText = stringReference("Unavailable"),
|
||||
)
|
||||
Column {
|
||||
TangemTheme(isDark = false) {
|
||||
ProviderItemBlock(state = state)
|
||||
}
|
||||
private class ProviderItemParameterProvider : CollectionPreviewParameterProvider<Pair<ProviderState, Boolean>>(
|
||||
collection = buildList {
|
||||
val contentState = ProviderState.Content(
|
||||
id = "1",
|
||||
name = "1inch",
|
||||
type = "DEX",
|
||||
iconUrl = "",
|
||||
subtitle = stringReference(value = "0,64554846 DAI ≈ 1 MATIC"),
|
||||
additionalBadge = ProviderState.AdditionalBadge.Empty,
|
||||
percentLowerThenBest = PercentDifference.Value(value = 12.0f),
|
||||
selectionType = ProviderState.SelectionType.SELECT,
|
||||
namePrefix = ProviderState.PrefixType.PROVIDED_BY,
|
||||
onProviderClick = {},
|
||||
)
|
||||
val contentState2 = contentState.copy(
|
||||
subtitle = stringReference(value = "1 132,46 MATIC"),
|
||||
additionalBadge = ProviderState.AdditionalBadge.PermissionRequired,
|
||||
percentLowerThenBest = PercentDifference.Value(value = 5f),
|
||||
)
|
||||
val unavailableState = ProviderState.Unavailable(
|
||||
id = "1",
|
||||
name = "1inch",
|
||||
type = "DEX",
|
||||
iconUrl = "",
|
||||
alertText = stringReference(value = "Not available"),
|
||||
selectionType = ProviderState.SelectionType.SELECT,
|
||||
onProviderClick = {},
|
||||
)
|
||||
val loadingState = ProviderState.Loading()
|
||||
|
||||
SpacerH24()
|
||||
add(contentState to true)
|
||||
add(contentState to false)
|
||||
|
||||
TangemTheme(isDark = true) {
|
||||
ProviderItemBlock(state = state)
|
||||
}
|
||||
add(contentState2 to true)
|
||||
add(contentState2 to false)
|
||||
|
||||
SpacerH24()
|
||||
add(unavailableState to true)
|
||||
add(unavailableState to false)
|
||||
|
||||
TangemTheme(isDark = true) {
|
||||
ProviderItem(state = state, isSelected = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
add(loadingState to true)
|
||||
add(loadingState to false)
|
||||
},
|
||||
)
|
||||
// endregion Preview
|
||||
|
|
@ -1287,6 +1287,7 @@ internal class StateBuilder(
|
|||
additionalBadge = badge,
|
||||
selectionType = selectionType,
|
||||
percentLowerThenBest = PercentDifference.Empty,
|
||||
namePrefix = ProviderState.PrefixType.PROVIDED_BY,
|
||||
onProviderClick = onProviderClick,
|
||||
)
|
||||
}
|
||||
|
|
@ -1318,6 +1319,7 @@ internal class StateBuilder(
|
|||
percentLowerThenBest = pricesLowerBest[this.providerId]?.let { percent ->
|
||||
PercentDifference.Value(percent)
|
||||
} ?: PercentDifference.Value(0f),
|
||||
namePrefix = ProviderState.PrefixType.NONE,
|
||||
onProviderClick = onProviderClick,
|
||||
)
|
||||
}
|
||||
|
|
@ -1352,6 +1354,11 @@ internal class StateBuilder(
|
|||
subtitle = alertText,
|
||||
additionalBadge = ProviderState.AdditionalBadge.Empty,
|
||||
percentLowerThenBest = PercentDifference.Empty,
|
||||
namePrefix = if (selectionType != ProviderState.SelectionType.SELECT) {
|
||||
ProviderState.PrefixType.PROVIDED_BY
|
||||
} else {
|
||||
ProviderState.PrefixType.NONE
|
||||
},
|
||||
onProviderClick = onProviderClick,
|
||||
)
|
||||
}
|
||||
|
|
@ -1377,7 +1384,7 @@ internal class StateBuilder(
|
|||
}
|
||||
|
||||
private fun SwapAmount.getFormattedCryptoAmount(token: CryptoCurrency): String {
|
||||
return "${this.formatToUIRepresentation()} ${token.symbol}"
|
||||
return BigDecimalFormatter.formatCryptoAmount(value, token.symbol, token.decimals)
|
||||
}
|
||||
|
||||
private fun BigDecimal.calculateRate(to: BigDecimal, decimals: Int): BigDecimal {
|
||||
|
|
|
|||
|
|
@ -147,9 +147,7 @@ internal class OrganizeTokensViewModel @Inject constructor(
|
|||
ifLeft = stateHolder::updateStateWithError,
|
||||
ifRight = {
|
||||
stateHolder.updateStateToHideProgress()
|
||||
withContext(
|
||||
dispatchers.main,
|
||||
) { router.popBackStack() }
|
||||
withContext(dispatchers.main) { router.popBackStack() }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,11 +13,14 @@ import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTokenListErrorTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTokenListTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
|
||||
import com.tangem.utils.coroutines.JobHolder
|
||||
import com.tangem.utils.coroutines.saveIn
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
internal typealias MaybeTokenListFlow = Flow<Either<TokenListError, TokenList>>
|
||||
|
|
@ -32,24 +35,25 @@ internal abstract class BasicTokenListSubscriber(
|
|||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
) : WalletSubscriber() {
|
||||
|
||||
protected abstract fun tokenListFlow(): MaybeTokenListFlow
|
||||
private val sendAnalyticsJobHolder = JobHolder()
|
||||
private val onTokenListReceivedJobHolder = JobHolder()
|
||||
|
||||
protected open suspend fun onTokenListReceived(tokenList: TokenList) { /* no-op */
|
||||
}
|
||||
protected abstract fun tokenListFlow(): MaybeTokenListFlow
|
||||
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<*> {
|
||||
return combine(
|
||||
flow = tokenListFlow()
|
||||
.onEach { maybeTokenList ->
|
||||
val displayedState = stateHolder.getWalletStateIfSelected(userWallet.walletId)
|
||||
|
||||
tokenListAnalyticsSender.send(
|
||||
displayedUiState = displayedState,
|
||||
userWallet = userWallet,
|
||||
tokenList = maybeTokenList.getOrElse { return@onEach },
|
||||
)
|
||||
coroutineScope.launch {
|
||||
sendTokenListAnalytics(maybeTokenList)
|
||||
}.saveIn(sendAnalyticsJobHolder)
|
||||
}
|
||||
.distinctUntilChanged(),
|
||||
.distinctUntilChanged()
|
||||
.onEach { maybeTokenList ->
|
||||
coroutineScope.launch {
|
||||
onTokenListReceived(maybeTokenList)
|
||||
}.saveIn(onTokenListReceivedJobHolder)
|
||||
},
|
||||
flow2 = getSelectedAppCurrencyUseCase().distinctUntilChanged(),
|
||||
transform = { maybeTokenList, maybeAppCurrency ->
|
||||
val tokenList = maybeTokenList.getOrElse { e ->
|
||||
|
|
@ -64,11 +68,24 @@ internal abstract class BasicTokenListSubscriber(
|
|||
|
||||
updateContent(tokenList, appCurrency)
|
||||
walletWithFundsChecker.check(tokenList)
|
||||
onTokenListReceived(tokenList)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
protected open suspend fun onTokenListReceived(maybeTokenList: Either<TokenListError, TokenList>) {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
private suspend fun sendTokenListAnalytics(maybeTokenList: Either<TokenListError, TokenList>) {
|
||||
val displayedState = stateHolder.getWalletStateIfSelected(userWallet.walletId)
|
||||
|
||||
tokenListAnalyticsSender.send(
|
||||
displayedUiState = displayedState,
|
||||
userWallet = userWallet,
|
||||
tokenList = maybeTokenList.getOrElse { return },
|
||||
)
|
||||
}
|
||||
|
||||
private fun updateContent(tokenList: TokenList, appCurrency: AppCurrency) {
|
||||
stateHolder.update(
|
||||
SetTokenListTransformer(
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
|
||||
import com.tangem.domain.tokens.GetTokenListUseCase
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.TokenList
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
|
||||
|
|
@ -31,28 +35,34 @@ internal class MultiWalletTokenListSubscriber(
|
|||
|
||||
override fun tokenListFlow(): MaybeTokenListFlow = getTokenListUseCase(userWallet.walletId)
|
||||
|
||||
override suspend fun onTokenListReceived(tokenList: TokenList) {
|
||||
updateSortingIfNeeded(tokenList)
|
||||
override suspend fun onTokenListReceived(maybeTokenList: Either<TokenListError, TokenList>) {
|
||||
updateSortingIfNeeded(maybeTokenList)
|
||||
}
|
||||
|
||||
private suspend fun updateSortingIfNeeded(tokenList: TokenList) {
|
||||
if (tokenList.totalFiatBalance is TokenList.FiatBalance.Loading ||
|
||||
tokenList.sortedBy == TokenList.SortType.NONE
|
||||
) {
|
||||
return
|
||||
}
|
||||
private suspend fun updateSortingIfNeeded(maybeTokenList: Either<TokenListError, TokenList>) {
|
||||
val tokenList = maybeTokenList.getOrElse { return }
|
||||
if (!checkNeedSorting(tokenList)) return
|
||||
|
||||
applyTokenListSortingUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
sortedTokensIds = when (tokenList) {
|
||||
is TokenList.GroupedByNetwork -> tokenList.groups.flatMap { group ->
|
||||
group.currencies.map { it.currency.id }
|
||||
}
|
||||
is TokenList.Ungrouped -> tokenList.currencies.map { it.currency.id }
|
||||
is TokenList.Empty -> return
|
||||
},
|
||||
sortedTokensIds = getCurrenciesIds(tokenList),
|
||||
isGroupedByNetwork = tokenList is TokenList.GroupedByNetwork,
|
||||
isSortedByBalance = true,
|
||||
isSortedByBalance = tokenList.sortedBy == TokenList.SortType.BALANCE,
|
||||
)
|
||||
}
|
||||
|
||||
private fun checkNeedSorting(tokenList: TokenList): Boolean {
|
||||
return tokenList.totalFiatBalance !is TokenList.FiatBalance.Loading &&
|
||||
tokenList.sortedBy == TokenList.SortType.BALANCE
|
||||
}
|
||||
|
||||
private fun getCurrenciesIds(tokenList: TokenList): List<CryptoCurrency.ID> {
|
||||
return when (tokenList) {
|
||||
is TokenList.GroupedByNetwork -> tokenList.groups.flatMap { group ->
|
||||
group.currencies.map { it.currency.id }
|
||||
}
|
||||
is TokenList.Ungrouped -> tokenList.currencies.map { it.currency.id }
|
||||
is TokenList.Empty -> emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -85,7 +85,7 @@ web3j = "4.10.1"
|
|||
# endregion Other libraries
|
||||
|
||||
# region Tangem
|
||||
tangemBlockchainSdk = "develop-503"
|
||||
tangemBlockchainSdk = "develop-505"
|
||||
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
|
||||
tangemCardSdk = "develop-324"
|
||||
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue