Updated on 2026-08-14

This commit is contained in:
Tangem 2025-04-01 13:10:07 +05:00
parent aea7f5f415
commit c83ce2ada3
53 changed files with 1062 additions and 238 deletions

View file

@ -12,6 +12,7 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.TokenActionsState
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.feature.wallet.presentation.wallet.domain.OnrampStatusFactory
import com.tangem.feature.wallet.presentation.wallet.domain.unwrap
@ -55,6 +56,8 @@ internal interface WalletContentClickIntents {
fun onConfirmDisposeExpressStatus()
fun onDisposeExpressStatus()
fun onNFTClick(userWalletId: UserWalletId)
}
@Suppress("LongParameterList")
@ -235,4 +238,8 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
}
stateHolder.update(CloseBottomSheetTransformer(userWalletId))
}
override fun onNFTClick(userWalletId: UserWalletId) {
router.openNFTCollectionsScreen(userWalletId)
}
}

View file

@ -125,6 +125,7 @@ internal object WalletScreenPreviewData {
collectionsCount = 1,
assetsCount = 3,
isFlickering = false,
onItemClick = { },
),
)
}

View file

@ -115,4 +115,8 @@ internal class DefaultWalletRouter @Inject constructor(
override fun openScanFailedDialog(onTryAgain: () -> Unit) {
reduxStateHolder.dispatchDialogShow(StateDialog.ScanFailsDialog(StateDialog.ScanFailsSource.MAIN, onTryAgain))
}
override fun openNFTCollectionsScreen(userWalletId: UserWalletId) {
router.push(AppRoute.NFTCollections(userWalletId))
}
}

View file

@ -56,4 +56,7 @@ internal interface InnerWalletRouter {
/** Open scan failed dialog */
fun openScanFailedDialog(onTryAgain: () -> Unit)
/** Open NFT collections screen */
fun openNFTCollectionsScreen(userWalletId: UserWalletId)
}

View file

