Updated on 2026-08-14

This commit is contained in:
Tangem 2023-08-15 14:08:30 +03:00
parent 2a0e8f78a6
commit 7aa44c4da7
17 changed files with 364 additions and 61 deletions

View file

@ -30,4 +30,5 @@ dependencies {
/** Other */
implementation(deps.kotlin.coroutines)
implementation(deps.timber)
implementation(deps.jodatime)
}

View file

@ -0,0 +1,105 @@
package com.tangem.data.appcurrency
import com.tangem.data.appcurrency.utils.AppCurrencyConverter
import com.tangem.data.common.cache.CacheRegistry
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.api.tangemTech.models.CurrenciesResponse
import com.tangem.datasource.local.appcurrency.AvailableAppCurrenciesStore
import com.tangem.datasource.local.appcurrency.SelectedAppCurrencyStore
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.appcurrency.repository.AppCurrencyRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEmpty
import kotlinx.coroutines.withContext
import org.joda.time.Duration
import timber.log.Timber
internal class DefaultAppCurrencyRepository(
private val tangemTechApi: TangemTechApi,
private val availableAppCurrenciesStore: AvailableAppCurrenciesStore,
private val selectedAppCurrencyStore: SelectedAppCurrencyStore,
private val cacheRegistry: CacheRegistry,
private val dispatchers: CoroutineDispatcherProvider,
) : AppCurrencyRepository {
private val appCurrencyConverter = AppCurrencyConverter()
override fun getSelectedAppCurrency(): Flow<AppCurrency> {
return selectedAppCurrencyStore.get()
.onEmpty { fetchDefaultAppCurrency() }
.map(appCurrencyConverter::convert)
.flowOn(dispatchers.io)
}
override suspend fun getAvailableAppCurrencies(): List<AppCurrency> {
return withContext(dispatchers.io) {
fetchAvailableCurrenciesIfExpired()
val currencies = availableAppCurrenciesStore.getAllSyncOrNull()
?.map(appCurrencyConverter::convert)
requireNotNull(currencies) {
"No available currencies stored"
}
}
}
override suspend fun changeAppCurrency(currencyCode: String) {
withContext(dispatchers.io) {
val currency = requireNotNull(availableAppCurrenciesStore.getSyncOrNull(currencyCode)) {
"Unable to find app currency with provided code: $currencyCode"
}
selectedAppCurrencyStore.store(currency)
}
}
private suspend fun fetchDefaultAppCurrency() {
fetchAvailableCurrenciesIfExpired()
changeAppCurrency(DEFAULT_CURRENCY_CODE)
}
private suspend fun fetchAvailableCurrenciesIfExpired() {
cacheRegistry.invokeOnExpire(
key = AVAILABLE_CURRENCIES_CACHE_KEY,
skipCache = false,
expireIn = Duration.standardMinutes(AVAILABLE_CURRENCIES_CACHE_KEY_EXPIRE_MINUTES),
block = { fetchAvailableCurrencies() },
)
}
private suspend fun fetchAvailableCurrencies() {
try {
val response = tangemTechApi.getCurrencyList()
availableAppCurrenciesStore.store(response)
} catch (e: Throwable) {
Timber.e(e, "Unable to fetch available currencies")
availableAppCurrenciesStore.store(getDefaultCurrenciesResponse())
}
}
private fun getDefaultCurrenciesResponse(): CurrenciesResponse = CurrenciesResponse(
currencies = listOf(
CurrenciesResponse.Currency(
id = DEFAULT_CURRENCY_CODE.lowercase(),
code = DEFAULT_CURRENCY_CODE,
name = "US Dollar",
unit = "$",
type = "fiat",
rateBTC = "",
),
),
)
private companion object {
const val AVAILABLE_CURRENCIES_CACHE_KEY = "available_currencies"
const val AVAILABLE_CURRENCIES_CACHE_KEY_EXPIRE_MINUTES = 15L
const val DEFAULT_CURRENCY_CODE = "USD"
}
}

View file

@ -1,23 +0,0 @@
package com.tangem.data.appcurrency
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.appcurrency.repository.AppCurrencyRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
internal class MockAppCurrencyRepository : AppCurrencyRepository {
private val mockAppCurrencies = listOf(AppCurrency(code = "USD", name = "US Dollar", symbol = "$"))
override fun getSelectedAppCurrency(): Flow<AppCurrency> {
return flowOf(mockAppCurrencies.first())
}
override suspend fun getAvailableAppCurrencies(): List<AppCurrency> {
return mockAppCurrencies
}
override suspend fun changeAppCurrency(currencyCode: String) {
/* no-op */
}
}

View file

@ -1,7 +1,12 @@
package com.tangem.data.appcurrency.di
import com.tangem.data.appcurrency.MockAppCurrencyRepository
import com.tangem.data.appcurrency.DefaultAppCurrencyRepository
import com.tangem.data.common.cache.CacheRegistry
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.local.appcurrency.AvailableAppCurrenciesStore
import com.tangem.datasource.local.appcurrency.SelectedAppCurrencyStore
import com.tangem.domain.appcurrency.repository.AppCurrencyRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -14,7 +19,19 @@ internal object AppCurrencyDataModule {
@Provides
@Singleton
fun provideAppCurrencyRepository(): AppCurrencyRepository {
return MockAppCurrencyRepository()
fun provideAppCurrencyRepository(
tangemTechApi: TangemTechApi,
availableAppCurrenciesStore: AvailableAppCurrenciesStore,
selectedAppCurrencyStore: SelectedAppCurrencyStore,
cacheRegistry: CacheRegistry,
dispatchers: CoroutineDispatcherProvider,
): AppCurrencyRepository {
return DefaultAppCurrencyRepository(
tangemTechApi,
availableAppCurrenciesStore,
selectedAppCurrencyStore,
cacheRegistry,
dispatchers,
)
}
}

View file

@ -0,0 +1,16 @@
package com.tangem.data.appcurrency.utils
import com.tangem.datasource.api.tangemTech.models.CurrenciesResponse
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.utils.converter.Converter
internal class AppCurrencyConverter : Converter<CurrenciesResponse.Currency, AppCurrency> {
override fun convert(value: CurrenciesResponse.Currency): AppCurrency {
return AppCurrency(
code = value.code,
name = value.name,
symbol = value.unit,
)
}
}