Updated on 2026-08-14

This commit is contained in:
Tangem 2024-03-01 14:13:25 +05:00
commit a25b99f933
10 changed files with 74 additions and 8 deletions

View file

@ -1,5 +1,6 @@
package com.tangem.tap.di.domain
import com.tangem.domain.appcurrency.FetchAppCurrenciesUseCase
import com.tangem.domain.appcurrency.GetAvailableCurrenciesUseCase
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.SelectAppCurrencyUseCase
@ -8,6 +9,7 @@ import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ViewModelComponent
import dagger.hilt.android.scopes.ViewModelScoped
@Module
@InstallIn(ViewModelComponent::class)
@ -31,4 +33,10 @@ internal object AppCurrencyDomainModule {
): GetAvailableCurrenciesUseCase {
return GetAvailableCurrenciesUseCase(appCurrencyRepository)
}
@Provides
@ViewModelScoped
fun provideFetchAppCurrenciesUseCase(appCurrencyRepository: AppCurrencyRepository): FetchAppCurrenciesUseCase {
return FetchAppCurrenciesUseCase(appCurrencyRepository)
}
}

View file

@ -5,11 +5,13 @@ import androidx.lifecycle.viewModelScope
import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
import com.tangem.core.navigation.ReduxNavController
import com.tangem.domain.appcurrency.FetchAppCurrenciesUseCase
import com.tangem.domain.balancehiding.BalanceHidingSettings
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.balancehiding.ListenToFlipsUseCase
import com.tangem.domain.balancehiding.UpdateBalanceHidingSettingsUseCase
import com.tangem.tap.features.main.model.MainScreenState
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
@ -20,6 +22,8 @@ internal class MainViewModel @Inject constructor(
private val updateBalanceHidingSettingsUseCase: UpdateBalanceHidingSettingsUseCase,
private val listenToFlipsUseCase: ListenToFlipsUseCase,
private val reduxNavController: ReduxNavController,
private val fetchAppCurrenciesUseCase: FetchAppCurrenciesUseCase,
private val dispatchers: CoroutineDispatcherProvider,
getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
) : ViewModel(), MainIntents {
@ -33,11 +37,18 @@ internal class MainViewModel @Inject constructor(
val state: StateFlow<MainScreenState> = stateHolder.stateFlow
init {
updateAppCurrencies()
observeFlips()
displayBalancesHidingStatusToast()
displayHiddenBalancesModalNotification()
}
private fun updateAppCurrencies() {
viewModelScope.launch(dispatchers.main) {
fetchAppCurrenciesUseCase.invoke()
}
}
private fun observeFlips() {
listenToFlipsUseCase().launchIn(viewModelScope)
}

View file

@ -1,11 +1,15 @@
package com.tangem.datasource.api.tangemTech.models
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class CurrenciesResponse(
@Json(name = "currencies") val currencies: List<Currency>,
@Json(name = "imageHost") val imageHost: String? = null,
) {
@JsonClass(generateAdapter = true)
data class Currency(
@Json(name = "id") val id: String,
@Json(name = "code") val code: String, // this is an uppercase id
@ -13,5 +17,7 @@ data class CurrenciesResponse(
@Json(name = "rateBTC") val rateBTC: String,
@Json(name = "unit") val unit: String, // $, €, ₽
@Json(name = "type") val type: String,
val iconSmallUrl: String? = null,
val iconMediumUrl: String? = null,
)
}

View file

@ -15,8 +15,20 @@ internal class DefaultAvailableAppCurrenciesStore(
}
override suspend fun store(response: CurrenciesResponse) {
val currencies = response.currencies.associateBy(CurrenciesResponse.Currency::code)
val currencies = response.currencies
.map {
it.copy(
iconSmallUrl = response.imageHost?.plus(IMAGE_SMALL)?.format(it.id),
iconMediumUrl = response.imageHost?.plus(IMAGE_MEDIUM)?.format(it.id),
)
}
.associateBy(CurrenciesResponse.Currency::code)
dataStore.store(currencies)
}
private companion object {
const val IMAGE_MEDIUM = "medium/%s.png"
const val IMAGE_SMALL = "small/%s.png"
}
}

View file

@ -77,16 +77,18 @@ internal class DefaultAppCurrencyRepository(
}
}
private suspend fun fetchDefaultAppCurrency() {
fetchAvailableCurrenciesIfExpired()
changeAppCurrency(DEFAULT_CURRENCY_CODE)
override suspend fun fetchDefaultAppCurrency(isRefresh: Boolean) {
withContext(dispatchers.io) {
fetchAvailableCurrenciesIfExpired(isRefresh)
val appCurrency = appPreferencesStore.getSyncOrNull(PreferencesKeys.SELECTED_APP_CURRENCY_KEY)
changeAppCurrency(appCurrency ?: DEFAULT_CURRENCY_CODE)
}
}
private suspend fun fetchAvailableCurrenciesIfExpired() {
private suspend fun fetchAvailableCurrenciesIfExpired(isRefresh: Boolean = false) {
cacheRegistry.invokeOnExpire(
key = AVAILABLE_CURRENCIES_CACHE_KEY,
skipCache = false,
skipCache = isRefresh,
expireIn = Duration.standardMinutes(AVAILABLE_CURRENCIES_CACHE_KEY_EXPIRE_MINUTES),
block = { fetchAvailableCurrencies() },
)

View file

@ -11,6 +11,8 @@ internal class AppCurrencyConverter : Converter<CurrenciesResponse.Currency, App
code = value.code,
name = value.name,
symbol = value.unit,
iconSmallUrl = value.iconSmallUrl,
iconMediumUrl = value.iconMediumUrl,
)
}
}

View file

@ -4,6 +4,8 @@ data class AppCurrency(
val code: String,
val name: String,
val symbol: String,
val iconSmallUrl: String? = null,
val iconMediumUrl: String? = null,
) {
companion object {

View file

@ -0,0 +1,18 @@
package com.tangem.domain.appcurrency
import arrow.core.Either
import arrow.core.raise.catch
import arrow.core.raise.either
import com.tangem.domain.appcurrency.error.SelectedAppCurrencyError
import com.tangem.domain.appcurrency.repository.AppCurrencyRepository
class FetchAppCurrenciesUseCase(
private val appCurrencyRepository: AppCurrencyRepository,
) {
suspend operator fun invoke(): Either<Throwable, Unit> = either {
catch(
block = { appCurrencyRepository.fetchDefaultAppCurrency(isRefresh = true) },
catch = { SelectedAppCurrencyError.DataError(it) },
)
}
}

View file

@ -5,9 +5,14 @@ import kotlinx.coroutines.flow.Flow
interface AppCurrencyRepository {
/**
* Get Selected App Currency from cache or load from network.
*/
fun getSelectedAppCurrency(): Flow<AppCurrency>
suspend fun getAvailableAppCurrencies(): List<AppCurrency>
suspend fun changeAppCurrency(currencyCode: String)
suspend fun fetchDefaultAppCurrency(isRefresh: Boolean = false)
}

View file

@ -45,7 +45,7 @@ internal class SendAmountStateConverter(
),
SendAmountSegmentedButtonsConfig(
title = stringReference(appCurrency.code),
iconState = iconStateConverter.convert(status),
iconUrl = appCurrency.iconSmallUrl,
isFiat = true,
),
),