Updated on 2026-08-14
This commit is contained in:
commit
b9c8d4fed1
28 changed files with 511 additions and 442 deletions
|
|
@ -79,7 +79,8 @@ dependencies {
|
|||
implementation 'com.google.android.play:core-ktx:1.8.1'
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
||||
|
||||
implementation 'com.tangem:blockchain:develop-65'
|
||||
implementation 'com.tangem:blockchain:develop-66'
|
||||
// implementation 'com.tangem:blockchain:0.0.1'
|
||||
implementation 'com.tangem.tangem-sdk-kotlin:core:develop-140'
|
||||
implementation 'com.tangem.tangem-sdk-kotlin:android:develop-140'
|
||||
|
||||
|
|
|
|||
|
|
@ -7,10 +7,6 @@ import kotlinx.coroutines.withContext
|
|||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
suspend fun <T> withMainContext(block: suspend CoroutineScope.() -> T) {
|
||||
withContext(Dispatchers.Main, block)
|
||||
}
|
||||
suspend fun <T> withMainContext(block: suspend CoroutineScope.() -> T): T = withContext(Dispatchers.Main, block)
|
||||
|
||||
suspend fun <T> withIOContext(block: suspend CoroutineScope.() -> T) {
|
||||
withContext(Dispatchers.IO, block)
|
||||
}
|
||||
suspend fun <T> withIOContext(block: suspend CoroutineScope.() -> T): T = withContext(Dispatchers.IO, block)
|
||||
|
|
@ -18,6 +18,10 @@ fun Store<*>.dispatchOnMain(action: Action) {
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun dispatchOnMain(vararg actions: Action) {
|
||||
withMainContext { actions.forEach { store.dispatch(it) } }
|
||||
}
|
||||
|
||||
suspend fun Store<*>.onCardScanned(scanResponse: ScanResponse) {
|
||||
store.state.globalState.tapWalletManager.onCardScanned(scanResponse)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.tap.common.extensions.compose
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.core.graphics.alpha
|
||||
import androidx.core.graphics.blue
|
||||
import androidx.core.graphics.green
|
||||
import androidx.core.graphics.red
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
fun Color.argb(): Int {
|
||||
val argb = this.toArgb()
|
||||
return android.graphics.Color.argb(argb.alpha, argb.red, argb.green, argb.blue)
|
||||
}
|
||||
|
|
@ -4,10 +4,10 @@ import com.tangem.blockchain.blockchains.solana.RentProvider
|
|||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.tap.common.ThrottlerWithValues
|
||||
import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.safeUpdate
|
||||
import com.tangem.tap.common.extensions.stripZeroPlainString
|
||||
import com.tangem.tap.common.extensions.withMainContext
|
||||
import com.tangem.tap.common.redux.global.FiatCurrencyName
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.currenciesRepository
|
||||
|
|
@ -25,11 +25,8 @@ import com.tangem.tap.features.wallet.redux.Currency
|
|||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.network.NetworkConnectivity
|
||||
import com.tangem.tap.network.coinmarketcap.CoinMarketCapService
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.math.BigDecimal
|
||||
|
||||
|
|
@ -49,32 +46,30 @@ class TapWalletManager {
|
|||
suspend fun loadWalletData(walletManager: WalletManager) {
|
||||
val blockchain = walletManager.wallet.blockchain
|
||||
val result = if (walletManagersThrottler.isStillThrottled(blockchain)) {
|
||||
delay(500)
|
||||
walletManagersThrottler.geValue(blockchain)!!
|
||||
} else {
|
||||
updateThrottlingForWalletManager(walletManager)
|
||||
}
|
||||
handleUpdateWalletResult(result, walletManager)
|
||||
}
|
||||
|
||||
suspend fun updateWallet(walletManager: WalletManager, force: Boolean) {
|
||||
val result = if (force) {
|
||||
updateThrottlingForWalletManager(walletManager)
|
||||
} else {
|
||||
val blockchain = walletManager.wallet.blockchain
|
||||
if (walletManagersThrottler.isStillThrottled(blockchain)) {
|
||||
delay(500)
|
||||
walletManagersThrottler.geValue(blockchain)!!
|
||||
} else {
|
||||
updateThrottlingForWalletManager(walletManager)
|
||||
when (result) {
|
||||
is Result.Success -> {
|
||||
checkForRentWarning(walletManager)
|
||||
dispatchOnMain(WalletAction.LoadWallet.Success(result.data))
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
when (result) {
|
||||
is Result.Success ->
|
||||
store.dispatch(WalletAction.UpdateWallet.Success(result.data))
|
||||
is Result.Failure ->
|
||||
store.dispatch(WalletAction.UpdateWallet.Failure(result.error.localizedMessage))
|
||||
is Result.Failure -> {
|
||||
when (result.error) {
|
||||
is TapError.WalletManagerUpdate.NoAccountError -> {
|
||||
dispatchOnMain(WalletAction.LoadWallet.NoAccount(
|
||||
walletManager.wallet,
|
||||
(result.error as TapError.WalletManagerUpdate.NoAccountError).customMessage
|
||||
))
|
||||
}
|
||||
else -> {
|
||||
dispatchOnMain(WalletAction.LoadWallet.Failure(
|
||||
walletManager.wallet,
|
||||
result.error.localizedMessage
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -115,10 +110,10 @@ class TapWalletManager {
|
|||
suspend fun onCardScanned(data: ScanResponse) {
|
||||
walletManagersThrottler.clear()
|
||||
// fiatRatesThrottler.clear()
|
||||
store.state.globalState.feedbackManager?.infoHolder?.setCardInfo(data.card)
|
||||
store.state.globalState.feedbackManager?.infoHolder?.setCardInfo(data)
|
||||
updateConfigManager(data)
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
withMainContext {
|
||||
store.dispatch(WalletAction.ResetState)
|
||||
store.dispatch(GlobalAction.SaveScanNoteResponse(data))
|
||||
store.dispatch(WalletAction.SetIfTestnetCard(data.card.isTestCard))
|
||||
|
|
@ -144,42 +139,43 @@ class TapWalletManager {
|
|||
}
|
||||
|
||||
suspend fun loadData(data: ScanResponse) {
|
||||
withContext(Dispatchers.Main) {
|
||||
store.dispatch(WalletAction.LoadCardInfo(data.card))
|
||||
getActionIfUnknownBlockchainOrEmptyWallet(data)?.let {
|
||||
store.dispatch(it)
|
||||
return@withContext
|
||||
}
|
||||
|
||||
val blockchain = data.getBlockchain()
|
||||
val primaryWalletManager = walletManagerFactory.makePrimaryWalletManager(data)
|
||||
|
||||
if (blockchain != Blockchain.Unknown && primaryWalletManager != null) {
|
||||
val primaryToken = data.getPrimaryToken()
|
||||
|
||||
store.dispatch(WalletAction.MultiWallet.SetPrimaryBlockchain(blockchain))
|
||||
if (primaryToken != null) {
|
||||
primaryWalletManager.addToken(primaryToken)
|
||||
store.dispatch(WalletAction.MultiWallet.SetPrimaryToken(primaryToken))
|
||||
}
|
||||
if (data.card.isMultiwalletAllowed) {
|
||||
loadMultiWalletData(data, blockchain, primaryWalletManager)
|
||||
} else {
|
||||
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(primaryWalletManager))
|
||||
store.dispatch(WalletAction.MultiWallet.AddBlockchains(listOf(blockchain)))
|
||||
}
|
||||
|
||||
} else {
|
||||
if (data.card.isMultiwalletAllowed) {
|
||||
loadMultiWalletData(data, blockchain, null)
|
||||
}
|
||||
}
|
||||
store.dispatch(WalletAction.LoadWallet())
|
||||
store.dispatch(WalletAction.LoadFiatRate())
|
||||
dispatchOnMain(WalletAction.LoadCardInfo(data.card))
|
||||
getActionIfUnknownBlockchainOrEmptyWallet(data)?.let {
|
||||
dispatchOnMain(it)
|
||||
return
|
||||
}
|
||||
|
||||
val blockchain = data.getBlockchain()
|
||||
val primaryWalletManager = walletManagerFactory.makePrimaryWalletManager(data)
|
||||
|
||||
if (blockchain != Blockchain.Unknown && primaryWalletManager != null) {
|
||||
val primaryToken = data.getPrimaryToken()
|
||||
|
||||
dispatchOnMain(WalletAction.MultiWallet.SetPrimaryBlockchain(blockchain))
|
||||
if (primaryToken != null) {
|
||||
primaryWalletManager.addToken(primaryToken)
|
||||
dispatchOnMain(WalletAction.MultiWallet.SetPrimaryToken(primaryToken))
|
||||
}
|
||||
if (data.card.isMultiwalletAllowed) {
|
||||
loadMultiWalletData(data, blockchain, primaryWalletManager)
|
||||
} else {
|
||||
dispatchOnMain(
|
||||
WalletAction.MultiWallet.AddWalletManagers(primaryWalletManager),
|
||||
WalletAction.MultiWallet.AddBlockchains(listOf(blockchain))
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if (data.card.isMultiwalletAllowed) {
|
||||
loadMultiWalletData(data, blockchain, null)
|
||||
}
|
||||
}
|
||||
dispatchOnMain(
|
||||
WalletAction.LoadWallet(),
|
||||
WalletAction.LoadFiatRate()
|
||||
)
|
||||
}
|
||||
|
||||
private fun loadMultiWalletData(
|
||||
private suspend fun loadMultiWalletData(
|
||||
scanResponse: ScanResponse, primaryBlockchain: Blockchain?, primaryWalletManager: WalletManager?
|
||||
) {
|
||||
val primaryTokens = primaryWalletManager?.cardTokens?.toList() ?: emptyList()
|
||||
|
|
@ -187,25 +183,31 @@ class TapWalletManager {
|
|||
|
||||
if (savedCurrencies == null) {
|
||||
if (primaryBlockchain != null && primaryWalletManager != null) {
|
||||
store.dispatch(WalletAction.MultiWallet.SaveCurrencies(
|
||||
CardCurrencies(
|
||||
blockchains = listOf(primaryBlockchain), tokens = primaryTokens
|
||||
)))
|
||||
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(primaryWalletManager))
|
||||
store.dispatch(WalletAction.MultiWallet.AddBlockchains(listOf(primaryBlockchain)))
|
||||
store.dispatch(WalletAction.MultiWallet.AddTokens(primaryTokens.toList()))
|
||||
dispatchOnMain(
|
||||
WalletAction.MultiWallet.SaveCurrencies(
|
||||
CardCurrencies(blockchains = listOf(primaryBlockchain), tokens = primaryTokens)
|
||||
),
|
||||
WalletAction.MultiWallet.AddWalletManagers(primaryWalletManager),
|
||||
WalletAction.MultiWallet.AddBlockchains(listOf(primaryBlockchain)),
|
||||
WalletAction.MultiWallet.AddTokens(primaryTokens.toList())
|
||||
)
|
||||
|
||||
} else {
|
||||
val blockchains = listOf(Blockchain.Bitcoin, Blockchain.Ethereum)
|
||||
store.dispatch(WalletAction.MultiWallet.SaveCurrencies(
|
||||
CardCurrencies(blockchains = blockchains, tokens = emptyList())
|
||||
))
|
||||
val walletManagers =
|
||||
walletManagerFactory.makeWalletManagersForApp(scanResponse, blockchains.toList())
|
||||
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(walletManagers))
|
||||
store.dispatch(WalletAction.MultiWallet.AddBlockchains(blockchains.toList()))
|
||||
val walletManagers = walletManagerFactory.makeWalletManagersForApp(scanResponse, blockchains.toList())
|
||||
dispatchOnMain(
|
||||
WalletAction.MultiWallet.SaveCurrencies(CardCurrencies(
|
||||
blockchains = blockchains,
|
||||
tokens = emptyList()
|
||||
)),
|
||||
WalletAction.MultiWallet.AddWalletManagers(walletManagers),
|
||||
WalletAction.MultiWallet.AddBlockchains(blockchains.toList()),
|
||||
)
|
||||
}
|
||||
store.dispatch(WalletAction.MultiWallet.FindBlockchainsInUse)
|
||||
store.dispatch(WalletAction.MultiWallet.FindTokensInUse)
|
||||
dispatchOnMain(
|
||||
WalletAction.MultiWallet.FindBlockchainsInUse,
|
||||
WalletAction.MultiWallet.FindTokensInUse,
|
||||
)
|
||||
} else {
|
||||
val blockchains = savedCurrencies.blockchains.toList()
|
||||
val walletManagers = if (
|
||||
|
|
@ -218,10 +220,11 @@ class TapWalletManager {
|
|||
} else {
|
||||
walletManagerFactory.makeWalletManagersForApp(scanResponse, blockchains)
|
||||
}
|
||||
|
||||
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(walletManagers))
|
||||
store.dispatch(WalletAction.MultiWallet.AddBlockchains(blockchains))
|
||||
store.dispatch(WalletAction.MultiWallet.AddTokens(savedCurrencies.tokens.toList()))
|
||||
dispatchOnMain(
|
||||
WalletAction.MultiWallet.AddWalletManagers(walletManagers),
|
||||
WalletAction.MultiWallet.AddBlockchains(blockchains),
|
||||
WalletAction.MultiWallet.AddTokens(savedCurrencies.tokens.toList()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -260,79 +263,45 @@ class TapWalletManager {
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun handleUpdateWalletResult(result: Result<Wallet>, walletManager: WalletManager) {
|
||||
withContext(Dispatchers.Main) {
|
||||
when (result) {
|
||||
is Result.Success -> {
|
||||
checkForRentWarning(walletManager)
|
||||
store.dispatch(WalletAction.LoadWallet.Success(result.data))
|
||||
}
|
||||
is Result.Failure -> {
|
||||
val error = (result.error as? TapError) ?: TapError.UnknownError
|
||||
when (error) {
|
||||
TapError.NoInternetConnection -> {
|
||||
store.dispatch(WalletAction.LoadData.Failure(error))
|
||||
}
|
||||
is TapError.WalletManagerUpdate.NoAccountError -> {
|
||||
store.dispatch(WalletAction.LoadWallet
|
||||
.NoAccount(walletManager.wallet, error.customMessage))
|
||||
}
|
||||
is TapError.WalletManagerUpdate.InternalError -> {
|
||||
store.dispatch(WalletAction.LoadWallet
|
||||
.Failure(walletManager.wallet, error.customMessage))
|
||||
}
|
||||
else -> {
|
||||
store.dispatchDebugErrorNotification(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkForRentWarning(walletManager: WalletManager) {
|
||||
private suspend fun checkForRentWarning(walletManager: WalletManager) {
|
||||
val rentProvider = walletManager as? RentProvider ?: return
|
||||
|
||||
scope.launch {
|
||||
when (val result = rentProvider.minimalBalanceForRentExemption()) {
|
||||
is com.tangem.blockchain.extensions.Result.Success -> {
|
||||
fun isNeedToShowWarning(balance: BigDecimal, rentExempt: BigDecimal): Boolean = balance < rentExempt
|
||||
when (val result = rentProvider.minimalBalanceForRentExemption()) {
|
||||
is com.tangem.blockchain.extensions.Result.Success -> {
|
||||
fun isNeedToShowWarning(balance: BigDecimal, rentExempt: BigDecimal): Boolean = balance < rentExempt
|
||||
|
||||
val balance = walletManager.wallet.fundsAvailable(AmountType.Coin)
|
||||
val outgoingTxs = walletManager.wallet.getPendingTransactions(PendingTransactionType.Outgoing)
|
||||
val rentExempt = result.data
|
||||
val show = if (outgoingTxs.isEmpty()) {
|
||||
isNeedToShowWarning(balance, rentExempt)
|
||||
} else {
|
||||
val outgoingAmount = outgoingTxs.sumOf { it.amount ?: BigDecimal.ZERO }
|
||||
val rest = balance.minus(outgoingAmount)
|
||||
isNeedToShowWarning(rest, rentExempt)
|
||||
}
|
||||
if (!show) return@launch
|
||||
|
||||
val currency = walletManager.wallet.blockchain.currency
|
||||
store.dispatchOnMain(WalletAction.SetWalletRent(
|
||||
blockchain = walletManager.wallet.blockchain,
|
||||
minRent = ("${rentProvider.rentAmount().stripZeroPlainString()} $currency"),
|
||||
rentExempt = ("${rentExempt.stripZeroPlainString()} $currency")
|
||||
))
|
||||
val balance = walletManager.wallet.fundsAvailable(AmountType.Coin)
|
||||
val outgoingTxs = walletManager.wallet.getPendingTransactions(PendingTransactionType.Outgoing)
|
||||
val rentExempt = result.data
|
||||
val show = if (outgoingTxs.isEmpty()) {
|
||||
isNeedToShowWarning(balance, rentExempt)
|
||||
} else {
|
||||
val outgoingAmount = outgoingTxs.sumOf { it.amount ?: BigDecimal.ZERO }
|
||||
val rest = balance.minus(outgoingAmount)
|
||||
isNeedToShowWarning(rest, rentExempt)
|
||||
}
|
||||
is com.tangem.blockchain.extensions.Result.Failure -> {}
|
||||
if (!show) return
|
||||
|
||||
val currency = walletManager.wallet.blockchain.currency
|
||||
dispatchOnMain(WalletAction.SetWalletRent(
|
||||
blockchain = walletManager.wallet.blockchain,
|
||||
minRent = ("${rentProvider.rentAmount().stripZeroPlainString()} $currency"),
|
||||
rentExempt = ("${rentExempt.stripZeroPlainString()} $currency")
|
||||
))
|
||||
}
|
||||
is com.tangem.blockchain.extensions.Result.Failure -> {}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun handleFiatRatesResult(results: List<Pair<Currency, Result<BigDecimal>?>>) {
|
||||
withContext(Dispatchers.Main) {
|
||||
results.map {
|
||||
when (it.second) {
|
||||
is Result.Success -> {
|
||||
val rate = it.first to (it.second as Result.Success<BigDecimal>).data
|
||||
store.dispatch(WalletAction.LoadFiatRate.Success(rate))
|
||||
}
|
||||
is Result.Failure -> store.dispatch(WalletAction.LoadFiatRate.Failure)
|
||||
null -> {}
|
||||
results.map {
|
||||
when (it.second) {
|
||||
is Result.Success -> {
|
||||
val rate = it.first to (it.second as Result.Success<BigDecimal>).data
|
||||
dispatchOnMain(WalletAction.LoadFiatRate.Success(rate))
|
||||
}
|
||||
is Result.Failure -> dispatchOnMain(WalletAction.LoadFiatRate.Failure)
|
||||
null -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.tangem.common.CompletionResult
|
|||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.core.CardSession
|
||||
import com.tangem.common.core.CardSessionRunnable
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.operations.CommandResponse
|
||||
import com.tangem.operations.wallet.CreateWalletResponse
|
||||
import com.tangem.operations.wallet.CreateWalletTask
|
||||
|
|
@ -22,6 +23,11 @@ class CreateWalletsTask(
|
|||
private val createdWalletsResponses = mutableListOf<CreateWalletResponse>()
|
||||
|
||||
override fun run(session: CardSession, callback: (result: CompletionResult<CreateWalletsResponse>) -> Unit) {
|
||||
if (curves.isEmpty()){
|
||||
callback(CompletionResult.Failure(TangemSdkError.WalletIsNotCreated()))
|
||||
return
|
||||
}
|
||||
|
||||
val curve = curves[createdWalletsResponses.size]
|
||||
createWallet(curve, session, callback)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,12 +7,10 @@ import android.os.Build
|
|||
import com.tangem.Log
|
||||
import com.tangem.TangemSdkLogger
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.tap.common.extensions.sendEmail
|
||||
import com.tangem.tap.common.extensions.stripZeroPlainString
|
||||
import com.tangem.tap.domain.TapWorkarounds
|
||||
import com.tangem.tap.features.feedback.EmailData.Companion.appendBlankLine
|
||||
import com.tangem.tap.features.feedback.EmailData.Companion.appendDelimiter
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.wallet.R
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
|
|
@ -86,16 +84,19 @@ class FeedbackManager(
|
|||
class TangemLogCollector : TangemSdkLogger {
|
||||
private val dateFormatter = SimpleDateFormat("HH:mm:ss.SSS")
|
||||
private val logs = mutableListOf<String>()
|
||||
private val mutex = Object()
|
||||
|
||||
override fun log(message: () -> String, level: Log.Level) {
|
||||
val time = dateFormatter.format(Date())
|
||||
logs.add("$time: ${message()}\n")
|
||||
synchronized(mutex) {
|
||||
logs.add("$time: ${message()}\n")
|
||||
}
|
||||
}
|
||||
|
||||
fun getLogs(): List<String> = logs.toList()
|
||||
fun getLogs(): List<String> = synchronized(mutex) { logs.toList() }
|
||||
|
||||
fun clearLogs() {
|
||||
logs.clear()
|
||||
synchronized(mutex) { logs.clear() }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -105,8 +106,6 @@ class AdditionalEmailInfo {
|
|||
var address: String = "",
|
||||
var explorerLink: String = "",
|
||||
var host: String = "",
|
||||
// var outputsCount: String = ""
|
||||
// var transactionHex: String = ""
|
||||
)
|
||||
|
||||
var appVersion: String = ""
|
||||
|
|
@ -115,6 +114,7 @@ class AdditionalEmailInfo {
|
|||
var cardId: String = ""
|
||||
var cardFirmwareVersion: String = ""
|
||||
var cardIssuer: String = ""
|
||||
var cardBlockchain: String = ""
|
||||
|
||||
// wallets
|
||||
internal val walletsInfo = mutableListOf<EmailWalletInfo>()
|
||||
|
|
@ -141,12 +141,13 @@ class AdditionalEmailInfo {
|
|||
}
|
||||
}
|
||||
|
||||
fun setCardInfo(card: Card) {
|
||||
cardId = card.cardId
|
||||
cardFirmwareVersion = card.firmwareVersion.stringValue
|
||||
cardIssuer = card.issuer.name
|
||||
signedHashesCount = card.wallets
|
||||
.joinToString(";") { "${it.curve?.curve} - ${it.totalSignedHashes}" }
|
||||
fun setCardInfo(data: ScanResponse) {
|
||||
cardId = data.card.cardId
|
||||
cardBlockchain = data.walletData?.blockchain ?: ""
|
||||
cardFirmwareVersion = data.card.firmwareVersion.stringValue
|
||||
cardIssuer = data.card.issuer.name
|
||||
signedHashesCount = data.card.wallets
|
||||
.joinToString("; ") { "${it.curve.curve} - ${it.totalSignedHashes}" }
|
||||
}
|
||||
|
||||
fun setWalletsInfo(walletManagers: List<WalletManager>) {
|
||||
|
|
@ -217,33 +218,23 @@ interface EmailData {
|
|||
fun joinTogether(context: Context, infoHolder: AdditionalEmailInfo): String {
|
||||
return StringBuilder().apply {
|
||||
append(context.getString(mainMessageResId))
|
||||
append("\n\n\n\n")
|
||||
appendLine(3)
|
||||
append(context.getString(getDataCollectionMessageResId()))
|
||||
appendLine()
|
||||
append(createOptionalMessage(infoHolder))
|
||||
}.toString()
|
||||
}
|
||||
|
||||
companion object {
|
||||
internal fun StringBuilder.appendDelimiter() = append("----------\n")
|
||||
internal fun StringBuilder.appendBlankLine() = append("\n")
|
||||
}
|
||||
}
|
||||
|
||||
class RateCanBeBetterEmail : EmailData {
|
||||
override val subjectResId: Int = R.string.feedback_subject_rate_negative
|
||||
override val mainMessageResId: Int = R.string.feedback_preface_rate_negative
|
||||
|
||||
override fun createOptionalMessage(infoHolder: AdditionalEmailInfo): String {
|
||||
val walletInfo = infoHolder.walletsInfo[0]
|
||||
return StringBuilder().apply {
|
||||
appendKeyValue("Card ID", infoHolder.cardId)
|
||||
appendKeyValue("Blockchain", walletInfo.blockchain.fullName)
|
||||
appendBlankLine()
|
||||
appendKeyValue("Phone model", infoHolder.phoneModel)
|
||||
appendKeyValue("OS version", infoHolder.osVersion)
|
||||
appendKeyValue("App version", infoHolder.appVersion)
|
||||
}.toString()
|
||||
}
|
||||
override fun createOptionalMessage(infoHolder: AdditionalEmailInfo): String = EmailDataBuilder(infoHolder)
|
||||
.appendCardInfo()
|
||||
.appendLine()
|
||||
.appendPhoneInfo()
|
||||
.build()
|
||||
}
|
||||
|
||||
class ScanFailsEmail : EmailData {
|
||||
|
|
@ -251,51 +242,31 @@ class ScanFailsEmail : EmailData {
|
|||
override val subjectResId: Int = R.string.feedback_subject_scan_failed
|
||||
override val mainMessageResId: Int = R.string.feedback_preface_scan_failed
|
||||
|
||||
override fun joinTogether(context: Context, infoHolder: AdditionalEmailInfo): String {
|
||||
return StringBuilder().apply {
|
||||
append(context.getString(mainMessageResId))
|
||||
append("\n\n\n\n")
|
||||
append(createOptionalMessage(infoHolder))
|
||||
}.toString()
|
||||
}
|
||||
override fun joinTogether(context: Context, infoHolder: AdditionalEmailInfo): String = StringBuilder().apply {
|
||||
append(context.getString(mainMessageResId))
|
||||
appendLine(4)
|
||||
append(createOptionalMessage(infoHolder))
|
||||
}.toString()
|
||||
|
||||
override fun createOptionalMessage(infoHolder: AdditionalEmailInfo): String {
|
||||
return StringBuilder().apply {
|
||||
appendBlankLine()
|
||||
appendKeyValue("Phone model", infoHolder.phoneModel)
|
||||
appendKeyValue("OS version", infoHolder.osVersion)
|
||||
appendKeyValue("App version", infoHolder.appVersion)
|
||||
}.toString()
|
||||
}
|
||||
override fun createOptionalMessage(infoHolder: AdditionalEmailInfo): String = EmailDataBuilder(infoHolder)
|
||||
.appendPhoneInfo()
|
||||
.build()
|
||||
}
|
||||
|
||||
class SendTransactionFailedEmail(private val error: String) : EmailData {
|
||||
class SendTransactionFailedEmail(
|
||||
val error: String
|
||||
) : EmailData {
|
||||
|
||||
override val subjectResId: Int = R.string.feedback_subject_tx_failed
|
||||
override val mainMessageResId: Int = R.string.feedback_preface_tx_failed
|
||||
|
||||
override fun createOptionalMessage(infoHolder: AdditionalEmailInfo): String {
|
||||
val walletInfo = infoHolder.onSendErrorWalletInfo ?: AdditionalEmailInfo.EmailWalletInfo()
|
||||
return StringBuilder().apply {
|
||||
appendKeyValue("Card ID", infoHolder.cardId)
|
||||
appendKeyValue("Firmware version", infoHolder.cardFirmwareVersion)
|
||||
appendKeyValue("Signed hashes", infoHolder.signedHashesCount)
|
||||
appendDelimiter()
|
||||
appendKeyValue("Blockchain", walletInfo.blockchain.fullName)
|
||||
appendKeyValue("Host", walletInfo.host)
|
||||
appendKeyValue("Token", infoHolder.token)
|
||||
appendKeyValue("Error", error)
|
||||
appendDelimiter()
|
||||
appendKeyValue("Source address", walletInfo.address)
|
||||
appendKeyValue("Destination address", infoHolder.destinationAddress)
|
||||
appendKeyValue("Amount", infoHolder.amount)
|
||||
appendKeyValue("Fee", infoHolder.fee)
|
||||
appendBlankLine()
|
||||
appendKeyValue("Phone model", infoHolder.phoneModel)
|
||||
appendKeyValue("OS version", infoHolder.osVersion)
|
||||
appendKeyValue("App version", infoHolder.appVersion)
|
||||
// appendKeyValue("Transaction HEX", infoHolder.transactionHex)
|
||||
}.toString()
|
||||
}
|
||||
override fun createOptionalMessage(infoHolder: AdditionalEmailInfo): String = EmailDataBuilder(infoHolder)
|
||||
.appendCardInfo()
|
||||
.appendDelimiter()
|
||||
.appendTxFailedBlockchainInfo(error)
|
||||
.appendLine()
|
||||
.appendPhoneInfo()
|
||||
.build()
|
||||
}
|
||||
|
||||
class FeedbackEmail : EmailData {
|
||||
|
|
@ -316,37 +287,88 @@ class FeedbackEmail : EmailData {
|
|||
isS2CCard = TapWorkarounds.isStart2CoinIssuer(infoHolder.cardIssuer)
|
||||
}
|
||||
|
||||
override fun createOptionalMessage(infoHolder: AdditionalEmailInfo): String {
|
||||
val builder = StringBuilder()
|
||||
override fun createOptionalMessage(infoHolder: AdditionalEmailInfo): String = EmailDataBuilder(infoHolder)
|
||||
.appendCardInfo()
|
||||
.appendWalletsInfo()
|
||||
.appendLine()
|
||||
.appendPhoneInfo()
|
||||
.build()
|
||||
}
|
||||
|
||||
|
||||
class EmailDataBuilder(
|
||||
private val infoHolder: AdditionalEmailInfo
|
||||
) {
|
||||
val builder = StringBuilder()
|
||||
|
||||
fun appendDelimiter(): EmailDataBuilder {
|
||||
builder.appendDelimiter()
|
||||
return this
|
||||
}
|
||||
|
||||
fun appendLine(count: Int = 1): EmailDataBuilder {
|
||||
builder.appendLine(count)
|
||||
return this
|
||||
}
|
||||
|
||||
fun appendCardInfo(): EmailDataBuilder {
|
||||
builder.appendKeyValue("Card ID", infoHolder.cardId)
|
||||
builder.appendKeyValue("Firmware version", infoHolder.cardFirmwareVersion)
|
||||
builder.appendKeyValue("Card Blockchain", infoHolder.cardBlockchain)
|
||||
builder.appendKeyValue("Signed hashes", infoHolder.signedHashesCount)
|
||||
return this
|
||||
}
|
||||
|
||||
fun appendWalletsInfo(): EmailDataBuilder {
|
||||
infoHolder.walletsInfo.forEach {
|
||||
builder.appendDelimiter()
|
||||
builder.appendKeyValue("Blockchain", it.blockchain.fullName)
|
||||
builder.appendKeyValue("Host", it.host)
|
||||
builder.appendKeyValue("Wallet address", it.address)
|
||||
builder.appendKeyValue("Explorer link", it.explorerLink)
|
||||
}
|
||||
builder.appendBlankLine()
|
||||
|
||||
infoHolder.tokens.forEach { tokens ->
|
||||
builder.appendDelimiter()
|
||||
builder.appendKeyValue("Blockchain", tokens.key.fullName)
|
||||
builder.appendKeyValue("Tokens", tokens.value.map { "${it.name} - ${it.symbol}" }.toString())
|
||||
infoHolder.tokens[it.blockchain]?.let { tokens ->
|
||||
builder.append("Tokens:")
|
||||
appendLine()
|
||||
tokens.forEach { token ->
|
||||
builder.appendKeyValue("Name", token.name)
|
||||
builder.appendKeyValue("Contract address", token.contractAddress)
|
||||
}
|
||||
}
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun appendTxFailedBlockchainInfo(error: String): EmailDataBuilder {
|
||||
val walletInfo = infoHolder.onSendErrorWalletInfo ?: AdditionalEmailInfo.EmailWalletInfo()
|
||||
builder.appendKeyValue("Blockchain", walletInfo.blockchain.fullName)
|
||||
builder.appendKeyValue("Host", walletInfo.host)
|
||||
builder.appendKeyValue("Token", infoHolder.token)
|
||||
builder.appendKeyValue("Error", error)
|
||||
builder.appendDelimiter()
|
||||
builder.appendBlankLine()
|
||||
// appendKeyValue("Outputs count", infoHolder.outputsCount)
|
||||
builder.appendKeyValue("Source address", walletInfo.address)
|
||||
builder.appendKeyValue("Destination address", infoHolder.destinationAddress)
|
||||
builder.appendKeyValue("Amount", infoHolder.amount)
|
||||
builder.appendKeyValue("Fee", infoHolder.fee)
|
||||
return this
|
||||
}
|
||||
|
||||
fun appendPhoneInfo(): EmailDataBuilder {
|
||||
builder.appendKeyValue("Phone model", infoHolder.phoneModel)
|
||||
builder.appendKeyValue("OS version", infoHolder.osVersion)
|
||||
builder.appendKeyValue("App version", infoHolder.appVersion)
|
||||
|
||||
return builder.toString()
|
||||
return this
|
||||
}
|
||||
|
||||
fun build(): String = builder.toString()
|
||||
}
|
||||
|
||||
private fun StringBuilder.appendKeyValue(key: String, value: String): StringBuilder {
|
||||
return if (value.isNotBlank()) this.append("$key: $value\n") else this
|
||||
}
|
||||
|
||||
private fun StringBuilder.appendDelimiter(): StringBuilder = append("----------\n")
|
||||
|
||||
private fun StringBuilder.appendLine(count: Int = 1): StringBuilder {
|
||||
return append(List(count) { "\n" }.joinToString(separator = ""))
|
||||
}
|
||||
|
|
@ -1,5 +1,11 @@
|
|||
package com.tangem.tap.features.home.compose
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Typeface
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.Text
|
||||
|
|
@ -14,6 +20,9 @@ import androidx.compose.ui.text.font.FontWeight
|
|||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import com.tangem.tangem_sdk_new.extensions.dpToPx
|
||||
import com.tangem.tap.common.extensions.compose.argb
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@Composable
|
||||
|
|
@ -22,8 +31,9 @@ fun StoriesGeneralContent(
|
|||
subtitleText: String,
|
||||
imageSource: Int?,
|
||||
isDarkBackground: Boolean,
|
||||
subtitleTextId: Int? = null,
|
||||
imageComposable: (() -> Unit)? = null,
|
||||
) {
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
|
|
@ -45,15 +55,7 @@ fun StoriesGeneralContent(
|
|||
|
||||
Spacer(modifier = Modifier.size(16.dp))
|
||||
|
||||
Text(
|
||||
text = subtitleText,
|
||||
fontSize = 20.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
modifier = Modifier
|
||||
.padding(start = 40.dp, end = 40.dp),
|
||||
color = Color(0xFFA6AAAD),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
SubtitleText(subtitleText, subtitleTextId)
|
||||
|
||||
Spacer(modifier = Modifier.size(25.dp))
|
||||
|
||||
|
|
@ -61,7 +63,7 @@ fun StoriesGeneralContent(
|
|||
Image(
|
||||
painter = painterResource(id = imageSource),
|
||||
contentDescription = null,
|
||||
contentScale = if(isDarkBackground) ContentScale.Inside else ContentScale.FillWidth,
|
||||
contentScale = if (isDarkBackground) ContentScale.Inside else ContentScale.FillWidth,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
|
|
@ -69,6 +71,34 @@ fun StoriesGeneralContent(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SubtitleText(subtitleText: String, subtitleTextId: Int?) {
|
||||
val color = Color(0xFFA6AAAD)
|
||||
|
||||
if (subtitleTextId == null) {
|
||||
Text(
|
||||
text = subtitleText,
|
||||
fontSize = 20.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
modifier = Modifier.padding(start = 40.dp, end = 40.dp),
|
||||
color = color,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
} else {
|
||||
HtmlText(subtitleTextId) { context ->
|
||||
val padding = context.dpToPx(40f).toInt()
|
||||
TextView(context).apply {
|
||||
layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
setPadding(padding, 0, padding, 0)
|
||||
textAlignment = View.TEXT_ALIGNMENT_CENTER
|
||||
typeface = Typeface.DEFAULT
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 20f)
|
||||
setTextColor(color.argb())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun StoriesRevolutionaryWallet() {
|
||||
StoriesGeneralContent(
|
||||
|
|
@ -85,6 +115,7 @@ fun StoriesUltraSecureBackup() {
|
|||
titleText = stringResource(id = R.string.story_backup_title),
|
||||
subtitleText = stringResource(id = R.string.story_backup_description),
|
||||
imageSource = R.drawable.floating_cards,
|
||||
subtitleTextId = R.string.story_backup_description,
|
||||
isDarkBackground = false
|
||||
)
|
||||
}
|
||||
|
|
@ -117,4 +148,13 @@ fun StoriesWalletForEveryone() {
|
|||
imageSource = R.drawable.wallet_for_everyone,
|
||||
isDarkBackground = true
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HtmlText(
|
||||
stringResId: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
factory: (Context) -> TextView
|
||||
) {
|
||||
AndroidView(factory, modifier) { it.text = it.context.getText(stringResId) }
|
||||
}
|
||||
|
|
@ -202,11 +202,11 @@ private fun sendTransaction(
|
|||
}
|
||||
scope.launch(Dispatchers.IO) {
|
||||
withContext(Dispatchers.Main) {
|
||||
dispatch(WalletAction.UpdateWallet(walletManager.wallet.blockchain))
|
||||
dispatch(WalletAction.LoadWallet(walletManager.wallet.blockchain))
|
||||
}
|
||||
delay(11000) // more than 10000 to avoid throttling
|
||||
withContext(Dispatchers.Main) {
|
||||
dispatch(WalletAction.UpdateWallet(walletManager.wallet.blockchain))
|
||||
dispatch(WalletAction.LoadWallet(walletManager.wallet.blockchain))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,12 +74,6 @@ sealed class WalletAction : Action {
|
|||
class CheckRemainingSignatures(val remainingSignatures: Int?) : Warnings()
|
||||
}
|
||||
|
||||
data class UpdateWallet(val blockchain: Blockchain? = null, val force: Boolean = true) : WalletAction() {
|
||||
object ScheduleUpdatingWallet : WalletAction()
|
||||
data class Success(val wallet: Wallet) : WalletAction()
|
||||
data class Failure(val errorMessage: String? = null) : WalletAction()
|
||||
}
|
||||
|
||||
data class LoadFiatRate(
|
||||
val wallet: Wallet? = null, val currencyList: List<Currency>? = null,
|
||||
) : WalletAction() {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import android.graphics.Bitmap
|
|||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.blockchain.extensions.isAboveZero
|
||||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.tap.common.entities.Button
|
||||
import com.tangem.tap.common.extensions.toQrCode
|
||||
import com.tangem.tap.common.redux.StateDialog
|
||||
|
|
@ -32,11 +33,11 @@ data class WalletState(
|
|||
val hashesCountVerified: Boolean? = null,
|
||||
val walletDialog: StateDialog? = null,
|
||||
val mainWarningsList: List<WarningMessage> = mutableListOf(),
|
||||
val wallets: List<WalletData> = emptyList(),
|
||||
val walletsData: List<WalletData> = emptyList(),
|
||||
val walletManagers: List<WalletManager> = emptyList(),
|
||||
val isMultiwalletAllowed: Boolean = false,
|
||||
val cardCurrency: CryptoCurrencyName? = null,
|
||||
val selectedWallet: Currency? = null,
|
||||
val selectedCurrency: Currency? = null,
|
||||
val primaryBlockchain: Blockchain? = null,
|
||||
val primaryToken: Token? = null,
|
||||
val isTestnet: Boolean = false,
|
||||
|
|
@ -51,7 +52,7 @@ data class WalletState(
|
|||
val isTangemTwins: Boolean
|
||||
get() = store.state.globalState.scanResponse?.isTangemTwins() == true
|
||||
|
||||
val primaryWallet = if (wallets.isNotEmpty()) wallets[0] else null
|
||||
val primaryWallet = if (walletsData.isNotEmpty()) walletsData[0] else null
|
||||
|
||||
val shouldShowDetails: Boolean =
|
||||
primaryWallet?.currencyData?.status != BalanceStatus.EmptyCard &&
|
||||
|
|
@ -61,7 +62,7 @@ data class WalletState(
|
|||
get() = walletManagers.map { it.wallet.blockchain }
|
||||
|
||||
val currencies: List<Currency>
|
||||
get() = wallets.mapNotNull { it.currency }
|
||||
get() = walletsData.mapNotNull { it.currency }
|
||||
|
||||
fun getWalletManager(token: Token?): WalletManager? {
|
||||
if (token == null) return null
|
||||
|
|
@ -79,21 +80,21 @@ data class WalletState(
|
|||
|
||||
fun getWalletData(currency: Currency?): WalletData? {
|
||||
if (currency == null) return null
|
||||
return wallets.find { it.currency == currency }
|
||||
return walletsData.find { it.currency == currency }
|
||||
}
|
||||
|
||||
fun getWalletData(blockchain: Blockchain?): WalletData? {
|
||||
if (blockchain == null) return null
|
||||
return wallets.find { (it.currency as? Currency.Blockchain)?.blockchain == blockchain }
|
||||
return walletsData.find { (it.currency as? Currency.Blockchain)?.blockchain == blockchain }
|
||||
}
|
||||
|
||||
fun getWalletData(token: Token?): WalletData? {
|
||||
if (token == null) return null
|
||||
return wallets.find { (it.currency as? Currency.Token)?.token == token }
|
||||
return walletsData.find { (it.currency as? Currency.Token)?.token == token }
|
||||
}
|
||||
|
||||
fun getSelectedWalletData(): WalletData? {
|
||||
return wallets.find { it.currency == selectedWallet }
|
||||
return walletsData.find { it.currency == selectedCurrency }
|
||||
}
|
||||
|
||||
fun canBeRemoved(walletData: WalletData?): Boolean {
|
||||
|
|
@ -132,9 +133,9 @@ data class WalletState(
|
|||
}
|
||||
|
||||
fun replaceWalletInWallets(walletData: WalletData?): List<WalletData> {
|
||||
if (walletData == null) return wallets
|
||||
if (walletData == null) return walletsData
|
||||
var changed = false
|
||||
val updatedWallets = wallets.map {
|
||||
val updatedWallets = walletsData.map {
|
||||
if (it.currency == walletData.currency) {
|
||||
changed = true
|
||||
walletData
|
||||
|
|
@ -142,12 +143,12 @@ data class WalletState(
|
|||
it
|
||||
}
|
||||
}
|
||||
return if (changed) updatedWallets else wallets + walletData
|
||||
return if (changed) updatedWallets else walletsData + walletData
|
||||
}
|
||||
|
||||
fun replaceSomeWallets(newWallets: List<WalletData>): List<WalletData> {
|
||||
val remainingWallets: MutableList<WalletData> = newWallets.toMutableList()
|
||||
val updatedWallets = wallets.map { wallet ->
|
||||
val updatedWallets = walletsData.map { wallet ->
|
||||
val newWallet = newWallets
|
||||
.firstOrNull { wallet.currency == it.currency }
|
||||
if (newWallet == null) {
|
||||
|
|
@ -248,17 +249,26 @@ data class WalletData(
|
|||
val currencyData: BalanceWidgetData = BalanceWidgetData(),
|
||||
val updatingWallet: Boolean = false,
|
||||
val tradeCryptoState: TradeCryptoState = TradeCryptoState(),
|
||||
val allowToSend: Boolean = true,
|
||||
val fiatRateString: String? = null,
|
||||
val fiatRate: BigDecimal? = null,
|
||||
val mainButton: WalletMainButton = WalletMainButton.SendButton(false),
|
||||
val currency: Currency,
|
||||
val walletRent: WalletRent? = null,
|
||||
val warningRent: WalletRent? = null,
|
||||
) {
|
||||
fun shouldShowMultipleAddress(): Boolean {
|
||||
val listOfAddresses = walletAddresses?.list ?: return false
|
||||
return listOfAddresses.size > 1
|
||||
}
|
||||
|
||||
fun shouldShowCoinAmountWarning(): Boolean = when (currency) {
|
||||
is Currency.Blockchain -> false
|
||||
is Currency.Token -> blockchainAmountIsEmpty() && !tokenAmountIsEmpty()
|
||||
}
|
||||
|
||||
fun shouldEnableTokenSendButton(): Boolean = !blockchainAmountIsEmpty() || !tokenAmountIsEmpty()
|
||||
|
||||
private fun blockchainAmountIsEmpty(): Boolean = currencyData.blockchainAmount?.isZero() ?: false
|
||||
private fun tokenAmountIsEmpty(): Boolean = currencyData.amount?.isZero() == true
|
||||
}
|
||||
|
||||
data class WalletRent(
|
||||
|
|
|
|||
|
|
@ -187,9 +187,7 @@ class MultiWalletMiddleware {
|
|||
store.dispatch(WalletAction.MultiWallet.AddBlockchain(blockchain))
|
||||
}
|
||||
store.dispatch(WalletAction.LoadFiatRate(currencyList = tokensList.map { Currency.Token(it) }))
|
||||
walletManager?.apply {
|
||||
scope.launch { async { addTokens(tokensList) }.await() }
|
||||
}
|
||||
walletManager?.apply { addTokens(tokensList) }
|
||||
}
|
||||
scope.launch {
|
||||
walletManagers.forEach { walletManager ->
|
||||
|
|
|
|||
|
|
@ -24,7 +24,10 @@ import com.tangem.tap.domain.extensions.toSendableAmounts
|
|||
import com.tangem.tap.features.demo.DemoHelper
|
||||
import com.tangem.tap.features.home.redux.HomeAction
|
||||
import com.tangem.tap.features.send.redux.PrepareSendScreen
|
||||
import com.tangem.tap.features.wallet.redux.*
|
||||
import com.tangem.tap.features.wallet.redux.Currency
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletData
|
||||
import com.tangem.tap.features.wallet.redux.WalletState
|
||||
import com.tangem.tap.network.NetworkStateChanged
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
|
|
@ -84,24 +87,27 @@ class WalletMiddleware {
|
|||
warningsMiddleware.tryToShowAppRatingWarning(action.wallet)
|
||||
}
|
||||
is WalletAction.LoadFiatRate -> {
|
||||
val tapWalletManager = globalState.tapWalletManager
|
||||
val fiatAppCurrency = globalState.appCurrency
|
||||
scope.launch {
|
||||
when {
|
||||
action.wallet != null -> {
|
||||
globalState.tapWalletManager.loadFiatRate(
|
||||
fiatCurrency = globalState.appCurrency,
|
||||
fiatCurrency = fiatAppCurrency,
|
||||
wallet = action.wallet,
|
||||
)
|
||||
}
|
||||
action.currencyList != null -> {
|
||||
globalState.tapWalletManager.loadFiatRate(
|
||||
fiatCurrency = globalState.appCurrency,
|
||||
fiatCurrency = fiatAppCurrency,
|
||||
currencies = action.currencyList,
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
val currencyList = walletState.walletsData.map { it.currency }
|
||||
globalState.tapWalletManager.loadFiatRate(
|
||||
fiatCurrency = globalState.appCurrency,
|
||||
currencies = walletState.wallets.map { it.currency },
|
||||
fiatCurrency = fiatAppCurrency,
|
||||
currencies = currencyList,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -129,22 +135,6 @@ class WalletMiddleware {
|
|||
}
|
||||
}
|
||||
}
|
||||
is WalletAction.UpdateWallet -> {
|
||||
if (action.blockchain != null) {
|
||||
scope.launch {
|
||||
val walletManager = walletState.getWalletManager(action.blockchain)
|
||||
walletManager?.let { globalState.tapWalletManager.updateWallet(it, action.force) }
|
||||
}
|
||||
} else {
|
||||
scope.launch {
|
||||
if (walletState.state == ProgressState.Done) {
|
||||
walletState.walletManagers.map { walletManager ->
|
||||
globalState.tapWalletManager.updateWallet(walletManager, action.force)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is WalletAction.Scan -> {
|
||||
store.dispatch(HomeAction.ShouldScanCardOnResume(true))
|
||||
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
|
||||
|
|
@ -173,7 +163,7 @@ class WalletMiddleware {
|
|||
is WalletAction.LoadData -> {
|
||||
scope.launch {
|
||||
val scanNoteResponse = globalState.scanResponse ?: return@launch
|
||||
if (!walletState.wallets.isEmpty()) {
|
||||
if (walletState.walletsData.isNotEmpty()) {
|
||||
globalState.tapWalletManager.reloadData(scanNoteResponse)
|
||||
} else {
|
||||
globalState.tapWalletManager.loadData(scanNoteResponse)
|
||||
|
|
@ -206,7 +196,7 @@ class WalletMiddleware {
|
|||
}
|
||||
}
|
||||
is WalletAction.ShowDialog.QrCode -> {
|
||||
val selectedWalletData = walletState.getWalletData(walletState.selectedWallet) ?: return
|
||||
val selectedWalletData = walletState.getWalletData(walletState.selectedCurrency) ?: return
|
||||
val selectedAddressData = selectedWalletData.walletAddresses?.selectedAddress ?: return
|
||||
|
||||
val currency = selectedWalletData.currency
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class MultiWalletReducer {
|
|||
state.addWalletManagers(action.walletManagers)
|
||||
}
|
||||
is WalletAction.MultiWallet.AddBlockchains -> {
|
||||
val wallets = action.blockchains.map { blockchain ->
|
||||
val walletsData = action.blockchains.map { blockchain ->
|
||||
val wallet = state.getWalletManager(blockchain)?.wallet
|
||||
val cardToken = if (!state.isMultiwalletAllowed) {
|
||||
wallet?.getFirstToken()?.symbol?.let { TokenData("", tokenSymbol = it) }
|
||||
|
|
@ -29,41 +29,41 @@ class MultiWalletReducer {
|
|||
null
|
||||
}
|
||||
WalletData(
|
||||
currencyData = BalanceWidgetData(
|
||||
BalanceStatus.Loading,
|
||||
blockchain.fullName,
|
||||
currencySymbol = blockchain.currency,
|
||||
token = cardToken
|
||||
),
|
||||
walletAddresses = createAddressList(wallet),
|
||||
mainButton = WalletMainButton.SendButton(false),
|
||||
currency = Currency.Blockchain(blockchain),
|
||||
currencyData = BalanceWidgetData(
|
||||
BalanceStatus.Loading,
|
||||
blockchain.fullName,
|
||||
currencySymbol = blockchain.currency,
|
||||
token = cardToken
|
||||
),
|
||||
walletAddresses = createAddressList(wallet),
|
||||
mainButton = WalletMainButton.SendButton(false),
|
||||
currency = Currency.Blockchain(blockchain),
|
||||
)
|
||||
}
|
||||
|
||||
val selectedWallet = if (!state.isMultiwalletAllowed) {
|
||||
wallets[0].currency
|
||||
val selectedCurrency = if (!state.isMultiwalletAllowed) {
|
||||
walletsData[0].currency
|
||||
} else {
|
||||
state.selectedWallet
|
||||
state.selectedCurrency
|
||||
}
|
||||
state.copy(
|
||||
wallets = wallets,
|
||||
selectedWallet = selectedWallet
|
||||
walletsData = walletsData,
|
||||
selectedCurrency = selectedCurrency
|
||||
)
|
||||
}
|
||||
is WalletAction.MultiWallet.AddBlockchain -> {
|
||||
val wallet = state.getWalletManager(action.blockchain)?.wallet
|
||||
val walletData = WalletData(
|
||||
currencyData = BalanceWidgetData(
|
||||
BalanceStatus.Loading,
|
||||
action.blockchain.fullName,
|
||||
currencySymbol = action.blockchain.currency,
|
||||
),
|
||||
walletAddresses = createAddressList(wallet),
|
||||
mainButton = WalletMainButton.SendButton(false),
|
||||
currency = Currency.Blockchain(action.blockchain),
|
||||
currencyData = BalanceWidgetData(
|
||||
BalanceStatus.Loading,
|
||||
action.blockchain.fullName,
|
||||
currencySymbol = action.blockchain.currency,
|
||||
),
|
||||
walletAddresses = createAddressList(wallet),
|
||||
mainButton = WalletMainButton.SendButton(false),
|
||||
currency = Currency.Blockchain(action.blockchain),
|
||||
)
|
||||
val newState = state.copy(wallets = state.replaceWalletInWallets(walletData))
|
||||
val newState = state.copy(walletsData = state.replaceWalletInWallets(walletData))
|
||||
if (wallet != null && wallet.amounts[AmountType.Coin]?.value != null) {
|
||||
OnWalletLoadedReducer().reduce(wallet, newState)
|
||||
} else {
|
||||
|
|
@ -72,21 +72,21 @@ class MultiWalletReducer {
|
|||
}
|
||||
is WalletAction.MultiWallet.AddTokens -> {
|
||||
val wallets = action.tokens.mapNotNull { token -> token.toWallet(state) }
|
||||
state.copy(wallets = state.replaceSomeWallets(wallets))
|
||||
state.copy(walletsData = state.replaceSomeWallets(wallets))
|
||||
}
|
||||
is WalletAction.MultiWallet.AddToken -> {
|
||||
val walletData = action.token.toWallet(state) ?: return state
|
||||
state.copy(wallets = state.replaceWalletInWallets(walletData))
|
||||
state.copy(walletsData = state.replaceWalletInWallets(walletData))
|
||||
}
|
||||
is WalletAction.MultiWallet.TokenLoaded -> {
|
||||
val pendingTransactions = state.getWalletManager(action.token)
|
||||
?.wallet?.let { wallet ->
|
||||
wallet.recentTransactions.toPendingTransactions(wallet.address)
|
||||
} ?: emptyList()
|
||||
?.wallet?.let { wallet ->
|
||||
wallet.recentTransactions.toPendingTransactions(wallet.address)
|
||||
} ?: emptyList()
|
||||
|
||||
val sendButtonEnabled = action.amount.value?.isZero() == false && pendingTransactions.isEmpty()
|
||||
val tokenPendingTransactions = pendingTransactions
|
||||
.filter { it.currency == action.amount.currencySymbol }
|
||||
.filter { it.currency == action.amount.currencySymbol }
|
||||
val tokenBalanceStatus = when {
|
||||
tokenPendingTransactions.isNotEmpty() -> BalanceStatus.TransactionInProgress
|
||||
pendingTransactions.isNotEmpty() -> BalanceStatus.SameCurrencyTransactionInProgress
|
||||
|
|
@ -94,42 +94,43 @@ class MultiWalletReducer {
|
|||
}
|
||||
val tokenWalletData = state.getWalletData(action.token)
|
||||
val newTokenWalletData = tokenWalletData?.copy(
|
||||
currencyData = tokenWalletData.currencyData.copy(
|
||||
status = tokenBalanceStatus,
|
||||
amount = action.amount.value?.toFormattedCurrencyString(
|
||||
action.amount.decimals, action.amount.currencySymbol
|
||||
),
|
||||
fiatAmountFormatted = tokenWalletData.fiatRate?.let {
|
||||
action.amount.value
|
||||
?.toFiatString(it, store.state.globalState.appCurrency)
|
||||
}
|
||||
currencyData = tokenWalletData.currencyData.copy(
|
||||
status = tokenBalanceStatus,
|
||||
amount = action.amount.value,
|
||||
amountFormatted = action.amount.value?.toFormattedCurrencyString(
|
||||
action.amount.decimals, action.amount.currencySymbol
|
||||
),
|
||||
pendingTransactions = pendingTransactions.removeUnknownTransactions(),
|
||||
mainButton = WalletMainButton.SendButton(sendButtonEnabled),
|
||||
currency = Currency.Token(action.token)
|
||||
fiatAmountFormatted = tokenWalletData.fiatRate?.let {
|
||||
action.amount.value
|
||||
?.toFiatString(it, store.state.globalState.appCurrency)
|
||||
}
|
||||
),
|
||||
pendingTransactions = pendingTransactions.removeUnknownTransactions(),
|
||||
mainButton = WalletMainButton.SendButton(sendButtonEnabled),
|
||||
currency = Currency.Token(action.token)
|
||||
)
|
||||
val wallets = state.replaceWalletInWallets(newTokenWalletData)
|
||||
state.copy(wallets = wallets)
|
||||
state.copy(walletsData = wallets)
|
||||
}
|
||||
is WalletAction.MultiWallet.SetIsMultiwalletAllowed ->
|
||||
state.copy(isMultiwalletAllowed = action.isMultiwalletAllowed)
|
||||
|
||||
is WalletAction.MultiWallet.SelectWallet ->
|
||||
state.copy(selectedWallet = action.walletData?.currency)
|
||||
state.copy(selectedCurrency = action.walletData?.currency)
|
||||
|
||||
is WalletAction.MultiWallet.RemoveWallet -> {
|
||||
val wallets = state.wallets.filterNot {
|
||||
val wallets = state.walletsData.filterNot {
|
||||
it.currency == action.walletData.currency
|
||||
}
|
||||
if (action.walletData.currency is Currency.Blockchain) {
|
||||
state.copy(
|
||||
wallets = wallets,
|
||||
walletsData = wallets,
|
||||
walletManagers = state.walletManagers.filterNot {
|
||||
it.wallet.blockchain == action.walletData.currency.blockchain
|
||||
}
|
||||
)
|
||||
} else {
|
||||
state.copy(wallets = wallets)
|
||||
state.copy(walletsData = wallets)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -36,11 +36,11 @@ class OnWalletLoadedReducer {
|
|||
return walletState
|
||||
}
|
||||
val formattedAmount = coinAmountValue?.toFormattedCurrencyString(
|
||||
wallet.blockchain.decimals(),
|
||||
wallet.blockchain.currency)
|
||||
wallet.blockchain.decimals(),
|
||||
wallet.blockchain.currency)
|
||||
|
||||
val pendingTransactions = wallet.recentTransactions
|
||||
.toPendingTransactions(wallet.address)
|
||||
.toPendingTransactions(wallet.address)
|
||||
|
||||
val coinSendButton = coinAmountValue?.isZero() == false && pendingTransactions.isEmpty()
|
||||
val balanceStatus = if (pendingTransactions.isNotEmpty()) {
|
||||
|
|
@ -52,17 +52,19 @@ class OnWalletLoadedReducer {
|
|||
|
||||
val fiatAmount = walletData?.fiatRate?.let { coinAmountValue?.toFiatValue(it) }
|
||||
val newWalletData = walletData?.copy(
|
||||
currencyData = walletData.currencyData.copy(
|
||||
status = balanceStatus, currency = wallet.blockchain.fullName,
|
||||
currencySymbol = wallet.blockchain.currency,
|
||||
amount = formattedAmount,
|
||||
fiatAmount = fiatAmount,
|
||||
fiatAmountFormatted = fiatAmount?.toFormattedFiatValue(fiatCurrencySymbol)
|
||||
),
|
||||
pendingTransactions = pendingTransactions.removeUnknownTransactions(),
|
||||
mainButton = WalletMainButton.SendButton(coinSendButton),
|
||||
currency = Currency.Blockchain(wallet.blockchain),
|
||||
tradeCryptoState = TradeCryptoState.from(exchangeManager, walletData)
|
||||
currencyData = walletData.currencyData.copy(
|
||||
status = balanceStatus, currency = wallet.blockchain.fullName,
|
||||
currencySymbol = wallet.blockchain.currency,
|
||||
blockchainAmount = coinAmountValue,
|
||||
amount = coinAmountValue,
|
||||
amountFormatted = formattedAmount,
|
||||
fiatAmount = fiatAmount,
|
||||
fiatAmountFormatted = fiatAmount?.toFormattedFiatValue(fiatCurrencySymbol)
|
||||
),
|
||||
pendingTransactions = pendingTransactions.removeUnknownTransactions(),
|
||||
mainButton = WalletMainButton.SendButton(coinSendButton),
|
||||
currency = Currency.Blockchain(wallet.blockchain),
|
||||
tradeCryptoState = TradeCryptoState.from(exchangeManager, walletData),
|
||||
)
|
||||
|
||||
val tokens = wallet.getTokens().mapNotNull { token ->
|
||||
|
|
@ -73,20 +75,23 @@ class OnWalletLoadedReducer {
|
|||
pendingTransactions.isNotEmpty() -> BalanceStatus.SameCurrencyTransactionInProgress
|
||||
else -> BalanceStatus.VerifiedOnline
|
||||
}
|
||||
val tokenAmountValue = wallet.getTokenAmount(token)?.value
|
||||
val tokenFiatAmount = tokenWalletData?.fiatRate?.let { rate -> tokenAmountValue?.toFiatValue(rate)}
|
||||
val tokenAmountValue = wallet.getTokenAmount(token)?.value
|
||||
val tokenFiatAmount = tokenWalletData?.fiatRate?.let { rate -> tokenAmountValue?.toFiatValue(rate) }
|
||||
|
||||
val tokenSendButton = tokenAmountValue?.isZero() == false && tokenPendingTransactions.isEmpty()
|
||||
val tokenSendButton = newWalletData?.shouldEnableTokenSendButton() == true
|
||||
&& tokenPendingTransactions.isEmpty()
|
||||
tokenWalletData?.copy(
|
||||
currencyData = tokenWalletData.currencyData.copy(
|
||||
status = tokenBalanceStatus,
|
||||
amount = tokenAmountValue?.toFormattedCurrencyString(token.decimals, token.symbol),
|
||||
fiatAmount = tokenFiatAmount,
|
||||
fiatAmountFormatted = tokenFiatAmount?.toFormattedFiatValue(fiatCurrencySymbol)
|
||||
),
|
||||
pendingTransactions = tokenPendingTransactions.removeUnknownTransactions(),
|
||||
mainButton = WalletMainButton.SendButton(tokenSendButton),
|
||||
tradeCryptoState = TradeCryptoState.from(exchangeManager, tokenWalletData)
|
||||
currencyData = tokenWalletData.currencyData.copy(
|
||||
status = tokenBalanceStatus,
|
||||
blockchainAmount = coinAmountValue,
|
||||
amount = tokenAmountValue,
|
||||
amountFormatted = tokenAmountValue?.toFormattedCurrencyString(token.decimals, token.symbol),
|
||||
fiatAmount = tokenFiatAmount,
|
||||
fiatAmountFormatted = tokenFiatAmount?.toFormattedFiatValue(fiatCurrencySymbol)
|
||||
),
|
||||
pendingTransactions = tokenPendingTransactions.removeUnknownTransactions(),
|
||||
mainButton = WalletMainButton.SendButton(tokenSendButton),
|
||||
tradeCryptoState = TradeCryptoState.from(exchangeManager, tokenWalletData),
|
||||
)
|
||||
}
|
||||
val newWallets = (tokens + newWalletData).mapNotNull { it }
|
||||
|
|
@ -98,7 +103,7 @@ class OnWalletLoadedReducer {
|
|||
ProgressState.Done
|
||||
}
|
||||
return walletState.copy(
|
||||
state = state, wallets = wallets, error = null
|
||||
state = state, walletsData = wallets, error = null
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -128,15 +133,13 @@ class OnWalletLoadedReducer {
|
|||
}
|
||||
val amount = wallet.amounts[AmountType.Coin]?.value
|
||||
val formattedAmount = amount?.toFormattedCurrencyString(
|
||||
wallet.blockchain.decimals(),
|
||||
wallet.blockchain.currency)
|
||||
wallet.blockchain.decimals(),
|
||||
wallet.blockchain.currency)
|
||||
val fiatRate = walletState.primaryWallet?.fiatRate
|
||||
val fiatAmountRaw = fiatRate?.multiply(amount)?.setScale(2, RoundingMode.DOWN)
|
||||
val fiatAmount = fiatRate?.let { amount?.toFiatString(it, fiatCurrencySymbol) }
|
||||
|
||||
val pendingTransactions = wallet.recentTransactions
|
||||
.toPendingTransactions(wallet.address)
|
||||
|
||||
val pendingTransactions = wallet.recentTransactions.toPendingTransactions(wallet.address)
|
||||
val sendButtonEnabled = amount?.isZero() == false && pendingTransactions.isEmpty()
|
||||
val balanceStatus = if (pendingTransactions.isNotEmpty()) {
|
||||
BalanceStatus.TransactionInProgress
|
||||
|
|
@ -144,21 +147,23 @@ class OnWalletLoadedReducer {
|
|||
BalanceStatus.VerifiedOnline
|
||||
}
|
||||
val walletData = walletState.primaryWallet?.copy(
|
||||
currencyData = BalanceWidgetData(
|
||||
balanceStatus, wallet.blockchain.fullName,
|
||||
currencySymbol = wallet.blockchain.currency,
|
||||
formattedAmount,
|
||||
token = tokenData,
|
||||
fiatAmountFormatted = fiatAmount,
|
||||
fiatAmount = fiatAmountRaw
|
||||
),
|
||||
pendingTransactions = pendingTransactions.removeUnknownTransactions(),
|
||||
mainButton = WalletMainButton.SendButton(sendButtonEnabled),
|
||||
tradeCryptoState = TradeCryptoState.from(exchangeManager, walletState.primaryWallet)
|
||||
currencyData = BalanceWidgetData(
|
||||
balanceStatus, wallet.blockchain.fullName,
|
||||
currencySymbol = wallet.blockchain.currency,
|
||||
token = tokenData,
|
||||
blockchainAmount = amount,
|
||||
amount = amount,
|
||||
amountFormatted = formattedAmount,
|
||||
fiatAmountFormatted = fiatAmount,
|
||||
fiatAmount = fiatAmountRaw
|
||||
),
|
||||
pendingTransactions = pendingTransactions.removeUnknownTransactions(),
|
||||
mainButton = WalletMainButton.SendButton(sendButtonEnabled),
|
||||
tradeCryptoState = TradeCryptoState.from(exchangeManager, walletState.primaryWallet),
|
||||
)
|
||||
val wallets = walletData?.let { listOf(walletData) } ?: emptyList()
|
||||
return walletState.copy(
|
||||
state = ProgressState.Done, wallets = wallets, error = null
|
||||
state = ProgressState.Done, walletsData = wallets, error = null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -46,7 +46,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
is WalletAction.EmptyWallet -> {
|
||||
newState = newState.copy(
|
||||
state = ProgressState.Done,
|
||||
wallets = listOf(
|
||||
walletsData = listOf(
|
||||
WalletData(
|
||||
currencyData = BalanceWidgetData(BalanceStatus.EmptyCard),
|
||||
mainButton = WalletMainButton.CreateWalletButton(true),
|
||||
|
|
@ -58,7 +58,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
is WalletAction.LoadData.Failure -> {
|
||||
when (action.error) {
|
||||
is TapError.NoInternetConnection -> {
|
||||
val wallets = newState.wallets
|
||||
val wallets = newState.walletsData
|
||||
.map {
|
||||
it.copy(
|
||||
currencyData = it.currencyData.copy(
|
||||
|
|
@ -69,13 +69,13 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
newState = newState.copy(
|
||||
state = ProgressState.Error,
|
||||
error = ErrorType.NoInternetConnection,
|
||||
wallets = wallets
|
||||
walletsData = wallets
|
||||
)
|
||||
}
|
||||
is TapError.UnknownBlockchain -> {
|
||||
newState = newState.copy(
|
||||
state = ProgressState.Done,
|
||||
wallets = listOf(
|
||||
walletsData = listOf(
|
||||
WalletData(
|
||||
currencyData = BalanceWidgetData(BalanceStatus.UnknownBlockchain),
|
||||
currency = Currency.Blockchain(Blockchain.Unknown)
|
||||
|
|
@ -94,7 +94,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
}
|
||||
is WalletAction.LoadWallet -> {
|
||||
if (action.blockchain == null) {
|
||||
val wallets = newState.wallets.map { wallet ->
|
||||
val wallets = newState.walletsData.map { wallet ->
|
||||
wallet.copy(
|
||||
currencyData = wallet.currencyData.copy(
|
||||
status = BalanceStatus.Loading,
|
||||
|
|
@ -107,14 +107,14 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
}
|
||||
newState = newState.copy(
|
||||
state = ProgressState.Loading,
|
||||
wallets = wallets,
|
||||
walletsData = wallets,
|
||||
)
|
||||
} else {
|
||||
val walletManager = newState.getWalletManager(action.blockchain) ?: return newState
|
||||
val blockchain = walletManager.wallet.blockchain
|
||||
val currencies = listOf(Currency.Blockchain(blockchain)) +
|
||||
walletManager.cardTokens.map { Currency.Token(it) }
|
||||
val newWallets = newState.wallets.filter { currencies.contains(it.currency) }
|
||||
val newWallets = newState.walletsData.filter { currencies.contains(it.currency) }
|
||||
.map { wallet ->
|
||||
wallet.copy(
|
||||
currencyData = wallet.currencyData.copy(
|
||||
|
|
@ -127,14 +127,10 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
)
|
||||
}
|
||||
val wallets = newState.replaceSomeWallets(newWallets)
|
||||
newState = newState.copy(wallets = newState.updateTradeCryptoState(exchangeManager, wallets))
|
||||
newState = newState.copy(walletsData = newState.updateTradeCryptoState(exchangeManager, wallets))
|
||||
}
|
||||
}
|
||||
is WalletAction.LoadWallet.Success -> newState =
|
||||
onWalletLoadedReducer.reduce(action.wallet, newState)
|
||||
is WalletAction.UpdateWallet.Success -> {
|
||||
newState = onWalletLoadedReducer.reduce(action.wallet, newState)
|
||||
}
|
||||
is WalletAction.LoadWallet.Success -> newState = onWalletLoadedReducer.reduce(action.wallet, newState)
|
||||
is WalletAction.LoadWallet.NoAccount -> {
|
||||
val walletData = newState.getWalletData(action.wallet.blockchain)?.copy(
|
||||
currencyData = BalanceWidgetData(
|
||||
|
|
@ -152,7 +148,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
}
|
||||
newState = newState.copy(
|
||||
state = progressState,
|
||||
wallets = newState.updateTradeCryptoState(exchangeManager, wallets)
|
||||
walletsData = newState.updateTradeCryptoState(exchangeManager, wallets)
|
||||
)
|
||||
}
|
||||
is WalletAction.LoadWallet.Failure -> {
|
||||
|
|
@ -187,7 +183,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
}
|
||||
newState = newState.copy(
|
||||
state = progressState,
|
||||
wallets = newState.updateTradeCryptoState(exchangeManager, wallets)
|
||||
walletsData = newState.updateTradeCryptoState(exchangeManager, wallets)
|
||||
)
|
||||
}
|
||||
is WalletAction.SetArtworkId -> {
|
||||
|
|
@ -227,7 +223,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
is WalletAction.Send.Cancel -> newState = newState.copy(walletDialog = null)
|
||||
is WalletAction.TradeCryptoAction -> return newState
|
||||
is WalletAction.ChangeSelectedAddress -> {
|
||||
val selectedWalletData = newState.getWalletData(newState.selectedWallet)
|
||||
val selectedWalletData = newState.getWalletData(newState.selectedCurrency)
|
||||
|
||||
val walletAddresses =
|
||||
newState.getWalletData(selectedWalletData?.currency)?.walletAddresses
|
||||
|
|
@ -242,16 +238,16 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
)
|
||||
)
|
||||
)
|
||||
newState = newState.copy(wallets = wallets)
|
||||
newState = newState.copy(walletsData = wallets)
|
||||
}
|
||||
is WalletAction.SetWalletRent -> {
|
||||
var walletData = newState.getWalletData(action.blockchain)
|
||||
if (walletData == null) {
|
||||
newState
|
||||
} else {
|
||||
walletData = walletData.copy(walletRent = WalletRent(action.minRent, action.rentExempt))
|
||||
walletData = walletData.copy(warningRent = WalletRent(action.minRent, action.rentExempt))
|
||||
newState = newState.copy(
|
||||
wallets = newState.replaceSomeWallets(listOf(walletData))
|
||||
walletsData = newState.replaceSomeWallets(listOf(walletData))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -342,7 +338,7 @@ private fun setMultiWalletFiatRate(
|
|||
),
|
||||
fiatRate = rate, fiatRateString = rateFormatted
|
||||
)
|
||||
return state.copy(wallets = state.replaceWalletInWallets(newWalletData))
|
||||
return state.copy(walletsData = state.replaceWalletInWallets(newWalletData))
|
||||
}
|
||||
|
||||
private fun setSingeWalletFiatRate(
|
||||
|
|
@ -360,7 +356,7 @@ private fun setSingeWalletFiatRate(
|
|||
fiatRate = rate,
|
||||
fiatRateString = rateFormatted
|
||||
)
|
||||
return state.copy(wallets = listOf(walletData))
|
||||
return state.copy(walletsData = listOf(walletData))
|
||||
} else if (currency is Currency.Token && currency.token == token) {
|
||||
val tokenFiatAmount = wallet.getTokenAmount(token)?.value?.toFiatString(rate, appCurrency)
|
||||
val tokenData = state.primaryWallet?.currencyData?.token?.copy(
|
||||
|
|
@ -375,7 +371,7 @@ private fun setSingeWalletFiatRate(
|
|||
)
|
||||
)
|
||||
val wallets = walletData?.let { listOf(walletData) } ?: emptyList()
|
||||
return state.copy(wallets = wallets)
|
||||
return state.copy(walletsData = wallets)
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
|
@ -22,7 +22,9 @@ data class BalanceWidgetData(
|
|||
val status: BalanceStatus? = null,
|
||||
val currency: String? = null,
|
||||
val currencySymbol: String? = null,
|
||||
val amount: String? = null,
|
||||
val blockchainAmount: BigDecimal? = BigDecimal.ZERO,
|
||||
val amount: BigDecimal? = null,
|
||||
val amountFormatted: String? = null,
|
||||
val fiatAmountFormatted: String? = null,
|
||||
val fiatAmount: BigDecimal? = null,
|
||||
val token: TokenData? = null,
|
||||
|
|
@ -149,7 +151,7 @@ class BalanceWidget(
|
|||
tvCurrency.text = data.token?.tokenSymbol
|
||||
tvBaseCurrency.text = data.currency
|
||||
tvAmount.text = if (showAmount) data.token?.amount else ""
|
||||
tvBaseAmount.text = if (showAmount) data.amount else ""
|
||||
tvBaseAmount.text = if (showAmount) data.amountFormatted else ""
|
||||
if (showAmount) {
|
||||
tvFiatAmount.show()
|
||||
tvFiatAmount.text = data.token?.fiatAmount
|
||||
|
|
@ -160,7 +162,7 @@ class BalanceWidget(
|
|||
with(binding.lBalance) {
|
||||
groupBaseCurrency.hide()
|
||||
tvCurrency.text = data.currency
|
||||
tvAmount.text = if (showAmount) data.amount else ""
|
||||
tvAmount.text = if (showAmount) data.amountFormatted else ""
|
||||
if (showAmount) {
|
||||
tvFiatAmount.show()
|
||||
tvFiatAmount.text = data.fiatAmountFormatted
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
|
|||
|
||||
override fun newState(state: WalletState) {
|
||||
if (activity == null || view == null) return
|
||||
if (state.selectedWallet == null) return
|
||||
if (state.selectedCurrency == null) return
|
||||
val selectedWallet = state.getSelectedWalletData() ?: return
|
||||
|
||||
|
||||
|
|
@ -107,8 +107,8 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
|
|||
|
||||
handleDialogs(state.walletDialog)
|
||||
handleCurrencyIcon(selectedWallet)
|
||||
handleWalletRent(selectedWallet.walletRent)
|
||||
|
||||
handleWalletRent(selectedWallet.warningRent)
|
||||
handleNotEnoughFundsOnMainCurrency(selectedWallet)
|
||||
|
||||
binding.srlWalletDetails.setOnRefreshListener {
|
||||
if (selectedWallet.currencyData.status != BalanceStatus.Loading) {
|
||||
|
|
@ -153,14 +153,27 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
|
|||
|
||||
private fun handleWalletRent(rent: WalletRent?) = with(binding) {
|
||||
val rent = rent.guard {
|
||||
lRentWarning.root.hide()
|
||||
lWarning.root.hide()
|
||||
return
|
||||
}
|
||||
val warningMessage = requireContext().getString(
|
||||
R.string.solana_rent_warning, rent.minRentValue, rent.rentExemptValue
|
||||
)
|
||||
lRentWarning.tvRentWarningMessage.text = warningMessage
|
||||
lRentWarning.root.show()
|
||||
lWarning.tvWarningMessage.text = warningMessage
|
||||
lWarning.root.show()
|
||||
}
|
||||
|
||||
private fun handleNotEnoughFundsOnMainCurrency(selectedWalletData: WalletData) = with(binding) {
|
||||
if (selectedWalletData.shouldShowCoinAmountWarning()) {
|
||||
val blockchainName = selectedWalletData.currency.blockchain.fullName
|
||||
val warningMessage = requireContext().getString(
|
||||
R.string.token_details_send_blocked_fee_format, blockchainName, blockchainName
|
||||
)
|
||||
lWarning.tvWarningMessage.text = warningMessage
|
||||
lWarning.root.show()
|
||||
} else {
|
||||
lWarning.root.hide()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCurrencyIcon(wallet: WalletData) = with(binding.lWalletDetails.lBalance) {
|
||||
|
|
@ -234,7 +247,7 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
|
|||
lBalance.root.show()
|
||||
lBalance.groupBalance.show()
|
||||
lBalance.tvError.hide()
|
||||
lBalance.tvAmount.text = data.amount
|
||||
lBalance.tvAmount.text = data.amountFormatted
|
||||
lBalance.tvFiatAmount.text = data.fiatAmountFormatted
|
||||
lBalance.tvStatus.setLoadingStatus(R.string.wallet_balance_loading)
|
||||
}
|
||||
|
|
@ -244,7 +257,7 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
|
|||
lBalance.root.show()
|
||||
lBalance.groupBalance.show()
|
||||
lBalance.tvError.hide()
|
||||
lBalance.tvAmount.text = data.amount
|
||||
lBalance.tvAmount.text = data.amountFormatted
|
||||
lBalance.tvFiatAmount.text = data.fiatAmountFormatted
|
||||
when (data.status) {
|
||||
BalanceStatus.VerifiedOnline, BalanceStatus.SameCurrencyTransactionInProgress -> {
|
||||
|
|
|
|||
|
|
@ -63,7 +63,8 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
}.select { it.walletState }
|
||||
}
|
||||
walletView.setFragment(this, binding)
|
||||
store.dispatch(WalletAction.UpdateWallet(force = false))
|
||||
// store.dispatch(WalletAction.UpdateWallet(force = false))
|
||||
store.dispatch(WalletAction.LoadWallet())
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
|
|
|
|||
|
|
@ -57,8 +57,8 @@ class WalletAdapter
|
|||
|
||||
fun bind(wallet: WalletData) = with(binding) {
|
||||
tvCurrency.text = wallet.currencyData.currency
|
||||
tvAmount.text = wallet.currencyData.amount?.takeWhile { !it.isWhitespace() }
|
||||
tvCurrencySymbol.text = wallet.currencyData.amount?.takeLastWhile { !it.isWhitespace() }
|
||||
tvAmount.text = wallet.currencyData.amountFormatted?.takeWhile { !it.isWhitespace() }
|
||||
tvCurrencySymbol.text = wallet.currencyData.amountFormatted?.takeLastWhile { !it.isWhitespace() }
|
||||
tvAmountFiat.text = wallet.currencyData.fiatAmountFormatted
|
||||
tvExchangeRate.text = wallet.fiatRateString
|
||||
cardWallet.setOnClickListener {
|
||||
|
|
@ -77,7 +77,7 @@ class WalletAdapter
|
|||
BalanceStatus.VerifiedOnline, BalanceStatus.SameCurrencyTransactionInProgress -> hideWarning()
|
||||
BalanceStatus.Loading -> {
|
||||
hideWarning()
|
||||
if (wallet.currencyData.amount == null) {
|
||||
if (wallet.currencyData.amountFormatted == null) {
|
||||
tvExchangeRate.text = root.getString(R.string.wallet_balance_loading)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,12 +94,12 @@ class MultiWalletView : WalletView {
|
|||
val fragment = fragment ?: return
|
||||
val binding = binding ?: return
|
||||
|
||||
walletsAdapter.submitList(state.wallets, state.primaryBlockchain, state.primaryToken)
|
||||
walletsAdapter.submitList(state.walletsData, state.primaryBlockchain, state.primaryToken)
|
||||
|
||||
binding.btnAddToken.setOnClickListener {
|
||||
store.dispatch(TokensAction.LoadCurrencies)
|
||||
store.dispatch(TokensAction.AllowToAddTokens(true))
|
||||
store.dispatch(TokensAction.SetAddedCurrencies(state.wallets))
|
||||
store.dispatch(TokensAction.SetAddedCurrencies(state.walletsData))
|
||||
store.dispatch(TokensAction.SetNonRemovableCurrencies(
|
||||
state.wallets.filterNot { state.canBeRemoved(it) })
|
||||
)
|
||||
|
|
|
|||
|
|
@ -99,8 +99,8 @@
|
|||
app:layout_constraintTop_toBottomOf="@id/rv_pending_transaction" />
|
||||
|
||||
<include
|
||||
android:id="@+id/l_rent_warning"
|
||||
layout="@layout/layout_wallet_details_rent"
|
||||
android:id="@+id/l_warning"
|
||||
layout="@layout/layout_wallet_details_warning"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
|
|
@ -168,7 +168,7 @@
|
|||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/btn_sell"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/l_rent_warning"
|
||||
app:layout_constraintTop_toBottomOf="@+id/l_warning"
|
||||
app:layout_constraintVertical_bias="1" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
|
|
@ -185,7 +185,7 @@
|
|||
app:layout_constraintEnd_toStartOf="@id/btn_confirm"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toEndOf="@id/btn_trade"
|
||||
app:layout_constraintTop_toBottomOf="@+id/l_rent_warning"
|
||||
app:layout_constraintTop_toBottomOf="@+id/l_warning"
|
||||
app:layout_constraintVertical_bias="1" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
|
|
@ -201,7 +201,7 @@
|
|||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/btn_sell"
|
||||
app:layout_constraintTop_toBottomOf="@+id/l_rent_warning"
|
||||
app:layout_constraintTop_toBottomOf="@+id/l_warning"
|
||||
app:layout_constraintVertical_bias="1" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_rent_warning_title"
|
||||
android:id="@+id/tv_warning_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
|
|
@ -27,7 +27,7 @@
|
|||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_rent_warning_message"
|
||||
android:id="@+id/tv_warning_message"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
|
|
@ -39,7 +39,7 @@
|
|||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/tv_rent_warning_title"
|
||||
app:layout_constraintTop_toBottomOf="@+id/tv_warning_title"
|
||||
tools:text="@string/solana_rent_warning" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
<string name="shop_2_cards">2 cards</string>
|
||||
<string name="shop_shipping">Shipping</string>
|
||||
<string name="shop_free">Free</string>
|
||||
<string name="shop_i_have_a_promo_code">I have a promo code…</string>
|
||||
<string name="shop_i_have_a_promo_code">I have a promo code...</string>
|
||||
<string name="shop_total">Total</string>
|
||||
<string name="shop_other_payment_methods">Other payment methods</string>
|
||||
<string name="shop_buy_now">Buy now</string>
|
||||
|
|
@ -23,18 +23,19 @@
|
|||
<string name="story_awe_title">Revolutionary Hardware Wallet</string>
|
||||
<string name="story_awe_description">Store your crypto assets secure while keeping private keys contained in your card</string>
|
||||
<string name="story_backup_title">Ultra Secure Backup</string>
|
||||
<string name="story_backup_description">Up to 3 physical cards to one wallet</string>
|
||||
<string name="story_backup_description">Up to <b>3 physical cards</b> to one wallet</string>
|
||||
<string name="story_currencies_title">Thousands of Currencies</string>
|
||||
<string name="story_currencies_description">A hardware wallet for your Bitcoin, Ethereum and many more currencies simultaneously – all in one card</string>
|
||||
<string name="story_web3_title">DeFi Compatible</string>
|
||||
<string name="story_web3_description">Exchange, buy NFT’s, make loans and deposits in more than 100 different decentralized services</string>
|
||||
<string name="story_finish_title">The Wallet for Everyone</string>
|
||||
<string name="story_finish_description">Use it on the go, anywhere, anytime. No wires or batteries. Just tap the card to your phone when you need your crypto.</string>
|
||||
<string name="home_button_order">Order card</string>
|
||||
<string name="home_button_order">Order</string>
|
||||
<string name="home_button_search_tokens">Search tokens</string>
|
||||
<string name="search_tokens_title">Search tokens</string>
|
||||
<string name="alert_demo_message">You are currently running in Demo mode. All funds are not real.</string>
|
||||
<string name="alert_demo_feature_disabled">This feature is disabled in Demo mode</string>
|
||||
<string name="token_details_send_blocked_fee_format">Not enough funds for fee on your %s wallet to send a transaction. Top up your %s wallet first.</string>
|
||||
|
||||
<string name="currency_subtitle_expanded">Available networks</string>
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
<string name="shop_2_cards">2 cards</string>
|
||||
<string name="shop_shipping">Shipping</string>
|
||||
<string name="shop_free">Free</string>
|
||||
<string name="shop_i_have_a_promo_code">I have a promo code…</string>
|
||||
<string name="shop_i_have_a_promo_code">I have a promo code...</string>
|
||||
<string name="shop_total">Total</string>
|
||||
<string name="shop_other_payment_methods">Other payment methods</string>
|
||||
<string name="shop_buy_now">Buy now</string>
|
||||
|
|
@ -23,18 +23,19 @@
|
|||
<string name="story_awe_title">Revolutionary Hardware Wallet</string>
|
||||
<string name="story_awe_description">Store your crypto assets secure while keeping private keys contained in your card</string>
|
||||
<string name="story_backup_title">Ultra Secure Backup</string>
|
||||
<string name="story_backup_description">Up to 3 physical cards to one wallet</string>
|
||||
<string name="story_backup_description">Up to <b>3 physical cards</b> to one wallet</string>
|
||||
<string name="story_currencies_title">Thousands of Currencies</string>
|
||||
<string name="story_currencies_description">A hardware wallet for your Bitcoin, Ethereum and many more currencies simultaneously – all in one card</string>
|
||||
<string name="story_web3_title">DeFi Compatible</string>
|
||||
<string name="story_web3_description">Exchange, buy NFT’s, make loans and deposits in more than 100 different decentralized services</string>
|
||||
<string name="story_finish_title">The Wallet for Everyone</string>
|
||||
<string name="story_finish_description">Use it on the go, anywhere, anytime. No wires or batteries. Just tap the card to your phone when you need your crypto.</string>
|
||||
<string name="home_button_order">Order card</string>
|
||||
<string name="home_button_order">Order</string>
|
||||
<string name="home_button_search_tokens">Search tokens</string>
|
||||
<string name="search_tokens_title">Search tokens</string>
|
||||
<string name="alert_demo_message">You are currently running in Demo mode. All funds are not real.</string>
|
||||
<string name="alert_demo_feature_disabled">This feature is disabled in Demo mode</string>
|
||||
<string name="token_details_send_blocked_fee_format">Not enough funds for fee on your %s wallet to send a transaction. Top up your %s wallet first.</string>
|
||||
|
||||
<string name="currency_subtitle_expanded">Available networks</string>
|
||||
</resources>
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
<string name="shop_2_cards">2 cards</string>
|
||||
<string name="shop_shipping">Shipping</string>
|
||||
<string name="shop_free">Free</string>
|
||||
<string name="shop_i_have_a_promo_code">I have a promo code…</string>
|
||||
<string name="shop_i_have_a_promo_code">I have a promo code...</string>
|
||||
<string name="shop_total">Total</string>
|
||||
<string name="shop_other_payment_methods">Other payment methods</string>
|
||||
<string name="shop_buy_now">Buy now</string>
|
||||
|
|
@ -23,18 +23,19 @@
|
|||
<string name="story_awe_title">Revolutionary Hardware Wallet</string>
|
||||
<string name="story_awe_description">Store your crypto assets secure while keeping private keys contained in your card</string>
|
||||
<string name="story_backup_title">Ultra Secure Backup</string>
|
||||
<string name="story_backup_description">Up to 3 physical cards to one wallet</string>
|
||||
<string name="story_backup_description">Up to <b>3 physical cards</b> to one wallet</string>
|
||||
<string name="story_currencies_title">Thousands of Currencies</string>
|
||||
<string name="story_currencies_description">A hardware wallet for your Bitcoin, Ethereum and many more currencies simultaneously – all in one card</string>
|
||||
<string name="story_web3_title">DeFi Compatible</string>
|
||||
<string name="story_web3_description">Exchange, buy NFT’s, make loans and deposits in more than 100 different decentralized services</string>
|
||||
<string name="story_finish_title">The Wallet for Everyone</string>
|
||||
<string name="story_finish_description">Use it on the go, anywhere, anytime. No wires or batteries. Just tap the card to your phone when you need your crypto.</string>
|
||||
<string name="home_button_order">Order card</string>
|
||||
<string name="home_button_order">Order</string>
|
||||
<string name="home_button_search_tokens">Search tokens</string>
|
||||
<string name="search_tokens_title">Search tokens</string>
|
||||
<string name="alert_demo_message">You are currently running in Demo mode. All funds are not real.</string>
|
||||
<string name="alert_demo_feature_disabled">This feature is disabled in Demo mode</string>
|
||||
<string name="token_details_send_blocked_fee_format">Not enough funds for fee on your %s wallet to send a transaction. Top up your %s wallet first.</string>
|
||||
|
||||
<string name="currency_subtitle_expanded">Available networks</string>
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
<string name="story_awe_title">Революционный аппаратный кошелек</string>
|
||||
<string name="story_awe_description">Держите свои криптосбережения в безопасности. Приватные ключи надежно хранятся на карте.</string>
|
||||
<string name="story_backup_title">Все ключи в безопасности</string>
|
||||
<string name="story_backup_description">До трех карт с одним кошельком</string>
|
||||
<string name="story_backup_description">До <b>трех карт</b> с одним кошельком</string>
|
||||
<string name="story_currencies_title">Тысячи криптовалют</string>
|
||||
<string name="story_currencies_description">Аппаратный кошелек для ваших биткоинов, эфира и многих других валют одновременно — все в одной карте</string>
|
||||
<string name="story_web3_title">Поддержка DeFi</string>
|
||||
|
|
@ -35,6 +35,7 @@
|
|||
<string name="search_tokens_title">Поиск токенов</string>
|
||||
<string name="alert_demo_message">Приложение работает в демонстрационном режиме. Средства на всех счетах ненастоящие.</string>
|
||||
<string name="alert_demo_feature_disabled">Эта функция недоступна в демонстрационном режиме</string>
|
||||
<string name="token_details_send_blocked_fee_format">Недостаточно средств для комиссии на вашем %s кошельке для отправки транзакции. Сначала пополните свой %s кошелек.</string>
|
||||
|
||||
<string name="currency_subtitle_expanded">Доступные сети</string>
|
||||
</resources>
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
<string name="shop_2_cards">2 cards</string>
|
||||
<string name="shop_shipping">Shipping</string>
|
||||
<string name="shop_free">Free</string>
|
||||
<string name="shop_i_have_a_promo_code">I have a promo code…</string>
|
||||
<string name="shop_i_have_a_promo_code">I have a promo code...</string>
|
||||
<string name="shop_total">Total</string>
|
||||
<string name="shop_other_payment_methods">Other payment methods</string>
|
||||
<string name="shop_buy_now">Buy now</string>
|
||||
|
|
@ -23,18 +23,19 @@
|
|||
<string name="story_awe_title">Revolutionary Hardware Wallet</string>
|
||||
<string name="story_awe_description">Store your crypto assets secure while keeping private keys contained in your card</string>
|
||||
<string name="story_backup_title">Ultra Secure Backup</string>
|
||||
<string name="story_backup_description">Up to 3 physical cards to one wallet</string>
|
||||
<string name="story_backup_description">Up to <b>3 physical cards</b> to one wallet</string>
|
||||
<string name="story_currencies_title">Thousands of Currencies</string>
|
||||
<string name="story_currencies_description">A hardware wallet for your Bitcoin, Ethereum and many more currencies simultaneously – all in one card</string>
|
||||
<string name="story_web3_title">DeFi Compatible</string>
|
||||
<string name="story_web3_description">Exchange, buy NFT’s, make loans and deposits in more than 100 different decentralized services</string>
|
||||
<string name="story_finish_title">The Wallet for Everyone</string>
|
||||
<string name="story_finish_description">Use it on the go, anywhere, anytime. No wires or batteries. Just tap the card to your phone when you need your crypto.</string>
|
||||
<string name="home_button_order">Order card</string>
|
||||
<string name="home_button_order">Order</string>
|
||||
<string name="home_button_search_tokens">Search tokens</string>
|
||||
<string name="search_tokens_title">Search tokens</string>
|
||||
<string name="alert_demo_message">You are currently running in Demo mode. All funds are not real.</string>
|
||||
<string name="alert_demo_feature_disabled">This feature is disabled in Demo mode</string>
|
||||
<string name="token_details_send_blocked_fee_format">Not enough funds for fee on your %s wallet to send a transaction. Top up your %s wallet first.</string>
|
||||
|
||||
<string name="currency_subtitle_expanded">Available networks</string>
|
||||
</resources>
|
||||
Loading…
Add table
Add a link
Reference in a new issue