@ -10,7 +10,9 @@ sealed class WalletNFTItemUM {
data object Loading : WalletNFTItemUM()
data object Empty : WalletNFTItemUM()
data class Empty(
val onItemClick: () -> Unit,
) : WalletNFTItemUM()
data object Failed : WalletNFTItemUM()
@ -19,6 +21,7 @@ sealed class WalletNFTItemUM {
val collectionsCount: Int,
val assetsCount: Int,
val isFlickering: Boolean,
val onItemClick: () -> Unit,
) : WalletNFTItemUM() {
@Immutable

View file

@ -1,7 +1,7 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers
import com.tangem.domain.models.StatusSource
import com.tangem.domain.nft.models.NFTCollections
import com.tangem.domain.nft.models.*
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNFTItemUM
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
@ -10,16 +10,21 @@ import kotlinx.collections.immutable.toPersistentList
internal class SetNFTCollectionsTransformer(
userWalletId: UserWalletId,
private val nftCollections: List<NFTCollections>,
private val onItemClick: () -> Unit,
) : WalletStateTransformer(userWalletId) {
override fun transform(prevState: WalletState): WalletState = when (prevState) {
is WalletState.MultiCurrency.Content -> prevState.copy(
nftState = when {
allCollectionsFailed() -> WalletNFTItemUM.Failed
anyCollectionFailed() && allLoadedCollectionsEmpty() -> WalletNFTItemUM.Failed
allCollectionsLoaded() && allCollectionsEmpty() -> WalletNFTItemUM.Empty
!allCollectionsLoaded() && allCollectionsEmpty() -> WalletNFTItemUM.Loading
else -> createContentNFTItemUM()
nftCollections.allCollectionsFailed() ->
WalletNFTItemUM.Failed
nftCollections.anyCollectionFailed() && nftCollections.allLoadedCollectionsEmpty() ->
WalletNFTItemUM.Failed
nftCollections.allCollectionsLoaded() && nftCollections.allCollectionsEmpty() ->
WalletNFTItemUM.Empty(onItemClick)
!nftCollections.allCollectionsLoaded() && nftCollections.allCollectionsEmpty() ->
WalletNFTItemUM.Loading
else -> createContentNFTItemUM(onItemClick)
},
)
is WalletState.SingleCurrency.Content,
@ -31,7 +36,7 @@ internal class SetNFTCollectionsTransformer(
-> prevState
}
private fun createContentNFTItemUM(): WalletNFTItemUM.Content {
private fun createContentNFTItemUM(onItemClick: () -> Unit): WalletNFTItemUM.Content {
val collectionsContent = nftCollections
.map { it.content }
.filterIsInstance<NFTCollections.Content.Collections>()
@ -61,34 +66,10 @@ internal class SetNFTCollectionsTransformer(
assetsCount = collections
.sumOf { it.count },
isFlickering = isFlickering,
onItemClick = onItemClick,
)
}
private fun allCollectionsFailed() = nftCollections.all {
it.content is NFTCollections.Content.Error
}
private fun anyCollectionFailed() = nftCollections.any {
it.content is NFTCollections.Content.Error
}
private fun allLoadedCollectionsEmpty() = nftCollections
.map { it.content }
.filterIsInstance<NFTCollections.Content.Collections>()
.all { it.collections.isNullOrEmpty() }
private fun allCollectionsLoaded() = nftCollections.all {
val content = it.content
content is NFTCollections.Content.Collections &&
content.source != StatusSource.CACHE
}
private fun allCollectionsEmpty() = nftCollections.all {
val content = it.content
content is NFTCollections.Content.Collections &&
content.collections.isNullOrEmpty()
}
companion object {
private const val NFT_COLLECTIONS_MAX_PREVIEWS_COUNT = 4
}

View file

@ -11,13 +11,12 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.*
@Suppress("UnusedPrivateMember")
internal class WalletNFTListSubscriber(
private val userWallet: UserWallet,
private val stateHolder: WalletStateController,
private val walletsRepository: WalletsRepository,
private val getNFTCollectionsUseCase: GetNFTCollectionsUseCase,
clickIntents: WalletClickIntents,
private val clickIntents: WalletClickIntents,
) : WalletSubscriber() {
@OptIn(ExperimentalCoroutinesApi::class)
@ -27,8 +26,7 @@ internal class WalletNFTListSubscriber(
.flatMapLatest { nftEnabled ->
// if NFT is enabled for this wallet, then start observing changes from store and apply transformer if need
if (nftEnabled) {
getNFTCollectionsUseCase
.launch(userWallet.walletId)
getNFTCollectionsUseCase(userWallet.walletId)
.shareIn(
scope = coroutineScope,
started = SharingStarted.WhileSubscribed(),
@ -36,7 +34,11 @@ internal class WalletNFTListSubscriber(
)
.onEach {
stateHolder.update(
SetNFTCollectionsTransformer(userWallet.walletId, it),
SetNFTCollectionsTransformer(
userWalletId = userWallet.walletId,
nftCollections = it,
onItemClick = { clickIntents.onNFTClick(userWallet.walletId) },
),
)
}
} else {

View file

@ -713,7 +713,6 @@ internal fun LazyListScope.nftCollections(state: WalletState, itemModifier: Modi
nftCollections(
modifier = itemModifier,
state = it.nftState,
onClick = {},
)
}
}

View file

@ -34,19 +34,19 @@ import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
@Composable
internal fun WalletNFTItem(state: WalletNFTItemUM, modifier: Modifier = Modifier, onClick: () -> Unit = { }) {
internal fun WalletNFTItem(state: WalletNFTItemUM, modifier: Modifier = Modifier) {
when (state) {
is WalletNFTItemUM.Hidden -> Unit
is WalletNFTItemUM.Empty -> WalletNFTItemEmpty(
modifier = modifier,
onClick = onClick,
onClick = state.onItemClick,
)
is WalletNFTItemUM.Failed -> WalletNFTItemFailed(modifier = modifier)
is WalletNFTItemUM.Loading -> WalletNFTItemLoading(modifier = modifier)
is WalletNFTItemUM.Content -> WalletNFTItemContent(
state = state,
onClick = onClick,
onClick = state.onItemClick,
modifier = modifier,
)
}
@ -396,16 +396,15 @@ private fun RowContentContainer(
@Composable
private fun Preview_WalletNFTItem(@PreviewParameter(WalletNFTItemProvider::class) state: WalletNFTItemUM) {
TangemThemePreview {
WalletNFTItem(
state = state,
onClick = {},
)
WalletNFTItem(state = state)
}
}
private class WalletNFTItemProvider : CollectionPreviewParameterProvider<WalletNFTItemUM>(
collection = listOf(
WalletNFTItemUM.Empty,
WalletNFTItemUM.Empty(
onItemClick = { },
),
WalletNFTItemUM.Loading,
WalletNFTItemUM.Failed,
WalletNFTItemUM.Content(
@ -415,6 +414,7 @@ private class WalletNFTItemProvider : CollectionPreviewParameterProvider<WalletN
assetsCount = 125,
collectionsCount = 11,
isFlickering = true,
onItemClick = { },
),
WalletNFTItemUM.Content(
previews = persistentListOf(
@ -424,6 +424,7 @@ private class WalletNFTItemProvider : CollectionPreviewParameterProvider<WalletN
assetsCount = 125,
collectionsCount = 11,
isFlickering = false,
onItemClick = { },
),
WalletNFTItemUM.Content(
previews = persistentListOf(
@ -434,6 +435,7 @@ private class WalletNFTItemProvider : CollectionPreviewParameterProvider<WalletN
assetsCount = 125,
collectionsCount = 11,
isFlickering = false,
onItemClick = { },
),
WalletNFTItemUM.Content(
previews = persistentListOf(
@ -445,6 +447,7 @@ private class WalletNFTItemProvider : CollectionPreviewParameterProvider<WalletN
assetsCount = 125,
collectionsCount = 11,
isFlickering = false,
onItemClick = { },
),
WalletNFTItemUM.Content(
previews = persistentListOf(
@ -456,6 +459,7 @@ private class WalletNFTItemProvider : CollectionPreviewParameterProvider<WalletN
assetsCount = 125,
collectionsCount = 11,
isFlickering = true,
onItemClick = { },
),
),
)

View file

@ -7,12 +7,11 @@ import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletNFTItem
private const val NFT_COLLECTIONS_CONTENT_TYPE = "NFTCollections"
internal fun LazyListScope.nftCollections(state: WalletNFTItemUM, onClick: () -> Unit, modifier: Modifier = Modifier) {
internal fun LazyListScope.nftCollections(state: WalletNFTItemUM, modifier: Modifier = Modifier) {
item(key = NFT_COLLECTIONS_CONTENT_TYPE, contentType = NFT_COLLECTIONS_CONTENT_TYPE) {
WalletNFTItem(
modifier = modifier,
state = state,
onClick = onClick,
)
}
}