diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index e55d54f88e..45e7e92da3 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -4,6 +4,7 @@ import android.app.Application import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.appReducer import com.tangem.tap.network.NetworkConnectivity +import com.tangem.tap.persistence.PreferencesStorage import com.tangem.wallet.BuildConfig import org.rekotlin.Store import timber.log.Timber @@ -13,10 +14,12 @@ val store = Store( middleware = AppState.getMiddleware(), state = AppState() ) +lateinit var preferencesStorage: PreferencesStorage class TapApplication : Application() { override fun onCreate() { super.onCreate() + preferencesStorage = PreferencesStorage(this) if (BuildConfig.DEBUG) { Timber.plant(Timber.DebugTree()) diff --git a/app/src/main/java/com/tangem/tap/common/entities/TapCurrency.kt b/app/src/main/java/com/tangem/tap/common/entities/TapCurrency.kt index 9e00a33525..1aa3f5c351 100644 --- a/app/src/main/java/com/tangem/tap/common/entities/TapCurrency.kt +++ b/app/src/main/java/com/tangem/tap/common/entities/TapCurrency.kt @@ -5,6 +5,6 @@ package com.tangem.tap.common.entities */ class TapCurrency { companion object{ - val main = "USD" + const val DEFAULT_FIAT_CURRENCY = "USD" } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt b/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt index d4d8847066..bf905a0e9e 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt @@ -6,6 +6,8 @@ import com.google.zxing.BarcodeFormat import com.google.zxing.EncodeHintType import com.google.zxing.qrcode.QRCodeWriter import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel +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 @@ -46,10 +48,10 @@ fun BigDecimal.toFormattedString(decimals: Int): String { return df.format(bd) } -fun BigDecimal.toFiatString(rateValue: BigDecimal): String? { +fun BigDecimal.toFiatString(rateValue: BigDecimal, fiatCurrencyName: FiatCurrencyName): String? { var fiatValue = rateValue.multiply(this) fiatValue = fiatValue.setScale(2, RoundingMode.DOWN) - return "≈ USD  $fiatValue" + return "≈ ${fiatCurrencyName}  $fiatValue" } fun BigDecimal.stripZeroPlainString(): String = this.stripTrailingZeros().toPlainString() @@ -67,4 +69,6 @@ fun BigDecimal.isGreaterThanOrEqual(value: BigDecimal): Boolean { fun BigDecimal.isLessThanOrEqual(value: BigDecimal): Boolean { val compareResult = this.compareTo(value) return compareResult == -1 || compareResult == 0 -} \ No newline at end of file +} + +fun FiatCurrency.toFormattedString(): String = "${this.name} (${this.symbol}) - ${this.sign}" \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt index 8a4412c114..b262647600 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt @@ -7,5 +7,11 @@ import java.math.BigDecimal sealed class GlobalAction : Action { data class SaveScanNoteResponse(val scanNoteResponse: ScanNoteResponse) : GlobalAction() - data class SetFiatRate(val fiatRates: Pair) : GlobalAction() + data class SetFiatRate( + val fiatRates: Pair + ) : GlobalAction() + data class ChangeAppCurrency(val appCurrency: FiatCurrencyName) : GlobalAction() + object RestoreAppCurrency : GlobalAction() { + data class Success(val appCurrency: FiatCurrencyName) : GlobalAction() + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt new file mode 100644 index 0000000000..d7ec41960f --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt @@ -0,0 +1,21 @@ +package com.tangem.tap.common.redux.global + +import com.tangem.tap.common.redux.AppState +import com.tangem.tap.preferencesStorage +import com.tangem.tap.store +import org.rekotlin.Middleware + +val globalMiddleware: Middleware = { dispatch, appState -> + { nextDispatch -> + { action -> + when (action) { + is GlobalAction.RestoreAppCurrency -> { + store.dispatch(GlobalAction.RestoreAppCurrency.Success( + preferencesStorage.getAppCurrency() + )) + } + } + nextDispatch(action) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt index 71fa1cfb50..b3f496719c 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt @@ -13,9 +13,15 @@ fun globalReducer(action: Action, state: AppState): GlobalState { is GlobalAction.SaveScanNoteResponse -> newState = newState.copy(scanNoteResponse = action.scanNoteResponse) is GlobalAction.SetFiatRate -> { - val rates = newState.fiatRates.rates.toMutableMap() + val rates = newState.conversionRates.rates.toMutableMap() rates[action.fiatRates.first] = action.fiatRates.second - newState = newState.copy(fiatRates = FiatRates(rates)) + newState = newState.copy(conversionRates = ConversionRates(rates)) + } + is GlobalAction.ChangeAppCurrency -> { + newState = newState.copy(appCurrency = action.appCurrency, conversionRates = ConversionRates(mapOf())) + } + is GlobalAction.RestoreAppCurrency.Success -> { + newState = newState.copy(appCurrency = action.appCurrency, conversionRates = ConversionRates(mapOf())) } } return newState diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt index 6ea19cc94e..9da6906d9c 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt @@ -1,22 +1,33 @@ package com.tangem.tap.common.redux.global +import com.tangem.commands.common.network.TangemService +import com.tangem.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY +import com.tangem.tap.domain.PayIdManager import com.tangem.tap.domain.TapWalletManager import com.tangem.tap.domain.tasks.ScanNoteResponse +import com.tangem.tap.network.coinmarketcap.CoinMarketCapService import org.rekotlin.StateType import java.math.BigDecimal data class GlobalState( val scanNoteResponse: ScanNoteResponse? = null, val tapWalletManager: TapWalletManager = TapWalletManager(), - val fiatRates: FiatRates = FiatRates(emptyMap()), + val payIdManager: PayIdManager = PayIdManager(), + val coinMarketCapService: CoinMarketCapService = CoinMarketCapService(), + val tangemService: TangemService = TangemService(), + val conversionRates: ConversionRates = ConversionRates(emptyMap()), + val appCurrency: FiatCurrencyName = DEFAULT_FIAT_CURRENCY ) : StateType -data class FiatRates( - val rates: Map +data class ConversionRates( + val rates: Map, ) { - fun getRateForCryptoCurrency(currency: String): BigDecimal? { - return rates[currency] + + fun getRate(cryptoCurrency: CryptoCurrencyName): BigDecimal? { + return rates[cryptoCurrency] } } +typealias CryptoCurrencyName = String +typealias FiatCurrencyName = String diff --git a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt index 373d4d5f41..30e36dfd81 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -8,6 +8,8 @@ import com.tangem.commands.common.network.Result import com.tangem.commands.common.network.TangemService import com.tangem.common.extensions.toHexString import com.tangem.tap.TapConfig +import com.tangem.tap.common.redux.global.CryptoCurrencyName +import com.tangem.tap.common.redux.global.FiatCurrencyName import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.domain.tasks.ScanNoteResponse import com.tangem.tap.features.wallet.redux.PayIdState @@ -54,15 +56,15 @@ class TapWalletManager { result?.let { handlePayIdResult(it) } } - suspend fun loadFiatRate() { + suspend fun loadFiatRate(fiatCurrency: FiatCurrencyName) { val wallet = store.state.globalState.scanNoteResponse?.walletManager?.wallet val blockchainCurrency = wallet?.blockchain?.currency val tokenCurrency = wallet?.token?.symbol - val blockchainRate = blockchainCurrency?.let { coinMarketCapService.getRate(it) } - val tokenRate = tokenCurrency?.let { coinMarketCapService.getRate(it) } + val blockchainRate = blockchainCurrency?.let { coinMarketCapService.getRate(it, fiatCurrency) } + val tokenRate = tokenCurrency?.let { coinMarketCapService.getRate(it, fiatCurrency) } - val results = mutableListOf?>>() + val results = mutableListOf?>>() if (blockchainCurrency != null) results.add(blockchainCurrency to blockchainRate) if (tokenCurrency != null) results.add(tokenCurrency to tokenRate) @@ -83,7 +85,7 @@ class TapWalletManager { store.dispatch(WalletAction.LoadWallet) store.dispatch(WalletAction.LoadFiatRate) store.dispatch(WalletAction.LoadPayId) - } else if (data.card.status == CardStatus.Empty){ + } else if (data.card.status == CardStatus.Empty) { store.dispatch(WalletAction.EmptyWallet) } else { store.dispatch(WalletAction.LoadData.Failure(TapError.UnknownBlockchain)) @@ -153,7 +155,7 @@ class TapWalletManager { } } - private suspend fun handleFiatRatesResult(results: List?>>) { + private suspend fun handleFiatRatesResult(results: List?>>) { withContext(Dispatchers.Main) { results.map { when (it.second) { diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/CurrencySelectionDialog.kt b/app/src/main/java/com/tangem/tap/features/details/ui/CurrencySelectionDialog.kt new file mode 100644 index 0000000000..a2267999e0 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/details/ui/CurrencySelectionDialog.kt @@ -0,0 +1,45 @@ +package com.tangem.tap.features.details.ui + +import android.content.Context +import androidx.appcompat.app.AlertDialog +import com.google.android.material.dialog.MaterialAlertDialogBuilder +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 + +class CurrencySelectionDialog { + + var dialog: AlertDialog? = null + + fun show(currencies: List, currentAppCurrency: FiatCurrencyName, context: Context) { + + if (dialog == null) { + val currenciesToShow = currencies.map { it.toFormattedString() }.toTypedArray() + var currentSelection = currencies.indexOfFirst { it.symbol == currentAppCurrency } + + dialog = MaterialAlertDialogBuilder(context) + .setTitle(context.getString(R.string.details_currency)) + .setNeutralButton(context.getString(R.string.generic_cancel)) { _, _ -> + store.dispatch(DetailsAction.AppCurrencyAction.Cancel) + } + .setPositiveButton(context.getString(R.string.generic_done)) { _, _ -> + val selectedCurrency = currencies[currentSelection] + store.dispatch(DetailsAction.AppCurrencyAction.SelectAppCurrency(selectedCurrency.symbol)) + } + .setOnDismissListener { + store.dispatch(DetailsAction.AppCurrencyAction.Cancel) + } + .setSingleChoiceItems(currenciesToShow, currentSelection) { _, which -> + currentSelection = which + }.show() + } + } + + fun clear() { + dialog = null + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt index 6a25e7251d..bbb05df3e4 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt @@ -5,6 +5,7 @@ import android.net.Uri import androidx.core.content.ContextCompat.startActivity import com.tangem.common.CompletionResult import com.tangem.tap.common.redux.AppState +import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.scope @@ -25,6 +26,7 @@ val homeMiddleware: Middleware = { dispatch, state -> withContext(Dispatchers.Main) { when (result) { is CompletionResult.Success -> { + store.dispatch(GlobalAction.RestoreAppCurrency) store.state.globalState.tapWalletManager.onCardScanned(result.data) store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) } diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AmountReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AmountReducer.kt index efeae4552c..5129a95646 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AmountReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AmountReducer.kt @@ -2,7 +2,6 @@ package com.tangem.tap.features.send.redux.reducers import com.tangem.common.extensions.isZero import com.tangem.tap.common.CurrencyConverter -import com.tangem.tap.common.entities.TapCurrency import com.tangem.tap.common.extensions.isNegative import com.tangem.tap.common.extensions.stripZeroPlainString import com.tangem.tap.features.send.redux.AmountAction @@ -13,6 +12,7 @@ import com.tangem.tap.features.send.redux.states.AmountState import com.tangem.tap.features.send.redux.states.MainCurrencyType import com.tangem.tap.features.send.redux.states.SendState import com.tangem.tap.features.send.redux.states.Value +import com.tangem.tap.store import java.math.BigDecimal /** @@ -42,7 +42,7 @@ class AmountReducer : SendInternalReducer { state.copy( viewAmountValue = fiatToSend.stripZeroPlainString(), viewBalanceValue = converter.toFiat(state.balanceCrypto).stripZeroPlainString(), - mainCurrency = Value(MainCurrencyType.FIAT, TapCurrency.main), + mainCurrency = Value(MainCurrencyType.FIAT, store.state.globalState.appCurrency), maxLengthOfAmount = sendState.getDecimals(action.mainCurrency), cursorAtTheSamePosition = false ) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt index 0776c99fe4..91726cb199 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt @@ -3,11 +3,11 @@ package com.tangem.tap.features.send.redux.reducers import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.Wallet import com.tangem.tap.common.CurrencyConverter -import com.tangem.tap.common.entities.TapCurrency import com.tangem.tap.common.extensions.stripZeroPlainString import com.tangem.tap.features.send.redux.ReceiptAction.RefreshReceipt import com.tangem.tap.features.send.redux.SendScreenAction import com.tangem.tap.features.send.redux.states.* +import com.tangem.tap.store /** [REDACTED_AUTHOR] @@ -163,7 +163,7 @@ class ReceiptReducer : SendInternalReducer { private fun determineSymbols(wallet: Wallet): ReceiptSymbols { return ReceiptSymbols( - fiat = TapCurrency.main, + fiat = store.state.globalState.appCurrency, crypto = wallet.blockchain.currency, token = wallet.amounts[AmountType.Token]?.currencySymbol ) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt index 6d932b4654..858b626b15 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt @@ -65,7 +65,7 @@ private class PrepareSendScreenStatesReducer : SendInternalReducer { } private fun createCurrencyConverter(walletManager: WalletManager): CurrencyConverter { - val rate = store.state.globalState.fiatRates.getRateForCryptoCurrency(walletManager.wallet.blockchain.currency) + val rate = store.state.globalState.conversionRates.getRate(walletManager.wallet.blockchain.currency) return if (rate == null) CurrencyConverter(BigDecimal.ONE) else CurrencyConverter(rate) } } diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt index 4d3cc3cd98..c7af7809bd 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt @@ -5,7 +5,6 @@ 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.TapCurrency import com.tangem.tap.features.send.redux.AmountAction import com.tangem.tap.store import org.rekotlin.StateType @@ -60,7 +59,7 @@ enum class SendButtonState { data class AmountState( val viewAmountValue: String = BigDecimal.ZERO.toPlainString(), val viewBalanceValue: String = BigDecimal.ZERO.toPlainString(), - val mainCurrency: Value = Value(MainCurrencyType.FIAT, TapCurrency.main), + val mainCurrency: Value = Value(MainCurrencyType.FIAT, store.state.globalState.appCurrency), val typeOfAmount: AmountType = AmountType.Coin, val amountToSendCrypto: BigDecimal = BigDecimal.ZERO, val balanceCrypto: BigDecimal = BigDecimal.ZERO, diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt index 5665e0ffa2..8201b03f38 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt @@ -197,7 +197,7 @@ 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.main) + val mainCurrency = sp.getString("mainCurrency", TapCurrency.DEFAULT_FIAT_CURRENCY) val foundType = MainCurrencyType.values() .firstOrNull { it.name.toLowerCase() == mainCurrency!!.toLowerCase() } ?: MainCurrencyType.FIAT return foundType diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt index af4908547c..4e1d6ea32e 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt @@ -6,6 +6,7 @@ import com.tangem.blockchain.common.Wallet import com.tangem.commands.Card import com.tangem.tap.common.redux.ErrorAction import com.tangem.tap.common.redux.NotificationAction +import com.tangem.tap.common.redux.global.CryptoCurrencyName import com.tangem.tap.domain.TapError import com.tangem.wallet.R import org.rekotlin.Action @@ -24,7 +25,7 @@ sealed class WalletAction : Action { } object LoadFiatRate : WalletAction() { - data class Success(val fiatRates: Pair) : WalletAction() + data class Success(val fiatRates: Pair) : WalletAction() object Failure : WalletAction() } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletMiddleware.kt index 0bd11d47b6..558f348582 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletMiddleware.kt @@ -42,7 +42,7 @@ val walletMiddleware: Middleware = { dispatch, state -> } is WalletAction.LoadFiatRate -> { scope.launch { - store.state.globalState.tapWalletManager.loadFiatRate() + store.state.globalState.tapWalletManager.loadFiatRate(store.state.globalState.appCurrency) } } is WalletAction.LoadArtwork -> { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletReducer.kt index f0b29211a5..d015483883 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletReducer.kt @@ -86,10 +86,11 @@ private fun internalReduce(action: Action, state: AppState): WalletState { ) } is WalletAction.LoadWallet.Success -> { + val fiatCurrencySymbol = state.globalState.appCurrency val token = action.wallet.amounts[AmountType.Token] val tokenData = if (token != null) { - val tokenFiatRate = state.globalState.fiatRates.getRateForCryptoCurrency(token.currencySymbol) - val tokenFiatAmount = tokenFiatRate?.let { token.value?.toFiatString(it) } + val tokenFiatRate = state.globalState.conversionRates.getRate(token.currencySymbol) + val tokenFiatAmount = tokenFiatRate?.let { token.value?.toFiatString(it, fiatCurrencySymbol) } TokenData( token.value?.toFormattedString(token.decimals) ?: "", token.currencySymbol, tokenFiatAmount) @@ -97,8 +98,8 @@ private fun internalReduce(action: Action, state: AppState): WalletState { null } val amount = action.wallet.amounts[AmountType.Coin]?.value - val fiatRate = state.globalState.fiatRates.getRateForCryptoCurrency(action.wallet.blockchain.currency) - val fiatAmount = fiatRate?.let { amount?.toFiatString(it) } + val fiatRate = state.globalState.conversionRates.getRate(action.wallet.blockchain.currency) + val fiatAmount = fiatRate?.let { amount?.toFiatString(it, fiatCurrencySymbol) } val pendingTransactions = action.wallet.transactions .toPendingTransactions(action.wallet.address) @@ -135,16 +136,24 @@ private fun internalReduce(action: Action, state: AppState): WalletState { errorMessage = action.errorMessage ) ) + is WalletAction.LoadFiatRate -> { + newState.copy(currencyData = newState.currencyData.copy( + fiatAmount = null, + token = newState.currencyData.token?.copy(fiatAmount = null)) + ) + } is WalletAction.LoadFiatRate.Success -> { - val rate = action.fiatRates.second + val rate = action.fiatRates.second ?: return newState val currency = action.fiatRates.first val fiatAmount = if (currency == newState.wallet?.blockchain?.currency) { - newState.wallet?.amounts?.get(AmountType.Coin)?.value?.toFiatString(rate) + newState.wallet?.amounts?.get(AmountType.Coin)?.value + ?.toFiatString(rate, state.globalState.appCurrency) } else { newState.currencyData.fiatAmount } val tokenFiatAmount = if (currency == newState.wallet?.token?.symbol) { - newState.wallet?.amounts?.get(AmountType.Token)?.value?.toFiatString(rate) + newState.wallet?.amounts?.get(AmountType.Token)?.value + ?.toFiatString(rate, state.globalState.appCurrency) } else { newState.currencyData.token?.fiatAmount } diff --git a/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapApi.kt b/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapApi.kt index 0c356d478e..47889ff06d 100644 --- a/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapApi.kt +++ b/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapApi.kt @@ -12,9 +12,14 @@ interface CoinMarketCapApi { @GET("v1/tools/price-conversion") suspend fun getRateInfo( @Query("amount") amount: Int, - @Query("symbol") cryptoId: String + @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/" diff --git a/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapService.kt b/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapService.kt index 5350bad116..4dc7931623 100644 --- a/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapService.kt +++ b/app/src/main/java/com/tangem/tap/network/coinmarketcap/CoinMarketCapService.kt @@ -2,6 +2,7 @@ package com.tangem.tap.network.coinmarketcap import com.tangem.commands.common.network.Result import com.tangem.commands.common.network.performRequest +import com.tangem.tap.common.redux.global.FiatCurrencyName import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.math.BigDecimal @@ -9,10 +10,20 @@ import java.math.BigDecimal class CoinMarketCapService { private val api: CoinMarketCapApi by lazy { CoinMarketCapApi.create() } - suspend fun getRate(currency: String): Result = withContext(Dispatchers.IO) { - val response = performRequest { api.getRateInfo(1, currency) } + suspend fun getRate( + currency: String, fiatCurrency: FiatCurrencyName? = null + ): Result = withContext(Dispatchers.IO) { + val response = performRequest { api.getRateInfo(1, currency, fiatCurrency) } return@withContext when (response) { - is Result.Success -> Result.Success(response.data.data.quote.usd.price) + is Result.Success -> Result.Success(response.data.data.getRate()) + is Result.Failure -> response + } + } + + suspend fun getFiatCurrencies(): Result> = 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 } } diff --git a/app/src/main/java/com/tangem/tap/network/coinmarketcap/Response.kt b/app/src/main/java/com/tangem/tap/network/coinmarketcap/Response.kt index f4ec9fa804..7320d9467c 100644 --- a/app/src/main/java/com/tangem/tap/network/coinmarketcap/Response.kt +++ b/app/src/main/java/com/tangem/tap/network/coinmarketcap/Response.kt @@ -5,21 +5,14 @@ import com.squareup.moshi.JsonClass import java.math.BigDecimal @JsonClass(generateAdapter = true) -data class RateInfoResponse( - val status: Status, - val data: RateData -) +class RateInfoResponse : CoinMarketResponse() @JsonClass(generateAdapter = true) data class RateData( - val quote: Quote -) - -@JsonClass(generateAdapter = true) -data class Quote( - @Json(name = "USD") - val usd: CurrencyRate -) + val quote: Map +) { + fun getRate(): BigDecimal = quote.values.first().price +} @JsonClass(generateAdapter = true) data class CurrencyRate( @@ -38,4 +31,21 @@ data class Status( @Json(name = "credit_count") val creditCount: Int, val notice: String? -) \ No newline at end of file +) + +@JsonClass(generateAdapter = true) +class FiatMapResponse : CoinMarketResponse>() + +@JsonClass(generateAdapter = true) +open class CoinMarketResponse { + 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 +) diff --git a/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt b/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt new file mode 100644 index 0000000000..bbaa2aa7b2 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt @@ -0,0 +1,54 @@ +package com.tangem.tap.persistence + +import android.app.Application +import android.content.Context +import android.content.SharedPreferences +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.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY +import com.tangem.tap.common.redux.global.FiatCurrencyName +import com.tangem.tap.network.coinmarketcap.FiatCurrency + + +class PreferencesStorage(applicationContext: Application) { + + private val preferences: SharedPreferences by lazy { + applicationContext.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE) + } + + private val fiatCurrenciesAdapter: JsonAdapter> by lazy { + val moshi = Moshi.Builder() + .add(KotlinJsonAdapterFactory()) + .build() + val type = Types.newParameterizedType(List::class.java, FiatCurrency::class.java) + moshi.adapter(type) + } + + fun getAppCurrency(): FiatCurrencyName { + return preferences.getString(APP_CURRENCY_KEY, DEFAULT_FIAT_CURRENCY) + ?: DEFAULT_FIAT_CURRENCY + } + + fun saveAppCurrency(fiatCurrencyName: FiatCurrencyName) { + return preferences.edit().putString(APP_CURRENCY_KEY, fiatCurrencyName).apply() + } + + fun getFiatCurrencies(): List? { + val json = preferences.getString(FIAT_CURRENCIES_KEY, "") + return if (json.isNullOrBlank()) null else fiatCurrenciesAdapter.fromJson(json) as List + } + + fun saveFiatCurrencies(currencies: List) { + val json: String = fiatCurrenciesAdapter.toJson(currencies) + return preferences.edit().putString(FIAT_CURRENCIES_KEY, json).apply() + } + + companion object { + private const val PREFERENCES_NAME = "tapPrefs" + private const val APP_CURRENCY_KEY = "appCurrency" + private const val FIAT_CURRENCIES_KEY = "fiatCurrencies" + } + +} \ No newline at end of file