Updated on 2026-08-14

This commit is contained in:
Tangem 2022-05-19 17:06:33 +03:00
commit aabae87495
34 changed files with 438 additions and 278 deletions

View file

@ -0,0 +1,18 @@
package com.tangem.tap.common.entities
data class FiatCurrency(
val code: String,
val name: String,
val symbol: String,
) {
val displayName: String
get() = "${this.name} (${this.code}) - ${this.symbol}"
companion object {
val Default = FiatCurrency(
symbol = "$",
code = "USD",
name = "US Dollar",
)
}
}

View file

@ -1,10 +0,0 @@
package com.tangem.tap.common.entities
/**
[REDACTED_AUTHOR]
*/
class TapCurrency {
companion object{
const val DEFAULT_FIAT_CURRENCY = "USD"
}
}

View file

@ -1,8 +1,10 @@
package com.tangem.tap.common.extensions
import android.text.Spanned
import android.text.SpannedString
import android.text.style.RelativeSizeSpan
import androidx.core.text.buildSpannedString
import com.tangem.common.extensions.isZero
import com.tangem.network.api.tangemTech.CurrenciesResponse
import com.tangem.tap.common.redux.global.FiatCurrencyName
import java.math.BigDecimal
import java.math.RoundingMode
import java.text.DecimalFormat
@ -37,7 +39,7 @@ fun BigDecimal.toFormattedCurrencyString(
return "$formattedAmount $currency"
}
fun BigDecimal.toFiatString(rateValue: BigDecimal, fiatCurrencyName: FiatCurrencyName): String {
fun BigDecimal.toFiatString(rateValue: BigDecimal, fiatCurrencyName: String): String {
var fiatValue = rateValue.multiply(this)
fiatValue = fiatValue.setScale(2, RoundingMode.HALF_UP)
return "≈ ${fiatCurrencyName} $fiatValue"
@ -48,12 +50,10 @@ fun BigDecimal.toFiatValue(rateValue: BigDecimal): BigDecimal {
return fiatValue.setScale(2, RoundingMode.HALF_UP)
}
fun BigDecimal.toFormattedFiatValue(fiatCurrencyName: FiatCurrencyName): String {
fun BigDecimal.toFormattedFiatValue(fiatCurrencyName: String): String {
return "≈ ${fiatCurrencyName} $this"
}
fun CurrenciesResponse.Currency.toFormattedString(): String = "${this.name} (${this.code}) - ${this.unit}"
fun BigDecimal.stripZeroPlainString(): String = this.stripTrailingZeros().toPlainString()
// 0.00 -> 0.00
@ -87,4 +87,26 @@ fun BigDecimal.isGreaterThanOrEqual(value: BigDecimal): Boolean {
fun BigDecimal.isLessThanOrEqual(value: BigDecimal): Boolean {
val compareResult = this.compareTo(value)
return compareResult == -1 || compareResult == 0
}
fun BigDecimal.formatAmountAsSpannedString(
currencySymbol: String,
integerPartSizeProportion: Float = 1.4f
): SpannedString {
val amount = this.scaleToFiat(applyPrecision = true)
.stripZeroPlainString()
val integer = amount.substringAfter('.')
val reminder = amount.substringBefore('.')
return buildSpannedString {
append(
integer,
RelativeSizeSpan(integerPartSizeProportion),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
append('.')
append(reminder)
append(' ')
append(currencySymbol)
}
}

View file

@ -57,7 +57,7 @@ fun WalletManager?.getToUpUrl(): String? {
action = CurrencyExchangeManager.Action.Buy,
blockchain = wallet.blockchain,
cryptoCurrencyName = wallet.blockchain.currency,
fiatCurrency = globalState.appCurrency,
fiatCurrencyName = globalState.appCurrency.code,
walletAddress = defaultAddress,
)
}

View file

@ -6,6 +6,7 @@ import com.tangem.common.CompletionResult
import com.tangem.common.core.TangemError
import com.tangem.domain.common.ScanResponse
import com.tangem.tap.common.analytics.GlobalAnalyticsHandler
import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.common.redux.*
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.configurable.config.ConfigManager
@ -54,9 +55,9 @@ sealed class GlobalAction : Action {
data class SetIfCardVerifiedOnline(val verified: Boolean) : GlobalAction()
data class ChangeAppCurrency(val appCurrency: FiatCurrencyName) : GlobalAction()
data class ChangeAppCurrency(val appCurrency: FiatCurrency) : GlobalAction()
object RestoreAppCurrency : GlobalAction() {
data class Success(val appCurrency: FiatCurrencyName) : GlobalAction()
data class Success(val appCurrency: FiatCurrency) : GlobalAction()
}
data class UpdateWalletSignedHashes(

View file

@ -24,7 +24,7 @@ class GlobalMiddleware {
}
}
private val globalMiddlewareHandler: Middleware<AppState> = { dispatch, appState ->
private val globalMiddlewareHandler: Middleware<AppState> = { _, appState ->
{ nextDispatch ->
{ action ->
when (action) {
@ -45,7 +45,7 @@ private val globalMiddlewareHandler: Middleware<AppState> = { dispatch, appState
}
is GlobalAction.RestoreAppCurrency -> {
store.dispatch(GlobalAction.RestoreAppCurrency.Success(
preferencesStorage.getAppCurrency()
preferencesStorage.fiatCurrenciesPrefStorage.getAppCurrency()
))
}
is GlobalAction.HideWarningMessage -> {

View file

@ -2,7 +2,7 @@ package com.tangem.tap.common.redux.global
import com.tangem.domain.common.ScanResponse
import com.tangem.tap.common.analytics.AnalyticsHandler
import com.tangem.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY
import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.common.redux.StateDialog
import com.tangem.tap.domain.PayIdManager
import com.tangem.tap.domain.TapWalletManager
@ -22,7 +22,7 @@ data class GlobalState(
val configManager: ConfigManager? = null,
val warningManager: WarningMessagesManager? = null,
val feedbackManager: FeedbackManager? = null,
val appCurrency: FiatCurrencyName = DEFAULT_FIAT_CURRENCY,
val appCurrency: FiatCurrency = FiatCurrency.Default,
val scanCardFailsCounter: Int = 0,
val dialog: StateDialog? = null,
val currencyExchangeManager: CurrencyExchangeManager? = null,
@ -40,7 +40,6 @@ data class AndroidResources(
)
}
typealias CryptoCurrencyName = String
typealias FiatCurrencyName = String
data class OnboardingState(
val onboardingStarted: Boolean = false,

View file

@ -4,11 +4,10 @@ import com.tangem.blockchain.common.Wallet
import com.tangem.common.card.Card
import com.tangem.domain.common.ScanResponse
import com.tangem.domain.common.TwinCardNumber
import com.tangem.network.api.tangemTech.CurrenciesResponse
import com.tangem.network.api.tangemTech.TangemTechService
import com.tangem.operations.pins.CheckUserCodesResponse
import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.common.redux.NotificationAction
import com.tangem.tap.common.redux.global.FiatCurrencyName
import com.tangem.tap.domain.termsOfUse.CardTou
import com.tangem.wallet.R
import org.rekotlin.Action
@ -19,8 +18,8 @@ sealed class DetailsAction : Action {
val scanResponse: ScanResponse,
val wallets: List<Wallet>,
val cardTou: CardTou,
val fiatCurrencyName: FiatCurrencyName,
val fiatCurrencies: List<FiatCurrencyName>? = null,
val fiatCurrencyName: FiatCurrency,
val fiatCurrencies: List<FiatCurrency>? = null,
val tangemTechService: TangemTechService,
) : DetailsAction()
@ -48,10 +47,10 @@ sealed class DetailsAction : Action {
object CreateBackup : DetailsAction()
sealed class AppCurrencyAction : DetailsAction() {
data class SetCurrencies(val currencies: List<CurrenciesResponse.Currency>) : AppCurrencyAction()
data class SetCurrencies(val currencies: List<FiatCurrency>) : AppCurrencyAction()
object ChooseAppCurrency : AppCurrencyAction()
object Cancel : AppCurrencyAction()
data class SelectAppCurrency(val fiatCurrencyName: FiatCurrencyName) : AppCurrencyAction()
data class SelectAppCurrency(val fiatCurrency: FiatCurrency) : AppCurrencyAction()
}
sealed class ManageSecurity : DetailsAction() {

View file

@ -4,10 +4,12 @@ import com.tangem.common.CompletionResult
import com.tangem.common.card.FirmwareVersion
import com.tangem.common.core.TangemSdkError
import com.tangem.common.services.Result
import com.tangem.network.api.tangemTech.CurrenciesResponse
import com.tangem.operations.pins.CheckUserCodesResponse
import com.tangem.tap.*
import com.tangem.tap.common.analytics.Analytics
import com.tangem.tap.common.analytics.AnalyticsParam
import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.common.extensions.dispatchNotification
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.redux.AppState
@ -28,7 +30,7 @@ class DetailsMiddleware {
private val eraseWalletMiddleware = EraseWalletMiddleware()
private val appCurrencyMiddleware = AppCurrencyMiddleware()
private val manageSecurityMiddleware = ManageSecurityMiddleware()
val detailsMiddleware: Middleware<AppState> = { dispatch, state ->
val detailsMiddleware: Middleware<AppState> = { _, _ ->
{ next ->
{ action ->
when (action) {
@ -71,7 +73,11 @@ class DetailsMiddleware {
val fiatCurrenciesPrefStorage = preferencesStorage.fiatCurrenciesPrefStorage
val storedFiatCurrencies = fiatCurrenciesPrefStorage.restore()
if (storedFiatCurrencies.isNotEmpty()) {
store.dispatch(DetailsAction.AppCurrencyAction.SetCurrencies(storedFiatCurrencies))
store.dispatch(
DetailsAction.AppCurrencyAction.SetCurrencies(
currencies = storedFiatCurrencies.mapToUiModel()
)
)
}
scope.launch {
@ -79,9 +85,15 @@ class DetailsMiddleware {
when (val result = tangemTechService.currencies()) {
is Result.Success -> {
val currenciesList = result.data.currencies
if (currenciesList.isNotEmpty() && currenciesList.toSet() != storedFiatCurrencies.toSet()) {
if (currenciesList.isNotEmpty() &&
currenciesList.toSet() != storedFiatCurrencies.toSet()
) {
fiatCurrenciesPrefStorage.save(currenciesList)
dispatchOnMain(DetailsAction.AppCurrencyAction.SetCurrencies(currenciesList))
dispatchOnMain(
DetailsAction.AppCurrencyAction.SetCurrencies(
currencies = currenciesList.mapToUiModel()
)
)
}
}
is Result.Failure -> {}
@ -89,7 +101,17 @@ class DetailsMiddleware {
}
}
class EraseWalletMiddleware() {
private fun List<CurrenciesResponse.Currency>.mapToUiModel(): List<FiatCurrency> {
return this.map {
FiatCurrency(
code = it.code,
name = it.name,
symbol = it.unit
)
}
}
class EraseWalletMiddleware {
fun handle(action: DetailsAction.ResetToFactory) {
when (action) {
is DetailsAction.ResetToFactory.Proceed -> {
@ -100,6 +122,8 @@ class DetailsMiddleware {
store.dispatch(DetailsAction.ResetToFactory.Proceed.NotAllowedByCard)
EraseWalletState.NotEmpty ->
store.dispatch(DetailsAction.ResetToFactory.Proceed.NotEmpty)
else -> { /* no-op */
}
}
}
is DetailsAction.ResetToFactory.Cancel -> {
@ -128,6 +152,8 @@ class DetailsMiddleware {
}
}
}
else -> { /* no-op */
}
}
}
}
@ -137,10 +163,13 @@ class DetailsMiddleware {
when (action) {
is DetailsAction.AppCurrencyAction.SelectAppCurrency -> {
store.state.globalState.tapWalletManager.rates.clear()
preferencesStorage.saveAppCurrency(action.fiatCurrencyName)
store.dispatch(GlobalAction.ChangeAppCurrency(action.fiatCurrencyName))
preferencesStorage.fiatCurrenciesPrefStorage
.saveAppCurrency(action.fiatCurrency)
store.dispatch(GlobalAction.ChangeAppCurrency(action.fiatCurrency))
store.dispatch(WalletAction.LoadFiatRate())
}
else -> { /* no-op */
}
}
}
}
@ -211,18 +240,22 @@ class DetailsMiddleware {
actionToLog = Analytics.ActionToLog.ChangeSecOptions,
parameters = mapOf(
AnalyticsParam.NEW_SECURITY_OPTION to
(selectedOption?.name ?: "")
(selectedOption?.name ?: "")
),
card = store.state.detailsState.scanResponse?.card
)
}
store.dispatch(DetailsAction.ManageSecurity.SaveChanges.Failure)
}
else -> { /* no-op */
}
}
}
}
}
else -> { /* no-op */
}
}
}
}
}
}

View file

@ -49,7 +49,7 @@ private fun handlePrepareScreen(
wallets = action.wallets,
cardInfo = action.scanResponse.card.toCardInfo(),
appCurrencyState = state.appCurrencyState.copy(
fiatCurrencyName = action.fiatCurrencyName,
currentFiatCurrency = action.fiatCurrencyName,
showAppCurrencyDialog = false,
),
cardTermsOfUseUrl = action.cardTou.getUrl(action.scanResponse.card),
@ -109,7 +109,8 @@ private fun handleAppCurrencyAction(
is DetailsAction.AppCurrencyAction.SelectAppCurrency -> {
state.copy(
appCurrencyState = state.appCurrencyState.copy(
fiatCurrencyName = action.fiatCurrencyName, showAppCurrencyDialog = false
currentFiatCurrency = action.fiatCurrency,
showAppCurrencyDialog = false
)
)
}

View file

@ -3,10 +3,8 @@ package com.tangem.tap.features.details.redux
import android.net.Uri
import com.tangem.blockchain.common.Wallet
import com.tangem.domain.common.ScanResponse
import com.tangem.network.api.tangemTech.CurrenciesResponse
import com.tangem.tap.common.entities.Button
import com.tangem.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY
import com.tangem.tap.common.redux.global.FiatCurrencyName
import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
import com.tangem.tap.store
import org.rekotlin.StateType
@ -53,7 +51,7 @@ data class SecurityScreenState(
enum class SecurityOption { LongTap, PassCode, AccessCode }
data class AppCurrencyState(
val fiatCurrencyName: FiatCurrencyName = DEFAULT_FIAT_CURRENCY,
val currentFiatCurrency: FiatCurrency = FiatCurrency.Default,
val showAppCurrencyDialog: Boolean = false,
val fiatCurrencies: List<CurrenciesResponse.Currency>? = null,
val fiatCurrencies: List<FiatCurrency>? = null,
)

View file

@ -2,9 +2,7 @@ package com.tangem.tap.features.details.ui
import android.content.Context
import androidx.appcompat.app.AlertDialog
import com.tangem.network.api.tangemTech.CurrenciesResponse
import com.tangem.tap.common.extensions.toFormattedString
import com.tangem.tap.common.redux.global.FiatCurrencyName
import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.features.details.redux.DetailsAction
import com.tangem.tap.store
import com.tangem.wallet.R
@ -13,20 +11,30 @@ class CurrencySelectionDialog {
var dialog: AlertDialog? = null
fun show(currenciesList: List<CurrenciesResponse.Currency>, currentAppCurrency: FiatCurrencyName, context: Context) {
fun show(
currenciesList: List<FiatCurrency>,
currentAppCurrency: FiatCurrency,
context: Context
) {
if (dialog == null) {
val currenciesToShow = currenciesList.map { it.toFormattedString() }.toTypedArray()
var currentSelection = currenciesList.indexOfFirst { it.code == currentAppCurrency }
val currenciesToShow = currenciesList
.map { it.displayName }
.toTypedArray()
var currentSelection = currenciesList
.indexOfFirst { it.code == currentAppCurrency.code }
dialog = AlertDialog.Builder(context)
.setTitle(context.getString(R.string.details_row_title_currency))
.setNegativeButton(context.getString(R.string.common_cancel)) { _, _ ->
store.dispatch(DetailsAction.AppCurrencyAction.Cancel)
}
.setPositiveButton(context.getString(R.string.common_done)) { _, _ ->
val selectedCurrency = currenciesList[currentSelection]
store.dispatch(DetailsAction.AppCurrencyAction.SelectAppCurrency(selectedCurrency.code))
.setTitle(context.getString(R.string.details_row_title_currency))
.setNegativeButton(context.getString(R.string.common_cancel)) { _, _ ->
store.dispatch(DetailsAction.AppCurrencyAction.Cancel)
}
.setPositiveButton(context.getString(R.string.common_done)) { _, _ ->
val selectedCurrency = currenciesList[currentSelection]
store.dispatch(
DetailsAction.AppCurrencyAction.SelectAppCurrency(
fiatCurrency = selectedCurrency
)
)
}
.setOnDismissListener {
store.dispatch(DetailsAction.AppCurrencyAction.Cancel)

View file

@ -115,7 +115,7 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<Det
store.dispatch(DetailsAction.CreateBackup)
}
tvAppCurrency.text = state.appCurrencyState.fiatCurrencyName
tvAppCurrency.text = state.appCurrencyState.currentFiatCurrency.code
tvAppCurrencyTitle.setOnClickListener {
store.dispatch(DetailsAction.AppCurrencyAction.ChooseAppCurrency)
@ -144,9 +144,9 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<Det
if (state.appCurrencyState.showAppCurrencyDialog &&
!state.appCurrencyState.fiatCurrencies.isNullOrEmpty()) {
currencySelectionDialog.show(
state.appCurrencyState.fiatCurrencies,
state.appCurrencyState.fiatCurrencyName,
requireContext()
currenciesList = state.appCurrencyState.fiatCurrencies,
currentAppCurrency = state.appCurrencyState.currentFiatCurrency,
context = requireContext()
)
} else {
currencySelectionDialog.clear()

View file

@ -177,7 +177,7 @@ class ReceiptReducer : SendInternalReducer {
private fun determineSymbols(wallet: Wallet, amountType: AmountType): ReceiptSymbols {
return ReceiptSymbols(
fiat = store.state.globalState.appCurrency,
fiat = store.state.globalState.appCurrency.code,
crypto = wallet.blockchain.currency,
token = when (amountType) {
is AmountType.Token -> amountType.token.symbol

View file

@ -5,8 +5,8 @@ import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.WalletManager
import com.tangem.common.extensions.isZero
import com.tangem.tap.common.CurrencyConverter
import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.common.entities.IndeterminateProgressButton
import com.tangem.tap.common.entities.TapCurrency
import com.tangem.tap.common.redux.StateDialog
import com.tangem.tap.common.text.DecimalDigitsInputFilter
import com.tangem.tap.domain.TapError
@ -123,18 +123,18 @@ enum class ButtonState {
}
data class AmountState(
val amountToExtract: Amount? = null,
val typeOfAmount: AmountType = AmountType.Coin,
val viewAmountValue: InputViewValue = InputViewValue(BigDecimal.ZERO.toPlainString()),
val viewBalanceValue: String = BigDecimal.ZERO.toPlainString(),
val mainCurrency: MainCurrency = MainCurrency(MainCurrencyType.FIAT, TapCurrency.DEFAULT_FIAT_CURRENCY),
val amountToSendCrypto: BigDecimal = BigDecimal.ZERO,
val balanceCrypto: BigDecimal = BigDecimal.ZERO,
val cursorAtTheSamePosition: Boolean = true,
val maxLengthOfAmount: Int = 2,
val decimalSeparator: String = ".",
val error: TapError? = null,
val inputIsEnabled: Boolean = true,
val amountToExtract: Amount? = null,
val typeOfAmount: AmountType = AmountType.Coin,
val viewAmountValue: InputViewValue = InputViewValue(BigDecimal.ZERO.toPlainString()),
val viewBalanceValue: String = BigDecimal.ZERO.toPlainString(),
val mainCurrency: MainCurrency = MainCurrency(MainCurrencyType.FIAT, FiatCurrency.Default.code),
val amountToSendCrypto: BigDecimal = BigDecimal.ZERO,
val balanceCrypto: BigDecimal = BigDecimal.ZERO,
val cursorAtTheSamePosition: Boolean = true,
val maxLengthOfAmount: Int = 2,
val decimalSeparator: String = ".",
val error: TapError? = null,
val inputIsEnabled: Boolean = true,
) : SendScreenState {
override val stateId: StateId = StateId.AMOUNT
@ -146,7 +146,7 @@ data class AmountState(
fun createMainCurrency(type: MainCurrencyType, canSwitched: Boolean): MainCurrency {
return if (!canSwitched) MainCurrency(type, amountToExtract?.currencySymbol ?: "NONE", false)
else when (type) {
MainCurrencyType.FIAT -> MainCurrency(type, store.state.globalState.appCurrency)
MainCurrencyType.FIAT -> MainCurrency(type, store.state.globalState.appCurrency.code)
MainCurrencyType.CRYPTO -> MainCurrency(type, amountToExtract?.currencySymbol ?: "NONE")
}
}

View file

@ -17,7 +17,7 @@ import com.tangem.Message
import com.tangem.tangem_sdk_new.extensions.dpToPx
import com.tangem.tangem_sdk_new.extensions.hideSoftKeyboard
import com.tangem.tap.common.KeyboardObserver
import com.tangem.tap.common.entities.TapCurrency
import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.common.extensions.getFromClipboard
import com.tangem.tap.common.extensions.setOnImeActionListener
import com.tangem.tap.common.qrCodeScan.ScanQrCodeActivity
@ -261,11 +261,10 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
private fun restoreMainCurrency(): MainCurrencyType {
val sp = requireContext().getSharedPreferences("SendScreen", Context.MODE_PRIVATE)
val mainCurrency = sp.getString("mainCurrency", TapCurrency.DEFAULT_FIAT_CURRENCY)
val foundType = MainCurrencyType.values()
.firstOrNull { it.name.equals(mainCurrency!!, ignoreCase = true) }
?: MainCurrencyType.CRYPTO
return foundType
val mainCurrency = sp.getString("mainCurrency", FiatCurrency.Default.code)
return MainCurrencyType.values()
.firstOrNull { it.name.equals(mainCurrency!!, ignoreCase = true) }
?: MainCurrencyType.CRYPTO
}
fun saveMainCurrency(type: MainCurrencyType) {

View file

@ -0,0 +1,16 @@
package com.tangem.tap.features.wallet.models
import com.tangem.tap.common.entities.FiatCurrency
import java.math.BigDecimal
data class TotalBalance(
val state: State,
val fiatAmount: BigDecimal,
val fiatCurrency: FiatCurrency,
) {
enum class State {
Loading,
Failed,
Success
}
}

View file

@ -20,6 +20,7 @@ import com.tangem.tap.domain.tokens.BlockchainNetwork
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
import com.tangem.tap.features.tokens.redux.TokenWithBlockchain
import com.tangem.tap.features.wallet.models.PendingTransaction
import com.tangem.tap.features.wallet.models.TotalBalance
import com.tangem.tap.features.wallet.models.toPendingTransactions
import com.tangem.tap.features.wallet.models.toPendingTransactionsForToken
import com.tangem.tap.features.wallet.ui.BalanceStatus
@ -44,6 +45,7 @@ data class WalletState(
val primaryBlockchain: Blockchain? = null,
val primaryToken: Token? = null,
val isTestnet: Boolean = false,
val totalBalance: TotalBalance? = null,
) : StateType {
// if you do not delegate - the application crashes on startup,
@ -196,18 +198,20 @@ data class WalletState(
return copy(wallets = replaceWalletInWallets(walletStore))
}
fun updateWalletStores(walletStores: List<WalletStore>): WalletState {
val walletStores = walletStores.toMutableList()
private fun updateWalletStores(walletStores: List<WalletStore>): WalletState {
val walletStoresMutable = walletStores.toMutableList()
val updatedWallets = wallets.map { oldWalletStore ->
val walletStore = walletStores.find { it.blockchainNetwork == oldWalletStore.blockchainNetwork }
val walletStore = walletStoresMutable.find {
it.blockchainNetwork == oldWalletStore.blockchainNetwork
}
if (walletStore != null) {
walletStores.remove(walletStore)
walletStoresMutable.remove(walletStore)
walletStore
} else {
oldWalletStore
}
}
return copy(wallets = updatedWallets + walletStores)
return copy(wallets = updatedWallets + walletStoresMutable)
}
fun removeWallet(walletData: WalletData?): WalletState {
@ -268,6 +272,14 @@ data class WalletState(
)
}
}
fun updateTotalBalance(
totalBalance: TotalBalance
): WalletState {
return this.copy(
totalBalance = totalBalance
)
}
}
sealed class WalletDialog : StateDialog {

View file

@ -61,8 +61,9 @@ class TradeCryptoMiddleware {
action = exchangeAction,
blockchain = currency.blockchain,
cryptoCurrencyName = currencySymbol,
fiatCurrency = appCurrency,
walletAddress = defaultAddress)?.let { store.dispatchOnMain(NavigationAction.OpenUrl(it)) }
fiatCurrencyName = appCurrency.code,
walletAddress = defaultAddress
)?.let { store.dispatchOnMain(NavigationAction.OpenUrl(it)) }
}

View file

@ -104,7 +104,7 @@ class WalletMiddleware {
warningsMiddleware.tryToShowAppRatingWarning(action.wallet)
}
is WalletAction.LoadFiatRate -> {
val appCurrencyId = globalState.appCurrency
val appCurrencyId = globalState.appCurrency.code
scope.launch {
val coinsList = when {
action.wallet != null -> {
@ -344,4 +344,4 @@ class WalletMiddleware {
}
}
}
}
}

View file

@ -126,7 +126,7 @@ class MultiWalletReducer {
),
fiatAmountFormatted = tokenWalletData.fiatRate?.let {
action.amount.value
?.toFiatString(it, store.state.globalState.appCurrency)
?.toFiatString(it, store.state.globalState.appCurrency.code)
},
blockchainAmount = wallet.amounts[AmountType.Coin]?.value ?: BigDecimal.ZERO
),

View file

@ -9,6 +9,7 @@ import com.tangem.tap.common.extensions.toFormattedCurrencyString
import com.tangem.tap.common.extensions.toFormattedFiatValue
import com.tangem.tap.domain.getFirstToken
import com.tangem.tap.domain.tokens.BlockchainNetwork
import com.tangem.tap.features.wallet.models.TotalBalance
import com.tangem.tap.features.wallet.models.removeUnknownTransactions
import com.tangem.tap.features.wallet.models.toPendingTransactions
import com.tangem.tap.features.wallet.redux.*
@ -16,6 +17,7 @@ import com.tangem.tap.features.wallet.ui.BalanceStatus
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
import com.tangem.tap.features.wallet.ui.TokenData
import com.tangem.tap.store
import java.math.BigDecimal
import java.math.RoundingMode
class OnWalletLoadedReducer {
@ -33,13 +35,12 @@ class OnWalletLoadedReducer {
blockchainNetwork: BlockchainNetwork,
walletState: WalletState
): WalletState {
val fiatCurrencySymbol = store.state.globalState.appCurrency
val walletData = walletState.getWalletData(blockchainNetwork) ?: return walletState
val fiatCurrency = store.state.globalState.appCurrency
val exchangeManager = store.state.globalState.currencyExchangeManager
val coinAmountValue = wallet.amounts[AmountType.Coin]?.value
if (walletState.getWalletData(blockchainNetwork) == null) {
return walletState
}
val formattedAmount = coinAmountValue?.toFormattedCurrencyString(
wallet.blockchain.decimals(),
wallet.blockchain.currency
@ -54,10 +55,9 @@ class OnWalletLoadedReducer {
} else {
BalanceStatus.VerifiedOnline
}
val walletData = walletState.getWalletData(blockchainNetwork)
val fiatAmount = walletData?.fiatRate?.let { coinAmountValue?.toFiatValue(it) }
val newWalletData = walletData?.copy(
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,
@ -65,7 +65,7 @@ class OnWalletLoadedReducer {
amount = coinAmountValue,
amountFormatted = formattedAmount,
fiatAmount = fiatAmount,
fiatAmountFormatted = fiatAmount?.toFormattedFiatValue(fiatCurrencySymbol)
fiatAmountFormatted = fiatAmount?.toFormattedFiatValue(fiatCurrency.code)
),
pendingTransactions = pendingTransactions.removeUnknownTransactions(),
mainButton = WalletMainButton.SendButton(coinSendButton),
@ -87,7 +87,7 @@ class OnWalletLoadedReducer {
val tokenFiatAmount =
tokenWalletData?.fiatRate?.let { rate -> tokenAmountValue?.toFiatValue(rate) }
val tokenSendButton = newWalletData?.shouldEnableTokenSendButton() == true
val tokenSendButton = newWalletData.shouldEnableTokenSendButton()
&& tokenPendingTransactions.isEmpty()
tokenWalletData?.copy(
currencyData = tokenWalletData.currencyData.copy(
@ -99,31 +99,40 @@ class OnWalletLoadedReducer {
token.symbol
),
fiatAmount = tokenFiatAmount,
fiatAmountFormatted = tokenFiatAmount?.toFormattedFiatValue(fiatCurrencySymbol)
fiatAmountFormatted = tokenFiatAmount?.toFormattedFiatValue(fiatCurrency.code)
),
pendingTransactions = tokenPendingTransactions.removeUnknownTransactions(),
mainButton = WalletMainButton.SendButton(tokenSendButton),
tradeCryptoState = TradeCryptoState.from(exchangeManager, tokenWalletData),
)
}
val newWallets = (tokens + newWalletData).mapNotNull { it }
val newWallets = tokens + newWalletData
val wallets = walletState.replaceSomeWallets((newWallets))
val totalBalance = TotalBalance(
state = wallets.findTotalBalanceState(),
fiatAmount = wallets.calculateTotalFiatAmount(),
fiatCurrency = fiatCurrency,
)
val state = if (wallets.any { it.currencyData.status == BalanceStatus.Loading }) {
ProgressState.Loading
} else {
ProgressState.Done
}
val newState = walletState.updateWalletsData(wallets)
return newState.copy(
state = state, error = null
)
return walletState
.updateWalletsData(wallets)
.updateTotalBalance(totalBalance)
.copy(
state = state,
error = null
)
}
private fun onSingleWalletLoaded(wallet: Wallet, walletState: WalletState): WalletState {
if (wallet.blockchain != walletState.primaryBlockchain) return walletState
val fiatCurrencySymbol = store.state.globalState.appCurrency
val fiatCurrencyName = store.state.globalState.appCurrency.code
val exchangeManager = store.state.globalState.currencyExchangeManager
val token = wallet.getFirstToken()
@ -132,7 +141,7 @@ class OnWalletLoadedReducer {
if (tokenAmount != null) {
val tokenFiatRate = walletState.primaryWallet?.currencyData?.token?.fiatRate
val tokenFiatAmount =
tokenFiatRate?.let { tokenAmount.value?.toFiatString(it, fiatCurrencySymbol) }
tokenFiatRate?.let { tokenAmount.value?.toFiatString(it, fiatCurrencyName) }
TokenData(
tokenAmount.value?.toFormattedCurrencyString(
token.decimals, token.symbol
@ -152,7 +161,7 @@ class OnWalletLoadedReducer {
)
val fiatRate = walletState.primaryWallet?.fiatRate
val fiatAmountRaw = fiatRate?.multiply(amount)?.setScale(2, RoundingMode.DOWN)
val fiatAmount = fiatRate?.let { amount?.toFiatString(it, fiatCurrencySymbol) }
val fiatAmount = fiatRate?.let { amount?.toFiatString(it, fiatCurrencyName) }
val pendingTransactions = wallet.recentTransactions.toPendingTransactions(wallet.address)
val sendButtonEnabled = amount?.isZero() == false && pendingTransactions.isEmpty()
@ -183,4 +192,48 @@ class OnWalletLoadedReducer {
state = ProgressState.Done, error = null
)
}
private fun List<WalletData>.findTotalBalanceState(): TotalBalance.State {
return this.mapToTotalBalanceState()
.fold(initial = TotalBalance.State.Loading) { accState, newState ->
accState or newState
}
}
private fun List<WalletData>.calculateTotalFiatAmount(): BigDecimal {
return this.map { it.currencyData.fiatAmount ?: BigDecimal.ZERO }
.reduce(BigDecimal::plus)
}
private fun List<WalletData>.mapToTotalBalanceState(): List<TotalBalance.State> {
return this.map {
when (it.currencyData.status) {
BalanceStatus.VerifiedOnline,
BalanceStatus.SameCurrencyTransactionInProgress,
BalanceStatus.TransactionInProgress -> TotalBalance.State.Success
BalanceStatus.Unreachable,
BalanceStatus.NoAccount,
BalanceStatus.EmptyCard,
BalanceStatus.UnknownBlockchain -> TotalBalance.State.Failed
BalanceStatus.Loading,
null -> TotalBalance.State.Loading
}
}
}
infix fun TotalBalance.State.or(newState: TotalBalance.State): TotalBalance.State {
return when (this) {
TotalBalance.State.Loading -> when (newState) {
TotalBalance.State.Loading -> this
TotalBalance.State.Failed,
TotalBalance.State.Success -> newState
}
TotalBalance.State.Success,
TotalBalance.State.Failed -> when (newState) {
TotalBalance.State.Loading,
TotalBalance.State.Failed -> newState
TotalBalance.State.Success -> this
}
}
}
}

View file

@ -4,12 +4,12 @@ import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Wallet
import com.tangem.domain.common.TwinCardNumber
import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.common.extensions.toFiatString
import com.tangem.tap.common.extensions.toFiatValue
import com.tangem.tap.common.extensions.toFormattedCurrencyString
import com.tangem.tap.common.extensions.toFormattedFiatValue
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.FiatCurrencyName
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.extensions.getArtworkUrl
import com.tangem.tap.domain.getFirstToken
@ -70,9 +70,9 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
when (action.error) {
is TapError.NoInternetConnection -> {
val wallets = newState.wallets
.map {
it.copy(
walletsData = it.walletsData.map {
.map { store ->
store.copy(
walletsData = store.walletsData.map {
it.copy(
currencyData = it.currencyData.copy(
status = BalanceStatus.Unreachable
@ -110,6 +110,8 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
)
)
}
else -> { /* no-op */
}
}
}
@ -306,23 +308,23 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
}
is WalletAction.SetWalletRent -> {
var walletData = newState.getWalletData(action.blockchain)
if (walletData == null) {
newState
} else {
walletData =
walletData.copy(warningRent = WalletRent(action.minRent, action.rentExempt))
if (walletData != null) {
walletData = walletData.copy(
warningRent = WalletRent(action.minRent, action.rentExempt)
)
newState = newState.updateWalletsData(listOf(walletData))
}
}
is WalletAction.RemoveWalletRent -> {
var walletData = newState.getWalletData(action.blockchain)
if (walletData == null) {
newState
} else {
if (walletData != null) {
walletData = walletData.copy(warningRent = null)
newState = newState.updateWalletsData(listOf(walletData))
}
}
else -> { /* no-op */
}
}
return newState
}
@ -377,22 +379,30 @@ private fun handleCheckSignedHashesActions(
private fun setNewFiatRate(
fiatRate: Pair<Currency, BigDecimal?>,
appCurrency: FiatCurrencyName, state: WalletState
appCurrency: FiatCurrency,
state: WalletState
): WalletState {
val rate = fiatRate.second ?: return state
val rateFormatted = rate.toFormattedCurrencyString(2, appCurrency, RoundingMode.HALF_UP)
val rateFormatted = rate.toFormattedCurrencyString(
decimals = 2,
currency = appCurrency.code,
roundingMode = RoundingMode.HALF_UP
)
val currency = fiatRate.first
return if (!state.isMultiwalletAllowed) {
setSingeWalletFiatRate(rate, rateFormatted, currency, appCurrency, state)
setSingleWalletFiatRate(rate, rateFormatted, currency, appCurrency, state)
} else {
setMultiWalletFiatRate(rate, rateFormatted, currency, appCurrency, state)
}
}
private fun setMultiWalletFiatRate(
rate: BigDecimal, rateFormatted: String, currency: Currency,
appCurrency: FiatCurrencyName, state: WalletState
rate: BigDecimal,
rateFormatted: String,
currency: Currency,
appCurrency: FiatCurrency,
state: WalletState
): WalletState {
val walletStore = state.getWalletStore(currency) ?: return state
@ -405,7 +415,7 @@ private fun setMultiWalletFiatRate(
is Currency.Token ->
wallet?.getTokenAmount(currency.token)?.value?.toFiatValue(rate)
}
val fiatAmountFormatted = fiatAmount?.toFormattedFiatValue(appCurrency)
val fiatAmountFormatted = fiatAmount?.toFormattedFiatValue(appCurrency.code)
val newWalletData = state.getWalletData(currency)?.copy(
currencyData = walletData.currencyData.copy(
fiatAmountFormatted = fiatAmountFormatted,
@ -416,16 +426,19 @@ private fun setMultiWalletFiatRate(
return state.updateWalletData(newWalletData)
}
private fun setSingeWalletFiatRate(
rate: BigDecimal, rateFormatted: String, currency: Currency,
appCurrency: FiatCurrencyName, state: WalletState
private fun setSingleWalletFiatRate(
rate: BigDecimal,
rateFormatted: String,
currency: Currency,
appCurrency: FiatCurrency,
state: WalletState
): WalletState {
val wallet = state.primaryWalletManager?.wallet ?: return state
val token = wallet.getFirstToken()
if (currency == state.primaryWallet?.currency) {
val fiatAmount = wallet.amounts[AmountType.Coin]?.value
?.toFiatString(rate, appCurrency)
?.toFiatString(rate, appCurrency.code)
val walletData = state.primaryWallet.copy(
currencyData = state.primaryWallet.currencyData.copy(fiatAmountFormatted = fiatAmount),
fiatRate = rate,
@ -433,7 +446,9 @@ private fun setSingeWalletFiatRate(
)
return state.updateWalletData(walletData)
} else if (currency is Currency.Token && currency.token == token) {
val tokenFiatAmount = wallet.getTokenAmount(token)?.value?.toFiatString(rate, appCurrency)
val tokenFiatAmount = wallet.getTokenAmount(token)
?.value
?.toFiatString(rate, appCurrency.code)
val tokenData = state.primaryWallet?.currencyData?.token?.copy(
fiatAmount = tokenFiatAmount,
fiatRate = rate,

View file

@ -5,6 +5,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import com.tangem.common.card.Card
import com.tangem.domain.common.TapWorkarounds.derivationStyle
import com.tangem.domain.common.TapWorkarounds.isTestCard
import com.tangem.tap.common.extensions.formatAmountAsSpannedString
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.redux.StateDialog
@ -12,6 +13,7 @@ import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.currenciesRepository
import com.tangem.tap.features.tokens.redux.TokensAction
import com.tangem.tap.features.wallet.models.TotalBalance
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletDialog
import com.tangem.tap.features.wallet.redux.WalletState
@ -51,6 +53,7 @@ class MultiWalletView : WalletView {
btnScanMultiwallet.show()
rvMultiwallet.show()
btnAddToken.show()
lCardTotalBalance.root.show()
setupWalletCardNumber(binding)
}
@ -97,6 +100,7 @@ class MultiWalletView : WalletView {
val fragment = fragment ?: return
val binding = binding ?: return
state.totalBalance?.let { handleTotalBalance(binding, it) }
walletsAdapter.submitList(state.walletsData, state.primaryBlockchain, state.primaryToken)
binding.btnAddToken.setOnClickListener {
@ -127,6 +131,19 @@ class MultiWalletView : WalletView {
handleDialogs(state.walletDialog)
}
private fun handleTotalBalance(
binding: FragmentWalletBinding,
totalBalance: TotalBalance,
) = with(binding.lCardTotalBalance) {
tvBalance.text = totalBalance.fiatAmount.formatAmountAsSpannedString(
currencySymbol = totalBalance.fiatCurrency.symbol
)
tvCurrencyName.text = totalBalance.fiatCurrency.code
tvCurrencyName.setOnClickListener {
// TODO: Open app currency selector
}
}
private fun handleErrorStates(
state: WalletState,
binding: FragmentWalletBinding,
@ -148,6 +165,8 @@ class MultiWalletView : WalletView {
fragment.getString(R.string.wallet_error_unsupported_blockchain_subtitle)
)
}
else -> { /* no-op */
}
}
}

View file

@ -1,33 +0,0 @@
package com.tangem.tap.network.coinmarketcap
import com.tangem.network.common.AddHeaderInterceptor
import com.tangem.network.common.createRetrofitInstance
import retrofit2.http.GET
import retrofit2.http.Query
interface CoinMarketCapApi {
@GET("v1/tools/price-conversion")
suspend fun getRateInfo(
@Query("amount") amount: Int,
@Query("symbol") cryptoCurrencyName: String,
@Query("convert") fiatCurrencyName: String? = null
): RateInfoResponse
@GET("v1/fiat/map")
suspend fun getFiatMap(): FiatMapResponse
companion object {
private const val baseUrl = "https://pro-api.coinmarketcap.com/"
fun create(apiKey: String): CoinMarketCapApi {
return createRetrofitInstance(
baseUrl = baseUrl,
interceptors = listOf(
AddHeaderInterceptor(mapOf("X-CMC_PRO_API_KEY" to apiKey)),
),
).create(CoinMarketCapApi::class.java)
}
}
}

View file

@ -1,36 +0,0 @@
package com.tangem.tap.network.coinmarketcap
import com.tangem.common.services.Result
import com.tangem.common.services.performRequest
import com.tangem.tap.common.redux.global.FiatCurrencyName
import com.tangem.tap.store
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.math.BigDecimal
//TODO: refactoring: move to the domain module and aggregate it as the alternative service for TangemTech
class CoinMarketCapService() {
private val api: CoinMarketCapApi by lazy { CoinMarketCapApi.create(getApiKey()) }
private fun getApiKey(): String {
return store.state.globalState.configManager?.config?.coinMarketCapKey ?: ""
}
suspend fun getRate(
currency: String, fiatCurrency: FiatCurrencyName? = null
): Result<BigDecimal> = withContext(Dispatchers.IO) {
val response = performRequest { api.getRateInfo(1, currency, fiatCurrency) }
return@withContext when (response) {
is Result.Success -> Result.Success(response.data.data.getRate())
is Result.Failure -> response
}
}
suspend fun getFiatCurrencies(): Result<List<FiatCurrency>> = withContext(Dispatchers.IO) {
val response = performRequest { api.getFiatMap() }
return@withContext when (response) {
is Result.Success -> Result.Success(response.data.data.sortedBy { it.name })
is Result.Failure -> response
}
}
}

View file

@ -1,51 +0,0 @@
package com.tangem.tap.network.coinmarketcap
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import java.math.BigDecimal
@JsonClass(generateAdapter = true)
class RateInfoResponse : CoinMarketResponse<RateData>()
@JsonClass(generateAdapter = true)
data class RateData(
val quote: Map<String, CurrencyRate>
) {
fun getRate(): BigDecimal = quote.values.first().price
}
@JsonClass(generateAdapter = true)
data class CurrencyRate(
val price: BigDecimal
)
@JsonClass(generateAdapter = true)
data class Status(
val timestamp: String,
@Json(name = "error_code")
val errorCode: Int,
@Json(name = "error_message")
val errorMessage: String?,
val elapsed: Int,
@Json(name = "credit_count")
val creditCount: Int,
val notice: String?
)
@JsonClass(generateAdapter = true)
class FiatMapResponse : CoinMarketResponse<List<FiatCurrency>>()
@JsonClass(generateAdapter = true)
open class CoinMarketResponse<T : Any> {
lateinit var status: Status
lateinit var data: T
}
@JsonClass(generateAdapter = true)
data class FiatCurrency(
val id: Int,
val name: String,
val sign: String,
val symbol: String
)

View file

@ -30,7 +30,7 @@ interface ExchangeUrlBuilder {
action: CurrencyExchangeManager.Action,
blockchain: Blockchain,
cryptoCurrencyName: CryptoCurrencyName,
fiatCurrency: String,
fiatCurrencyName: String,
walletAddress: String,
): String?
@ -74,13 +74,19 @@ class CurrencyExchangeManager(
action: Action,
blockchain: Blockchain,
cryptoCurrencyName: CryptoCurrencyName,
fiatCurrency: String,
fiatCurrencyName: String,
walletAddress: String,
): String? {
if (blockchain.isTestnet()) return blockchain.getTestnetTopUpUrl()
val urlBuilder = getExchangeUrlBuilder(action)
return urlBuilder.getUrl(action, blockchain, cryptoCurrencyName, fiatCurrency, walletAddress)
return urlBuilder.getUrl(
action,
blockchain,
cryptoCurrencyName,
fiatCurrencyName,
walletAddress
)
}
override fun getSellCryptoReceiptUrl(action: Action, transactionId: String): String? {

View file

@ -4,6 +4,7 @@ import android.content.SharedPreferences
import androidx.core.content.edit
import com.tangem.common.json.MoshiJsonConverter
import com.tangem.network.api.tangemTech.CurrenciesResponse
import com.tangem.tap.common.entities.FiatCurrency
/**
[REDACTED_AUTHOR]
@ -12,15 +13,25 @@ class FiatCurrenciesPrefStorage(
private val preferences: SharedPreferences,
private val converter: MoshiJsonConverter,
) {
private val FIAT_CURRENCIES_KEY_OLD = "fiatCurrencies"
private val FIAT_CURRENCIES_KEY = "fiatCurrencies_v2"
fun migrate() {
preferences.edit(true) {
remove(FIAT_CURRENCIES_KEY_OLD)
remove(APP_CURRENCY_KEY_OLD)
}
}
fun getAppCurrency(): FiatCurrency {
val json = preferences.getString(APP_CURRENCY_KEY, "")
if (json.isNullOrBlank()) return FiatCurrency.Default
return converter.fromJson(json) ?: FiatCurrency.Default
}
fun saveAppCurrency(fiatCurrency: FiatCurrency) {
val json = converter.toJson(fiatCurrency)
preferences.edit { putString(APP_CURRENCY_KEY, json) }
}
fun save(currencies: List<CurrenciesResponse.Currency>) {
val json: String = converter.toJson(currencies)
return preferences.edit().putString(FIAT_CURRENCIES_KEY, json).apply()
@ -33,4 +44,12 @@ class FiatCurrenciesPrefStorage(
return converter.fromJson(json, type) ?: emptyList()
}
companion object {
private const val FIAT_CURRENCIES_KEY_OLD = "fiatCurrencies"
private const val APP_CURRENCY_KEY_OLD = "appCurrency"
private const val FIAT_CURRENCIES_KEY = "fiatCurrencies_v2"
private const val APP_CURRENCY_KEY = "appCurrency_v2"
}
}

View file

@ -5,8 +5,6 @@ import android.content.Context
import android.content.SharedPreferences
import androidx.core.content.edit
import com.tangem.common.json.MoshiJsonConverter
import com.tangem.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY
import com.tangem.tap.common.redux.global.FiatCurrencyName
import java.util.*
@ -27,15 +25,6 @@ class PreferencesStorage(applicationContext: Application) {
fiatCurrenciesPrefStorage.migrate()
}
fun getAppCurrency(): FiatCurrencyName {
return preferences.getString(APP_CURRENCY_KEY, DEFAULT_FIAT_CURRENCY)
?: DEFAULT_FIAT_CURRENCY
}
fun saveAppCurrency(fiatCurrencyName: FiatCurrencyName) {
preferences.edit { putString(APP_CURRENCY_KEY, fiatCurrencyName) }
}
fun getCountOfLaunches(): Int = preferences.getInt(APP_LAUNCH_COUNT_KEY, 1)
@Deprecated("Use UsedCardsPrefStorage instead")
@ -74,7 +63,6 @@ class PreferencesStorage(applicationContext: Application) {
companion object {
private const val PREFERENCES_NAME = "tapPrefs"
private const val APP_CURRENCY_KEY = "appCurrency"
private const val DISCLAIMER_ACCEPTED_KEY = "disclaimerAccepted"
private const val TWINS_ONBOARDING_SHOWN_KEY = "twinsOnboardingShown"
private const val APP_LAUNCH_COUNT_KEY = "launchCount"

View file

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/fl_balance"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:minWidth="300dp">
<com.google.android.material.card.MaterialCardView
android:id="@+id/card_balance"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginTop="12dp"
android:layout_marginBottom="4dp"
android:elevation="3dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="18dp"
android:paddingBottom="18dp"
android:paddingStart="16dp"
android:paddingEnd="16dp"
>
<TextView
android:id="@+id/tv_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:textColor="@color/iconGray"
android:textSize="14sp"
android:text="@string/main_page_balance"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/tv_currency_name" />
<TextView
android:id="@+id/tv_balance"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:textColor="@color/darkGray6"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintTop_toBottomOf="@id/tv_title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
tools:text="$ 22 325.40" />
<TextView
android:id="@+id/tv_currency_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/darkGray1"
android:textSize="16sp"
app:drawableEndCompat="@drawable/ic_arrow_angle_down"
app:drawableTint="@color/darkGray1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
tools:text="USD"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
</FrameLayout>

View file

@ -101,16 +101,26 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/barrier" />
<include
android:id="@+id/l_card_total_balance"
layout="@layout/card_total_balance"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/rv_warning_messages"
/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_pending_transaction"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
app:layout_goneMarginTop="12dp"
android:nestedScrollingEnabled="false"
android:overScrollMode="never"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/rv_warning_messages" />
app:layout_constraintTop_toBottomOf="@id/l_card_total_balance" />
<include
android:id="@+id/l_card_balance"
@ -204,4 +214,4 @@
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -382,4 +382,5 @@
<string name="feedback_preface_support">Привет, команда поддержки,</string>
<string name="feedback_preface_tx_push_failed">Пожалуйста, расскажите нам больше о Вашей проблеме. Каждая деталь может быть полезной.</string>
<string name="feedback_data_collection_message">Информация ниже не является обязательной. Вы можете стереть её, если хотите.</string>
</resources>
<string name="main_page_balance">Баланс</string>
</resources>

View file

@ -206,4 +206,5 @@
<string name="xtz_withdrawal_message_warning">To avoid paying an increased commission the next time you top up your wallet, reduce the amount by %s XTZ</string>
<string name="xtz_withdrawal_message_reduce">Reduce by %s XTZ</string>
<string name="xtz_withdrawal_message_ignore">No, send all</string>
</resources>
<string name="main_page_balance">Total balance</string>
</resources>