From e6bd0754ada3c20b4a9e228eb640bf083f8dbad5 Mon Sep 17 00:00:00 2001 From: Rafael Date: Wed, 22 May 2024 11:58:24 +0600 Subject: [PATCH] Remove old classes in Market module --- .../bankwallet/modules/main/MainFragment.kt | 4 +- .../bankwallet/modules/market/MarketModule.kt | 22 +- .../bankwallet/modules/market/MarketScreen.kt | 229 ++++++++++++++---- .../modules/market/MarketService.kt | 20 -- .../modules/market/MarketViewModel.kt | 160 ++++++++++-- .../market/overviewxxx/MarketModuleXxx.kt | 38 --- .../market/overviewxxx/MarketScreenXxx.kt | 210 ---------------- .../market/overviewxxx/MarketViewModelXxx.kt | 152 ------------ 8 files changed, 343 insertions(+), 492 deletions(-) delete mode 100644 app/src/main/java/io/horizontalsystems/bankwallet/modules/market/MarketService.kt delete mode 100644 app/src/main/java/io/horizontalsystems/bankwallet/modules/market/overviewxxx/MarketModuleXxx.kt delete mode 100644 app/src/main/java/io/horizontalsystems/bankwallet/modules/market/overviewxxx/MarketScreenXxx.kt delete mode 100644 app/src/main/java/io/horizontalsystems/bankwallet/modules/market/overviewxxx/MarketViewModelXxx.kt diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/main/MainFragment.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/main/MainFragment.kt index 14d19241244..e08ee891513 100644 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/main/MainFragment.kt +++ b/app/src/main/java/io/horizontalsystems/bankwallet/modules/main/MainFragment.kt @@ -51,7 +51,7 @@ import io.horizontalsystems.bankwallet.core.stats.statTab import io.horizontalsystems.bankwallet.modules.balance.ui.BalanceScreen import io.horizontalsystems.bankwallet.modules.main.MainModule.MainNavigation import io.horizontalsystems.bankwallet.modules.manageaccount.dialogs.BackupRequiredDialog -import io.horizontalsystems.bankwallet.modules.market.overviewxxx.MarketScreenXxx +import io.horizontalsystems.bankwallet.modules.market.MarketScreen import io.horizontalsystems.bankwallet.modules.rateapp.RateApp import io.horizontalsystems.bankwallet.modules.releasenotes.ReleaseNotesFragment import io.horizontalsystems.bankwallet.modules.rooteddevice.RootedDeviceModule @@ -217,7 +217,7 @@ private fun MainScreen( verticalAlignment = Alignment.Top ) { page -> when (uiState.mainNavItems[page].mainNavItem) { - MainNavigation.Market -> MarketScreenXxx(fragmentNavController) + MainNavigation.Market -> MarketScreen(fragmentNavController) MainNavigation.Balance -> BalanceScreen(fragmentNavController) MainNavigation.Transactions -> TransactionsScreen( fragmentNavController, 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 53589ceb353..5cd421306ed 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,6 +14,7 @@ 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.ui.compose.TranslatableString import io.horizontalsystems.bankwallet.ui.compose.WithTranslatableTitle import io.horizontalsystems.marketkit.models.FullCoin @@ -28,12 +29,29 @@ object MarketModule { @Suppress("UNCHECKED_CAST") override fun create(modelClass: Class): T { - val service = MarketService(App.marketStorage, App.localStorage) - return MarketViewModel(service) as T + return MarketViewModel( + App.marketStorage, + App.marketKit, + App.currencyManager, + App.localStorage + ) as T } } + 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, + ) + enum class Tab(@StringRes val titleResId: Int) { Coins(R.string.Market_Tab_Coins), Watchlist(R.string.Market_Tab_Watchlist), 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 0172cd2064d..895b694525e 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 @@ -2,82 +2,209 @@ package io.horizontalsystems.bankwallet.modules.market import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.foundation.rememberScrollState +import androidx.compose.material.Divider +import androidx.compose.material.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +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.slideFromBottom import io.horizontalsystems.bankwallet.core.slideFromRight import io.horizontalsystems.bankwallet.core.stats.StatEvent import io.horizontalsystems.bankwallet.core.stats.StatPage +import io.horizontalsystems.bankwallet.core.stats.StatSection import io.horizontalsystems.bankwallet.core.stats.stat -import io.horizontalsystems.bankwallet.core.stats.statTab +import io.horizontalsystems.bankwallet.core.stats.statPage +import io.horizontalsystems.bankwallet.modules.coin.CoinFragment +import io.horizontalsystems.bankwallet.modules.market.MarketModule.Tab +import io.horizontalsystems.bankwallet.modules.market.favorites.MarketFavoritesScreen +import io.horizontalsystems.bankwallet.modules.market.posts.MarketPostsScreen +import io.horizontalsystems.bankwallet.modules.market.topcoins.TopCoins +import io.horizontalsystems.bankwallet.modules.market.topplatforms.TopPlatforms +import io.horizontalsystems.bankwallet.modules.metricchart.MetricsType import io.horizontalsystems.bankwallet.ui.compose.ComposeAppTheme import io.horizontalsystems.bankwallet.ui.compose.TranslatableString import io.horizontalsystems.bankwallet.ui.compose.components.AppBar +import io.horizontalsystems.bankwallet.ui.compose.components.HSpacer import io.horizontalsystems.bankwallet.ui.compose.components.MenuItem +import io.horizontalsystems.bankwallet.ui.compose.components.ScrollableTabs import io.horizontalsystems.bankwallet.ui.compose.components.TabItem -import io.horizontalsystems.bankwallet.ui.compose.components.Tabs +import io.horizontalsystems.bankwallet.ui.compose.components.caption_bran +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 -@OptIn(ExperimentalFoundationApi::class) @Composable fun MarketScreen(navController: NavController) { - val marketViewModel = viewModel(factory = MarketModule.Factory()) - val tabs = marketViewModel.tabs - val selectedTab = marketViewModel.selectedTab + val viewModel = viewModel(factory = MarketModule.Factory()) + val uiState = viewModel.uiState + val tabs = viewModel.tabs - val pagerState = rememberPagerState(initialPage = selectedTab.ordinal) { tabs.size } + Scaffold( + backgroundColor = ComposeAppTheme.colors.tyler, + topBar = { + AppBar( + title = stringResource(R.string.Market_Title), + menuItems = listOf( + MenuItem( + title = TranslatableString.ResString(R.string.Market_Search), + icon = R.drawable.icon_search, + tint = ComposeAppTheme.colors.jacob, + onClick = { + navController.slideFromRight(R.id.marketSearchFragment) - Column(modifier = Modifier.background(color = ComposeAppTheme.colors.tyler)) { - AppBar( - title = stringResource(R.string.Market_Title), - menuItems = listOf( - MenuItem( - title = TranslatableString.ResString(R.string.Market_Search), - icon = R.drawable.icon_search, - onClick = { - navController.slideFromRight(R.id.marketSearchFragment) - - stat(page = StatPage.Markets, event = StatEvent.Open(StatPage.MarketSearch)) - } - ), - MenuItem( - title = TranslatableString.ResString(R.string.Market_Filters), - icon = R.drawable.ic_manage_2_24, - onClick = { - navController.slideFromRight(R.id.marketAdvancedSearchFragment) - - stat(page = StatPage.Markets, event = StatEvent.Open(StatPage.AdvancedSearch)) - } - ), + stat( + page = StatPage.Markets, + event = StatEvent.Open(StatPage.MarketSearch) + ) + }, + ), + MenuItem( + title = TranslatableString.ResString(R.string.Market_Filters), + icon = R.drawable.ic_manage_2_24, + onClick = { + navController.slideFromRight(R.id.marketAdvancedSearchFragment) + + stat( + page = StatPage.Markets, + event = StatEvent.Open(StatPage.AdvancedSearch) + ) + }, + ), + ) ) - ) + } + ) { + Column( + Modifier + .padding(it) + .background(ComposeAppTheme.colors.tyler) + ) { + MetricsBoard(navController, uiState.marketOverviewItems) + Divider( + color = ComposeAppTheme.colors.steel10, + thickness = 1.dp + ) + TabsSection(navController, tabs, uiState.selectedTab) { tab -> + viewModel.onSelect(tab) + } + } + } +} + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun TabsSection( + navController: NavController, + tabs: Array, + selectedTab: Tab, + onTabClick: (Tab) -> Unit +) { + val pagerState = rememberPagerState(initialPage = selectedTab.ordinal) { tabs.size } + + LaunchedEffect(key1 = selectedTab, block = { + pagerState.scrollToPage(selectedTab.ordinal) + }) + val tabItems = tabs.map { + TabItem(stringResource(id = it.titleResId), it == selectedTab, it) + } + + ScrollableTabs(tabItems) { + onTabClick(it) + } + + HorizontalPager( + state = pagerState, + userScrollEnabled = false + ) { page -> + when (tabs[page]) { + Tab.Coins -> { + TopCoins(onCoinClick = { onCoinClick(it, navController) }) + stat(page = StatPage.MarketOverview, section = StatSection.TopGainers, event = StatEvent.Open(StatPage.TopCoins)) + } + + Tab.Watchlist -> MarketFavoritesScreen(navController) + Tab.Posts -> MarketPostsScreen() + Tab.Platform -> { + TopPlatforms(navController) + stat(page = StatPage.MarketOverview, event = StatEvent.Open(StatPage.TopPlatforms)) + } + Tab.Pairs -> { + } - LaunchedEffect(key1 = selectedTab, block = { - pagerState.scrollToPage(selectedTab.ordinal) - }) - val tabItems = tabs.map { - TabItem(stringResource(id = it.titleResId), it == selectedTab, it) + Tab.Sectors -> { + } + } + } +} + +@Composable +fun MetricsBoard( + navController: NavController, + marketOverviewItems: List +) { + Row( + modifier = Modifier + .fillMaxWidth() + .height(40.dp) + .background(ComposeAppTheme.colors.tyler) + .horizontalScroll(rememberScrollState()), + verticalAlignment = androidx.compose.ui.Alignment.CenterVertically + ) { + HSpacer(4.dp) + marketOverviewItems.forEach { item -> + Row( + modifier = Modifier + .clickable { + openMetricsPage(item.metricsType, navController) + } + .padding(8.dp) + ) { + HSpacer(12.dp) + caption_grey(text = item.title) + HSpacer(4.dp) + caption_bran(text = item.value) + HSpacer(4.dp) + if (item.changePositive) { + caption_remus(text = item.change) + } else { + caption_lucian(text = item.change) + } + HSpacer(12.dp) + } } - Tabs(tabItems, onClick = { - marketViewModel.onSelect(it) - - stat(page = StatPage.Markets, event = StatEvent.SwitchTab(it.statTab)) - }) - -// HorizontalPager( -// state = pagerState, -// userScrollEnabled = false -// ) { page -> -// when (tabs[page]) { -// MarketModule.Tab.Overview -> MarketOverviewScreen(navController) -// MarketModule.Tab.Posts -> MarketPostsScreen() -// MarketModule.Tab.Watchlist -> MarketFavoritesScreen(navController) -// } -// } + HSpacer(4.dp) } } + +private fun openMetricsPage(metricsType: MetricsType, navController: NavController) { + if (metricsType == MetricsType.TvlInDefi) { + navController.slideFromBottom(R.id.tvlFragment) + } else { + navController.slideFromBottom(R.id.metricsPageFragment, metricsType) + } + + stat(page = StatPage.MarketOverview, event = StatEvent.Open(metricsType.statPage)) +} + +private fun onCoinClick(coinUid: String, navController: NavController) { + val arguments = CoinFragment.Input(coinUid) + + navController.slideFromRight(R.id.coinFragment, arguments) + + stat(page = StatPage.TopCoins, event = StatEvent.OpenCoin(coinUid)) +} diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/MarketService.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/MarketService.kt deleted file mode 100644 index aa77fc4b9e2..00000000000 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/MarketService.kt +++ /dev/null @@ -1,20 +0,0 @@ -package io.horizontalsystems.bankwallet.modules.market - -import io.horizontalsystems.bankwallet.core.ILocalStorage -import io.horizontalsystems.bankwallet.core.IMarketStorage -import io.horizontalsystems.bankwallet.entities.LaunchPage - -class MarketService( - private val storage: IMarketStorage, - private val localStorage: ILocalStorage, -) { - val launchPage: LaunchPage? - get() = localStorage.launchPage - - var currentTab: MarketModule.Tab? - get() = storage.currentMarketTab - set(value) { - storage.currentMarketTab = value - } - -} 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 44126e0c6d8..d9bdf89b1b8 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 @@ -1,25 +1,151 @@ package io.horizontalsystems.bankwallet.modules.market -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue -import androidx.lifecycle.ViewModel +import android.util.Log +import androidx.lifecycle.viewModelScope +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.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.GlobalMarketPoint +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 service: MarketService) : ViewModel() { +class MarketViewModel( + private val marketStorage: IMarketStorage, + private val marketKit: MarketKitWrapper, + private val currencyManager: CurrencyManager, + localStorage: ILocalStorage +) : ViewModelUiState() { - val tabs = MarketModule.Tab.values() - var selectedTab by mutableStateOf(getInitialTab()) - private set + val tabs = Tab.entries.toTypedArray() + private var marketOverviewJob: Job? = null + private var marketOverviewItems: List = listOf() + private var selectedTab: Tab = getInitialTab(localStorage.launchPage) - fun onSelect(tab: MarketModule.Tab) { - service.currentTab = tab + init { + updateMarketOverview() + } + + override fun createState(): MarketModule.UiState { + return MarketModule.UiState( + selectedTab, + marketOverviewItems + ) + } + + private fun updateMarketOverview() { + marketOverviewJob?.cancel() + marketOverviewJob = viewModelScope.launch(Dispatchers.IO) { + try { + val marketOverview = + marketKit.marketOverviewSingle(currencyManager.baseCurrency.code).await() + marketOverview?.globalMarketPoints?.let { + marketOverviewItems = getMarketMetrics(it, currencyManager.baseCurrency) + emitState() + } + } catch (e: Throwable) { + Log.e("TAG", "updateMarketOverview: ", e) + } + } + } + + fun onSelect(tab: Tab) { selectedTab = tab + marketStorage.currentMarketTab = tab + emitState() + } + + private fun getMarketMetrics( + globalMarketPoints: List, + baseCurrency: Currency + ): List { + var marketCap: BigDecimal? = null + var marketCapDiff: BigDecimal? = null + var defiMarketCap: BigDecimal? = null + var defiMarketCapDiff: BigDecimal? = null + var volume24h: BigDecimal? = null + var volume24hDiff: BigDecimal? = null + var tvl: BigDecimal? = null + var tvlDiff: BigDecimal? = null + + if (globalMarketPoints.isNotEmpty()) { + val startingPoint = globalMarketPoints.first() + val endingPoint = globalMarketPoints.last() + + marketCap = endingPoint.marketCap + marketCapDiff = diff(startingPoint.marketCap, marketCap) + + defiMarketCap = endingPoint.defiMarketCap + defiMarketCapDiff = diff(startingPoint.defiMarketCap, defiMarketCap) + + volume24h = endingPoint.volume24h + volume24hDiff = diff(startingPoint.volume24h, volume24h) + + tvl = endingPoint.tvl + tvlDiff = diff(startingPoint.tvl, tvl) + } + + val metrics: List = listOf( + MarketOverviewViewItem( + "Total.Cap", + marketCap?.let { formatFiatShortened(it, baseCurrency.symbol) } ?: "-", + marketCapDiff?.let { getDiff(it) } ?: "----", + marketCapDiff?.let { it > BigDecimal.ZERO } ?: false, + MetricsType.TotalMarketCap + ), + MarketOverviewViewItem( + "24h Vol.", + volume24h?.let { formatFiatShortened(it, baseCurrency.symbol) } ?: "-", + volume24hDiff?.let { getDiff(it) } ?: "----", + volume24hDiff?.let { it > BigDecimal.ZERO } ?: false, + MetricsType.Volume24h + ), + MarketOverviewViewItem( + "ETF", + defiMarketCap?.let { formatFiatShortened(it, baseCurrency.symbol) } ?: "-", + defiMarketCapDiff?.let { getDiff(it) } ?: "----", + defiMarketCapDiff?.let { it > BigDecimal.ZERO } ?: false, + MetricsType.DefiCap + ), + MarketOverviewViewItem( + "TVL", + tvl?.let { formatFiatShortened(it, baseCurrency.symbol) } ?: "-", + tvlDiff?.let { getDiff(it) } ?: "----", + tvlDiff?.let { it > BigDecimal.ZERO } ?: false, + MetricsType.TvlInDefi + ) + ) + + return metrics + } + + private fun getDiff(it: BigDecimal): String { + val sign = if (it >= BigDecimal.ZERO) "+" else "-" + return "${App.numberFormatter.format(it.abs(), 0, 2, sign, "%")}%" } - private fun getInitialTab() = MarketModule.Tab.Watchlist -// when (service.launchPage) { -// LaunchPage.Market -> MarketModule.Tab.Overview -// LaunchPage.Watchlist -> MarketModule.Tab.Watchlist -// else -> service.currentTab ?: MarketModule.Tab.Overview -// } -} + 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 + } +} \ No newline at end of file diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/overviewxxx/MarketModuleXxx.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/overviewxxx/MarketModuleXxx.kt deleted file mode 100644 index 2e5549f850f..00000000000 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/overviewxxx/MarketModuleXxx.kt +++ /dev/null @@ -1,38 +0,0 @@ -package io.horizontalsystems.bankwallet.modules.market.overviewxxx - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import io.horizontalsystems.bankwallet.core.App -import io.horizontalsystems.bankwallet.modules.market.MarketModule -import io.horizontalsystems.bankwallet.modules.metricchart.MetricsType - -object MarketModuleXxx { - - class Factory : ViewModelProvider.Factory { - - @Suppress("UNCHECKED_CAST") - override fun create(modelClass: Class): T { - return MarketViewModelXxx( - App.marketStorage, - App.marketKit, - App.currencyManager, - App.localStorage - ) as T - } - - } - - data class UiState( - val selectedTab: MarketModule.Tab, - val marketOverviewItems: List - ) - - data class MarketOverviewViewItem( - val title: String, - val value: String, - val change: String, - val changePositive: Boolean, - val metricsType: MetricsType, - ) - -} diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/overviewxxx/MarketScreenXxx.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/overviewxxx/MarketScreenXxx.kt deleted file mode 100644 index 1bbdb83da2d..00000000000 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/overviewxxx/MarketScreenXxx.kt +++ /dev/null @@ -1,210 +0,0 @@ -package io.horizontalsystems.bankwallet.modules.market.overviewxxx - -import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.horizontalScroll -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.pager.HorizontalPager -import androidx.compose.foundation.pager.rememberPagerState -import androidx.compose.foundation.rememberScrollState -import androidx.compose.material.Divider -import androidx.compose.material.Scaffold -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -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.slideFromBottom -import io.horizontalsystems.bankwallet.core.slideFromRight -import io.horizontalsystems.bankwallet.core.stats.StatEvent -import io.horizontalsystems.bankwallet.core.stats.StatPage -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.modules.coin.CoinFragment -import io.horizontalsystems.bankwallet.modules.market.MarketModule.Tab -import io.horizontalsystems.bankwallet.modules.market.favorites.MarketFavoritesScreen -import io.horizontalsystems.bankwallet.modules.market.posts.MarketPostsScreen -import io.horizontalsystems.bankwallet.modules.market.topcoins.TopCoins -import io.horizontalsystems.bankwallet.modules.market.topplatforms.TopPlatforms -import io.horizontalsystems.bankwallet.modules.metricchart.MetricsType -import io.horizontalsystems.bankwallet.ui.compose.ComposeAppTheme -import io.horizontalsystems.bankwallet.ui.compose.TranslatableString -import io.horizontalsystems.bankwallet.ui.compose.components.AppBar -import io.horizontalsystems.bankwallet.ui.compose.components.HSpacer -import io.horizontalsystems.bankwallet.ui.compose.components.MenuItem -import io.horizontalsystems.bankwallet.ui.compose.components.ScrollableTabs -import io.horizontalsystems.bankwallet.ui.compose.components.TabItem -import io.horizontalsystems.bankwallet.ui.compose.components.caption_bran -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 - -@Composable -fun MarketScreenXxx(navController: NavController) { - val viewModel = viewModel(factory = MarketModuleXxx.Factory()) - val uiState = viewModel.uiState - val tabs = viewModel.tabs - - Scaffold( - backgroundColor = ComposeAppTheme.colors.tyler, - topBar = { - AppBar( - title = stringResource(R.string.Market_Title), - menuItems = listOf( - MenuItem( - title = TranslatableString.ResString(R.string.Market_Search), - icon = R.drawable.icon_search, - tint = ComposeAppTheme.colors.jacob, - onClick = { - navController.slideFromRight(R.id.marketSearchFragment) - - stat( - page = StatPage.Markets, - event = StatEvent.Open(StatPage.MarketSearch) - ) - }, - ), - MenuItem( - title = TranslatableString.ResString(R.string.Market_Filters), - icon = R.drawable.ic_manage_2_24, - onClick = { - navController.slideFromRight(R.id.marketAdvancedSearchFragment) - - stat( - page = StatPage.Markets, - event = StatEvent.Open(StatPage.AdvancedSearch) - ) - }, - ), - ) - ) - } - ) { - Column( - Modifier - .padding(it) - .background(ComposeAppTheme.colors.tyler) - ) { - MetricsBoard(navController, uiState.marketOverviewItems) - Divider( - color = ComposeAppTheme.colors.steel10, - thickness = 1.dp - ) - TabsSection(navController, tabs, uiState.selectedTab) { tab -> - viewModel.onSelect(tab) - } - } - } -} - -@OptIn(ExperimentalFoundationApi::class) -@Composable -fun TabsSection( - navController: NavController, - tabs: Array, - selectedTab: Tab, - onTabClick: (Tab) -> Unit -) { - val pagerState = rememberPagerState(initialPage = selectedTab.ordinal) { tabs.size } - - LaunchedEffect(key1 = selectedTab, block = { - pagerState.scrollToPage(selectedTab.ordinal) - }) - val tabItems = tabs.map { - TabItem(stringResource(id = it.titleResId), it == selectedTab, it) - } - - ScrollableTabs(tabItems) { - onTabClick(it) - } - - HorizontalPager( - state = pagerState, - userScrollEnabled = false - ) { page -> - when (tabs[page]) { - Tab.Coins -> { - TopCoins(onCoinClick = { onCoinClick(it, navController) }) - stat(page = StatPage.MarketOverview, section = StatSection.TopGainers, event = StatEvent.Open(StatPage.TopCoins)) - } - - Tab.Watchlist -> MarketFavoritesScreen(navController) - Tab.Posts -> MarketPostsScreen() - Tab.Platform -> { - TopPlatforms(navController) - stat(page = StatPage.MarketOverview, event = StatEvent.Open(StatPage.TopPlatforms)) - } - Tab.Pairs -> { - } - - Tab.Sectors -> { - } - } - } -} - -@Composable -fun MetricsBoard( - navController: NavController, - marketOverviewItems: List -) { - Row( - modifier = Modifier - .fillMaxWidth() - .height(40.dp) - .background(ComposeAppTheme.colors.tyler) - .horizontalScroll(rememberScrollState()), - verticalAlignment = androidx.compose.ui.Alignment.CenterVertically - ) { - HSpacer(4.dp) - marketOverviewItems.forEach { item -> - Row( - modifier = Modifier - .clickable { - openMetricsPage(item.metricsType, navController) - } - .padding(8.dp) - ) { - HSpacer(12.dp) - caption_grey(text = item.title) - HSpacer(4.dp) - caption_bran(text = item.value) - HSpacer(4.dp) - if (item.changePositive) { - caption_remus(text = item.change) - } else { - caption_lucian(text = item.change) - } - HSpacer(12.dp) - } - } - HSpacer(4.dp) - } -} - -private fun openMetricsPage(metricsType: MetricsType, navController: NavController) { - if (metricsType == MetricsType.TvlInDefi) { - navController.slideFromBottom(R.id.tvlFragment) - } else { - navController.slideFromBottom(R.id.metricsPageFragment, metricsType) - } - - stat(page = StatPage.MarketOverview, event = StatEvent.Open(metricsType.statPage)) -} - -private fun onCoinClick(coinUid: String, navController: NavController) { - val arguments = CoinFragment.Input(coinUid) - - navController.slideFromRight(R.id.coinFragment, arguments) - - stat(page = StatPage.TopCoins, event = StatEvent.OpenCoin(coinUid)) -} diff --git a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/overviewxxx/MarketViewModelXxx.kt b/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/overviewxxx/MarketViewModelXxx.kt deleted file mode 100644 index 6c53bc34d03..00000000000 --- a/app/src/main/java/io/horizontalsystems/bankwallet/modules/market/overviewxxx/MarketViewModelXxx.kt +++ /dev/null @@ -1,152 +0,0 @@ -package io.horizontalsystems.bankwallet.modules.market.overviewxxx - -import android.util.Log -import androidx.lifecycle.viewModelScope -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.entities.Currency -import io.horizontalsystems.bankwallet.entities.LaunchPage -import io.horizontalsystems.bankwallet.modules.market.MarketModule.Tab -import io.horizontalsystems.bankwallet.modules.market.overviewxxx.MarketModuleXxx.MarketOverviewViewItem -import io.horizontalsystems.bankwallet.modules.metricchart.MetricsType -import io.horizontalsystems.marketkit.models.GlobalMarketPoint -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.launch -import kotlinx.coroutines.rx2.await -import java.math.BigDecimal - -class MarketViewModelXxx( - private val marketStorage: IMarketStorage, - private val marketKit: MarketKitWrapper, - private val currencyManager: CurrencyManager, - localStorage: ILocalStorage -) : ViewModelUiState() { - - val tabs = Tab.entries.toTypedArray() - private var marketOverviewJob: Job? = null - private var marketOverviewItems: List = listOf() - private var selectedTab: Tab = getInitialTab(localStorage.launchPage) - - init { - updateMarketOverview() - } - - override fun createState(): MarketModuleXxx.UiState { - return MarketModuleXxx.UiState( - selectedTab, - marketOverviewItems - ) - } - - private fun updateMarketOverview() { - marketOverviewJob?.cancel() - marketOverviewJob = viewModelScope.launch(Dispatchers.IO) { - try { - val marketOverview = - marketKit.marketOverviewSingle(currencyManager.baseCurrency.code).await() - marketOverview?.globalMarketPoints?.let { - marketOverviewItems = getMarketMetrics(it, currencyManager.baseCurrency) - emitState() - } - } catch (e: Throwable) { - //handle error state - Log.e("TAG", "updateMarketOverview: ", e) - } - } - } - - fun onSelect(tab: Tab) { - selectedTab = tab - marketStorage.currentMarketTab = tab - emitState() - } - - private fun getMarketMetrics( - globalMarketPoints: List, - baseCurrency: Currency - ): List { - var marketCap: BigDecimal? = null - var marketCapDiff: BigDecimal? = null - var defiMarketCap: BigDecimal? = null - var defiMarketCapDiff: BigDecimal? = null - var volume24h: BigDecimal? = null - var volume24hDiff: BigDecimal? = null - var tvl: BigDecimal? = null - var tvlDiff: BigDecimal? = null - - if (globalMarketPoints.isNotEmpty()) { - val startingPoint = globalMarketPoints.first() - val endingPoint = globalMarketPoints.last() - - marketCap = endingPoint.marketCap - marketCapDiff = diff(startingPoint.marketCap, marketCap) - - defiMarketCap = endingPoint.defiMarketCap - defiMarketCapDiff = diff(startingPoint.defiMarketCap, defiMarketCap) - - volume24h = endingPoint.volume24h - volume24hDiff = diff(startingPoint.volume24h, volume24h) - - tvl = endingPoint.tvl - tvlDiff = diff(startingPoint.tvl, tvl) - } - - val metrics: List = listOf( - MarketOverviewViewItem( - "Total.Cap", - marketCap?.let { formatFiatShortened(it, baseCurrency.symbol) } ?: "-", - marketCapDiff?.let { getDiff(it) } ?: "----", - marketCapDiff?.let { it > BigDecimal.ZERO } ?: false, - MetricsType.TotalMarketCap - ), - MarketOverviewViewItem( - "24h Vol.", - volume24h?.let { formatFiatShortened(it, baseCurrency.symbol) } ?: "-", - volume24hDiff?.let { getDiff(it) } ?: "----", - volume24hDiff?.let { it > BigDecimal.ZERO } ?: false, - MetricsType.Volume24h - ), - MarketOverviewViewItem( - "ETF", - defiMarketCap?.let { formatFiatShortened(it, baseCurrency.symbol) } ?: "-", - defiMarketCapDiff?.let { getDiff(it) } ?: "----", - defiMarketCapDiff?.let { it > BigDecimal.ZERO } ?: false, - MetricsType.DefiCap - ), - MarketOverviewViewItem( - "TVL", - tvl?.let { formatFiatShortened(it, baseCurrency.symbol) } ?: "-", - tvlDiff?.let { getDiff(it) } ?: "----", - tvlDiff?.let { it > BigDecimal.ZERO } ?: false, - MetricsType.TvlInDefi - ) - ) - - return metrics - } - - 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 - } -} \ No newline at end of file