Updated on 2026-08-14

This commit is contained in:
Tangem 2023-08-07 18:09:33 +08:00
parent dc0b2a7e70
commit 1943ed248c
21 changed files with 573 additions and 418 deletions

View file

@ -19,320 +19,357 @@ import kotlinx.collections.immutable.toPersistentList
import kotlinx.coroutines.flow.flowOf
import java.util.UUID
@Suppress("LargeClass")
internal object WalletPreviewData {
val walletTopBarConfig = WalletTopBarConfig(onScanCardClick = {}, onMoreClick = {})
val walletTopBarConfig by lazy { WalletTopBarConfig(onScanCardClick = {}, onMoreClick = {}) }
val walletCardContentState = WalletCardState.Content(
id = UserWalletId(UUID.randomUUID().toString()),
title = "Wallet 1",
balance = "8923,05 $",
additionalInfo = "3 cards • Seed enabled",
imageResId = R.drawable.ill_businessman_3d,
onClick = null,
)
val walletCardContentState by lazy {
WalletCardState.Content(
id = UserWalletId("123"),
title = "Wallet 1",
balance = "8923,05 $",
additionalInfo = "3 cards • Seed enabled",
imageResId = R.drawable.ill_businessman_3d,
onClick = null,
)
}
val walletCardLoadingState = WalletCardState.Loading(
id = UserWalletId(UUID.randomUUID().toString()),
title = "Wallet 1",
additionalInfo = "3 cards • Seed enabled",
imageResId = R.drawable.ill_businessman_3d,
onClick = null,
)
val walletCardLoadingState by lazy {
WalletCardState.Loading(
id = UserWalletId("321"),
title = "Wallet 1",
additionalInfo = "3 cards • Seed enabled",
imageResId = R.drawable.ill_businessman_3d,
onClick = null,
)
}
val walletCardHiddenContentState = WalletCardState.HiddenContent(
id = UserWalletId(UUID.randomUUID().toString()),
title = "Wallet 1",
additionalInfo = "3 cards • Seed enabled",
imageResId = R.drawable.ill_businessman_3d,
onClick = null,
)
val walletCardHiddenContentState by lazy {
WalletCardState.HiddenContent(
id = UserWalletId("42"),
title = "Wallet 1",
additionalInfo = "3 cards • Seed enabled",
imageResId = R.drawable.ill_businessman_3d,
onClick = null,
)
}
val walletCardErrorState = WalletCardState.Error(
id = UserWalletId(UUID.randomUUID().toString()),
title = "Wallet 1",
additionalInfo = "3 cards • Seed enabled",
imageResId = R.drawable.ill_businessman_3d,
onClick = null,
)
val walletCardErrorState by lazy {
WalletCardState.Error(
id = UserWalletId("24"),
title = "Wallet 1",
additionalInfo = "3 cards • Seed enabled",
imageResId = R.drawable.ill_businessman_3d,
onClick = null,
)
}
val wallets = mapOf(
UserWalletId(stringValue = "123") to walletCardContentState,
UserWalletId(stringValue = "321") to walletCardLoadingState,
UserWalletId(stringValue = "42") to walletCardHiddenContentState,
UserWalletId(stringValue = "24") to walletCardErrorState,
)
val wallets by lazy {
mapOf(
UserWalletId(stringValue = "123") to walletCardContentState,
UserWalletId(stringValue = "321") to walletCardLoadingState,
UserWalletId(stringValue = "42") to walletCardHiddenContentState,
UserWalletId(stringValue = "24") to walletCardErrorState,
)
}
val walletListConfig = WalletsListConfig(
selectedWalletIndex = 0,
wallets = wallets.values.toPersistentList(),
onWalletChange = {},
)
val walletListConfig by lazy {
WalletsListConfig(
selectedWalletIndex = 0,
wallets = wallets.values.toPersistentList(),
onWalletChange = {},
)
}
val tokenItemVisibleState = TokenItemState.Content(
id = UUID.randomUUID().toString(),
tokenIconUrl = null,
tokenIconResId = R.drawable.img_polygon_22,
networkIconResId = R.drawable.img_polygon_22,
name = "Polygon",
amount = "5,412 MATIC",
hasPending = true,
tokenOptions = TokenOptionsState.Visible(
fiatAmount = "321 $",
priceChange = PriceChangeConfig(
valueInPercent = "2%",
type = PriceChangeConfig.Type.UP,
val tokenItemVisibleState by lazy {
TokenItemState.Content(
id = UUID.randomUUID().toString(),
tokenIconUrl = null,
tokenIconResId = R.drawable.img_polygon_22,
networkIconResId = R.drawable.img_polygon_22,
name = "Polygon",
amount = "5,412 MATIC",
hasPending = true,
tokenOptions = TokenOptionsState.Visible(
fiatAmount = "321 $",
priceChange = PriceChangeConfig(
valueInPercent = "2%",
type = PriceChangeConfig.Type.UP,
),
),
),
)
)
}
val tokenItemHiddenState = TokenItemState.Content(
id = UUID.randomUUID().toString(),
tokenIconUrl = null,
tokenIconResId = R.drawable.img_polygon_22,
networkIconResId = R.drawable.img_polygon_22,
name = "Polygon",
amount = "5,412 MATIC",
hasPending = true,
tokenOptions = TokenOptionsState.Hidden(
priceChange = PriceChangeConfig(
valueInPercent = "2%",
type = PriceChangeConfig.Type.UP,
val tokenItemHiddenState by lazy {
TokenItemState.Content(
id = UUID.randomUUID().toString(),
tokenIconUrl = null,
tokenIconResId = R.drawable.img_polygon_22,
networkIconResId = R.drawable.img_polygon_22,
name = "Polygon",
amount = "5,412 MATIC",
hasPending = true,
tokenOptions = TokenOptionsState.Hidden(
priceChange = PriceChangeConfig(
valueInPercent = "2%",
type = PriceChangeConfig.Type.UP,
),
),
),
)
)
}
val tokenItemDragState = TokenItemState.Draggable(
id = UUID.randomUUID().toString(),
tokenIconUrl = null,
tokenIconResId = R.drawable.img_polygon_22,
networkIconResId = R.drawable.img_polygon_22,
name = "Polygon",
fiatAmount = "3 172,14 $",
)
val tokenItemDragState by lazy {
TokenItemState.Draggable(
id = UUID.randomUUID().toString(),
tokenIconUrl = null,
tokenIconResId = R.drawable.img_polygon_22,
networkIconResId = R.drawable.img_polygon_22,
name = "Polygon",
fiatAmount = "3 172,14 $",
)
}
val tokenItemUnreachableState = TokenItemState.Unreachable(
id = UUID.randomUUID().toString(),
tokenIconUrl = null,
tokenIconResId = R.drawable.img_polygon_22,
networkIconResId = R.drawable.img_polygon_22,
name = "Polygon",
)
val tokenItemUnreachableState by lazy {
TokenItemState.Unreachable(
id = UUID.randomUUID().toString(),
tokenIconUrl = null,
tokenIconResId = R.drawable.img_polygon_22,
networkIconResId = R.drawable.img_polygon_22,
name = "Polygon",
)
}
val loadingTokenItemState = TokenItemState.Loading
val loadingTokenItemState by lazy { TokenItemState.Loading }
private const val networksSize = 10
private const val tokensSize = 3
val draggableItems = List(networksSize) { it }
.flatMap { index ->
val lastNetworkIndex = networksSize - 1
val lastTokenIndex = tokensSize - 1
val networkNumber = index + 1
val draggableItems by lazy {
List(networksSize) { it }
.flatMap { index ->
val lastNetworkIndex = networksSize - 1
val lastTokenIndex = tokensSize - 1
val networkNumber = index + 1
val group = DraggableItem.GroupHeader(
id = "group_$networkNumber",
networkName = "$networkNumber",
roundingMode = when (index) {
0 -> DraggableItem.RoundingMode.Top()
lastNetworkIndex -> DraggableItem.RoundingMode.Bottom()
else -> DraggableItem.RoundingMode.None
},
)
val tokens: MutableList<DraggableItem.Token> = mutableListOf()
repeat(times = tokensSize) { i ->
val tokenNumber = i + 1
tokens.add(
DraggableItem.Token(
tokenItemState = tokenItemDragState.copy(
id = "${group.id}_token_$tokenNumber",
name = "Token $tokenNumber from $networkNumber network",
networkIconResId = R.drawable.img_eth_22.takeIf { i != 0 },
),
groupId = group.id,
roundingMode = when {
i == lastTokenIndex && index == lastNetworkIndex -> DraggableItem.RoundingMode.Bottom()
else -> DraggableItem.RoundingMode.None
},
),
val group = DraggableItem.GroupHeader(
id = "group_$networkNumber",
networkName = "$networkNumber",
roundingMode = when (index) {
0 -> DraggableItem.RoundingMode.Top()
lastNetworkIndex -> DraggableItem.RoundingMode.Bottom()
else -> DraggableItem.RoundingMode.None
},
)
}
val divider = DraggableItem.GroupPlaceholder(id = "divider_$networkNumber")
val tokens: MutableList<DraggableItem.Token> = mutableListOf()
repeat(times = tokensSize) { i ->
val tokenNumber = i + 1
tokens.add(
DraggableItem.Token(
tokenItemState = tokenItemDragState.copy(
id = "${group.id}_token_$tokenNumber",
name = "Token $tokenNumber from $networkNumber network",
networkIconResId = R.drawable.img_eth_22.takeIf { i != 0 },
),
groupId = group.id,
roundingMode = when {
i == lastTokenIndex && index == lastNetworkIndex -> DraggableItem.RoundingMode.Bottom()
else -> DraggableItem.RoundingMode.None
},
),
)
}
buildList {
add(group)
addAll(tokens)
if (index != lastNetworkIndex) {
add(divider)
val divider = DraggableItem.GroupPlaceholder(id = "divider_$networkNumber")
buildList {
add(group)
addAll(tokens)
if (index != lastNetworkIndex) {
add(divider)
}
}
}
}
.toPersistentList()
.toPersistentList()
}
val draggableTokens = draggableItems
.filterIsInstance<DraggableItem.Token>()
.toMutableList()
.also {
it[0] = it[0].copy(roundingMode = DraggableItem.RoundingMode.Top())
}
.toPersistentList()
val draggableTokens by lazy {
draggableItems
.filterIsInstance<DraggableItem.Token>()
.toMutableList()
.also {
it[0] = it[0].copy(roundingMode = DraggableItem.RoundingMode.Top())
}
.toPersistentList()
}
val groupedOrganizeTokensState = OrganizeTokensStateHolder(
itemsState = OrganizeTokensListState.GroupedByNetwork(
items = draggableItems,
),
header = OrganizeTokensStateHolder.HeaderConfig(
onSortByBalanceClick = {},
onGroupByNetworkClick = {},
),
dragConfig = OrganizeTokensStateHolder.DragConfig(
onItemDragged = { _, _ -> },
onDragStart = {},
canDragItemOver = { _, _ -> false },
onItemDragEnd = {},
),
actions = OrganizeTokensStateHolder.ActionsConfig(
onApplyClick = {},
onCancelClick = {},
),
)
val organizeTokensState = groupedOrganizeTokensState.copy(
itemsState = OrganizeTokensListState.Ungrouped(
items = draggableTokens,
),
)
val bottomSheet = WalletBottomSheetConfig(
isShow = false,
onDismissRequest = {},
content = WalletBottomSheetConfig.BottomSheetContentConfig.UnlockWallets(
onUnlockClick = {},
onScanClick = {},
),
)
private val manageButtons = persistentListOf(
WalletManageButton.Buy(onClick = {}),
WalletManageButton.Send(onClick = {}),
WalletManageButton.Receive(onClick = {}),
WalletManageButton.Exchange(onClick = {}),
WalletManageButton.CopyAddress(onClick = {}),
)
val multicurrencyWalletScreenState = WalletStateHolder.MultiCurrencyContent(
onBackClick = {},
topBarConfig = walletTopBarConfig,
walletsListConfig = walletListConfig,
tokensListState = WalletTokensListState.Content(
persistentListOf(
WalletTokensListState.TokensListItemState.NetworkGroupTitle("Bitcoin"),
WalletTokensListState.TokensListItemState.Token(
tokenItemVisibleState.copy(
id = "token_1",
name = "Ethereum",
tokenIconResId = R.drawable.img_eth_22,
networkIconResId = null,
amount = "1,89340821 ETH",
),
),
WalletTokensListState.TokensListItemState.Token(
tokenItemVisibleState.copy(
id = "token_2",
name = "Ethereum",
tokenIconResId = R.drawable.img_eth_22,
networkIconResId = null,
amount = "1,89340821 ETH",
),
),
WalletTokensListState.TokensListItemState.Token(
tokenItemVisibleState.copy(
id = "token_3",
name = "Ethereum",
tokenIconResId = R.drawable.img_eth_22,
networkIconResId = null,
amount = "1,89340821 ETH",
),
),
WalletTokensListState.TokensListItemState.Token(
tokenItemVisibleState.copy(
id = "token_4",
name = "Ethereum",
tokenIconResId = R.drawable.img_eth_22,
networkIconResId = null,
amount = "1,89340821 ETH",
),
),
WalletTokensListState.TokensListItemState.NetworkGroupTitle("Ethereum"),
WalletTokensListState.TokensListItemState.Token(
tokenItemVisibleState.copy(
id = "token_5",
name = "Ethereum",
tokenIconResId = R.drawable.img_eth_22,
networkIconResId = null,
amount = "1,89340821 ETH",
),
),
val groupedOrganizeTokensState by lazy {
OrganizeTokensStateHolder(
itemsState = OrganizeTokensListState.GroupedByNetwork(
items = draggableItems,
),
onOrganizeTokensClick = {},
),
pullToRefreshConfig = WalletPullToRefreshConfig(
isRefreshing = false,
onRefresh = {},
),
notifications = persistentListOf(
WalletNotification.UnreachableNetworks,
WalletNotification.LikeTangemApp(onClick = {}),
WalletNotification.BackupCard(onClick = {}),
WalletNotification.ScanCard(onClick = {}),
),
bottomSheet = bottomSheet,
)
val singleWalletScreenState = WalletStateHolder.SingleCurrencyContent(
onBackClick = {},
topBarConfig = walletTopBarConfig,
walletsListConfig = walletListConfig,
pullToRefreshConfig = WalletPullToRefreshConfig(
isRefreshing = false,
onRefresh = {},
),
notifications = persistentListOf(WalletNotification.LikeTangemApp(onClick = {})),
buttons = manageButtons.map(WalletManageButton::config).toPersistentList(),
bottomSheet = bottomSheet,
marketPriceBlockState = MarketPriceBlockState.Content(
currencyName = "BTC",
price = "98900.12$",
priceChangeConfig = PriceChangeConfig(
valueInPercent = "5.16%",
type = PriceChangeConfig.Type.UP,
header = OrganizeTokensStateHolder.HeaderConfig(
onSortByBalanceClick = {},
onGroupByNetworkClick = {},
),
),
txHistoryState = WalletTxHistoryState.Content(
flowOf(
PagingData.from(
listOf(
WalletTxHistoryState.TxHistoryItemState.Title(onExploreClick = {}),
WalletTxHistoryState.TxHistoryItemState.GroupTitle("Today"),
WalletTxHistoryState.TxHistoryItemState.Transaction(
TransactionState.Sending(
address = "33BddS...ga2B",
amount = "-0.500913 BTC",
timestamp = "8:41",
),
dragConfig = OrganizeTokensStateHolder.DragConfig(
onItemDragged = { _, _ -> },
onDragStart = {},
canDragItemOver = { _, _ -> false },
onItemDragEnd = {},
),
actions = OrganizeTokensStateHolder.ActionsConfig(
onApplyClick = {},
onCancelClick = {},
),
)
}
val organizeTokensState by lazy {
groupedOrganizeTokensState.copy(
itemsState = OrganizeTokensListState.Ungrouped(
items = draggableTokens,
),
)
}
val bottomSheet by lazy {
WalletBottomSheetConfig(
isShow = false,
onDismissRequest = {},
content = WalletBottomSheetConfig.BottomSheetContentConfig.UnlockWallets(
onUnlockClick = {},
onScanClick = {},
),
)
}
private val manageButtons by lazy {
persistentListOf(
WalletManageButton.Buy(onClick = {}),
WalletManageButton.Send(onClick = {}),
WalletManageButton.Receive(onClick = {}),
WalletManageButton.Exchange(onClick = {}),
WalletManageButton.CopyAddress(onClick = {}),
)
}
val multicurrencyWalletScreenState by lazy {
WalletStateHolder.MultiCurrencyContent(
onBackClick = {},
topBarConfig = walletTopBarConfig,
walletsListConfig = walletListConfig,
tokensListState = WalletTokensListState.Content(
persistentListOf(
WalletTokensListState.TokensListItemState.NetworkGroupTitle("Bitcoin"),
WalletTokensListState.TokensListItemState.Token(
tokenItemVisibleState.copy(
id = "token_1",
name = "Ethereum",
tokenIconResId = R.drawable.img_eth_22,
networkIconResId = null,
amount = "1,89340821 ETH",
),
WalletTxHistoryState.TxHistoryItemState.GroupTitle("Yesterday"),
WalletTxHistoryState.TxHistoryItemState.Transaction(
TransactionState.Sending(
address = "33BddS...ga2B",
amount = "-0.500913 BTC",
timestamp = "8:41",
),
WalletTokensListState.TokensListItemState.Token(
tokenItemVisibleState.copy(
id = "token_2",
name = "Ethereum",
tokenIconResId = R.drawable.img_eth_22,
networkIconResId = null,
amount = "1,89340821 ETH",
),
),
WalletTokensListState.TokensListItemState.Token(
tokenItemVisibleState.copy(
id = "token_3",
name = "Ethereum",
tokenIconResId = R.drawable.img_eth_22,
networkIconResId = null,
amount = "1,89340821 ETH",
),
),
WalletTokensListState.TokensListItemState.Token(
tokenItemVisibleState.copy(
id = "token_4",
name = "Ethereum",
tokenIconResId = R.drawable.img_eth_22,
networkIconResId = null,
amount = "1,89340821 ETH",
),
),
WalletTokensListState.TokensListItemState.NetworkGroupTitle("Ethereum"),
WalletTokensListState.TokensListItemState.Token(
tokenItemVisibleState.copy(
id = "token_5",
name = "Ethereum",
tokenIconResId = R.drawable.img_eth_22,
networkIconResId = null,
amount = "1,89340821 ETH",
),
),
),
onOrganizeTokensClick = {},
),
pullToRefreshConfig = WalletPullToRefreshConfig(
isRefreshing = false,
onRefresh = {},
),
notifications = persistentListOf(
WalletNotification.UnreachableNetworks,
WalletNotification.LikeTangemApp(onClick = {}),
WalletNotification.BackupCard(onClick = {}),
WalletNotification.ScanCard(onClick = {}),
),
bottomSheetConfig = bottomSheet,
)
}
val singleWalletScreenState by lazy {
WalletStateHolder.SingleCurrencyContent(
onBackClick = {},
topBarConfig = walletTopBarConfig,
walletsListConfig = walletListConfig,
pullToRefreshConfig = WalletPullToRefreshConfig(
isRefreshing = false,
onRefresh = {},
),
notifications = persistentListOf(WalletNotification.LikeTangemApp(onClick = {})),
buttons = manageButtons.map(WalletManageButton::config).toPersistentList(),
bottomSheetConfig = bottomSheet,
marketPriceBlockState = MarketPriceBlockState.Content(
currencyName = "BTC",
price = "98900.12$",
priceChangeConfig = PriceChangeConfig(
valueInPercent = "5.16%",
type = PriceChangeConfig.Type.UP,
),
),
txHistoryState = WalletTxHistoryState.Content(
flowOf(
PagingData.from(
listOf(
WalletTxHistoryState.TxHistoryItemState.Title(onExploreClick = {}),
WalletTxHistoryState.TxHistoryItemState.GroupTitle("Today"),
WalletTxHistoryState.TxHistoryItemState.Transaction(
TransactionState.Sending(
address = "33BddS...ga2B",
amount = "-0.500913 BTC",
timestamp = "8:41",
),
),
WalletTxHistoryState.TxHistoryItemState.GroupTitle("Yesterday"),
WalletTxHistoryState.TxHistoryItemState.Transaction(
TransactionState.Sending(
address = "33BddS...ga2B",
amount = "-0.500913 BTC",
timestamp = "8:41",
),
),
),
),
),
),
),
)
)
}
}

View file

@ -26,7 +26,7 @@ internal sealed class WalletStateHolder(
open val walletsListConfig: WalletsListConfig,
open val pullToRefreshConfig: WalletPullToRefreshConfig,
open val notifications: ImmutableList<WalletNotification>,
open val bottomSheet: WalletBottomSheetConfig? = null,
open val bottomSheetConfig: WalletBottomSheetConfig? = null,
) {
fun copySealed(
@ -35,7 +35,7 @@ internal sealed class WalletStateHolder(
walletsListConfig: WalletsListConfig = this.walletsListConfig,
pullToRefreshConfig: WalletPullToRefreshConfig = this.pullToRefreshConfig,
notifications: ImmutableList<WalletNotification> = this.notifications,
bottomSheet: WalletBottomSheetConfig? = this.bottomSheet,
bottomSheet: WalletBottomSheetConfig? = this.bottomSheetConfig,
): WalletStateHolder {
return when (this) {
is MultiCurrencyContent -> this.copy(
@ -44,7 +44,7 @@ internal sealed class WalletStateHolder(
walletsListConfig = walletsListConfig,
pullToRefreshConfig = pullToRefreshConfig,
notifications = notifications,
bottomSheet = bottomSheet,
bottomSheetConfig = bottomSheet,
)
is SingleCurrencyContent -> this.copy(
onBackClick = onBackClick,
@ -52,7 +52,7 @@ internal sealed class WalletStateHolder(
walletsListConfig = walletsListConfig,
pullToRefreshConfig = pullToRefreshConfig,
notifications = notifications,
bottomSheet = bottomSheet,
bottomSheetConfig = bottomSheet,
)
is UnlockWalletContent -> this.copy(
onBackClick = onBackClick,
@ -80,7 +80,7 @@ internal sealed class WalletStateHolder(
override val walletsListConfig: WalletsListConfig,
override val pullToRefreshConfig: WalletPullToRefreshConfig,
override val notifications: ImmutableList<WalletNotification>,
override val bottomSheet: WalletBottomSheetConfig? = null,
override val bottomSheetConfig: WalletBottomSheetConfig? = null,
val tokensListState: WalletTokensListState,
) : WalletStateHolder(
onBackClick = onBackClick,
@ -88,7 +88,7 @@ internal sealed class WalletStateHolder(
walletsListConfig = walletsListConfig,
pullToRefreshConfig = pullToRefreshConfig,
notifications = notifications,
bottomSheet = bottomSheet,
bottomSheetConfig = bottomSheetConfig,
)
/**
@ -109,7 +109,7 @@ internal sealed class WalletStateHolder(
override val walletsListConfig: WalletsListConfig,
override val pullToRefreshConfig: WalletPullToRefreshConfig,
override val notifications: ImmutableList<WalletNotification>,
override val bottomSheet: WalletBottomSheetConfig? = null,
override val bottomSheetConfig: WalletBottomSheetConfig? = null,
val buttons: ImmutableList<ActionButtonConfig>,
val marketPriceBlockState: MarketPriceBlockState,
val txHistoryState: WalletTxHistoryState,
@ -119,7 +119,7 @@ internal sealed class WalletStateHolder(
walletsListConfig = walletsListConfig,
pullToRefreshConfig = pullToRefreshConfig,
notifications = notifications,
bottomSheet = bottomSheet,
bottomSheetConfig = bottomSheetConfig,
)
/**
@ -151,7 +151,7 @@ internal sealed class WalletStateHolder(
walletsListConfig = walletsListConfig,
pullToRefreshConfig = pullToRefreshConfig,
notifications = persistentListOf(WalletNotification.UnlockWallets(onUnlockWalletsNotificationClick)),
bottomSheet = WalletBottomSheetConfig(
bottomSheetConfig = WalletBottomSheetConfig(
isShow = false,
onDismissRequest = onBottomSheetDismissRequest,
content = WalletBottomSheetConfig.BottomSheetContentConfig.UnlockWallets(
@ -183,6 +183,6 @@ internal sealed class WalletStateHolder(
),
pullToRefreshConfig = WalletPullToRefreshConfig(isRefreshing = false, onRefresh = {}),
notifications = persistentListOf(),
bottomSheet = null,
bottomSheetConfig = null,
)
}

View file

@ -49,7 +49,7 @@ internal class WalletSkeletonStateConverter(
onOrganizeTokensClick = clickIntents::onOrganizeTokensClick,
),
notifications = persistentListOf(),
bottomSheet = null,
bottomSheetConfig = null,
)
}
@ -63,7 +63,7 @@ internal class WalletSkeletonStateConverter(
walletsListConfig = createWalletsListConfig(wallets),
pullToRefreshConfig = createPullToRefreshConfig(),
notifications = persistentListOf(),
bottomSheet = null,
bottomSheetConfig = null,
buttons = WalletPreviewData.singleWalletScreenState.buttons, // TODO: create buttons
marketPriceBlockState = MarketPriceBlockState.Loading(
currencyName = cardTypeResolver.getBlockchain().currency,

View file

@ -93,7 +93,9 @@ internal class WalletStateFactory(
state.copySealed(
bottomSheet = WalletBottomSheetConfig(
isShow = true,
onDismissRequest = { state.copySealed(bottomSheet = state.bottomSheet?.copy(isShow = false)) },
onDismissRequest = {
state.copySealed(bottomSheet = state.bottomSheetConfig?.copy(isShow = false))
},
content = content,
),
)

View file

@ -69,7 +69,7 @@ internal class WalletLoadedTxHistoryConverter(
walletsListConfig = walletsListConfig,
pullToRefreshConfig = pullToRefreshConfig,
notifications = notifications,
bottomSheet = bottomSheet,
bottomSheetConfig = bottomSheetConfig,
buttons = getButtons(),
marketPriceBlockState = getLoadingMarketPriceBlockState(),
txHistoryState = txHistoryState,

View file

@ -76,7 +76,7 @@ internal class WalletLoadingTxHistoryConverter(
walletsListConfig = walletsListConfig,
pullToRefreshConfig = pullToRefreshConfig,
notifications = notifications,
bottomSheet = bottomSheet,
bottomSheetConfig = bottomSheetConfig,
buttons = getButtons(),
marketPriceBlockState = getLoadingMarketPriceBlockState(),
txHistoryState = txHistoryState,

View file

@ -6,8 +6,6 @@ import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.*
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.PullRefreshState
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.*
@ -17,22 +15,16 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import com.tangem.core.ui.components.buttons.HorizontalActionChips
import com.tangem.core.ui.components.marketprice.MarketPriceBlock
import com.tangem.core.ui.components.notifications.Notification
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder
import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTxHistoryState
import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletBottomSheet
import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletTopBar
import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletsList
import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency.OrganizeTokensButton
import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency.tokensListItems
import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.txHistoryItems
import com.tangem.feature.wallet.presentation.wallet.ui.utils.ScrollOffsetCollector
import com.tangem.feature.wallet.presentation.wallet.ui.components.common.*
import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency.organizeButton
import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.controlButtons
import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.marketPriceBlock
import com.tangem.feature.wallet.presentation.wallet.ui.utils.changeWalletAnimator
/**
@ -43,10 +35,10 @@ import com.tangem.feature.wallet.presentation.wallet.ui.utils.changeWalletAnimat
[REDACTED_AUTHOR]
*/
@OptIn(ExperimentalMaterialApi::class)
@Suppress("LongMethod")
@Composable
internal fun WalletScreen(state: WalletStateHolder) {
BackHandler(onBack = state.onBackClick)
val walletsListState = rememberLazyListState()
Scaffold(
@ -54,7 +46,7 @@ internal fun WalletScreen(state: WalletStateHolder) {
containerColor = TangemTheme.colors.background.secondary,
) { scaffoldPaddings ->
val changeableItemModifier = Modifier.changeWalletAnimator(walletsListState)
val movableItemModifier = Modifier.changeWalletAnimator(walletsListState)
val pullRefreshState = rememberPullRefreshState(
refreshing = state.pullToRefreshConfig.isRefreshing,
onRefresh = state.pullToRefreshConfig.onRefresh,
@ -65,76 +57,50 @@ internal fun WalletScreen(state: WalletStateHolder) {
.padding(paddingValues = scaffoldPaddings)
.pullRefresh(pullRefreshState),
) {
val txHistoryItems = if (state is WalletStateHolder.SingleCurrencyContent) {
if (state.txHistoryState is WalletTxHistoryState.ContentState) {
state.txHistoryState.items.collectAsLazyPagingItems()
} else {
null
}
val txHistoryItems = if (state is WalletStateHolder.SingleCurrencyContent &&
state.txHistoryState is WalletTxHistoryState.ContentState
) {
(state.txHistoryState as? WalletTxHistoryState.ContentState)?.items?.collectAsLazyPagingItems()
} else {
null
}
val betweenItemsPadding = TangemTheme.dimens.spacing14
val horizontalPadding = TangemTheme.dimens.spacing16
val itemModifier = movableItemModifier
.padding(top = betweenItemsPadding)
.padding(horizontal = horizontalPadding)
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(vertical = TangemTheme.dimens.spacing8),
horizontalAlignment = Alignment.CenterHorizontally,
) {
item {
WalletsList(
config = state.walletsListConfig,
lazyListState = walletsListState,
WalletsList(config = state.walletsListConfig, lazyListState = walletsListState)
}
if (state is WalletStateHolder.SingleCurrencyContent) {
controlButtons(
configs = state.buttons,
modifier = movableItemModifier.padding(top = betweenItemsPadding),
)
}
if (state is WalletStateHolder.SingleCurrencyContent) {
item {
HorizontalActionChips(
buttons = state.buttons,
modifier = changeableItemModifier.padding(top = TangemTheme.dimens.spacing14),
contentPadding = PaddingValues(horizontal = TangemTheme.dimens.spacing16),
)
}
}
items(
items = state.notifications,
itemContent = { item ->
Notification(
state = item.state,
modifier = changeableItemModifier
.padding(top = TangemTheme.dimens.spacing14)
.padding(horizontal = TangemTheme.dimens.spacing16),
)
},
)
notifications(configs = state.notifications, modifier = itemModifier)
if (state is WalletStateHolder.SingleCurrencyContent) {
item {
MarketPriceBlock(
state = state.marketPriceBlockState,
modifier = changeableItemModifier
.padding(top = TangemTheme.dimens.spacing14)
.padding(horizontal = TangemTheme.dimens.spacing16),
)
}
marketPriceBlock(state = state.marketPriceBlockState, modifier = itemModifier)
}
contentItems(state = state, txHistoryItems = txHistoryItems, modifier = changeableItemModifier)
contentItems(state = state, txHistoryItems = txHistoryItems, modifier = movableItemModifier)
if (state is WalletStateHolder.MultiCurrencyContent) {
item {
OrganizeTokensButton(
onClick = state.tokensListState.onOrganizeTokensClick,
modifier = changeableItemModifier
.padding(top = TangemTheme.dimens.spacing14)
.padding(horizontal = TangemTheme.dimens.spacing16),
)
}
organizeButton(onClick = state.tokensListState.onOrganizeTokensClick, modifier = itemModifier)
}
}
PullToRefreshIndicator(
WalletPullToRefreshIndicator(
isRefreshing = state.pullToRefreshConfig.isRefreshing,
state = pullRefreshState,
modifier = Modifier.align(Alignment.TopCenter),
@ -142,46 +108,12 @@ internal fun WalletScreen(state: WalletStateHolder) {
}
}
state.bottomSheet?.let { bottomSheetConfig ->
if (bottomSheetConfig.isShow) WalletBottomSheet(config = bottomSheetConfig)
val bottomSheetConfig = state.bottomSheetConfig
if (bottomSheetConfig != null && bottomSheetConfig.isShow) {
WalletBottomSheet(config = bottomSheetConfig)
}
LaunchedEffect(key1 = walletsListState, key2 = state.walletsListConfig.onWalletChange) {
snapshotFlow { walletsListState.layoutInfo.visibleItemsInfo }
.collect(collector = ScrollOffsetCollector(callback = state.walletsListConfig.onWalletChange))
}
}
private fun LazyListScope.contentItems(
state: WalletStateHolder,
txHistoryItems: LazyPagingItems<WalletTxHistoryState.TxHistoryItemState>?,
modifier: Modifier = Modifier,
) {
when (state) {
is WalletStateHolder.MultiCurrencyContent -> {
tokensListItems(state = state.tokensListState, modifier = modifier)
}
is WalletStateHolder.SingleCurrencyContent -> {
txHistoryItems(
state = state.txHistoryState,
txHistoryItems = txHistoryItems,
modifier = modifier,
)
}
is WalletStateHolder.Loading,
is WalletStateHolder.UnlockWalletContent,
-> Unit
}
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
private fun PullToRefreshIndicator(isRefreshing: Boolean, state: PullRefreshState, modifier: Modifier = Modifier) {
PullRefreshIndicator(
refreshing = isRefreshing,
state = state,
modifier = modifier,
)
WalletSideEffects(lazyListState = walletsListState, walletsListConfig = state.walletsListConfig)
}
// region Preview

View file

@ -11,6 +11,9 @@ import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.tooling.preview.Preview
@ -18,23 +21,25 @@ import androidx.compose.ui.unit.dp
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
import com.tangem.feature.wallet.presentation.wallet.state.WalletsListConfig
import com.tangem.feature.wallet.presentation.wallet.ui.components.common.WalletCard
/**
* Wallets list component
*
* @param config config
* @param modifier modifier
* @param config config
* @param lazyListState main content container list state
*
[REDACTED_AUTHOR]
*/
@OptIn(ExperimentalFoundationApi::class)
@Composable
internal fun WalletsList(config: WalletsListConfig, lazyListState: LazyListState, modifier: Modifier = Modifier) {
internal fun WalletsList(config: WalletsListConfig, lazyListState: LazyListState) {
val horizontalCardPadding = TangemTheme.dimens.spacing16
val itemWidth = LocalConfiguration.current.screenWidthDp.dp - horizontalCardPadding * 2
val screenWidth = LocalConfiguration.current.screenWidthDp.dp
val itemWidth by remember(screenWidth) { derivedStateOf { screenWidth - horizontalCardPadding * 2 } }
LazyRow(
modifier = modifier.background(color = TangemTheme.colors.background.secondary),
modifier = Modifier.background(color = TangemTheme.colors.background.secondary),
state = lazyListState,
contentPadding = PaddingValues(horizontal = TangemTheme.dimens.spacing16),
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
@ -48,22 +53,16 @@ internal fun WalletsList(config: WalletsListConfig, lazyListState: LazyListState
@Preview
@Composable
private fun Preview_WalletHeader_LightTheme() {
private fun Preview_WalletsList_LightTheme() {
TangemTheme(isDark = false) {
WalletsList(
config = WalletPreviewData.walletListConfig,
lazyListState = rememberLazyListState(),
)
WalletsList(config = WalletPreviewData.walletListConfig, lazyListState = rememberLazyListState())
}
}
@Preview
@Composable
private fun Preview_WalletHeader_DarkTheme() {
private fun Preview_WalletsList_DarkTheme() {
TangemTheme(isDark = true) {
WalletsList(
config = WalletPreviewData.walletListConfig,
lazyListState = rememberLazyListState(),
)
WalletsList(config = WalletPreviewData.walletListConfig, lazyListState = rememberLazyListState())
}
}

View file

@ -1,4 +1,4 @@
package com.tangem.feature.wallet.presentation.wallet.ui.components
package com.tangem.feature.wallet.presentation.wallet.ui.components.common
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*

View file

@ -1,4 +1,4 @@
package com.tangem.feature.wallet.presentation.wallet.ui.components
package com.tangem.feature.wallet.presentation.wallet.ui.components.common
import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image
@ -31,6 +31,8 @@ private const val DOTS = "•••"
*
* @param state state
* @param modifier modifier
*
[REDACTED_AUTHOR]
*/
@Composable
internal fun WalletCard(state: WalletCardState, modifier: Modifier = Modifier) {

View file

@ -0,0 +1,32 @@
package com.tangem.feature.wallet.presentation.wallet.ui.components.common
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.ui.Modifier
import androidx.paging.compose.LazyPagingItems
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder
import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTxHistoryState
import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency.tokensListItems
import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.txHistoryItems
/**
* Wallet content
*
* @param state wallet state
* @param txHistoryItems transaction history items
* @param modifier modifier
*
[REDACTED_AUTHOR]
*/
internal fun LazyListScope.contentItems(
state: WalletStateHolder,
txHistoryItems: LazyPagingItems<WalletTxHistoryState.TxHistoryItemState>?,
modifier: Modifier = Modifier,
) {
when (state) {
is WalletStateHolder.MultiCurrencyContent -> tokensListItems(state.tokensListState, modifier)
is WalletStateHolder.SingleCurrencyContent -> txHistoryItems(state.txHistoryState, txHistoryItems, modifier)
is WalletStateHolder.Loading,
is WalletStateHolder.UnlockWalletContent,
-> Unit
}
}

View file

@ -0,0 +1,20 @@
package com.tangem.feature.wallet.presentation.wallet.ui.components.common
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items
import androidx.compose.ui.Modifier
import com.tangem.core.ui.components.notifications.Notification
import com.tangem.feature.wallet.presentation.wallet.state.WalletNotification
import kotlinx.collections.immutable.ImmutableList
/**
* Wallet notifications
*
* @param configs list of notifications
* @param modifier modifier
*
[REDACTED_AUTHOR]
*/
internal fun LazyListScope.notifications(configs: ImmutableList<WalletNotification>, modifier: Modifier = Modifier) {
items(items = configs, itemContent = { Notification(state = it.state, modifier = modifier) })
}

View file

@ -0,0 +1,26 @@
package com.tangem.feature.wallet.presentation.wallet.ui.components.common
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.PullRefreshState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
/**
* "Pull to refresh" indicator
*
* @param isRefreshing indicator is currently refreshing or not
* @param state indicator state
* @param modifier modifier
*
[REDACTED_AUTHOR]
*/
@OptIn(ExperimentalMaterialApi::class)
@Composable
internal fun WalletPullToRefreshIndicator(
isRefreshing: Boolean,
state: PullRefreshState,
modifier: Modifier = Modifier,
) {
PullRefreshIndicator(refreshing = isRefreshing, state = state, modifier = modifier)
}

View file

@ -0,0 +1,36 @@
package com.tangem.feature.wallet.presentation.wallet.ui.components.common
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.snapshotFlow
import com.tangem.feature.wallet.presentation.wallet.state.WalletsListConfig
import com.tangem.feature.wallet.presentation.wallet.ui.utils.ScrollOffsetCollector
/**
* Wallet screen side effects
*
* @param lazyListState lazy list state
* @param walletsListConfig wallets list config
*
[REDACTED_AUTHOR]
*/
@Composable
internal fun WalletSideEffects(lazyListState: LazyListState, walletsListConfig: WalletsListConfig) {
LaunchedEffect(key1 = walletsListConfig.selectedWalletIndex) {
lazyListState.scrollToItem(walletsListConfig.selectedWalletIndex)
}
val dragInteraction = lazyListState.interactionSource.interactions.collectAsState(initial = null)
LaunchedEffect(key1 = lazyListState, key2 = walletsListConfig.onWalletChange) {
snapshotFlow { lazyListState.layoutInfo.visibleItemsInfo }
.collect(
collector = ScrollOffsetCollector(
lazyListState = lazyListState,
dragInteraction = dragInteraction,
callback = walletsListConfig.onWalletChange,
),
)
}
}

View file

@ -1,4 +1,4 @@
package com.tangem.feature.wallet.presentation.wallet.ui.components
package com.tangem.feature.wallet.presentation.wallet.ui.components.common
import androidx.compose.material3.*
import androidx.compose.runtime.Composable

View file

@ -0,0 +1,16 @@
package com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.ui.Modifier
/**
* Organize tokens button
*
* @param onClick callback is invoked when button is clicked
* @param modifier modifier
*
[REDACTED_AUTHOR]
*/
internal fun LazyListScope.organizeButton(onClick: (() -> Unit)?, modifier: Modifier = Modifier) {
item { OrganizeTokensButton(onClick = onClick, modifier = modifier) }
}

View file

@ -0,0 +1,27 @@
package com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.ui.Modifier
import com.tangem.core.ui.components.buttons.HorizontalActionChips
import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
import com.tangem.core.ui.res.TangemTheme
import kotlinx.collections.immutable.ImmutableList
/**
* Single currency control buttons. Like, "Buy", "Sell", etc
*
* @param configs list of buttons
* @param modifier modifier
*
[REDACTED_AUTHOR]
*/
internal fun LazyListScope.controlButtons(configs: ImmutableList<ActionButtonConfig>, modifier: Modifier = Modifier) {
item {
HorizontalActionChips(
buttons = configs,
modifier = modifier,
contentPadding = PaddingValues(horizontal = TangemTheme.dimens.spacing16),
)
}
}

View file

@ -0,0 +1,18 @@
package com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.ui.Modifier
import com.tangem.core.ui.components.marketprice.MarketPriceBlock
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
/**
* Single currency market price block
*
* @param state component state
* @param modifier modifier
*
[REDACTED_AUTHOR]
*/
internal fun LazyListScope.marketPriceBlock(state: MarketPriceBlockState, modifier: Modifier = Modifier) {
item { MarketPriceBlock(state = state, modifier = modifier) }
}

View file

@ -1,6 +1,9 @@
package com.tangem.feature.wallet.presentation.wallet.ui.utils
import androidx.compose.foundation.interaction.Interaction
import androidx.compose.foundation.lazy.LazyListItemInfo
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.runtime.State
import kotlinx.coroutines.flow.FlowCollector
import kotlin.math.abs
@ -9,17 +12,22 @@ import kotlin.math.abs
* If first visible item offset is greater than half item size, then [callback] be invoked.
* If last visible item offset is greater than half item size, then [callback] be invoked.
*
* @property callback lambda be invoked when current scroll items is changed
* @property lazyListState lazy list state
* @property dragInteraction current drag interaction
* @property callback lambda be invoked when current scroll items is changed
*
[REDACTED_AUTHOR]
*/
internal class ScrollOffsetCollector(
private val lazyListState: LazyListState,
private val dragInteraction: State<Interaction?>,
private val callback: (Int) -> Unit,
) : FlowCollector<List<LazyListItemInfo>> {
private val LazyListItemInfo.halfItemSize get() = size.div(other = 2)
override suspend fun emit(value: List<LazyListItemInfo>) {
if (!lazyListState.isScrollInProgress || dragInteraction.value == null || value.size <= 1) return
val firstItem = value.firstOrNull() ?: return
val lastItem = value.lastOrNull() ?: return

View file

@ -20,7 +20,7 @@ internal class TokenListErrorToWalletStateConverter(
walletsListConfig = state.walletsListConfig,
pullToRefreshConfig = state.pullToRefreshConfig,
notifications = state.notifications,
bottomSheet = state.bottomSheet,
bottomSheetConfig = state.bottomSheetConfig,
tokensListState = WalletTokensListState.Content(items = persistentListOf(), onOrganizeTokensClick = null),
)
}

View file

@ -50,7 +50,7 @@ internal class TokenListToWalletStateConverter(
walletsListConfig = walletsListConfig,
pullToRefreshConfig = pullToRefreshConfig,
notifications = notifications,
bottomSheet = bottomSheet,
bottomSheetConfig = bottomSheetConfig,
tokensListState = tokenListToContentConverter.convert(value = tokenList),
)
}