Updated on 2026-08-14
This commit is contained in:
parent
b93539504a
commit
0e3412711a
9 changed files with 121 additions and 32 deletions
|
|
@ -10,6 +10,7 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDeta
|
|||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsTopAppBarConfig
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenInfoBlockState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsActionButton
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsPullToRefreshConfig
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
|
@ -71,6 +72,11 @@ internal object TokenDetailsPreviewData {
|
|||
|
||||
private val marketPriceLoading = MarketPriceBlockState.Loading(currencyName = "USDT")
|
||||
|
||||
private val pullToRefreshConfig = TokenDetailsPullToRefreshConfig(
|
||||
isRefreshing = false,
|
||||
onRefresh = {},
|
||||
)
|
||||
|
||||
val tokenDetailsState = TokenDetailsState(
|
||||
topAppBarConfig = tokenDetailsTopAppBarConfig,
|
||||
tokenInfoBlockState = tokenInfoBlockState,
|
||||
|
|
@ -83,5 +89,6 @@ internal object TokenDetailsPreviewData {
|
|||
),
|
||||
dialogConfig = null,
|
||||
pendingTxs = persistentListOf(),
|
||||
pullToRefreshConfig = pullToRefreshConfig,
|
||||
)
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
|
|||
import com.tangem.core.ui.components.transactions.state.TransactionState
|
||||
import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsDialogConfig
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsPullToRefreshConfig
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
|
||||
internal data class TokenDetailsState(
|
||||
|
|
@ -14,4 +15,5 @@ internal data class TokenDetailsState(
|
|||
val pendingTxs: PersistentList<TransactionState>,
|
||||
val txHistoryState: TxHistoryState,
|
||||
val dialogConfig: TokenDetailsDialogConfig?,
|
||||
val pullToRefreshConfig: TokenDetailsPullToRefreshConfig,
|
||||
)
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.state.components
|
||||
|
||||
data class TokenDetailsPullToRefreshConfig(val isRefreshing: Boolean, val onRefresh: () -> Unit)
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory
|
||||
|
||||
import com.tangem.common.Provider
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal class TokenDetailsRefreshStateConverter(
|
||||
private val currentStateProvider: Provider<TokenDetailsState>,
|
||||
) : Converter<Boolean, TokenDetailsState> {
|
||||
|
||||
override fun convert(value: Boolean): TokenDetailsState {
|
||||
val state = currentStateProvider()
|
||||
return state.createPullToRefresh(value)
|
||||
}
|
||||
|
||||
private fun TokenDetailsState.createPullToRefresh(value: Boolean): TokenDetailsState {
|
||||
return copy(pullToRefreshConfig = pullToRefreshConfig.copy(isRefreshing = value))
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import com.tangem.core.ui.res.TangemTheme
|
|||
import com.tangem.domain.tokens.models.CryptoCurrency
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.*
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsActionButton
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsPullToRefreshConfig
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsSkeletonStateConverter.SkeletonModel
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
|
|
@ -49,6 +50,7 @@ internal class TokenDetailsSkeletonStateConverter(
|
|||
),
|
||||
),
|
||||
dialogConfig = null,
|
||||
pullToRefreshConfig = createPullToRefresh(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -72,5 +74,10 @@ internal class TokenDetailsSkeletonStateConverter(
|
|||
)
|
||||
}
|
||||
|
||||
private fun createPullToRefresh(): TokenDetailsPullToRefreshConfig = TokenDetailsPullToRefreshConfig(
|
||||
isRefreshing = false,
|
||||
onRefresh = clickIntents::onRefreshSwipe,
|
||||
)
|
||||
|
||||
data class SkeletonModel(val cryptoCurrency: CryptoCurrency)
|
||||
}
|
||||
|
|
@ -59,6 +59,12 @@ internal class TokenDetailsStateFactory(
|
|||
)
|
||||
}
|
||||
|
||||
private val refreshStateConverter by lazy {
|
||||
TokenDetailsRefreshStateConverter(
|
||||
currentStateProvider = currentStateProvider,
|
||||
)
|
||||
}
|
||||
|
||||
fun getInitialState(cryptoCurrency: CryptoCurrency): TokenDetailsState {
|
||||
return skeletonStateConverter.convert(
|
||||
TokenDetailsSkeletonStateConverter.SkeletonModel(cryptoCurrency = cryptoCurrency),
|
||||
|
|
@ -117,4 +123,12 @@ internal class TokenDetailsStateFactory(
|
|||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun getRefreshingState(): TokenDetailsState {
|
||||
return refreshStateConverter.convert(true)
|
||||
}
|
||||
|
||||
fun getRefreshedState(): TokenDetailsState {
|
||||
return refreshStateConverter.convert(false)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.ui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||
import androidx.compose.material.pullrefresh.pullRefresh
|
||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
|
|
@ -29,12 +30,18 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.T
|
|||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.TokenInfoBlock
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
internal fun TokenDetailsScreen(state: TokenDetailsState) {
|
||||
Scaffold(
|
||||
topBar = { TokenDetailsTopAppBar(config = state.topAppBarConfig) },
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
) { scaffoldPaddings ->
|
||||
val pullRefreshState = rememberPullRefreshState(
|
||||
refreshing = state.pullToRefreshConfig.isRefreshing,
|
||||
onRefresh = state.pullToRefreshConfig.onRefresh,
|
||||
)
|
||||
|
||||
val txHistoryItems = if (state.txHistoryState is TxHistoryState.Content) {
|
||||
state.txHistoryState.contentItems.collectAsLazyPagingItems()
|
||||
} else {
|
||||
|
|
@ -45,34 +52,46 @@ internal fun TokenDetailsScreen(state: TokenDetailsState) {
|
|||
val itemModifier = Modifier
|
||||
.padding(top = betweenItemsPadding)
|
||||
.padding(horizontal = horizontalPadding)
|
||||
LazyColumn(
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues = scaffoldPaddings)
|
||||
.fillMaxSize(),
|
||||
.padding(scaffoldPaddings)
|
||||
.pullRefresh(pullRefreshState),
|
||||
) {
|
||||
item {
|
||||
TokenInfoBlock(
|
||||
modifier = Modifier
|
||||
.padding(top = TangemTheme.dimens.spacing4)
|
||||
.padding(horizontal = horizontalPadding),
|
||||
state = state.tokenInfoBlockState,
|
||||
)
|
||||
}
|
||||
item { TokenDetailsBalanceBlock(modifier = itemModifier, state = state.tokenBalanceBlockState) }
|
||||
item(
|
||||
key = MarketPriceBlockState::class.java,
|
||||
contentType = MarketPriceBlockState::class.java,
|
||||
content = { MarketPriceBlock(modifier = itemModifier, state = state.marketPriceBlockState) },
|
||||
)
|
||||
if (state.txHistoryState is TxHistoryState.NotSupported && state.pendingTxs.isNotEmpty()) {
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
) {
|
||||
item {
|
||||
PendingTxsBlock(
|
||||
pendingTxs = state.pendingTxs,
|
||||
modifier = itemModifier,
|
||||
TokenInfoBlock(
|
||||
modifier = Modifier
|
||||
.padding(top = TangemTheme.dimens.spacing4)
|
||||
.padding(horizontal = horizontalPadding),
|
||||
state = state.tokenInfoBlockState,
|
||||
)
|
||||
}
|
||||
item { TokenDetailsBalanceBlock(modifier = itemModifier, state = state.tokenBalanceBlockState) }
|
||||
item(
|
||||
key = MarketPriceBlockState::class.java,
|
||||
contentType = MarketPriceBlockState::class.java,
|
||||
content = { MarketPriceBlock(modifier = itemModifier, state = state.marketPriceBlockState) },
|
||||
)
|
||||
if (state.txHistoryState is TxHistoryState.NotSupported && state.pendingTxs.isNotEmpty()) {
|
||||
item {
|
||||
PendingTxsBlock(
|
||||
pendingTxs = state.pendingTxs,
|
||||
modifier = itemModifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
txHistoryItems(state = state.txHistoryState, txHistoryItems = txHistoryItems)
|
||||
}
|
||||
txHistoryItems(state = state.txHistoryState, txHistoryItems = txHistoryItems)
|
||||
|
||||
PullRefreshIndicator(
|
||||
refreshing = state.pullToRefreshConfig.isRefreshing,
|
||||
state = pullRefreshState,
|
||||
modifier = Modifier.align(Alignment.TopCenter),
|
||||
)
|
||||
}
|
||||
|
||||
TokenDetailsDialogs(state = state)
|
||||
|
|
|
|||
|
|
@ -19,4 +19,6 @@ interface TokenDetailsClickIntents : TxHistoryClickIntents {
|
|||
fun onHideClick()
|
||||
|
||||
fun onHideConfirmed()
|
||||
|
||||
fun onRefreshSwipe()
|
||||
}
|
||||
|
|
@ -10,10 +10,7 @@ import com.tangem.common.Provider
|
|||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
|
||||
import com.tangem.domain.tokens.GetCurrencyStatusUpdatesUseCase
|
||||
import com.tangem.domain.tokens.GetNetworkCoinStatusUseCase
|
||||
import com.tangem.domain.tokens.RemoveCurrencyUseCase
|
||||
import com.tangem.domain.tokens.*
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency
|
||||
|
|
@ -44,6 +41,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
private val getSelectedWalletUseCase: GetSelectedWalletUseCase,
|
||||
private val getCurrencyStatusUpdatesUseCase: GetCurrencyStatusUpdatesUseCase,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase,
|
||||
private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
|
||||
private val txHistoryItemsUseCase: GetTxHistoryItemsUseCase,
|
||||
private val getExploreUrlUseCase: GetExploreUrlUseCase,
|
||||
|
|
@ -60,6 +58,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
var router by Delegates.notNull<InnerTokenDetailsRouter>()
|
||||
|
||||
private val marketPriceJobHolder = JobHolder()
|
||||
private val refreshStateJobHolder = JobHolder()
|
||||
private var cryptoCurrencyStatus: CryptoCurrencyStatus? = null
|
||||
private var wallet by Delegates.notNull<UserWallet>()
|
||||
|
||||
|
|
@ -116,14 +115,16 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
.saveIn(marketPriceJobHolder)
|
||||
}
|
||||
|
||||
private fun updateTxHistory() {
|
||||
private fun updateTxHistory(refresh: Boolean = false) {
|
||||
viewModelScope.launch(dispatchers.io) {
|
||||
val txHistoryItemsCountEither = txHistoryItemsCountUseCase(
|
||||
networkId = cryptoCurrency.network.id,
|
||||
derivationPath = cryptoCurrency.derivationPath,
|
||||
)
|
||||
|
||||
uiState = stateFactory.getLoadingTxHistoryState(itemsCountEither = txHistoryItemsCountEither)
|
||||
if (!refresh) {
|
||||
uiState = stateFactory.getLoadingTxHistoryState(itemsCountEither = txHistoryItemsCountEither)
|
||||
}
|
||||
|
||||
txHistoryItemsCountEither.onRight {
|
||||
uiState = stateFactory.getLoadedTxHistoryState(
|
||||
|
|
@ -258,4 +259,19 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRefreshSwipe() {
|
||||
uiState = stateFactory.getRefreshingState()
|
||||
|
||||
viewModelScope.launch(dispatchers.io) {
|
||||
fetchCurrencyStatusUseCase.invoke(
|
||||
userWalletId = wallet.walletId,
|
||||
id = cryptoCurrency.id,
|
||||
refresh = true,
|
||||
)
|
||||
updateTxHistory(refresh = true)
|
||||
|
||||
uiState = stateFactory.getRefreshedState()
|
||||
}.saveIn(refreshStateJobHolder)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue