diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/MarketModule.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/MarketModule.kt index 7948814724..64a059f227 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/MarketModule.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/MarketModule.kt @@ -14,11 +14,11 @@ import io.horizontalsystems.bankwallet.core.App import io.horizontalsystems.bankwallet.entities.Currency import io.horizontalsystems.bankwallet.entities.CurrencyValue import io.horizontalsystems.bankwallet.modules.market.filters.TimePeriod -import io.horizontalsystems.bankwallet.modules.metricchart.MetricsType import io.horizontalsystems.bankwallet.modules.settings.appearance.PriceChangeInterval import io.horizontalsystems.bankwallet.ui.compose.TranslatableString import io.horizontalsystems.bankwallet.ui.compose.WithTranslatableTitle import io.horizontalsystems.marketkit.models.FullCoin +import io.horizontalsystems.marketkit.models.MarketGlobal import io.horizontalsystems.marketkit.models.MarketInfo import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.Parcelize @@ -42,15 +42,8 @@ object MarketModule { data class UiState( val selectedTab: Tab, - val marketOverviewItems: List - ) - - data class MarketOverviewViewItem( - val title: String, - val value: String?, - val change: String?, - val changePositive: Boolean?, - val metricsType: MetricsType, + val marketGlobal: MarketGlobal?, + val currency: Currency ) enum class Tab(@StringRes val titleResId: Int) { diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/MarketScreen.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/MarketScreen.kt index d823bc7f05..947dc12aca 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/MarketScreen.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/MarketScreen.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.IntrinsicSize import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -28,6 +29,7 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController import io.horizontalsystems.bankwallet.R +import io.horizontalsystems.bankwallet.core.App import io.horizontalsystems.bankwallet.core.slideFromBottom import io.horizontalsystems.bankwallet.core.slideFromRight import io.horizontalsystems.bankwallet.core.stats.StatEvent @@ -36,6 +38,7 @@ import io.horizontalsystems.bankwallet.core.stats.StatSection import io.horizontalsystems.bankwallet.core.stats.stat import io.horizontalsystems.bankwallet.core.stats.statPage import io.horizontalsystems.bankwallet.core.stats.statTab +import io.horizontalsystems.bankwallet.entities.Currency import io.horizontalsystems.bankwallet.modules.coin.CoinFragment import io.horizontalsystems.bankwallet.modules.market.MarketModule.Tab import io.horizontalsystems.bankwallet.modules.market.favorites.MarketFavoritesScreen @@ -56,6 +59,8 @@ import io.horizontalsystems.bankwallet.ui.compose.components.caption_grey import io.horizontalsystems.bankwallet.ui.compose.components.caption_lucian import io.horizontalsystems.bankwallet.ui.compose.components.caption_remus import io.horizontalsystems.bankwallet.ui.compose.components.micro_grey +import io.horizontalsystems.marketkit.models.MarketGlobal +import java.math.BigDecimal @Composable fun MarketScreen(navController: NavController) { @@ -103,8 +108,8 @@ fun MarketScreen(navController: NavController) { .padding(it) .background(ComposeAppTheme.colors.tyler) ) { - Crossfade(uiState.marketOverviewItems) { - MetricsBoard(navController, it) + Crossfade(uiState.marketGlobal, label = "") { + MetricsBoard(navController, it, uiState.currency) } Divider( color = ComposeAppTheme.colors.steel10, @@ -168,11 +173,22 @@ fun TabsSection( } } +private fun formatFiatShortened(value: BigDecimal, symbol: String): String { + return App.numberFormatter.formatFiatShort(value, symbol, 2) +} + +private fun getDiff(it: BigDecimal): String { + val sign = if (it >= BigDecimal.ZERO) "+" else "-" + return App.numberFormatter.format(it.abs(), 0, 2, sign, "%") +} + + @OptIn(ExperimentalFoundationApi::class) @Composable fun MetricsBoard( navController: NavController, - marketOverviewItems: List + marketGlobal: MarketGlobal?, + currency: Currency ) { Row( modifier = Modifier @@ -182,55 +198,110 @@ fun MetricsBoard( .clip(RoundedCornerShape(12.dp)) .background(ComposeAppTheme.colors.lawrence) ) { - marketOverviewItems.forEachIndexed { index, item -> - if (index != 0) { - Box( - Modifier - .fillMaxHeight() - .width(1.dp) - .background(color = ComposeAppTheme.colors.steel10) - ) + MarketTotalCard( + title = stringResource(R.string.MarketGlobalMetrics_TotalMarketCap), + value = marketGlobal?.marketCap, + change = marketGlobal?.marketCapChange, + currency = currency, + onClick = { + openMetricsPage(MetricsType.TotalMarketCap, navController) } - Column( - modifier = Modifier - .clickable { - openMetricsPage(item.metricsType, navController) - } - .padding(12.dp) - .weight(1f) - ) { - micro_grey( - text = item.title, - overflow = TextOverflow.Ellipsis, - maxLines = 1 - ) - VSpacer(4.dp) - caption_bran( - text = item.value ?: "---", - overflow = TextOverflow.Ellipsis, - maxLines = 1 - ) - VSpacer(4.dp) - if (item.changePositive == null) { - caption_grey( - text = item.change ?: "---", - overflow = TextOverflow.Ellipsis, - maxLines = 1 - ) - } else if (item.changePositive) { - caption_remus( - text = item.change ?: "---", - overflow = TextOverflow.Ellipsis, - maxLines = 1 - ) - } else { - caption_lucian( - text = item.change ?: "---", - overflow = TextOverflow.Ellipsis, - maxLines = 1 - ) - } + ) + + VDivider() + + MarketTotalCard( + title = stringResource(R.string.MarketGlobalMetrics_Volume), + value = marketGlobal?.volume, + change = marketGlobal?.volumeChange, + currency = currency, + onClick = { + openMetricsPage(MetricsType.Volume24h, navController) } + ) + + VDivider() + + MarketTotalCard( + title = stringResource(R.string.MarketGlobalMetrics_TvlInDefi), + value = marketGlobal?.tvl, + change = marketGlobal?.tvlChange, + currency = currency, + onClick = { + openMetricsPage(MetricsType.TvlInDefi, navController) + } + ) + + VDivider() + + MarketTotalCard( + title = stringResource(R.string.MarketGlobalMetrics_EtfInflow), + value = marketGlobal?.etfTotalInflow, + change = marketGlobal?.etfDailyInflow, + currency = currency, + onClick = { + openMetricsPage(MetricsType.Etf, navController) + } + ) + } +} + +@Composable +private fun VDivider() { + Box( + Modifier + .fillMaxHeight() + .width(1.dp) + .background(color = ComposeAppTheme.colors.steel10) + ) +} + +@Composable +private fun RowScope.MarketTotalCard( + title: String, + value: BigDecimal?, + change: BigDecimal?, + currency: Currency, + onClick: () -> Unit, +) { + val changeStr = change?.let { getDiff(it) } + val changePositive = change?.let { it > BigDecimal.ZERO } + Column( + modifier = Modifier + .weight(1f) + .padding(12.dp) + .clickable(onClick = onClick) + ) { + micro_grey( + text = title, + overflow = TextOverflow.Ellipsis, + maxLines = 1 + ) + VSpacer(4.dp) + caption_bran( + text = value?.let { formatFiatShortened(it, currency.symbol) } ?: "---", + overflow = TextOverflow.Ellipsis, + maxLines = 1 + ) + VSpacer(4.dp) + if (changePositive == null) { + caption_grey( + text = changeStr ?: "---", + overflow = TextOverflow.Ellipsis, + maxLines = 1 + ) + } else if (changePositive) { + caption_remus( + text = changeStr ?: "---", + overflow = TextOverflow.Ellipsis, + maxLines = 1 + ) + } else { + caption_lucian( + text = changeStr ?: "---", + overflow = TextOverflow.Ellipsis, + maxLines = 1 + ) } } } diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/MarketViewModel.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/MarketViewModel.kt index e646931dea..bfa7750e9d 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/MarketViewModel.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/MarketViewModel.kt @@ -2,25 +2,17 @@ package io.horizontalsystems.bankwallet.modules.market import android.util.Log import androidx.lifecycle.viewModelScope -import io.horizontalsystems.bankwallet.R -import io.horizontalsystems.bankwallet.core.App import io.horizontalsystems.bankwallet.core.ILocalStorage import io.horizontalsystems.bankwallet.core.IMarketStorage import io.horizontalsystems.bankwallet.core.ViewModelUiState import io.horizontalsystems.bankwallet.core.managers.CurrencyManager import io.horizontalsystems.bankwallet.core.managers.MarketKitWrapper -import io.horizontalsystems.bankwallet.core.providers.Translator -import io.horizontalsystems.bankwallet.entities.Currency import io.horizontalsystems.bankwallet.entities.LaunchPage -import io.horizontalsystems.bankwallet.modules.market.MarketModule.MarketOverviewViewItem import io.horizontalsystems.bankwallet.modules.market.MarketModule.Tab -import io.horizontalsystems.bankwallet.modules.metricchart.MetricsType import io.horizontalsystems.marketkit.models.MarketGlobal import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job import kotlinx.coroutines.launch import kotlinx.coroutines.rx2.await -import java.math.BigDecimal class MarketViewModel( private val marketStorage: IMarketStorage, @@ -30,34 +22,22 @@ class MarketViewModel( ) : ViewModelUiState() { val tabs = Tab.entries.toTypedArray() - private var marketOverviewJob: Job? = null - private var marketOverviewItems = getMarketMetrics(null, currencyManager.baseCurrency) + private var currency = currencyManager.baseCurrency + + private var marketGlobal: MarketGlobal? = null private var selectedTab: Tab = getInitialTab(localStorage.launchPage) init { - updateMarketOverview() - viewModelScope.launch { currencyManager.baseCurrencyUpdatedFlow.collect { - updateMarketOverview() + currency = currencyManager.baseCurrency + emitState() } } - } - override fun createState(): MarketModule.UiState { - return MarketModule.UiState( - selectedTab, - marketOverviewItems - ) - } - - private fun updateMarketOverview() { - marketOverviewJob?.cancel() - marketOverviewJob = viewModelScope.launch(Dispatchers.IO) { + viewModelScope.launch(Dispatchers.IO) { try { - val marketGlobal = - marketKit.marketGlobalSingle(currencyManager.baseCurrency.code).await() - marketOverviewItems = getMarketMetrics(marketGlobal, currencyManager.baseCurrency) + marketGlobal = marketKit.marketGlobalSingle(currency.code).await() emitState() } catch (e: Throwable) { Log.e("TAG", "updateMarketOverview: ", e) @@ -65,66 +45,18 @@ class MarketViewModel( } } + override fun createState() = MarketModule.UiState( + selectedTab = selectedTab, + marketGlobal = marketGlobal, + currency = currency + ) + fun onSelect(tab: Tab) { selectedTab = tab marketStorage.currentMarketTab = tab emitState() } - private fun getMarketMetrics( - globalMarket: MarketGlobal?, - baseCurrency: Currency, - ): List { - return listOf( - MarketOverviewViewItem( - Translator.getString(R.string.MarketGlobalMetrics_TotalMarketCap), - globalMarket?.marketCap?.let { formatFiatShortened(it, baseCurrency.symbol) }, - globalMarket?.marketCapChange?.let { getDiff(it) }, - globalMarket?.marketCapChange?.let { it > BigDecimal.ZERO }, - MetricsType.TotalMarketCap - ), - MarketOverviewViewItem( - Translator.getString(R.string.MarketGlobalMetrics_Volume), - globalMarket?.volume?.let { formatFiatShortened(it, baseCurrency.symbol) }, - globalMarket?.volumeChange?.let { getDiff(it) }, - globalMarket?.volumeChange?.let { it > BigDecimal.ZERO }, - MetricsType.Volume24h - ), - MarketOverviewViewItem( - Translator.getString(R.string.MarketGlobalMetrics_TvlInDefi), - globalMarket?.tvl?.let { formatFiatShortened(it, baseCurrency.symbol) }, - globalMarket?.tvlChange?.let { getDiff(it) }, - globalMarket?.tvlChange?.let { it > BigDecimal.ZERO }, - MetricsType.TvlInDefi - ), - MarketOverviewViewItem( - Translator.getString(R.string.MarketGlobalMetrics_EtfInflow), - globalMarket?.etfTotalInflow?.let { formatFiatShortened(it, baseCurrency.symbol) }, - globalMarket?.etfDailyInflow?.let { - val sign = if (it >= BigDecimal.ZERO) "+" else "-" - "$sign${formatFiatShortened(it.abs(), baseCurrency.symbol)}" - }, - globalMarket?.etfDailyInflow?.let { it > BigDecimal.ZERO }, - MetricsType.Etf - ) - ) - } - - private fun getDiff(it: BigDecimal): String { - val sign = if (it >= BigDecimal.ZERO) "+" else "-" - return App.numberFormatter.format(it.abs(), 0, 2, sign, "%") - } - - private fun formatFiatShortened(value: BigDecimal, symbol: String): String { - return App.numberFormatter.formatFiatShort(value, symbol, 2) - } - - private fun diff(sourceValue: BigDecimal, targetValue: BigDecimal): BigDecimal = - if (sourceValue.compareTo(BigDecimal.ZERO) != 0) - ((targetValue - sourceValue) * BigDecimal(100)) / sourceValue - else BigDecimal.ZERO - - private fun getInitialTab(launchPage: LaunchPage?) = when (launchPage) { LaunchPage.Watchlist -> Tab.Watchlist else -> marketStorage.currentMarketTab ?: Tab.Coins