Updated on 2026-08-14

This commit is contained in:
Tangem 2025-03-05 13:16:45 +05:00
parent 1437c14019
commit 3f671e3e0f
46 changed files with 1454 additions and 43 deletions

View file

@ -17,6 +17,8 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDeta
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.TokenDetailsScreen
import com.tangem.features.markets.token.block.TokenMarketBlockComponent
import com.tangem.features.tokendetails.TokenDetailsComponent
import com.tangem.features.txhistory.TxHistoryFeatureToggles
import com.tangem.features.txhistory.component.TxHistoryComponent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@ -25,10 +27,20 @@ internal class DefaultTokenDetailsComponent @AssistedInject constructor(
@Assisted appComponentContext: AppComponentContext,
@Assisted params: TokenDetailsComponent.Params,
tokenMarketBlockComponentFactory: TokenMarketBlockComponent.Factory,
txHistoryComponentFactory: TxHistoryComponent.Factory,
txHistoryFeatureToggles: TxHistoryFeatureToggles,
deepLinksRegistry: DeepLinksRegistry,
) : TokenDetailsComponent, AppComponentContext by appComponentContext {
private val model: TokenDetailsModel = getOrCreateModel(params)
private val txHistoryComponent = txHistoryComponentFactory.create(
context = child("txHistoryComponent"),
params = TxHistoryComponent.Params(
userWalletId = params.userWalletId,
currency = params.currency,
openExplorer = { model.onExploreClick() },
),
).takeIf { txHistoryFeatureToggles.isFeatureEnabled }
init {
lifecycle.subscribe(
@ -54,11 +66,11 @@ internal class DefaultTokenDetailsComponent @AssistedInject constructor(
@Composable
override fun Content(modifier: Modifier) {
val state by model.uiState.collectAsStateWithLifecycle()
NavigationBar3ButtonsScrim()
TokenDetailsScreen(
state = state,
tokenMarketBlockComponent = tokenMarketBlockComponent,
txHistoryComponent = txHistoryComponent,
)
}

View file

@ -10,8 +10,8 @@ import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.di.GlobalUiMessageSender
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.ui.UiMessageSender
@ -76,6 +76,8 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.e
import com.tangem.features.onramp.OnrampFeatureToggles
import com.tangem.features.tokendetails.TokenDetailsComponent
import com.tangem.features.tokendetails.impl.R
import com.tangem.features.txhistory.TxHistoryFeatureToggles
import com.tangem.features.txhistory.entity.TxHistoryContentUpdateEmitter
import com.tangem.utils.Provider
import com.tangem.utils.coroutines.*
import kotlinx.collections.immutable.PersistentList
@ -122,6 +124,8 @@ internal class TokenDetailsModel @Inject constructor(
private val onrampFeatureToggles: OnrampFeatureToggles,
private val shareManager: ShareManager,
@GlobalUiMessageSender private val uiMessageSender: UiMessageSender,
private val txHistoryFeatureToggles: TxHistoryFeatureToggles,
private val txHistoryContentUpdateEmitter: TxHistoryContentUpdateEmitter,
paramsContainer: ParamsContainer,
expressStatusFactory: ExpressStatusFactory.Factory,
getUserWalletUseCase: GetUserWalletUseCase,
@ -240,7 +244,7 @@ internal class TokenDetailsModel @Inject constructor(
private fun updateContent() {
subscribeOnCurrencyStatusUpdates()
subscribeOnExpressTransactionsUpdates()
updateTxHistory(refresh = false, showItemsLoading = true)
updateTxHistory(refresh = false, showItemsLoading = true, initialUpdating = true)
updateStakingInfo()
}
@ -362,29 +366,33 @@ internal class TokenDetailsModel @Inject constructor(
* @param refresh - invalidate cache and get data from remote
* @param showItemsLoading - show loading items placeholder.
*/
private fun updateTxHistory(refresh: Boolean, showItemsLoading: Boolean) {
modelScope.launch(dispatchers.main) {
val txHistoryItemsCountEither = txHistoryItemsCountUseCase(
userWalletId = userWalletId,
currency = cryptoCurrency,
)
// if countEither is left, handling error state run inside getLoadingTxHistoryState
if (showItemsLoading || txHistoryItemsCountEither.isLeft()) {
internalUiState.value = stateFactory.getLoadingTxHistoryState(
itemsCountEither = txHistoryItemsCountEither,
pendingTransactions = internalUiState.value.pendingTxs,
)
}
txHistoryItemsCountEither.onRight {
val maybeTxHistory = txHistoryItemsUseCase(
private fun updateTxHistory(refresh: Boolean, showItemsLoading: Boolean, initialUpdating: Boolean = false) {
if (txHistoryFeatureToggles.isFeatureEnabled && !initialUpdating) {
modelScope.launch { txHistoryContentUpdateEmitter.triggerUpdate() }
} else {
modelScope.launch(dispatchers.main) {
val txHistoryItemsCountEither = txHistoryItemsCountUseCase(
userWalletId = userWalletId,
currency = cryptoCurrency,
refresh = refresh,
).map { it.cachedIn(modelScope) }
)
internalUiState.value = stateFactory.getLoadedTxHistoryState(maybeTxHistory)
// if countEither is left, handling error state run inside getLoadingTxHistoryState
if (showItemsLoading || txHistoryItemsCountEither.isLeft()) {
internalUiState.value = stateFactory.getLoadingTxHistoryState(
itemsCountEither = txHistoryItemsCountEither,
pendingTransactions = internalUiState.value.pendingTxs,
)
}
txHistoryItemsCountEither.onRight {
val maybeTxHistory = txHistoryItemsUseCase(
userWalletId = userWalletId,
currency = cryptoCurrency,
refresh = refresh,
).map { it.cachedIn(modelScope) }
internalUiState.value = stateFactory.getLoadedTxHistoryState(maybeTxHistory)
}
}
}
}

View file

@ -3,16 +3,18 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.ui
import android.content.res.Configuration
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.*
import androidx.compose.material3.Scaffold
import androidx.compose.material3.ScaffoldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
import com.tangem.common.ui.expressStatus.expressTransactionsItems
@ -39,11 +41,18 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.T
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.ExpressStatusBottomSheet
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.staking.TokenStakingBlock
import com.tangem.features.markets.token.block.TokenMarketBlockComponent
import com.tangem.features.txhistory.component.TxHistoryComponent
import com.tangem.features.txhistory.entity.TxHistoryUM
import kotlin.reflect.KProperty
// TODO: Split to blocks [REDACTED_JIRA]
@Suppress("LongMethod")
@Composable
internal fun TokenDetailsScreen(state: TokenDetailsState, tokenMarketBlockComponent: TokenMarketBlockComponent?) {
internal fun TokenDetailsScreen(
state: TokenDetailsState,
tokenMarketBlockComponent: TokenMarketBlockComponent?,
txHistoryComponent: TxHistoryComponent?,
) {
BackHandler(onBack = state.topAppBarConfig.onBackClick)
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
@ -57,6 +66,8 @@ internal fun TokenDetailsScreen(state: TokenDetailsState, tokenMarketBlockCompon
} else {
null
}
val listState = rememberLazyListState()
val txHistoryComponentState by txHistoryComponent?.txHistoryState?.collectAsStateWithLifecycle()
val betweenItemsPadding = TangemTheme.dimens.spacing12
val horizontalPadding = TangemTheme.dimens.spacing16
val itemModifier = Modifier
@ -69,6 +80,7 @@ internal fun TokenDetailsScreen(state: TokenDetailsState, tokenMarketBlockCompon
) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
state = listState,
contentPadding = PaddingValues(
bottom = TangemTheme.dimens.spacing16 + bottomBarHeight,
),
@ -145,9 +157,12 @@ internal fun TokenDetailsScreen(state: TokenDetailsState, tokenMarketBlockCompon
)
txHistoryItems(
state = state.txHistoryState,
isBalanceHidden = state.isBalanceHidden,
listState = listState,
txHistoryComponent = txHistoryComponent,
txHistoryComponentState = txHistoryComponentState,
txHistoryState = state.txHistoryState,
txHistoryItems = txHistoryItems,
isBalanceHidden = state.isBalanceHidden,
)
}
}
@ -170,6 +185,28 @@ internal fun TokenDetailsScreen(state: TokenDetailsState, tokenMarketBlockCompon
}
}
@Suppress("LongParameterList")
private fun LazyListScope.txHistoryItems(
listState: LazyListState,
txHistoryComponent: TxHistoryComponent?,
txHistoryComponentState: TxHistoryUM?,
txHistoryState: TxHistoryState,
txHistoryItems: LazyPagingItems<TxHistoryState.TxHistoryItemState>?,
isBalanceHidden: Boolean,
) {
if (txHistoryComponent != null && txHistoryComponentState != null) {
with(txHistoryComponent) { txHistoryContent(listState = listState, state = txHistoryComponentState) }
} else {
txHistoryItems(
state = txHistoryState,
isBalanceHidden = isBalanceHidden,
txHistoryItems = txHistoryItems,
)
}
}
private inline operator fun <T> State<T>?.getValue(thisObj: Any?, property: KProperty<*>): T? = this?.value
// region Preview
@Preview(showBackground = true, widthDp = 360)
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
@ -181,6 +218,7 @@ private fun TokenDetailsScreenPreview(
TokenDetailsScreen(
state = state,
tokenMarketBlockComponent = null,
txHistoryComponent = null,
)
}
}