Updated on 2026-08-14
This commit is contained in:
parent
268b74cb0e
commit
704315ddaf
18 changed files with 169 additions and 103 deletions
|
|
@ -1,8 +1,8 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.network.api.tangemTech.Coins
|
||||
import com.tangem.tap.common.redux.global.FiatCurrencyName
|
||||
import com.tangem.tap.network.coinmarketcap.FiatCurrency
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
import java.text.DecimalFormat
|
||||
|
|
@ -10,7 +10,7 @@ import java.text.DecimalFormatSymbols
|
|||
import java.util.*
|
||||
|
||||
fun BigDecimal.toFormattedString(
|
||||
decimals: Int, roundingMode: RoundingMode = RoundingMode.DOWN, locale: Locale = Locale.US
|
||||
decimals: Int, roundingMode: RoundingMode = RoundingMode.DOWN, locale: Locale = Locale.US
|
||||
): String {
|
||||
val symbols = DecimalFormatSymbols(locale)
|
||||
val df = DecimalFormat()
|
||||
|
|
@ -26,7 +26,7 @@ fun BigDecimal.toFormattedCurrencyString(
|
|||
decimals: Int, currency: String, roundingMode: RoundingMode = RoundingMode.DOWN,
|
||||
limitNumberOfDecimals: Boolean = true
|
||||
): String {
|
||||
val decimalsForRounding = if (limitNumberOfDecimals){
|
||||
val decimalsForRounding = if (limitNumberOfDecimals) {
|
||||
if (decimals > 8) 8 else decimals
|
||||
} else {
|
||||
decimals
|
||||
|
|
@ -52,7 +52,7 @@ fun BigDecimal.toFormattedFiatValue(fiatCurrencyName: FiatCurrencyName): String
|
|||
return "≈ ${fiatCurrencyName} $this"
|
||||
}
|
||||
|
||||
fun FiatCurrency.toFormattedString(): String = "${this.name} (${this.symbol}) - ${this.sign}"
|
||||
fun Coins.CurrenciesResponse.Currency.toFormattedString(): String = "${this.name} (${this.code}) - ${this.unit}"
|
||||
|
||||
fun BigDecimal.stripZeroPlainString(): String = this.stripTrailingZeros().toPlainString()
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.tap.common.redux
|
|||
|
||||
import com.tangem.domain.redux.DomainState
|
||||
import com.tangem.domain.redux.domainStore
|
||||
import com.tangem.domain.redux.global.NetworkServices
|
||||
import com.tangem.tap.common.redux.global.GlobalMiddleware
|
||||
import com.tangem.tap.common.redux.global.GlobalState
|
||||
import com.tangem.tap.common.redux.navigation.NavigationState
|
||||
|
|
@ -54,6 +55,9 @@ data class AppState(
|
|||
val domainState: DomainState
|
||||
get() = domainStore.state
|
||||
|
||||
val domainNetworks: NetworkServices
|
||||
get() = domainState.globalState.networkServices
|
||||
|
||||
companion object {
|
||||
fun getMiddleware(): List<Middleware<AppState>> {
|
||||
return listOf(
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import com.tangem.tap.domain.configurable.config.ConfigManager
|
|||
import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager
|
||||
import com.tangem.tap.features.feedback.FeedbackManager
|
||||
import com.tangem.tap.features.onboarding.OnboardingManager
|
||||
import com.tangem.tap.network.coinmarketcap.CoinMarketCapService
|
||||
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
|
||||
import org.rekotlin.StateType
|
||||
|
||||
|
|
@ -20,7 +19,6 @@ data class GlobalState(
|
|||
val cardVerifiedOnline: Boolean = false,
|
||||
val tapWalletManager: TapWalletManager = TapWalletManager(),
|
||||
val payIdManager: PayIdManager = PayIdManager(),
|
||||
val coinMarketCapService: CoinMarketCapService = CoinMarketCapService(),
|
||||
val configManager: ConfigManager? = null,
|
||||
val warningManager: WarningMessagesManager? = null,
|
||||
val feedbackManager: FeedbackManager? = null,
|
||||
|
|
|
|||
|
|
@ -2,13 +2,17 @@ package com.tangem.tap.domain
|
|||
|
||||
import com.tangem.blockchain.blockchains.solana.RentProvider
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.domain.common.TapWorkarounds.derivationStyle
|
||||
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
|
||||
import com.tangem.domain.common.TapWorkarounds.isTestCard
|
||||
import com.tangem.domain.common.extensions.toNetworkId
|
||||
import com.tangem.domain.common.extensions.withMainContext
|
||||
import com.tangem.network.api.tangemTech.TangemTechService
|
||||
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
|
||||
|
|
@ -26,7 +30,6 @@ import com.tangem.tap.features.wallet.models.getPendingTransactions
|
|||
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.store
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
|
@ -37,7 +40,9 @@ class TapWalletManager {
|
|||
val walletManagerFactory: WalletManagerFactory
|
||||
by lazy { WalletManagerFactory(blockchainSdkConfig) }
|
||||
|
||||
private val coinMarketCapService = CoinMarketCapService()
|
||||
private val tangemTechService: TangemTechService
|
||||
get() = store.state.domainNetworks.tangemTechService
|
||||
|
||||
private val blockchainSdkConfig by lazy {
|
||||
store.state.globalState.configManager?.config?.blockchainSdkConfig ?: BlockchainSdkConfig()
|
||||
}
|
||||
|
|
@ -98,20 +103,58 @@ class TapWalletManager {
|
|||
}
|
||||
|
||||
suspend fun loadFiatRate(fiatCurrency: FiatCurrencyName, currencies: List<Currency>) {
|
||||
suspend fun handleFiatRatesResult(rates: Map<Currency, Result<BigDecimal>?>) {
|
||||
rates.forEach { (currency, priceResult) ->
|
||||
when (priceResult) {
|
||||
is Result.Success -> {
|
||||
dispatchOnMain(
|
||||
WalletAction.LoadFiatRate.Success(
|
||||
currency to priceResult.data
|
||||
))
|
||||
}
|
||||
is Result.Failure -> dispatchOnMain(WalletAction.LoadFiatRate.Failure)
|
||||
null -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get and submit previous result of equivalents.
|
||||
val throttledResult = currencies.filter { fiatRatesThrottler.isStillThrottled(it) }.map {
|
||||
Pair(it, fiatRatesThrottler.geValue(it))
|
||||
}
|
||||
if (throttledResult.isNotEmpty()) handleFiatRatesResult(throttledResult)
|
||||
if (throttledResult.isNotEmpty()) {
|
||||
handleFiatRatesResult(throttledResult.toMap())
|
||||
}
|
||||
|
||||
val toUpdate = currencies.filter { !fiatRatesThrottler.isStillThrottled(it) }
|
||||
toUpdate.forEach {
|
||||
val result = coinMarketCapService.getRate(it.currencySymbol, fiatCurrency)
|
||||
if (result is Result.Success) {
|
||||
fiatRatesThrottler.updateThrottlingTo(it)
|
||||
fiatRatesThrottler.setValue(it, result)
|
||||
val toUpdateCurrencies = currencies.filter { !fiatRatesThrottler.isStillThrottled(it) }
|
||||
val toUpdateIds = toUpdateCurrencies.mapNotNull { it.id }.distinct()
|
||||
if (toUpdateIds.isEmpty()) return
|
||||
|
||||
//TODO: refactoring: move fiatRatesThrottler to the TangemTechRepository
|
||||
when (val result = tangemTechService.coins.prices(fiatCurrency, toUpdateIds)) {
|
||||
is Result.Success -> {
|
||||
val missedCurrencies = mutableListOf<String>()
|
||||
val updatedCurrencies = mutableMapOf<Currency, Result<BigDecimal>?>()
|
||||
|
||||
result.data.prices.forEach { (name, value) ->
|
||||
val currency = toUpdateCurrencies.firstOrNull { it.id == name }.guard {
|
||||
missedCurrencies.add(name)
|
||||
return@forEach
|
||||
}
|
||||
val priceResult = Result.Success(value.toBigDecimal())
|
||||
updatedCurrencies[currency] = priceResult
|
||||
|
||||
fiatRatesThrottler.updateThrottlingTo(currency)
|
||||
fiatRatesThrottler.setValue(currency, priceResult)
|
||||
}
|
||||
|
||||
if (missedCurrencies.isNotEmpty()) {
|
||||
val missedNames = missedCurrencies.joinToString(", ")
|
||||
store.dispatchDebugErrorNotification("Not found currencies to update: [$missedNames]")
|
||||
}
|
||||
handleFiatRatesResult(updatedCurrencies)
|
||||
}
|
||||
handleFiatRatesResult(listOf(it to result))
|
||||
is Result.Failure -> dispatchOnMain(WalletAction.LoadFiatRate.Failure)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -318,21 +361,14 @@ class TapWalletManager {
|
|||
is com.tangem.blockchain.extensions.Result.Failure -> {}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun handleFiatRatesResult(results: List<Pair<Currency, Result<BigDecimal>?>>) {
|
||||
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 -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Wallet.getFirstToken(): Token? {
|
||||
return getTokens().toList().getOrNull(0)
|
||||
}
|
||||
}
|
||||
|
||||
val Currency.id: String?
|
||||
get() = when (this) {
|
||||
is Currency.Blockchain -> blockchain.toNetworkId()
|
||||
is Currency.Token -> token.id
|
||||
}
|
||||
|
|
@ -4,11 +4,12 @@ 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.Coins
|
||||
import com.tangem.network.api.tangemTech.TangemTechService
|
||||
import com.tangem.operations.pins.CheckUserCodesResponse
|
||||
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.tap.network.coinmarketcap.FiatCurrency
|
||||
import com.tangem.wallet.R
|
||||
import org.rekotlin.Action
|
||||
|
||||
|
|
@ -20,6 +21,7 @@ sealed class DetailsAction : Action {
|
|||
val cardTou: CardTou,
|
||||
val fiatCurrencyName: FiatCurrencyName,
|
||||
val fiatCurrencies: List<FiatCurrencyName>? = null,
|
||||
val tangemTechService: TangemTechService,
|
||||
) : DetailsAction()
|
||||
|
||||
object ShowDisclaimer : DetailsAction()
|
||||
|
|
@ -46,7 +48,7 @@ sealed class DetailsAction : Action {
|
|||
object CreateBackup : DetailsAction()
|
||||
|
||||
sealed class AppCurrencyAction : DetailsAction() {
|
||||
data class SetCurrencies(val currencies: List<FiatCurrency>) : AppCurrencyAction()
|
||||
data class SetCurrencies(val currencies: List<Coins.CurrenciesResponse.Currency>) : AppCurrencyAction()
|
||||
object ChooseAppCurrency : AppCurrencyAction()
|
||||
object Cancel : AppCurrencyAction()
|
||||
data class SelectAppCurrency(val fiatCurrencyName: FiatCurrencyName) : AppCurrencyAction()
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ 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.Coins
|
||||
import com.tangem.operations.pins.CheckUserCodesResponse
|
||||
import com.tangem.tap.*
|
||||
import com.tangem.tap.common.analytics.Analytics
|
||||
|
|
@ -19,7 +20,6 @@ import com.tangem.tap.features.onboarding.products.twins.redux.CreateTwinWalletM
|
|||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
|
||||
import com.tangem.tap.features.wallet.models.hasSendableAmountsOrPendingTransactions
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.network.coinmarketcap.CoinMarketCapService
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
|
@ -33,7 +33,7 @@ class DetailsMiddleware {
|
|||
{ next ->
|
||||
{ action ->
|
||||
when (action) {
|
||||
is DetailsAction.PrepareScreen -> prepareData()
|
||||
is DetailsAction.PrepareScreen -> prepareData(action)
|
||||
is DetailsAction.ResetToFactory -> eraseWalletMiddleware.handle(action)
|
||||
is DetailsAction.AppCurrencyAction -> appCurrencyMiddleware.handle(action)
|
||||
is DetailsAction.ManageSecurity -> manageSecurityMiddleware.handle(action)
|
||||
|
|
@ -68,23 +68,26 @@ class DetailsMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
private fun prepareData() {
|
||||
private fun prepareData(action: DetailsAction.PrepareScreen) {
|
||||
val fiatCurrenciesPrefStorage = preferencesStorage.fiatCurrenciesPrefStorage
|
||||
val storedFiatCurrencies = fiatCurrenciesPrefStorage.restore()
|
||||
if (storedFiatCurrencies.isNotEmpty()) {
|
||||
store.dispatch(DetailsAction.AppCurrencyAction.SetCurrencies(storedFiatCurrencies))
|
||||
}
|
||||
|
||||
scope.launch {
|
||||
val loadedCurrencies = preferencesStorage.getFiatCurrencies()
|
||||
if (loadedCurrencies.isNullOrEmpty()) {
|
||||
val response = CoinMarketCapService().getFiatCurrencies()
|
||||
withContext(Dispatchers.Main) {
|
||||
when (response) {
|
||||
is Result.Success -> {
|
||||
preferencesStorage.saveFiatCurrencies(response.data)
|
||||
store.dispatch(DetailsAction.AppCurrencyAction.SetCurrencies(response.data))
|
||||
}
|
||||
val tangemTechService = action.tangemTechService
|
||||
when (val result = tangemTechService.coins.currencies()) {
|
||||
is Result.Success -> {
|
||||
val fiatCurrencies = result.data.currencies.filter {
|
||||
it.type == Coins.CurrenciesResponse.CurrencyType.Fiat.type
|
||||
}
|
||||
if (fiatCurrencies.isNotEmpty() && fiatCurrencies.toSet() != storedFiatCurrencies.toSet()) {
|
||||
fiatCurrenciesPrefStorage.save(fiatCurrencies)
|
||||
dispatchOnMain(DetailsAction.AppCurrencyAction.SetCurrencies(fiatCurrencies))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
withContext(Dispatchers.Main) {
|
||||
store.dispatch(DetailsAction.AppCurrencyAction.SetCurrencies(loadedCurrencies))
|
||||
}
|
||||
is Result.Failure -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,13 +43,15 @@ private fun handlePrepareScreen(
|
|||
action: DetailsAction.PrepareScreen,
|
||||
state: DetailsState,
|
||||
): DetailsState {
|
||||
val backupIsActive = action.scanResponse.card.backupStatus?.isActive ?: false
|
||||
|
||||
return DetailsState(
|
||||
scanResponse = action.scanResponse,
|
||||
wallets = action.wallets,
|
||||
cardInfo = action.scanResponse.card.toCardInfo(),
|
||||
appCurrencyState = AppCurrencyState(action.fiatCurrencyName),
|
||||
appCurrencyState = state.appCurrencyState.copy(
|
||||
fiatCurrencyName = action.fiatCurrencyName,
|
||||
showAppCurrencyDialog = false,
|
||||
),
|
||||
cardTermsOfUseUrl = action.cardTou.getUrl(action.scanResponse.card),
|
||||
createBackupAllowed = action.scanResponse.card.backupStatus == Card.BackupStatus.NoBackup,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ 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.Coins
|
||||
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.features.onboarding.products.twins.redux.TwinCardsState
|
||||
import com.tangem.tap.network.coinmarketcap.FiatCurrency
|
||||
import com.tangem.tap.store
|
||||
import org.rekotlin.StateType
|
||||
import java.util.*
|
||||
|
|
@ -51,8 +51,9 @@ data class SecurityScreenState(
|
|||
)
|
||||
|
||||
enum class SecurityOption { LongTap, PassCode, AccessCode }
|
||||
|
||||
data class AppCurrencyState(
|
||||
val fiatCurrencyName: FiatCurrencyName = DEFAULT_FIAT_CURRENCY,
|
||||
val showAppCurrencyDialog: Boolean = false,
|
||||
val fiatCurrencies: List<FiatCurrency>? = null,
|
||||
val fiatCurrencies: List<Coins.CurrenciesResponse.Currency>? = null,
|
||||
)
|
||||
|
|
@ -2,10 +2,10 @@ package com.tangem.tap.features.details.ui
|
|||
|
||||
import android.content.Context
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.tangem.network.api.tangemTech.Coins
|
||||
import com.tangem.tap.common.extensions.toFormattedString
|
||||
import com.tangem.tap.common.redux.global.FiatCurrencyName
|
||||
import com.tangem.tap.features.details.redux.DetailsAction
|
||||
import com.tangem.tap.network.coinmarketcap.FiatCurrency
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
|
||||
|
|
@ -13,11 +13,11 @@ class CurrencySelectionDialog {
|
|||
|
||||
var dialog: AlertDialog? = null
|
||||
|
||||
fun show(currencies: List<FiatCurrency>, currentAppCurrency: FiatCurrencyName, context: Context) {
|
||||
fun show(currencies: List<Coins.CurrenciesResponse.Currency>, currentAppCurrency: FiatCurrencyName, context: Context) {
|
||||
|
||||
if (dialog == null) {
|
||||
val currenciesToShow = currencies.map { it.toFormattedString() }.toTypedArray()
|
||||
var currentSelection = currencies.indexOfFirst { it.symbol == currentAppCurrency }
|
||||
var currentSelection = currencies.indexOfFirst { it.code == currentAppCurrency }
|
||||
|
||||
dialog = AlertDialog.Builder(context)
|
||||
.setTitle(context.getString(R.string.details_row_title_currency))
|
||||
|
|
@ -26,7 +26,7 @@ class CurrencySelectionDialog {
|
|||
}
|
||||
.setPositiveButton(context.getString(R.string.common_done)) { _, _ ->
|
||||
val selectedCurrency = currencies[currentSelection]
|
||||
store.dispatch(DetailsAction.AppCurrencyAction.SelectAppCurrency(selectedCurrency.symbol))
|
||||
store.dispatch(DetailsAction.AppCurrencyAction.SelectAppCurrency(selectedCurrency.code))
|
||||
}
|
||||
.setOnDismissListener {
|
||||
store.dispatch(DetailsAction.AppCurrencyAction.Cancel)
|
||||
|
|
|
|||
|
|
@ -166,7 +166,8 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
scanNoteResponse,
|
||||
store.state.walletState.walletManagers.map { it.wallet },
|
||||
CardTou(),
|
||||
store.state.globalState.appCurrency
|
||||
store.state.globalState.appCurrency,
|
||||
tangemTechService = store.state.domainNetworks.tangemTechService
|
||||
))
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Details))
|
||||
true
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ 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()) }
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
package com.tangem.tap.persistence
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import androidx.core.content.edit
|
||||
import com.tangem.common.json.MoshiJsonConverter
|
||||
import com.tangem.network.api.tangemTech.Coins
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
fun save(currencies: List<Coins.CurrenciesResponse.Currency>) {
|
||||
val json: String = converter.toJson(currencies)
|
||||
return preferences.edit().putString(FIAT_CURRENCIES_KEY, json).apply()
|
||||
}
|
||||
|
||||
fun restore(): List<Coins.CurrenciesResponse.Currency> {
|
||||
val json = preferences.getString(FIAT_CURRENCIES_KEY, "")
|
||||
val type = converter.typedList(Coins.CurrenciesResponse.Currency::class.java)
|
||||
if (json.isNullOrBlank()) return emptyList()
|
||||
|
||||
return converter.fromJson(json, type) ?: emptyList()
|
||||
}
|
||||
}
|
||||
|
|
@ -4,13 +4,9 @@ import android.app.Application
|
|||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.core.content.edit
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.Types
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
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 com.tangem.tap.network.coinmarketcap.FiatCurrency
|
||||
import java.util.*
|
||||
|
||||
|
||||
|
|
@ -20,39 +16,24 @@ class PreferencesStorage(applicationContext: Application) {
|
|||
|
||||
val appRatingLaunchObserver: AppRatingLaunchObserver
|
||||
val usedCardsPrefStorage: UsedCardsPrefStorage
|
||||
val fiatCurrenciesPrefStorage: FiatCurrenciesPrefStorage
|
||||
|
||||
init {
|
||||
incrementLaunchCounter()
|
||||
appRatingLaunchObserver = AppRatingLaunchObserver(preferences, getCountOfLaunches())
|
||||
usedCardsPrefStorage = UsedCardsPrefStorage(preferences)
|
||||
usedCardsPrefStorage = UsedCardsPrefStorage(preferences, MoshiJsonConverter.INSTANCE)
|
||||
usedCardsPrefStorage.migrate()
|
||||
}
|
||||
|
||||
private val fiatCurrenciesAdapter: JsonAdapter<List<FiatCurrency>> by lazy {
|
||||
val moshi = Moshi.Builder()
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.build()
|
||||
val type = Types.newParameterizedType(List::class.java, FiatCurrency::class.java)
|
||||
moshi.adapter(type)
|
||||
fiatCurrenciesPrefStorage = FiatCurrenciesPrefStorage(preferences, MoshiJsonConverter.INSTANCE)
|
||||
fiatCurrenciesPrefStorage.migrate()
|
||||
}
|
||||
|
||||
fun getAppCurrency(): FiatCurrencyName {
|
||||
return preferences.getString(APP_CURRENCY_KEY, DEFAULT_FIAT_CURRENCY)
|
||||
?: DEFAULT_FIAT_CURRENCY
|
||||
?: DEFAULT_FIAT_CURRENCY
|
||||
}
|
||||
|
||||
fun saveAppCurrency(fiatCurrencyName: FiatCurrencyName) {
|
||||
return preferences.edit().putString(APP_CURRENCY_KEY, fiatCurrencyName).apply()
|
||||
}
|
||||
|
||||
fun getFiatCurrencies(): List<FiatCurrency>? {
|
||||
val json = preferences.getString(FIAT_CURRENCIES_KEY, "")
|
||||
return if (json.isNullOrBlank()) null else fiatCurrenciesAdapter.fromJson(json) as List<FiatCurrency>
|
||||
}
|
||||
|
||||
fun saveFiatCurrencies(currencies: List<FiatCurrency>) {
|
||||
val json: String = fiatCurrenciesAdapter.toJson(currencies)
|
||||
return preferences.edit().putString(FIAT_CURRENCIES_KEY, json).apply()
|
||||
preferences.edit { putString(APP_CURRENCY_KEY, fiatCurrencyName) }
|
||||
}
|
||||
|
||||
fun getCountOfLaunches(): Int = preferences.getInt(APP_LAUNCH_COUNT_KEY, 1)
|
||||
|
|
@ -63,7 +44,7 @@ class PreferencesStorage(applicationContext: Application) {
|
|||
}
|
||||
|
||||
fun saveDisclaimerAccepted() {
|
||||
preferences.edit().putBoolean(DISCLAIMER_ACCEPTED_KEY, true).apply()
|
||||
preferences.edit { putBoolean(DISCLAIMER_ACCEPTED_KEY, true) }
|
||||
}
|
||||
|
||||
fun wasDisclaimerAccepted(): Boolean {
|
||||
|
|
@ -71,7 +52,7 @@ class PreferencesStorage(applicationContext: Application) {
|
|||
}
|
||||
|
||||
fun saveTwinsOnboardingShown() {
|
||||
preferences.edit().putBoolean(TWINS_ONBOARDING_SHOWN_KEY, true).apply()
|
||||
preferences.edit { putBoolean(TWINS_ONBOARDING_SHOWN_KEY, true) }
|
||||
}
|
||||
|
||||
fun wasTwinsOnboardingShown(): Boolean {
|
||||
|
|
@ -86,7 +67,6 @@ class PreferencesStorage(applicationContext: Application) {
|
|||
companion object {
|
||||
private const val PREFERENCES_NAME = "tapPrefs"
|
||||
private const val APP_CURRENCY_KEY = "appCurrency"
|
||||
private const val FIAT_CURRENCIES_KEY = "fiatCurrencies"
|
||||
private const val DISCLAIMER_ACCEPTED_KEY = "disclaimerAccepted"
|
||||
private const val TWINS_ONBOARDING_SHOWN_KEY = "twinsOnboardingShown"
|
||||
private const val APP_LAUNCH_COUNT_KEY = "launchCount"
|
||||
|
|
|
|||
|
|
@ -11,10 +11,9 @@ import timber.log.Timber
|
|||
*/
|
||||
class UsedCardsPrefStorage(
|
||||
private val preferences: SharedPreferences,
|
||||
private val jsonConverter: MoshiJsonConverter
|
||||
) {
|
||||
|
||||
private val jsonConverter = MoshiJsonConverter.INSTANCE
|
||||
|
||||
internal fun migrate() {
|
||||
val scannedIds = preferences.getString(SCANNED_CARDS_IDS_KEY, null) ?: return
|
||||
|
||||
|
|
@ -27,7 +26,7 @@ class UsedCardsPrefStorage(
|
|||
fun scanned(cardId: String) {
|
||||
val restoredList = restore()
|
||||
val foundItem = findCardInfo(cardId, restoredList)?.copy(isScanned = true)
|
||||
?: UsedCardInfo(cardId, true)
|
||||
?: UsedCardInfo(cardId, true)
|
||||
|
||||
save(foundItem, restoredList)
|
||||
}
|
||||
|
|
@ -39,7 +38,7 @@ class UsedCardsPrefStorage(
|
|||
fun activationStarted(cardId: String) {
|
||||
val restoredList = restore()
|
||||
val foundItem = findCardInfo(cardId, restoredList)?.copy(isActivationStarted = true)
|
||||
?: UsedCardInfo(cardId, isActivationStarted = true)
|
||||
?: UsedCardInfo(cardId, isActivationStarted = true)
|
||||
|
||||
save(foundItem, restoredList)
|
||||
}
|
||||
|
|
@ -55,7 +54,7 @@ class UsedCardsPrefStorage(
|
|||
fun activationFinished(cardId: String) {
|
||||
val restoredList = restore()
|
||||
val foundItem = findCardInfo(cardId, restoredList)?.copy(isActivationStarted = false)
|
||||
?: UsedCardInfo(cardId, isActivationStarted = false)
|
||||
?: UsedCardInfo(cardId, isActivationStarted = false)
|
||||
|
||||
save(foundItem, restoredList)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ import com.tangem.network.api.tangemTech.TangemTechService
|
|||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
data class DomainGlobalState(
|
||||
// there is a part of mirrors from the GlobalState
|
||||
// there is a part of mirrors from the GlobalState.
|
||||
// It updates on GlobalAction.SaveScanNoteResponse -> DomainGlobalAction.SaveScanNoteResponse(scanResponse)
|
||||
val scanResponse: ScanResponse? = null,
|
||||
//
|
||||
val networkServices: NetworkServices = NetworkServices(),
|
||||
|
|
|
|||
|
|
@ -9,12 +9,7 @@ interface HttpResponse
|
|||
sealed interface TangemTechResponse : HttpResponse
|
||||
|
||||
sealed class Coins : TangemTechResponse {
|
||||
data class PricesResponse(val prices: List<Price>) : Coins() {
|
||||
data class Price(
|
||||
val name: String,
|
||||
val price: BigDecimal,
|
||||
)
|
||||
}
|
||||
data class PricesResponse(val prices: Map<String, Double>) : Coins()
|
||||
|
||||
data class CheckAddressResponse(val imageHost: String?, val tokens: List<Token>, val total: Int) : Coins() {
|
||||
data class Token(
|
||||
|
|
@ -51,11 +46,15 @@ sealed class Coins : TangemTechResponse {
|
|||
data class CurrenciesResponse(val currencies: List<Currency>) {
|
||||
data class Currency(
|
||||
val id: String,
|
||||
val code: String,
|
||||
val code: String, // this is an uppercase id
|
||||
val name: String,
|
||||
val rateBTC: String,
|
||||
val unit: String,
|
||||
val unit: String, // $, €, ₽
|
||||
val type: String,
|
||||
)
|
||||
|
||||
enum class CurrencyType(val type: String) {
|
||||
Fiat("fiat"), Crypto("crypto")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@ interface TangemTechApi {
|
|||
@GET("coins/prices")
|
||||
suspend fun coinsPrices(
|
||||
@Query("currency") currency: String,
|
||||
@Query("ids") ids: List<String>,
|
||||
@Query("ids") ids: String,
|
||||
): Coins.PricesResponse
|
||||
|
||||
@GET("coins/check-address")
|
||||
|
|
|
|||
|
|
@ -34,7 +34,8 @@ class TangemTechService {
|
|||
private fun createApi(): TangemTechApi {
|
||||
val retrofit = createRetrofitInstance(
|
||||
baseUrl = baseUrl,
|
||||
interceptors = headerInterceptors.toList()
|
||||
interceptors = headerInterceptors.toList(),
|
||||
// logEnabled = true,
|
||||
)
|
||||
return retrofit.create(TangemTechApi::class.java).apply {
|
||||
techRoutes.forEach { it.setApi(this) }
|
||||
|
|
@ -62,7 +63,9 @@ class CoinsRoute : TangemTechRoute {
|
|||
currency: String,
|
||||
ids: List<String>
|
||||
): Result<Coins.PricesResponse> = withContext(Dispatchers.IO) {
|
||||
performRequest { api.coinsPrices(currency, ids) }
|
||||
performRequest {
|
||||
api.coinsPrices(currency.lowercase(), ids.joinToString(","))
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun checkAddress(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue