Updated on 2026-08-14

This commit is contained in:
Tangem 2023-08-15 13:24:38 +03:00
parent de890421cb
commit 40b616868d
25 changed files with 258 additions and 78 deletions

View file

@ -33,6 +33,7 @@ dependencies {
implementation(projects.domain.tokens)
implementation(projects.domain.txhistory)
implementation(projects.domain.appCurrency)
implementation(projects.domain.appCurrency.models)
implementation(project(":common"))
implementation(project(":core:analytics"))

View file

@ -23,6 +23,7 @@ import com.tangem.datasource.config.FeaturesLocalLoader
import com.tangem.datasource.config.models.Config
import com.tangem.datasource.connection.NetworkConnectionManager
import com.tangem.domain.DomainLayer
import com.tangem.domain.appcurrency.repository.AppCurrencyRepository
import com.tangem.domain.card.ScanCardProcessor
import com.tangem.domain.common.LogConfig
import com.tangem.domain.wallets.legacy.WalletManagersRepository
@ -161,6 +162,9 @@ class TapApplication : Application(), ImageLoaderFactory {
@Inject
lateinit var blockchainExceptionHandler: BlockchainExceptionHandler
@Inject
lateinit var appCurrencyRepository: AppCurrencyRepository
override fun onCreate() {
super.onCreate()
@ -179,6 +183,7 @@ class TapApplication : Application(), ImageLoaderFactory {
walletConnectSessionsRepository = walletConnectSessionsRepository,
tokenDetailsFeatureToggles = tokenDetailsFeatureToggles,
scanCardProcessor = scanCardProcessor,
appCurrencyRepository = appCurrencyRepository,
),
),
)

View file

@ -8,10 +8,12 @@ import com.tangem.domain.common.LogConfig
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.tap.*
import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
import com.tangem.tap.common.extensions.dispatchDialogShow
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.dispatchWithMain
import com.tangem.tap.common.redux.AppDialog
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager
@ -24,17 +26,13 @@ import com.tangem.tap.network.exchangeServices.ExchangeService
import com.tangem.tap.network.exchangeServices.mercuryo.MercuryoEnvironment
import com.tangem.tap.network.exchangeServices.mercuryo.MercuryoService
import com.tangem.tap.network.exchangeServices.moonpay.MoonPayService
import com.tangem.tap.preferencesStorage
import com.tangem.tap.scope
import com.tangem.tap.store
import com.tangem.tap.tangemSdkManager
import com.tangem.tap.userTokensRepository
import com.tangem.tap.walletCurrenciesManager
import com.tangem.tap.proxy.redux.DaggerGraphState
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.launch
import org.rekotlin.Action
import org.rekotlin.DispatchFunction
import org.rekotlin.Middleware
import java.util.*
import java.util.Locale
object GlobalMiddleware {
val handler = globalMiddlewareHandler
@ -68,13 +66,11 @@ private fun handleAction(action: Action, appState: () -> AppState?, dispatch: Di
}
}
is GlobalAction.RestoreAppCurrency -> {
store.dispatch(
GlobalAction.RestoreAppCurrency.Success(
preferencesStorage.fiatCurrenciesPrefStorage.getAppCurrency()
?.run { FiatCurrency(code, name, symbol) }
?: FiatCurrency.Default,
),
)
if (store.state.daggerGraphState.get(DaggerGraphState::walletFeatureToggles).isRedesignedScreenEnabled) {
restoreAppCurrencyNew()
} else {
restoreAppCurrencyLegacy()
}
}
is GlobalAction.HideWarningMessage -> {
store.state.globalState.warningManager?.let {
@ -193,6 +189,28 @@ private fun handleAction(action: Action, appState: () -> AppState?, dispatch: Di
}
}
private fun restoreAppCurrencyLegacy() {
store.dispatch(
GlobalAction.RestoreAppCurrency.Success(
preferencesStorage.fiatCurrenciesPrefStorage.getAppCurrency()
?.run { FiatCurrency(code, name, symbol) }
?: FiatCurrency.Default,
),
)
}
private fun restoreAppCurrencyNew() {
scope.launch {
val currency = store.state.daggerGraphState.get(DaggerGraphState::appCurrencyRepository)
.getSelectedAppCurrency()
.firstOrNull()
?.run { FiatCurrency(code, name, symbol) }
?: FiatCurrency.Default
store.dispatchWithMain(GlobalAction.RestoreAppCurrency.Success(currency))
}
}
private fun makeSellExchangeService(config: Config): ExchangeService {
return MoonPayService(
apiKey = config.moonPayApiKey,

View file

@ -3,12 +3,14 @@ package com.tangem.tap.features.wallet.redux.middlewares
import com.tangem.common.extensions.guard
import com.tangem.core.analytics.Analytics
import com.tangem.data.source.preferences.model.DataSourceCurrency
import com.tangem.data.source.preferences.model.DataSourceFiatCurrency
import com.tangem.data.source.preferences.storage.FiatCurrenciesPrefStorage
import com.tangem.domain.appcurrency.repository.AppCurrencyRepository
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.MainScreen
import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.common.extensions.dispatchDialogShow
import com.tangem.tap.common.extensions.dispatchWithMain
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.TapWalletManager
import com.tangem.tap.features.details.redux.DetailsAction
@ -19,6 +21,8 @@ import com.tangem.tap.features.walletSelector.redux.WalletSelectorAction
import com.tangem.tap.scope
import com.tangem.tap.store
import com.tangem.tap.userWalletsListManager
import com.tangem.utils.coroutines.JobHolder
import com.tangem.utils.coroutines.saveIn
import kotlinx.coroutines.launch
import timber.log.Timber
@ -26,8 +30,12 @@ class AppCurrencyMiddleware(
private val walletRepository: WalletRepository,
private val tapWalletManager: TapWalletManager,
private val fiatCurrenciesPrefStorage: FiatCurrenciesPrefStorage,
private val featureToggles: WalletFeatureToggles,
private val appCurrencyRepository: AppCurrencyRepository,
private val appCurrencyProvider: () -> FiatCurrency,
) {
private val showSelectorJobHolder = JobHolder()
fun handle(action: WalletAction.AppCurrencyAction) {
when (action) {
is WalletAction.AppCurrencyAction.ChooseAppCurrency -> showSelector()
@ -36,6 +44,52 @@ class AppCurrencyMiddleware(
}
private fun showSelector() {
if (featureToggles.isRedesignedScreenEnabled) {
showSelectorNew()
} else {
showSelectorLegacy()
}
}
private fun selectCurrency(action: WalletAction.AppCurrencyAction.SelectAppCurrency) {
Analytics.send(MainScreen.MainCurrencyChanged(AnalyticsParam.CurrencyType.FiatCurrency(action.fiatCurrency)))
scope.launch {
appCurrencyRepository.changeAppCurrency(action.fiatCurrency.code)
store.dispatchWithMain(GlobalAction.ChangeAppCurrency(action.fiatCurrency))
store.dispatchWithMain(DetailsAction.ChangeAppCurrency(action.fiatCurrency))
store.dispatchWithMain(WalletSelectorAction.ChangeAppCurrency(action.fiatCurrency))
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard {
Timber.e("Unable to select currency, no user wallet selected")
return@launch
}
tapWalletManager.loadData(selectedUserWallet, refresh = true)
}
}
private fun showSelectorNew() {
scope.launch {
val currencies = appCurrencyRepository.getAvailableAppCurrencies()
store.dispatchDialogShow(
WalletDialog.CurrencySelectionDialog(
currenciesList = currencies.map { appCurrency ->
FiatCurrency(
code = appCurrency.code,
name = appCurrency.name,
symbol = appCurrency.symbol,
)
},
currentAppCurrency = appCurrencyProvider.invoke(),
),
)
}.saveIn(showSelectorJobHolder)
}
private fun showSelectorLegacy() {
val storedFiatCurrencies = fiatCurrenciesPrefStorage.restore()
if (storedFiatCurrencies.isNotEmpty()) {
store.dispatchDialogShow(
@ -65,23 +119,6 @@ class AppCurrencyMiddleware(
}
}
private fun selectCurrency(action: WalletAction.AppCurrencyAction.SelectAppCurrency) {
Analytics.send(MainScreen.MainCurrencyChanged(AnalyticsParam.CurrencyType.FiatCurrency(action.fiatCurrency)))
fiatCurrenciesPrefStorage.saveAppCurrency(
with(action.fiatCurrency) { DataSourceFiatCurrency(code, name, symbol) },
)
store.dispatch(GlobalAction.ChangeAppCurrency(action.fiatCurrency))
store.dispatch(DetailsAction.ChangeAppCurrency(action.fiatCurrency))
store.dispatch(WalletSelectorAction.ChangeAppCurrency(action.fiatCurrency))
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard {
Timber.e("Unable to select currency, no user wallet selected")
return
}
scope.launch {
tapWalletManager.loadData(selectedUserWallet, refresh = true)
}
}
private fun List<DataSourceCurrency>.mapToUiModel(): List<FiatCurrency> {
return this.map {
FiatCurrency(

View file

@ -55,6 +55,8 @@ class WalletMiddleware {
walletRepository = store.state.featureRepositoryProvider.walletRepository,
tapWalletManager = store.state.globalState.tapWalletManager,
fiatCurrenciesPrefStorage = preferencesStorage.fiatCurrenciesPrefStorage,
appCurrencyRepository = store.state.daggerGraphState.get(DaggerGraphState::appCurrencyRepository),
featureToggles = store.state.daggerGraphState.get(DaggerGraphState::walletFeatureToggles),
appCurrencyProvider = { store.state.globalState.appCurrency },
)
}

View file

@ -2,6 +2,7 @@ package com.tangem.tap.proxy.redux
import com.tangem.datasource.asset.AssetReader
import com.tangem.datasource.connection.NetworkConnectionManager
import com.tangem.domain.appcurrency.repository.AppCurrencyRepository
import com.tangem.domain.card.ScanCardProcessor
import com.tangem.domain.card.ScanCardUseCase
import com.tangem.domain.card.repository.CardSdkConfigRepository
@ -31,6 +32,7 @@ data class DaggerGraphState(
val tokenDetailsRouter: TokenDetailsRouter? = null,
val scanCardProcessor: ScanCardProcessor? = null,
val cardSdkConfigRepository: CardSdkConfigRepository? = null,
val appCurrencyRepository: AppCurrencyRepository? = null,
) : StateType {
inline fun <reified T> get(getDependency: DaggerGraphState.() -> T?): T {

View file

@ -0,0 +1,20 @@
package com.tangem.datasource.di
import com.tangem.datasource.local.appcurrency.MockSelectedAppCurrencyStore
import com.tangem.datasource.local.appcurrency.SelectedAppCurrencyStore
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal object AppCurrencyDataModule {
@Provides
@Singleton
fun provideSelectedAppCurrencyStore(): SelectedAppCurrencyStore {
return MockSelectedAppCurrencyStore()
}
}

View file

@ -0,0 +1,26 @@
package com.tangem.datasource.local.appcurrency
import com.tangem.datasource.api.tangemTech.models.CurrenciesResponse
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
// TODO: Will be implemented in [REDACTED_TASK_KEY] task
internal class MockSelectedAppCurrencyStore : SelectedAppCurrencyStore {
override fun get(): Flow<CurrenciesResponse.Currency> {
return flowOf(
CurrenciesResponse.Currency(
id = "usd",
code = "USD",
name = "US Dollar",
unit = "$",
type = "fiat",
rateBTC = "",
),
)
}
override suspend fun store(item: CurrenciesResponse.Currency) {
/* no-op */
}
}

View file

@ -0,0 +1,11 @@
package com.tangem.datasource.local.appcurrency
import com.tangem.datasource.api.tangemTech.models.CurrenciesResponse
import kotlinx.coroutines.flow.Flow
interface SelectedAppCurrencyStore {
fun get(): Flow<CurrenciesResponse.Currency>
suspend fun store(item: CurrenciesResponse.Currency)
}

View file

@ -1,4 +1,4 @@
package com.tangem.feature.wallet.presentation.wallet.viewmodels
package com.tangem.utils.coroutines
import kotlinx.coroutines.Job
@ -7,7 +7,7 @@ import kotlinx.coroutines.Job
*
[REDACTED_AUTHOR]
*/
internal class JobHolder {
class JobHolder {
private var job: Job? = null
@ -18,4 +18,4 @@ internal class JobHolder {
}
}
internal fun Job.saveIn(jobHolder: JobHolder) = jobHolder.update(job = this)
fun Job.saveIn(jobHolder: JobHolder) = jobHolder.update(job = this)

View file

@ -17,7 +17,7 @@ internal class MockAppCurrencyRepository : AppCurrencyRepository {
return mockAppCurrencies
}
override suspend fun changeAppCurrency(appCurrency: AppCurrency) {
override suspend fun changeAppCurrency(currencyCode: String) {
/* no-op */
}
}

View file

@ -5,6 +5,7 @@ import com.tangem.data.tokens.repository.DefaultCurrenciesRepository
import com.tangem.data.tokens.repository.DefaultNetworksRepository
import com.tangem.data.tokens.repository.DefaultQuotesRepository
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.local.appcurrency.SelectedAppCurrencyStore
import com.tangem.datasource.local.quote.QuotesStore
import com.tangem.datasource.local.token.UserTokensStore
import com.tangem.datasource.local.userwallet.UserWalletsStore
@ -40,10 +41,17 @@ internal object TokensDataModule {
fun provideQuotesRepository(
tangemTechApi: TangemTechApi,
quotesStore: QuotesStore,
selectedAppCurrencyStore: SelectedAppCurrencyStore,
cacheRegistry: CacheRegistry,
dispatchers: CoroutineDispatcherProvider,
): QuotesRepository {
return DefaultQuotesRepository(tangemTechApi, quotesStore, cacheRegistry, dispatchers)
return DefaultQuotesRepository(
tangemTechApi,
quotesStore,
selectedAppCurrencyStore,
cacheRegistry,
dispatchers,
)
}
@Provides

View file

@ -3,6 +3,7 @@ package com.tangem.data.tokens.repository
import com.tangem.data.common.cache.CacheRegistry
import com.tangem.data.tokens.utils.QuotesConverter
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.local.appcurrency.SelectedAppCurrencyStore
import com.tangem.datasource.local.quote.QuotesStore
import com.tangem.domain.tokens.models.CryptoCurrency
import com.tangem.domain.tokens.models.Quote
@ -10,6 +11,7 @@ import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import timber.log.Timber
@ -17,12 +19,15 @@ import timber.log.Timber
internal class DefaultQuotesRepository(
private val tangemTechApi: TangemTechApi,
private val quotesStore: QuotesStore,
private val selectedAppCurrencyStore: SelectedAppCurrencyStore,
private val cacheRegistry: CacheRegistry,
private val dispatchers: CoroutineDispatcherProvider,
) : QuotesRepository {
private val quotesConverter = QuotesConverter()
private var quotesFetchedForAppCurrency: String? = null
override fun getQuotes(currenciesIds: Set<CryptoCurrency.ID>, refresh: Boolean): Flow<Set<Quote>> {
return channelFlow {
launch(dispatchers.io) {
@ -32,22 +37,33 @@ internal class DefaultQuotesRepository(
}
launch(dispatchers.io) {
fetchExpiredQuotes(currenciesIds, refresh)
selectedAppCurrencyStore.get().collectLatest { appCurrency ->
fetchExpiredQuotes(currenciesIds, appCurrency.id, refresh)
}
}
}
}
private suspend fun fetchExpiredQuotes(currenciesIds: Set<CryptoCurrency.ID>, refresh: Boolean) {
val expiredCurrenciesIds = filterExpiredCurrenciesIds(currenciesIds, refresh)
private suspend fun fetchExpiredQuotes(
currenciesIds: Set<CryptoCurrency.ID>,
appCurrencyId: String,
refresh: Boolean,
) {
val expiredCurrenciesIds = filterExpiredCurrenciesIds(
currenciesIds = currenciesIds,
refresh = refresh || quotesFetchedForAppCurrency != appCurrencyId,
)
if (expiredCurrenciesIds.isEmpty()) return
fetchQuotes(expiredCurrenciesIds)
quotesFetchedForAppCurrency = appCurrencyId
fetchQuotes(expiredCurrenciesIds, appCurrencyId)
}
private suspend fun fetchQuotes(rawCurrenciesIds: Set<String>) {
private suspend fun fetchQuotes(rawCurrenciesIds: Set<String>, appCurrencyId: String) {
try {
val response = tangemTechApi.getQuotes(
currencyId = "usd", // TODO: [REDACTED_JIRA]
currencyId = appCurrencyId,
coinIds = rawCurrenciesIds.joinToString(separator = ","),
)

View file

@ -9,5 +9,5 @@ interface AppCurrencyRepository {
suspend fun getAvailableAppCurrencies(): List<AppCurrency>
suspend fun changeAppCurrency(appCurrency: AppCurrency)
suspend fun changeAppCurrency(currencyCode: String)
}

View file

@ -4,11 +4,17 @@ plugins {
}
dependencies {
/** Project - Domain */
implementation(projects.domain.core)
implementation(projects.domain.tokens.models)
implementation(projects.domain.wallets.models)
implementation(projects.domain.appCurrency.models)
/** Project - Other */
implementation(projects.core.utils)
/** Tests */
testImplementation(deps.test.junit)
testImplementation(deps.test.coroutine)
}

View file

@ -56,6 +56,8 @@ dependencies {
implementation(projects.domain.txhistory.models)
implementation(projects.domain.wallets)
implementation(projects.domain.wallets.models)
implementation(projects.domain.appCurrency)
implementation(projects.domain.appCurrency.models)
/** Feature Apis */
implementation(projects.features.wallet.api)

View file

@ -83,6 +83,8 @@ internal class DefaultWalletRouter(private val navigationStateHolder: Navigation
}
override fun openDetailsScreen() {
// FIXME: Prepare details screen (e.g. dispatch action: `DetailsAction.PrepareScreen`)
// [REDACTED_JIRA]
navigationStateHolder.navigate(action = NavigationAction.NavigateTo(AppScreen.Details))
}

View file

@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.state.factory
import arrow.core.Either
import com.tangem.common.Provider
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.tokens.error.TokenListError
import com.tangem.domain.tokens.model.TokenList
@ -25,6 +26,7 @@ import com.tangem.utils.converter.Converter
*/
internal class WalletLoadedTokensListConverter(
private val currentStateProvider: Provider<WalletState>,
appCurrencyProvider: Provider<AppCurrency>,
cardTypeResolverProvider: Provider<CardTypesResolver>,
isLockedWalletProvider: Provider<Boolean>,
clickIntents: WalletClickIntents,
@ -34,9 +36,8 @@ internal class WalletLoadedTokensListConverter(
currentStateProvider = currentStateProvider,
cardTypeResolverProvider = cardTypeResolverProvider,
isLockedWalletProvider = isLockedWalletProvider,
appCurrencyProvider = appCurrencyProvider,
isWalletContentHidden = false, // TODO: [REDACTED_JIRA]
fiatCurrencyCode = "USD", // TODO: [REDACTED_JIRA]
fiatCurrencySymbol = "$", // TODO: [REDACTED_JIRA]
clickIntents = clickIntents,
)

View file

@ -5,6 +5,7 @@ import com.tangem.common.Provider
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
import com.tangem.core.ui.components.marketprice.PriceChangeConfig
import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.tokens.error.CurrencyError
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
@ -20,8 +21,7 @@ import java.math.BigDecimal
internal class WalletSingleCurrencyLoadedBalanceConverter(
private val currentStateProvider: Provider<WalletState>,
private val cardTypeResolverProvider: Provider<CardTypesResolver>,
private val fiatCurrencyCode: String,
private val fiatCurrencySymbol: String,
private val appCurrencyProvider: Provider<AppCurrency>,
) : Converter<Either<CurrencyError, CryptoCurrencyStatus>, WalletSingleCurrencyState.Content> {
override fun convert(value: Either<CurrencyError, CryptoCurrencyStatus>): WalletSingleCurrencyState.Content {
@ -47,7 +47,7 @@ internal class WalletSingleCurrencyLoadedBalanceConverter(
is CryptoCurrencyStatus.Loaded,
-> MarketPriceBlockState.Content(
currencyName = currencyName,
price = formatPrice(status),
price = formatPrice(status, appCurrencyProvider()),
priceChangeConfig = PriceChangeConfig(
valueInPercent = formatPriceChange(status),
type = getPriceChangeType(status),
@ -67,6 +67,7 @@ internal class WalletSingleCurrencyLoadedBalanceConverter(
state: WalletSingleCurrencyState,
): WalletsListConfig {
val selectedWallet = state.walletsListConfig.wallets[state.walletsListConfig.selectedWalletIndex]
val updatedWallet = when (status) {
is CryptoCurrencyStatus.NoQuote,
is CryptoCurrencyStatus.Loaded,
@ -81,7 +82,7 @@ internal class WalletSingleCurrencyLoadedBalanceConverter(
),
imageResId = selectedWallet.imageResId,
onClick = selectedWallet.onClick,
balance = formatFiatAmount(status),
balance = formatFiatAmount(status, appCurrencyProvider()),
)
}
is CryptoCurrencyStatus.Loading -> {
@ -133,23 +134,23 @@ internal class WalletSingleCurrencyLoadedBalanceConverter(
)
}
private fun formatPrice(status: CryptoCurrencyStatus.Status): String {
private fun formatPrice(status: CryptoCurrencyStatus.Status, appCurrency: AppCurrency): String {
val fiatRate = status.fiatRate ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN
return BigDecimalFormatter.formatFiatAmount(
fiatAmount = fiatRate,
fiatCurrencyCode = fiatCurrencyCode,
fiatCurrencySymbol = fiatCurrencySymbol,
fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol,
)
}
private fun formatFiatAmount(status: CryptoCurrencyStatus.Status): String {
private fun formatFiatAmount(status: CryptoCurrencyStatus.Status, appCurrency: AppCurrency): String {
val fiatAmount = status.fiatAmount ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN
return BigDecimalFormatter.formatFiatAmount(
fiatAmount = fiatAmount,
fiatCurrencyCode = fiatCurrencyCode,
fiatCurrencySymbol = fiatCurrencySymbol,
fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol,
)
}
}

View file

@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.state.factory
import androidx.paging.PagingData
import arrow.core.Either
import com.tangem.common.Provider
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.tokens.error.CurrencyError
import com.tangem.domain.tokens.error.TokenListError
@ -37,6 +38,7 @@ internal class WalletStateFactory(
private val currentStateProvider: Provider<WalletState>,
private val currentCardTypeResolverProvider: Provider<CardTypesResolver>,
private val isLockedWalletProvider: Provider<Boolean>,
private val appCurrencyProvider: Provider<AppCurrency>,
private val clickIntents: WalletClickIntents,
) {
@ -47,6 +49,7 @@ internal class WalletStateFactory(
currentStateProvider = currentStateProvider,
cardTypeResolverProvider = currentCardTypeResolverProvider,
isLockedWalletProvider = isLockedWalletProvider,
appCurrencyProvider = appCurrencyProvider,
clickIntents = clickIntents,
)
}
@ -70,8 +73,7 @@ internal class WalletStateFactory(
WalletSingleCurrencyLoadedBalanceConverter(
currentStateProvider = currentStateProvider,
cardTypeResolverProvider = currentCardTypeResolverProvider,
fiatCurrencyCode = "USD", // TODO: [REDACTED_JIRA]
fiatCurrencySymbol = "$", // TODO: [REDACTED_JIRA]
appCurrencyProvider = appCurrencyProvider,
)
}

View file

@ -1,8 +1,10 @@
package com.tangem.feature.wallet.presentation.wallet.utils
import androidx.annotation.DrawableRes
import com.tangem.common.Provider
import com.tangem.core.ui.components.marketprice.PriceChangeConfig
import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.models.CryptoCurrency
import com.tangem.feature.wallet.impl.R
@ -11,9 +13,8 @@ import com.tangem.utils.converter.Converter
import java.math.BigDecimal
internal class CryptoCurrencyStatusToTokenItemConverter(
private val appCurrencyProvider: Provider<AppCurrency>,
private val isWalletContentHidden: Boolean,
private val fiatCurrencyCode: String,
private val fiatCurrencySymbol: String,
) : Converter<CryptoCurrencyStatus, TokenItemState> {
private val CryptoCurrencyStatus.networkIconResId: Int?
@ -71,8 +72,9 @@ internal class CryptoCurrencyStatusToTokenItemConverter(
private fun CryptoCurrencyStatus.getFormattedFiatAmount(): String {
val fiatAmount = value.fiatAmount ?: return UNKNOWN_AMOUNT_SIGN
val appCurrency = appCurrencyProvider()
return BigDecimalFormatter.formatFiatAmount(fiatAmount, fiatCurrencyCode, fiatCurrencySymbol)
return BigDecimalFormatter.formatFiatAmount(fiatAmount, appCurrency.code, appCurrency.symbol)
}
private fun CryptoCurrencyStatus.mapToUnreachableTokenItemState() = TokenItemState.Unreachable(

View file

@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.utils
import com.tangem.common.Provider
import com.tangem.core.ui.utils.BigDecimalFormatter.formatFiatAmount
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.tokens.model.TokenList
import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory
@ -11,10 +12,9 @@ import com.tangem.utils.converter.Converter
internal class FiatBalanceToWalletCardConverter(
private val currentState: WalletCardState,
private val cardTypeResolverProvider: Provider<CardTypesResolver>,
private val appCurrencyProvider: Provider<AppCurrency>,
private val isLockedState: Boolean,
private val isWalletContentHidden: Boolean,
private val fiatCurrencyCode: String,
private val fiatCurrencySymbol: String,
) : Converter<TokenList.FiatBalance, WalletCardState> {
override fun convert(value: TokenList.FiatBalance): WalletCardState {
@ -33,13 +33,15 @@ internal class FiatBalanceToWalletCardConverter(
if (isWalletContentHidden) {
WalletCardState.HiddenContent(id, title, additionalInfo, imageResId, onClick)
} else {
val appCurrency = appCurrencyProvider()
WalletCardState.Content(
id = id,
title = title,
additionalInfo = additionalInfo,
imageResId = imageResId,
onClick = onClick,
balance = formatFiatAmount(value.amount, fiatCurrencyCode, fiatCurrencySymbol),
balance = formatFiatAmount(value.amount, appCurrency.code, appCurrency.symbol),
)
}
}

View file

@ -1,6 +1,8 @@
package com.tangem.feature.wallet.presentation.wallet.utils
import com.tangem.common.Provider
import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.NetworkGroup
import com.tangem.domain.tokens.model.TokenList
@ -14,16 +16,14 @@ import kotlinx.collections.immutable.mutate
import kotlinx.collections.immutable.persistentListOf
internal class TokenListToContentItemsConverter(
appCurrencyProvider: Provider<AppCurrency>,
isWalletContentHidden: Boolean,
fiatCurrencyCode: String,
fiatCurrencySymbol: String,
private val clickIntents: WalletClickIntents,
) : Converter<TokenList, WalletTokensListState> {
private val tokenStatusConverter = CryptoCurrencyStatusToTokenItemConverter(
isWalletContentHidden = isWalletContentHidden,
fiatCurrencyCode = fiatCurrencyCode,
fiatCurrencySymbol = fiatCurrencySymbol,
appCurrencyProvider = appCurrencyProvider,
)
override fun convert(value: TokenList): WalletTokensListState {

View file

@ -1,6 +1,7 @@
package com.tangem.feature.wallet.presentation.wallet.utils
import com.tangem.common.Provider
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.tokens.model.TokenList
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
@ -18,16 +19,14 @@ internal class TokenListToWalletStateConverter(
private val currentStateProvider: Provider<WalletState>,
private val cardTypeResolverProvider: Provider<CardTypesResolver>,
private val isLockedWalletProvider: Provider<Boolean>,
private val appCurrencyProvider: Provider<AppCurrency>,
private val isWalletContentHidden: Boolean,
private val fiatCurrencyCode: String,
private val fiatCurrencySymbol: String,
clickIntents: WalletClickIntents,
) : Converter<TokensListModel, WalletMultiCurrencyState.Content> {
private val tokenListToContentConverter = TokenListToContentItemsConverter(
isWalletContentHidden = isWalletContentHidden,
fiatCurrencyCode = fiatCurrencyCode,
fiatCurrencySymbol = fiatCurrencySymbol,
appCurrencyProvider = appCurrencyProvider,
clickIntents = clickIntents,
)
@ -51,9 +50,8 @@ internal class TokenListToWalletStateConverter(
currentState = selectedWalletCard,
isLockedState = isLockedWalletProvider(),
cardTypeResolverProvider = cardTypeResolverProvider,
appCurrencyProvider = appCurrencyProvider,
isWalletContentHidden = isWalletContentHidden,
fiatCurrencyCode = fiatCurrencyCode,
fiatCurrencySymbol = fiatCurrencySymbol,
)
return walletsListConfig.copy(

View file

@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.viewmodels
import androidx.lifecycle.*
import androidx.paging.cachedIn
import arrow.core.getOrElse
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.derivation.DerivationStyle
import com.tangem.common.Provider
@ -9,6 +10,8 @@ import com.tangem.common.doOnFailure
import com.tangem.common.doOnSuccess
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.card.*
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.common.extensions.derivationPath
@ -37,11 +40,10 @@ import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCard
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState
import com.tangem.feature.wallet.presentation.wallet.state.factory.WalletStateFactory
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.JobHolder
import com.tangem.utils.coroutines.saveIn
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import javax.inject.Inject
import kotlin.properties.Delegates
@ -71,12 +73,15 @@ internal class WalletViewModel @Inject constructor(
private val txHistoryItemsUseCase: GetTxHistoryItemsUseCase,
private val getExploreUrlUseCase: GetExploreUrlUseCase,
private val unlockWalletsUseCase: UnlockWalletsUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val dispatchers: CoroutineDispatcherProvider,
) : ViewModel(), DefaultLifecycleObserver, WalletClickIntents {
/** Feature router */
var router: InnerWalletRouter by Delegates.notNull()
private val selectedAppCurrencyFlow: StateFlow<AppCurrency> = createSelectedAppCurrencyFlow()
private val notificationsListFactory = WalletNotificationsListFactory(
currentStateProvider = Provider { uiState },
wasCardScannedCallback = getCardWasScannedUseCase::invoke,
@ -95,6 +100,7 @@ internal class WalletViewModel @Inject constructor(
isLockedWalletProvider = Provider {
wallets[requireNotNull(uiState as? WalletState.ContentState).walletsListConfig.selectedWalletIndex].isLocked
},
appCurrencyProvider = Provider(selectedAppCurrencyFlow::value),
clickIntents = this,
)
@ -432,4 +438,16 @@ internal class WalletViewModel @Inject constructor(
override fun onBottomSheetDismiss() {
uiState = stateFactory.getStateWithClosedBottomSheet()
}
private fun createSelectedAppCurrencyFlow(): StateFlow<AppCurrency> {
return getSelectedAppCurrencyUseCase()
.map { maybeAppCurrency ->
maybeAppCurrency.getOrElse { AppCurrency.Default }
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.Eagerly,
initialValue = AppCurrency.Default,
)
}
}