Updated on 2026-08-14
This commit is contained in:
parent
229dc89bcb
commit
3398db66e2
13 changed files with 609 additions and 156 deletions
|
|
@ -14,7 +14,8 @@ import androidx.compose.ui.util.fastForEach
|
||||||
import androidx.paging.compose.LazyPagingItems
|
import androidx.paging.compose.LazyPagingItems
|
||||||
import androidx.paging.compose.itemContentType
|
import androidx.paging.compose.itemContentType
|
||||||
import androidx.paging.compose.itemKey
|
import androidx.paging.compose.itemKey
|
||||||
import com.tangem.core.ui.components.transactions.empty.EmptyTransactionBlock
|
import com.tangem.core.ui.R
|
||||||
|
import com.tangem.core.ui.components.transactions.empty.EmptyTransactionBlockLegacy
|
||||||
import com.tangem.core.ui.components.transactions.empty.EmptyTransactionsBlockState
|
import com.tangem.core.ui.components.transactions.empty.EmptyTransactionsBlockState
|
||||||
import com.tangem.core.ui.components.transactions.state.TransactionState
|
import com.tangem.core.ui.components.transactions.state.TransactionState
|
||||||
import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
||||||
|
|
@ -45,13 +46,21 @@ fun LazyListScope.txHistoryItems(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is TxHistoryState.Empty -> {
|
is TxHistoryState.Empty -> {
|
||||||
nonContentItem(state = EmptyTransactionsBlockState.Empty(state.onExploreClick), modifier = modifier)
|
nonContentItem(
|
||||||
|
state = EmptyTransactionsBlockState.Empty(
|
||||||
|
onExplore = state.onExploreClick,
|
||||||
|
exploreIconResId = R.drawable.ic_arrow_top_right_24,
|
||||||
|
),
|
||||||
|
modifier = modifier,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
is TxHistoryState.Error -> {
|
is TxHistoryState.Error -> {
|
||||||
nonContentItem(
|
nonContentItem(
|
||||||
state = EmptyTransactionsBlockState.FailedToLoad(
|
state = EmptyTransactionsBlockState.FailedToLoad(
|
||||||
onReload = state.onReloadClick,
|
onReload = state.onReloadClick,
|
||||||
onExplore = state.onExploreClick,
|
onExplore = state.onExploreClick,
|
||||||
|
reloadIconResId = R.drawable.ic_refresh_24,
|
||||||
|
exploreIconResId = R.drawable.ic_arrow_top_right_24,
|
||||||
),
|
),
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
|
@ -64,7 +73,10 @@ fun LazyListScope.txHistoryItems(
|
||||||
}
|
}
|
||||||
|
|
||||||
nonContentItem(
|
nonContentItem(
|
||||||
state = EmptyTransactionsBlockState.NotImplemented(onExplore = state.onExploreClick),
|
state = EmptyTransactionsBlockState.NotImplemented(
|
||||||
|
onExplore = state.onExploreClick,
|
||||||
|
exploreIconResId = R.drawable.ic_arrow_top_right_24,
|
||||||
|
),
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -121,7 +133,7 @@ fun PendingTxsBlock(pendingTxs: ImmutableList<TransactionState>, isBalanceHidden
|
||||||
|
|
||||||
private fun LazyListScope.nonContentItem(state: EmptyTransactionsBlockState, modifier: Modifier = Modifier) {
|
private fun LazyListScope.nonContentItem(state: EmptyTransactionsBlockState, modifier: Modifier = Modifier) {
|
||||||
item(key = state::class.java, contentType = state::class.java) {
|
item(key = state::class.java, contentType = state::class.java) {
|
||||||
EmptyTransactionBlock(
|
EmptyTransactionBlockLegacy(
|
||||||
state = state,
|
state = state,
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.animateItem(fadeInSpec = null, fadeOutSpec = null)
|
.animateItem(fadeInSpec = null, fadeOutSpec = null)
|
||||||
|
|
|
||||||
|
|
@ -1,65 +1,74 @@
|
||||||
package com.tangem.core.ui.components.transactions.empty
|
package com.tangem.core.ui.components.transactions.empty
|
||||||
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
|
||||||
import androidx.compose.ui.platform.testTag
|
import androidx.compose.ui.platform.testTag
|
||||||
import androidx.compose.ui.res.painterResource
|
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||||
import com.tangem.core.ui.components.buttons.actions.ActionButton
|
import com.tangem.core.ui.R
|
||||||
|
import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
|
||||||
|
import com.tangem.core.ui.ds.button.TangemButton
|
||||||
|
import com.tangem.core.ui.ds.button.TangemButtonIconPosition
|
||||||
|
import com.tangem.core.ui.ds.button.TangemButtonShape
|
||||||
|
import com.tangem.core.ui.ds.button.TangemButtonSize
|
||||||
|
import com.tangem.core.ui.ds.button.TangemButtonType
|
||||||
|
import com.tangem.core.ui.ds.button.TangemButtonUM
|
||||||
|
import com.tangem.core.ui.ds.image.TangemIcon
|
||||||
|
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||||
import com.tangem.core.ui.extensions.resolveReference
|
import com.tangem.core.ui.extensions.resolveReference
|
||||||
import com.tangem.core.ui.res.TangemThemePreview
|
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
|
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||||
import com.tangem.core.ui.test.EmptyTransactionBlockTestTags
|
import com.tangem.core.ui.test.EmptyTransactionBlockTestTags
|
||||||
|
|
||||||
/**
|
|
||||||
* Placeholder for transaction's block without content
|
|
||||||
*
|
|
||||||
* @param state component state
|
|
||||||
* @param modifier modifier
|
|
||||||
*/
|
|
||||||
@Composable
|
@Composable
|
||||||
fun EmptyTransactionBlock(state: EmptyTransactionsBlockState, modifier: Modifier = Modifier) {
|
fun EmptyTransactionBlock(state: EmptyTransactionsBlockState, modifier: Modifier = Modifier) {
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
.fillMaxWidth()
|
||||||
.background(color = TangemTheme.colors.background.primary)
|
.padding(vertical = TangemTheme.dimens2.x6)
|
||||||
.padding(vertical = TangemTheme.dimens.spacing24)
|
|
||||||
.testTag(EmptyTransactionBlockTestTags.BLOCK),
|
.testTag(EmptyTransactionBlockTestTags.BLOCK),
|
||||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing24),
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
) {
|
) {
|
||||||
Icon(
|
TangemIcon(
|
||||||
|
tangemIconUM = TangemIconUM.Icon(
|
||||||
|
iconRes = state.iconRes,
|
||||||
|
tintReference = { TangemTheme.colors2.graphic.neutral.tertiary },
|
||||||
|
),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(TangemTheme.dimens.size64)
|
.size(TangemTheme.dimens2.x16)
|
||||||
.testTag(EmptyTransactionBlockTestTags.ICON),
|
.testTag(EmptyTransactionBlockTestTags.ICON),
|
||||||
painter = painterResource(id = state.iconRes),
|
|
||||||
tint = TangemTheme.colors.icon.inactive,
|
|
||||||
contentDescription = null,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(TangemTheme.dimens2.x4))
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(horizontal = TangemTheme.dimens.spacing32)
|
.padding(horizontal = TangemTheme.dimens2.x8)
|
||||||
.testTag(EmptyTransactionBlockTestTags.TEXT),
|
.testTag(EmptyTransactionBlockTestTags.TEXT),
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
text = state.text.resolveReference(),
|
text = state.text.resolveReference(),
|
||||||
style = TangemTheme.typography.body2,
|
style = TangemTheme.typography2.calloutRegular15,
|
||||||
color = TangemTheme.colors.text.tertiary,
|
color = TangemTheme.colors2.text.neutral.tertiary,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(TangemTheme.dimens2.x8))
|
||||||
|
|
||||||
Buttons(
|
Buttons(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(horizontal = TangemTheme.dimens.spacing18)
|
.padding(horizontal = TangemTheme.dimens2.x4)
|
||||||
.testTag(EmptyTransactionBlockTestTags.EXPLORE_BUTTON),
|
.testTag(EmptyTransactionBlockTestTags.EXPLORE_BUTTON),
|
||||||
state = state.buttonsState,
|
state = state.buttonsState,
|
||||||
)
|
)
|
||||||
|
|
@ -76,41 +85,70 @@ private fun Buttons(state: EmptyTransactionsBlockState.ButtonsState, modifier: M
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun SingleButton(state: EmptyTransactionsBlockState.ButtonsState.SingleButton, modifier: Modifier = Modifier) {
|
private fun SingleButton(state: EmptyTransactionsBlockState.ButtonsState.SingleButton, modifier: Modifier = Modifier) {
|
||||||
ActionButton(modifier = modifier, config = state.actionButtonConfig)
|
Row(
|
||||||
|
modifier = modifier,
|
||||||
|
horizontalArrangement = Arrangement.Center,
|
||||||
|
) {
|
||||||
|
TangemButton(buttonUM = state.actionButtonConfig.toButtonUM())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun PairButtons(state: EmptyTransactionsBlockState.ButtonsState.PairButtons, modifier: Modifier = Modifier) {
|
private fun PairButtons(state: EmptyTransactionsBlockState.ButtonsState.PairButtons, modifier: Modifier = Modifier) {
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
|
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2),
|
||||||
) {
|
) {
|
||||||
ActionButton(
|
TangemButton(
|
||||||
modifier = Modifier.weight(1F),
|
modifier = Modifier.weight(1F),
|
||||||
config = state.firstButtonConfig,
|
buttonUM = state.firstButtonConfig.toButtonUM(),
|
||||||
)
|
)
|
||||||
ActionButton(
|
TangemButton(
|
||||||
modifier = Modifier.weight(1F),
|
modifier = Modifier.weight(1F),
|
||||||
config = state.secondButtonConfig,
|
buttonUM = state.secondButtonConfig.toButtonUM(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun ActionButtonConfig.toButtonUM(): TangemButtonUM = TangemButtonUM(
|
||||||
|
text = text,
|
||||||
|
tangemIconUM = TangemIconUM.Icon(iconRes = iconResId),
|
||||||
|
type = TangemButtonType.Secondary,
|
||||||
|
size = TangemButtonSize.X12,
|
||||||
|
isEnabled = isEnabled,
|
||||||
|
isLoading = isInProgress,
|
||||||
|
onClick = onClick,
|
||||||
|
shape = TangemButtonShape.Rounded,
|
||||||
|
iconPosition = TangemButtonIconPosition.End,
|
||||||
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Preview(widthDp = 360, showBackground = true)
|
@Preview(widthDp = 360, showBackground = true)
|
||||||
@Preview(widthDp = 360, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
@Preview(widthDp = 360, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
private fun EmptyTransactionBlockPreview(
|
private fun EmptyTransactionBlockPreview(
|
||||||
@PreviewParameter(EmptyTransactionBlockStateProvider::class) state: EmptyTransactionsBlockState,
|
@PreviewParameter(EmptyTransactionBlockStateProvider::class) state: EmptyTransactionsBlockState,
|
||||||
) {
|
) {
|
||||||
TangemThemePreview {
|
TangemThemePreviewRedesign {
|
||||||
EmptyTransactionBlock(state = state)
|
EmptyTransactionBlock(state = state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class EmptyTransactionBlockStateProvider : CollectionPreviewParameterProvider<EmptyTransactionsBlockState>(
|
private class EmptyTransactionBlockStateProvider :
|
||||||
collection = listOf(
|
CollectionPreviewParameterProvider<EmptyTransactionsBlockState>(
|
||||||
EmptyTransactionsBlockState.Empty {},
|
collection = listOf(
|
||||||
EmptyTransactionsBlockState.FailedToLoad(onReload = {}, onExplore = {}),
|
EmptyTransactionsBlockState.Empty(
|
||||||
EmptyTransactionsBlockState.NotImplemented(onExplore = {}),
|
onExplore = {},
|
||||||
),
|
exploreIconResId = R.drawable.ic_compass_24,
|
||||||
)
|
),
|
||||||
|
EmptyTransactionsBlockState.FailedToLoad(
|
||||||
|
onReload = {},
|
||||||
|
onExplore = {},
|
||||||
|
reloadIconResId = R.drawable.ic_refresh_24,
|
||||||
|
exploreIconResId = R.drawable.ic_compass_24,
|
||||||
|
),
|
||||||
|
EmptyTransactionsBlockState.NotImplemented(
|
||||||
|
onExplore = {},
|
||||||
|
exploreIconResId = R.drawable.ic_compass_24,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,129 @@
|
||||||
|
package com.tangem.core.ui.components.transactions.empty
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.platform.testTag
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
|
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||||
|
import com.tangem.core.ui.R
|
||||||
|
import com.tangem.core.ui.components.buttons.actions.ActionButton
|
||||||
|
import com.tangem.core.ui.extensions.resolveReference
|
||||||
|
import com.tangem.core.ui.res.TangemThemePreview
|
||||||
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
|
import com.tangem.core.ui.test.EmptyTransactionBlockTestTags
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Placeholder for transaction's block without content
|
||||||
|
*
|
||||||
|
* @param state component state
|
||||||
|
* @param modifier modifier
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun EmptyTransactionBlockLegacy(state: EmptyTransactionsBlockState, modifier: Modifier = Modifier) {
|
||||||
|
Column(
|
||||||
|
modifier = modifier
|
||||||
|
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||||
|
.background(color = TangemTheme.colors.background.primary)
|
||||||
|
.padding(vertical = TangemTheme.dimens.spacing24)
|
||||||
|
.testTag(EmptyTransactionBlockTestTags.BLOCK),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing24),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(TangemTheme.dimens.size64)
|
||||||
|
.testTag(EmptyTransactionBlockTestTags.ICON),
|
||||||
|
painter = painterResource(id = state.iconRes),
|
||||||
|
tint = TangemTheme.colors.icon.inactive,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = TangemTheme.dimens.spacing32)
|
||||||
|
.testTag(EmptyTransactionBlockTestTags.TEXT),
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
text = state.text.resolveReference(),
|
||||||
|
style = TangemTheme.typography.body2,
|
||||||
|
color = TangemTheme.colors.text.tertiary,
|
||||||
|
)
|
||||||
|
|
||||||
|
Buttons(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = TangemTheme.dimens.spacing18)
|
||||||
|
.testTag(EmptyTransactionBlockTestTags.EXPLORE_BUTTON),
|
||||||
|
state = state.buttonsState,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun Buttons(state: EmptyTransactionsBlockState.ButtonsState, modifier: Modifier = Modifier) {
|
||||||
|
when (state) {
|
||||||
|
is EmptyTransactionsBlockState.ButtonsState.SingleButton -> SingleButton(state = state, modifier = modifier)
|
||||||
|
is EmptyTransactionsBlockState.ButtonsState.PairButtons -> PairButtons(state = state, modifier = modifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun SingleButton(state: EmptyTransactionsBlockState.ButtonsState.SingleButton, modifier: Modifier = Modifier) {
|
||||||
|
ActionButton(modifier = modifier, config = state.actionButtonConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun PairButtons(state: EmptyTransactionsBlockState.ButtonsState.PairButtons, modifier: Modifier = Modifier) {
|
||||||
|
Row(
|
||||||
|
modifier = modifier,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
|
||||||
|
) {
|
||||||
|
ActionButton(
|
||||||
|
modifier = Modifier.weight(1F),
|
||||||
|
config = state.firstButtonConfig,
|
||||||
|
)
|
||||||
|
ActionButton(
|
||||||
|
modifier = Modifier.weight(1F),
|
||||||
|
config = state.secondButtonConfig,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@Preview(widthDp = 360, showBackground = true)
|
||||||
|
@Preview(widthDp = 360, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
private fun EmptyTransactionBlockLegacyPreview(
|
||||||
|
@PreviewParameter(EmptyTransactionBlockLegacyStateProvider::class) state: EmptyTransactionsBlockState,
|
||||||
|
) {
|
||||||
|
TangemThemePreview {
|
||||||
|
EmptyTransactionBlockLegacy(state = state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class EmptyTransactionBlockLegacyStateProvider :
|
||||||
|
CollectionPreviewParameterProvider<EmptyTransactionsBlockState>(
|
||||||
|
collection = listOf(
|
||||||
|
EmptyTransactionsBlockState.Empty(
|
||||||
|
onExplore = {},
|
||||||
|
exploreIconResId = R.drawable.ic_arrow_top_right_24,
|
||||||
|
),
|
||||||
|
EmptyTransactionsBlockState.FailedToLoad(
|
||||||
|
onReload = {},
|
||||||
|
onExplore = {},
|
||||||
|
reloadIconResId = R.drawable.ic_refresh_24,
|
||||||
|
exploreIconResId = R.drawable.ic_arrow_top_right_24,
|
||||||
|
),
|
||||||
|
EmptyTransactionsBlockState.NotImplemented(
|
||||||
|
onExplore = {},
|
||||||
|
exploreIconResId = R.drawable.ic_arrow_top_right_24,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.tangem.core.ui.components.transactions.empty
|
package com.tangem.core.ui.components.transactions.empty
|
||||||
|
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
import com.tangem.core.ui.R
|
import com.tangem.core.ui.R
|
||||||
import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
|
import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
|
||||||
import com.tangem.core.ui.extensions.TextReference
|
import com.tangem.core.ui.extensions.TextReference
|
||||||
|
|
@ -21,17 +22,19 @@ sealed class EmptyTransactionsBlockState(
|
||||||
data class FailedToLoad(
|
data class FailedToLoad(
|
||||||
val onReload: () -> Unit,
|
val onReload: () -> Unit,
|
||||||
val onExplore: () -> Unit,
|
val onExplore: () -> Unit,
|
||||||
|
@DrawableRes val reloadIconResId: Int,
|
||||||
|
@DrawableRes val exploreIconResId: Int,
|
||||||
) : EmptyTransactionsBlockState(
|
) : EmptyTransactionsBlockState(
|
||||||
buttonsState = ButtonsState.PairButtons(
|
buttonsState = ButtonsState.PairButtons(
|
||||||
firstButtonConfig = ActionButtonConfig(
|
firstButtonConfig = ActionButtonConfig(
|
||||||
text = TextReference.Res(R.string.common_reload),
|
text = TextReference.Res(R.string.common_reload),
|
||||||
iconResId = R.drawable.ic_refresh_24,
|
iconResId = reloadIconResId,
|
||||||
onClick = onReload,
|
onClick = onReload,
|
||||||
isEnabled = true,
|
isEnabled = true,
|
||||||
),
|
),
|
||||||
secondButtonConfig = ActionButtonConfig(
|
secondButtonConfig = ActionButtonConfig(
|
||||||
text = TextReference.Res(R.string.common_explore),
|
text = TextReference.Res(R.string.common_explore),
|
||||||
iconResId = R.drawable.ic_arrow_top_right_24,
|
iconResId = exploreIconResId,
|
||||||
onClick = onExplore,
|
onClick = onExplore,
|
||||||
isEnabled = true,
|
isEnabled = true,
|
||||||
),
|
),
|
||||||
|
|
@ -40,11 +43,14 @@ sealed class EmptyTransactionsBlockState(
|
||||||
text = TextReference.Res(R.string.transaction_history_error_failed_to_load),
|
text = TextReference.Res(R.string.transaction_history_error_failed_to_load),
|
||||||
)
|
)
|
||||||
|
|
||||||
data class Empty(val onExplore: (() -> Unit)) : EmptyTransactionsBlockState(
|
data class Empty(
|
||||||
|
val onExplore: () -> Unit,
|
||||||
|
@DrawableRes val exploreIconResId: Int,
|
||||||
|
) : EmptyTransactionsBlockState(
|
||||||
buttonsState = ButtonsState.SingleButton(
|
buttonsState = ButtonsState.SingleButton(
|
||||||
actionButtonConfig = ActionButtonConfig(
|
actionButtonConfig = ActionButtonConfig(
|
||||||
text = TextReference.Res(R.string.common_explore),
|
text = TextReference.Res(R.string.common_explore),
|
||||||
iconResId = R.drawable.ic_arrow_top_right_24,
|
iconResId = exploreIconResId,
|
||||||
onClick = onExplore,
|
onClick = onExplore,
|
||||||
isEnabled = true,
|
isEnabled = true,
|
||||||
),
|
),
|
||||||
|
|
@ -53,11 +59,14 @@ sealed class EmptyTransactionsBlockState(
|
||||||
text = TextReference.Res(R.string.transaction_history_empty_transactions),
|
text = TextReference.Res(R.string.transaction_history_empty_transactions),
|
||||||
)
|
)
|
||||||
|
|
||||||
data class NotImplemented(val onExplore: () -> Unit) : EmptyTransactionsBlockState(
|
data class NotImplemented(
|
||||||
|
val onExplore: () -> Unit,
|
||||||
|
@DrawableRes val exploreIconResId: Int,
|
||||||
|
) : EmptyTransactionsBlockState(
|
||||||
buttonsState = ButtonsState.SingleButton(
|
buttonsState = ButtonsState.SingleButton(
|
||||||
actionButtonConfig = ActionButtonConfig(
|
actionButtonConfig = ActionButtonConfig(
|
||||||
text = TextReference.Res(R.string.common_explore_transaction_history),
|
text = TextReference.Res(R.string.common_explore_transaction_history),
|
||||||
iconResId = R.drawable.ic_arrow_top_right_24,
|
iconResId = exploreIconResId,
|
||||||
onClick = onExplore,
|
onClick = onExplore,
|
||||||
isEnabled = true,
|
isEnabled = true,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,7 @@ internal class DefaultTokenDetailsComponent @AssistedInject constructor(
|
||||||
tokenDetailsUM = tokenDetailsUM,
|
tokenDetailsUM = tokenDetailsUM,
|
||||||
tokenMarketBlockComponent = tokenMarketBlockComponent,
|
tokenMarketBlockComponent = tokenMarketBlockComponent,
|
||||||
yieldSupplyComponent = yieldSupplyComponent,
|
yieldSupplyComponent = yieldSupplyComponent,
|
||||||
|
txHistoryComponent = txHistoryComponent,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,9 @@ import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.statusBarsPadding
|
import androidx.compose.foundation.layout.statusBarsPadding
|
||||||
import androidx.compose.foundation.layout.systemBars
|
import androidx.compose.foundation.layout.systemBars
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.LazyListScope
|
||||||
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import com.tangem.common.ui.earn.EarnBlock
|
import com.tangem.common.ui.earn.EarnBlock
|
||||||
import com.tangem.common.ui.notifications.notifications
|
import com.tangem.common.ui.notifications.notifications
|
||||||
|
|
||||||
|
|
@ -21,6 +24,7 @@ import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Brush
|
import androidx.compose.ui.graphics.Brush
|
||||||
|
|
@ -56,10 +60,14 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDeta
|
||||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.TokenDetailsBalanceBlock
|
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.TokenDetailsBalanceBlock
|
||||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.TokenDetailsBalanceBlockHeight
|
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.TokenDetailsBalanceBlockHeight
|
||||||
import com.tangem.features.markets.token.block.TokenMarketBlockComponent
|
import com.tangem.features.markets.token.block.TokenMarketBlockComponent
|
||||||
|
import com.tangem.features.txhistory.component.TxHistoryComponent
|
||||||
|
import com.tangem.features.txhistory.entity.TxHistoryUM
|
||||||
import com.tangem.features.yield.supply.api.YieldSupplyComponent
|
import com.tangem.features.yield.supply.api.YieldSupplyComponent
|
||||||
import dev.chrisbanes.haze.HazeProgressive
|
import dev.chrisbanes.haze.HazeProgressive
|
||||||
import dev.chrisbanes.haze.HazeTint
|
import dev.chrisbanes.haze.HazeTint
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
private val TopBarHeight: Dp = 64.dp
|
private val TopBarHeight: Dp = 64.dp
|
||||||
private val MarketBlockHorizontalPadding: Dp = 14.dp
|
private val MarketBlockHorizontalPadding: Dp = 14.dp
|
||||||
|
|
@ -69,6 +77,7 @@ internal fun TokenDetailsScreen(
|
||||||
tokenDetailsUM: TokenDetailsUM,
|
tokenDetailsUM: TokenDetailsUM,
|
||||||
tokenMarketBlockComponent: TokenMarketBlockComponent?,
|
tokenMarketBlockComponent: TokenMarketBlockComponent?,
|
||||||
yieldSupplyComponent: YieldSupplyComponent,
|
yieldSupplyComponent: YieldSupplyComponent,
|
||||||
|
txHistoryComponent: TxHistoryComponent,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val statusBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getTop(this).toDp() }
|
val statusBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getTop(this).toDp() }
|
||||||
|
|
@ -110,6 +119,7 @@ internal fun TokenDetailsScreen(
|
||||||
TokenDetailsBody(
|
TokenDetailsBody(
|
||||||
tokenDetailsUM = tokenDetailsUM,
|
tokenDetailsUM = tokenDetailsUM,
|
||||||
yieldSupplyComponent = yieldSupplyComponent,
|
yieldSupplyComponent = yieldSupplyComponent,
|
||||||
|
txHistoryComponent = txHistoryComponent,
|
||||||
rootBackground = rootBackground,
|
rootBackground = rootBackground,
|
||||||
bottomContentPadding = marketBlockHeight,
|
bottomContentPadding = marketBlockHeight,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -197,13 +207,18 @@ private fun BoxScope.TokenDetailsMarketBlockOverlay(
|
||||||
private fun TokenDetailsBody(
|
private fun TokenDetailsBody(
|
||||||
tokenDetailsUM: TokenDetailsUM,
|
tokenDetailsUM: TokenDetailsUM,
|
||||||
yieldSupplyComponent: YieldSupplyComponent,
|
yieldSupplyComponent: YieldSupplyComponent,
|
||||||
|
txHistoryComponent: TxHistoryComponent,
|
||||||
rootBackground: Color,
|
rootBackground: Color,
|
||||||
bottomContentPadding: Dp,
|
bottomContentPadding: Dp,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
itemModifier: Modifier = Modifier,
|
itemModifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
val listState = rememberLazyListState()
|
||||||
|
val txHistoryState by txHistoryComponent.txHistoryState.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
|
state = listState,
|
||||||
contentPadding = PaddingValues(bottom = bottomContentPadding),
|
contentPadding = PaddingValues(bottom = bottomContentPadding),
|
||||||
) {
|
) {
|
||||||
notifications(
|
notifications(
|
||||||
|
|
@ -222,7 +237,9 @@ private fun TokenDetailsBody(
|
||||||
item(key = "yield_supply_block") {
|
item(key = "yield_supply_block") {
|
||||||
yieldSupplyComponent.Content(modifier = itemModifier.padding(vertical = TangemTheme.dimens2.x2))
|
yieldSupplyComponent.Content(modifier = itemModifier.padding(vertical = TangemTheme.dimens2.x2))
|
||||||
}
|
}
|
||||||
// TODO [REDACTED_TASK_KEY] Token Details Make Transaction History
|
with(txHistoryComponent) {
|
||||||
|
txHistoryContent(listState = listState, state = txHistoryState)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -273,6 +290,15 @@ private fun TokenDetailsScreen_Preview() {
|
||||||
@Composable
|
@Composable
|
||||||
override fun Content(modifier: Modifier) = Unit
|
override fun Content(modifier: Modifier) = Unit
|
||||||
},
|
},
|
||||||
|
txHistoryComponent = object : TxHistoryComponent {
|
||||||
|
override val txHistoryState: StateFlow<TxHistoryUM> = MutableStateFlow(
|
||||||
|
value = TxHistoryUM.Empty(isBalanceHidden = false, onExploreClick = {}),
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun LazyListScope.txHistoryContentLegacy(listState: LazyListState, state: TxHistoryUM) = Unit
|
||||||
|
|
||||||
|
override fun LazyListScope.txHistoryContent(listState: LazyListState, state: TxHistoryUM) = Unit
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -151,7 +151,9 @@ internal fun TokenDetailsScreenLegacy(
|
||||||
modifier = itemModifier,
|
modifier = itemModifier,
|
||||||
)
|
)
|
||||||
|
|
||||||
with(txHistoryComponent) { txHistoryContent(listState = listState, state = txHistoryComponentState) }
|
with(txHistoryComponent) {
|
||||||
|
txHistoryContentLegacy(listState = listState, state = txHistoryComponentState)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -179,6 +181,8 @@ private fun TokenDetailsScreenPreview(
|
||||||
value = TxHistoryUM.Empty(isBalanceHidden = false, onExploreClick = {}),
|
value = TxHistoryUM.Empty(isBalanceHidden = false, onExploreClick = {}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
override fun LazyListScope.txHistoryContentLegacy(listState: LazyListState, state: TxHistoryUM) = Unit
|
||||||
|
|
||||||
override fun LazyListScope.txHistoryContent(listState: LazyListState, state: TxHistoryUM) = Unit
|
override fun LazyListScope.txHistoryContent(listState: LazyListState, state: TxHistoryUM) = Unit
|
||||||
},
|
},
|
||||||
yieldSupplyComponent = object : YieldSupplyComponent {
|
yieldSupplyComponent = object : YieldSupplyComponent {
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ dependencies {
|
||||||
/** Compose */
|
/** Compose */
|
||||||
implementation(deps.compose.runtime)
|
implementation(deps.compose.runtime)
|
||||||
implementation(deps.compose.foundation)
|
implementation(deps.compose.foundation)
|
||||||
|
implementation(deps.compose.ui.tooling)
|
||||||
|
|
||||||
/** Other */
|
/** Other */
|
||||||
implementation(deps.kotlin.immutable.collections)
|
implementation(deps.kotlin.immutable.collections)
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ interface TxHistoryComponent {
|
||||||
|
|
||||||
val txHistoryState: StateFlow<TxHistoryUM>
|
val txHistoryState: StateFlow<TxHistoryUM>
|
||||||
|
|
||||||
|
fun LazyListScope.txHistoryContentLegacy(listState: LazyListState, state: TxHistoryUM)
|
||||||
|
|
||||||
fun LazyListScope.txHistoryContent(listState: LazyListState, state: TxHistoryUM)
|
fun LazyListScope.txHistoryContent(listState: LazyListState, state: TxHistoryUM)
|
||||||
|
|
||||||
data class Params(
|
data class Params(
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ sealed interface TxHistoryUM {
|
||||||
TxHistoryItemUM.Transaction(TransactionState.Loading("LOADING_TX_HASH_1")),
|
TxHistoryItemUM.Transaction(TransactionState.Loading("LOADING_TX_HASH_1")),
|
||||||
TxHistoryItemUM.Transaction(TransactionState.Loading("LOADING_TX_HASH_2")),
|
TxHistoryItemUM.Transaction(TransactionState.Loading("LOADING_TX_HASH_2")),
|
||||||
TxHistoryItemUM.Transaction(TransactionState.Loading("LOADING_TX_HASH_3")),
|
TxHistoryItemUM.Transaction(TransactionState.Loading("LOADING_TX_HASH_3")),
|
||||||
|
TxHistoryItemUM.Transaction(TransactionState.Loading("LOADING_TX_HASH_4")),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,139 +1,214 @@
|
||||||
package com.tangem.features.txhistory.ui
|
package com.tangem.features.txhistory.ui
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.lazy.LazyListScope
|
import androidx.compose.foundation.lazy.LazyListScope
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import com.tangem.core.ui.components.list.InfiniteListHandler
|
import androidx.compose.ui.draw.alpha
|
||||||
import com.tangem.core.ui.components.transactions.PendingTxsBlock
|
import androidx.compose.ui.layout.layoutId
|
||||||
import com.tangem.core.ui.components.transactions.Transaction
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import com.tangem.core.ui.components.transactions.TxHistoryGroupTitle
|
import androidx.compose.ui.unit.dp
|
||||||
import com.tangem.core.ui.components.transactions.TxHistoryTitle
|
import androidx.compose.ui.util.lerp
|
||||||
|
import com.tangem.core.ui.R
|
||||||
|
import com.tangem.core.ui.components.CircleShimmer
|
||||||
|
import com.tangem.core.ui.components.RectangleShimmer
|
||||||
import com.tangem.core.ui.components.transactions.empty.EmptyTransactionBlock
|
import com.tangem.core.ui.components.transactions.empty.EmptyTransactionBlock
|
||||||
import com.tangem.core.ui.components.transactions.empty.EmptyTransactionsBlockState
|
import com.tangem.core.ui.components.transactions.empty.EmptyTransactionsBlockState
|
||||||
import com.tangem.core.ui.components.transactions.state.TransactionState
|
import com.tangem.core.ui.ds.row.TangemRowContainer
|
||||||
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
|
import com.tangem.core.ui.ds.row.TangemRowLayoutId
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
|
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||||
import com.tangem.features.txhistory.entity.TxHistoryUM
|
import com.tangem.features.txhistory.entity.TxHistoryUM
|
||||||
|
|
||||||
private const val LOAD_ITEMS_BUFFER = 20
|
private val LoadingTitleShimmerWidth = 52.dp
|
||||||
|
private val LoadingPrimaryShimmerWidth = 110.dp
|
||||||
|
private val LoadingSecondaryShimmerWidth = 52.dp
|
||||||
|
private val LoadingEndTopShimmerWidth = 107.dp
|
||||||
|
private val LoadingEndBottomShimmerWidth = 52.dp
|
||||||
|
|
||||||
|
private const val LOADING_TRANSACTION_MIN_ALPHA = 0.1f
|
||||||
|
|
||||||
fun LazyListScope.txHistoryItems(listState: LazyListState, state: TxHistoryUM) {
|
fun LazyListScope.txHistoryItems(listState: LazyListState, state: TxHistoryUM) {
|
||||||
when (state) {
|
when (state) {
|
||||||
is TxHistoryUM.Content -> contentItems(listState, state)
|
is TxHistoryUM.Content -> contentItems(listState, state)
|
||||||
is TxHistoryUM.Empty -> nonContentItem(state = EmptyTransactionsBlockState.Empty(state.onExploreClick))
|
is TxHistoryUM.Empty -> emptyItem(state)
|
||||||
is TxHistoryUM.Error -> nonContentItem(
|
is TxHistoryUM.Error -> errorItem(state)
|
||||||
state = EmptyTransactionsBlockState.FailedToLoad(
|
|
||||||
onReload = state.onReloadClick,
|
|
||||||
onExplore = state.onExploreClick,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
is TxHistoryUM.Loading -> loadingItems(state)
|
is TxHistoryUM.Loading -> loadingItems(state)
|
||||||
is TxHistoryUM.NotSupported -> {
|
is TxHistoryUM.NotSupported -> notSupportedItem(state)
|
||||||
if (state.pendingTransactions.isNotEmpty()) {
|
|
||||||
item(key = "PendingTxsBlock", contentType = "PendingTxsBlock") {
|
|
||||||
PendingTxsBlock(pendingTxs = state.pendingTransactions, isBalanceHidden = state.isBalanceHidden)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nonContentItem(
|
|
||||||
state = EmptyTransactionsBlockState.NotImplemented(onExplore = state.onExploreClick),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun LazyListScope.nonContentItem(state: EmptyTransactionsBlockState, modifier: Modifier = Modifier) {
|
@Suppress("UNUSED_PARAMETER")
|
||||||
item(key = state::class.java, contentType = state::class.java) {
|
private fun LazyListScope.contentItems(listState: LazyListState, state: TxHistoryUM.Content) {
|
||||||
EmptyTransactionBlock(
|
item(key = "tx_history_content", contentType = "tx_history_content") {
|
||||||
state = state,
|
TxHistoryContentBlock(state = state)
|
||||||
modifier = modifier
|
}
|
||||||
.animateItem(fadeInSpec = null, fadeOutSpec = null)
|
}
|
||||||
.padding(horizontal = TangemTheme.dimens.spacing16, vertical = TangemTheme.dimens.spacing12)
|
|
||||||
.fillMaxWidth(),
|
private fun LazyListScope.emptyItem(state: TxHistoryUM.Empty) {
|
||||||
)
|
item(key = "tx_history_empty", contentType = "tx_history_empty") {
|
||||||
|
TxHistoryEmptyBlock(state = state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun LazyListScope.errorItem(state: TxHistoryUM.Error) {
|
||||||
|
item(key = "tx_history_error", contentType = "tx_history_error") {
|
||||||
|
TxHistoryErrorBlock(state = state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun LazyListScope.loadingItems(state: TxHistoryUM.Loading) {
|
private fun LazyListScope.loadingItems(state: TxHistoryUM.Loading) {
|
||||||
itemsIndexed(
|
item(key = "tx_history_loading", contentType = "tx_history_loading") {
|
||||||
items = state.items,
|
TxHistoryLoadingBlock(state = state)
|
||||||
key = { _, item ->
|
}
|
||||||
when (item) {
|
}
|
||||||
is TxHistoryUM.TxHistoryItemUM.GroupTitle -> item.itemKey
|
|
||||||
is TxHistoryUM.TxHistoryItemUM.Title -> item.onExploreClick.hashCode()
|
private fun LazyListScope.notSupportedItem(state: TxHistoryUM.NotSupported) {
|
||||||
is TxHistoryUM.TxHistoryItemUM.Transaction ->
|
item(key = "tx_history_not_supported", contentType = "tx_history_not_supported") {
|
||||||
item.state.txHash + (item.state as? TransactionState.Content)?.hashCode()
|
TxHistoryNotSupportedBlock(state = state)
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
contentType = { _, item -> item::class.java },
|
|
||||||
itemContent = { index, item ->
|
@Suppress("UNUSED_PARAMETER")
|
||||||
TxHistoryListItem(
|
@Composable
|
||||||
state = item,
|
private fun TxHistoryContentBlock(state: TxHistoryUM.Content, modifier: Modifier = Modifier) {
|
||||||
isBalanceHidden = true,
|
// TODO [REDACTED_TASK_KEY] redesign Content state
|
||||||
modifier = Modifier.roundedShapeItemDecoration(
|
}
|
||||||
currentIndex = index,
|
|
||||||
lastIndex = state.items.lastIndex,
|
@Composable
|
||||||
),
|
private fun TxHistoryEmptyBlock(state: TxHistoryUM.Empty, modifier: Modifier = Modifier) {
|
||||||
)
|
EmptyTransactionBlock(
|
||||||
},
|
state = EmptyTransactionsBlockState.Empty(
|
||||||
|
onExplore = state.onExploreClick,
|
||||||
|
exploreIconResId = R.drawable.ic_compass_24,
|
||||||
|
),
|
||||||
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun LazyListScope.contentItems(listState: LazyListState, state: TxHistoryUM.Content) {
|
@Composable
|
||||||
itemsIndexed(
|
private fun TxHistoryErrorBlock(state: TxHistoryUM.Error, modifier: Modifier = Modifier) {
|
||||||
items = state.items,
|
EmptyTransactionBlock(
|
||||||
key = { _, item ->
|
state = EmptyTransactionsBlockState.FailedToLoad(
|
||||||
when (item) {
|
onReload = state.onReloadClick,
|
||||||
is TxHistoryUM.TxHistoryItemUM.GroupTitle -> item.itemKey
|
onExplore = state.onExploreClick,
|
||||||
is TxHistoryUM.TxHistoryItemUM.Title -> item.onExploreClick.hashCode()
|
reloadIconResId = R.drawable.ic_refresh_24,
|
||||||
is TxHistoryUM.TxHistoryItemUM.Transaction ->
|
exploreIconResId = R.drawable.ic_compass_24,
|
||||||
item.state.txHash + (item.state as? TransactionState.Content)?.hashCode()
|
),
|
||||||
}
|
modifier = modifier,
|
||||||
},
|
|
||||||
contentType = { _, item -> item::class.java },
|
|
||||||
itemContent = { index, item ->
|
|
||||||
TxHistoryListItem(
|
|
||||||
state = item,
|
|
||||||
isBalanceHidden = state.isBalanceHidden,
|
|
||||||
modifier = Modifier.roundedShapeItemDecoration(
|
|
||||||
currentIndex = index,
|
|
||||||
lastIndex = state.items.lastIndex,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
item {
|
}
|
||||||
InfiniteListHandler(
|
|
||||||
listState = listState,
|
@Composable
|
||||||
buffer = LOAD_ITEMS_BUFFER,
|
private fun TxHistoryLoadingBlock(state: TxHistoryUM.Loading, modifier: Modifier = Modifier) {
|
||||||
onLoadMore = state.loadMore,
|
val transactionCount = state.items.count { it is TxHistoryUM.TxHistoryItemUM.Transaction }
|
||||||
)
|
Column(modifier = modifier.fillMaxWidth()) {
|
||||||
|
var transactionIndex = 0
|
||||||
|
state.items.forEach { item ->
|
||||||
|
when (item) {
|
||||||
|
is TxHistoryUM.TxHistoryItemUM.Title -> TxHistoryLoadingTitle()
|
||||||
|
is TxHistoryUM.TxHistoryItemUM.Transaction -> {
|
||||||
|
val fraction = if (transactionCount <= 1) {
|
||||||
|
0f
|
||||||
|
} else {
|
||||||
|
transactionIndex.toFloat() / (transactionCount - 1)
|
||||||
|
}
|
||||||
|
val alpha = lerp(start = 1f, stop = LOADING_TRANSACTION_MIN_ALPHA, fraction = fraction)
|
||||||
|
TxHistoryLoadingTransaction(modifier = Modifier.alpha(alpha))
|
||||||
|
transactionIndex++
|
||||||
|
}
|
||||||
|
is TxHistoryUM.TxHistoryItemUM.GroupTitle -> Unit
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun TxHistoryListItem(
|
private fun TxHistoryLoadingTitle(modifier: Modifier = Modifier) {
|
||||||
state: TxHistoryUM.TxHistoryItemUM,
|
RectangleShimmer(
|
||||||
isBalanceHidden: Boolean,
|
modifier = modifier
|
||||||
modifier: Modifier = Modifier,
|
.padding(
|
||||||
) {
|
top = TangemTheme.dimens2.x6,
|
||||||
when (state) {
|
bottom = TangemTheme.dimens2.x3,
|
||||||
is TxHistoryUM.TxHistoryItemUM.GroupTitle -> {
|
start = TangemTheme.dimens2.x4,
|
||||||
TxHistoryGroupTitle(config = state.legacyGroupTitle, modifier = modifier)
|
|
||||||
}
|
|
||||||
is TxHistoryUM.TxHistoryItemUM.Title -> {
|
|
||||||
TxHistoryTitle(onExploreClick = state.onExploreClick, modifier = modifier)
|
|
||||||
}
|
|
||||||
is TxHistoryUM.TxHistoryItemUM.Transaction -> {
|
|
||||||
Transaction(
|
|
||||||
state = state.state,
|
|
||||||
isBalanceHidden = isBalanceHidden,
|
|
||||||
modifier = modifier,
|
|
||||||
)
|
)
|
||||||
}
|
.size(width = LoadingTitleShimmerWidth, height = TangemTheme.dimens2.x4),
|
||||||
|
radius = TangemTheme.dimens2.x2,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun TxHistoryLoadingTransaction(modifier: Modifier = Modifier) {
|
||||||
|
TangemRowContainer(
|
||||||
|
modifier = modifier.fillMaxWidth(),
|
||||||
|
contentPadding = PaddingValues(
|
||||||
|
horizontal = TangemTheme.dimens2.x4,
|
||||||
|
vertical = TangemTheme.dimens2.x3,
|
||||||
|
),
|
||||||
|
content = {
|
||||||
|
CircleShimmer(
|
||||||
|
modifier = Modifier
|
||||||
|
.layoutId(TangemRowLayoutId.HEAD)
|
||||||
|
.padding(end = TangemTheme.dimens2.x3)
|
||||||
|
.size(TangemTheme.dimens2.x10),
|
||||||
|
)
|
||||||
|
RectangleShimmer(
|
||||||
|
modifier = Modifier
|
||||||
|
.layoutId(TangemRowLayoutId.START_TOP)
|
||||||
|
.size(width = LoadingPrimaryShimmerWidth, height = TangemTheme.dimens2.x5),
|
||||||
|
radius = TangemTheme.dimens2.x2,
|
||||||
|
)
|
||||||
|
RectangleShimmer(
|
||||||
|
modifier = Modifier
|
||||||
|
.layoutId(TangemRowLayoutId.START_BOTTOM)
|
||||||
|
.size(width = LoadingSecondaryShimmerWidth, height = TangemTheme.dimens2.x4),
|
||||||
|
radius = TangemTheme.dimens2.x2,
|
||||||
|
)
|
||||||
|
RectangleShimmer(
|
||||||
|
modifier = Modifier
|
||||||
|
.layoutId(TangemRowLayoutId.END_TOP)
|
||||||
|
.size(width = LoadingEndTopShimmerWidth, height = TangemTheme.dimens2.x5),
|
||||||
|
radius = TangemTheme.dimens2.x2,
|
||||||
|
)
|
||||||
|
RectangleShimmer(
|
||||||
|
modifier = Modifier
|
||||||
|
.layoutId(TangemRowLayoutId.END_BOTTOM)
|
||||||
|
.size(width = LoadingEndBottomShimmerWidth, height = TangemTheme.dimens2.x4),
|
||||||
|
radius = TangemTheme.dimens2.x2,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun TxHistoryNotSupportedBlock(state: TxHistoryUM.NotSupported, modifier: Modifier = Modifier) {
|
||||||
|
EmptyTransactionBlock(
|
||||||
|
state = EmptyTransactionsBlockState.NotImplemented(
|
||||||
|
onExplore = state.onExploreClick,
|
||||||
|
exploreIconResId = R.drawable.ic_compass_24,
|
||||||
|
),
|
||||||
|
modifier = modifier,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// region Preview
|
||||||
|
@Preview(showBackground = true, widthDp = 360)
|
||||||
|
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
@Composable
|
||||||
|
private fun TxHistoryLoadingBlock_Preview() {
|
||||||
|
TangemThemePreviewRedesign {
|
||||||
|
TxHistoryLoadingBlock(
|
||||||
|
state = TxHistoryUM.Loading(
|
||||||
|
isBalanceHidden = false,
|
||||||
|
onExploreClick = {},
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// endregion Preview
|
||||||
|
|
@ -0,0 +1,150 @@
|
||||||
|
package com.tangem.features.txhistory.ui
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyListScope
|
||||||
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import com.tangem.core.ui.R
|
||||||
|
import com.tangem.core.ui.components.list.InfiniteListHandler
|
||||||
|
import com.tangem.core.ui.components.transactions.PendingTxsBlock
|
||||||
|
import com.tangem.core.ui.components.transactions.Transaction
|
||||||
|
import com.tangem.core.ui.components.transactions.TxHistoryGroupTitle
|
||||||
|
import com.tangem.core.ui.components.transactions.TxHistoryTitle
|
||||||
|
import com.tangem.core.ui.components.transactions.empty.EmptyTransactionBlockLegacy
|
||||||
|
import com.tangem.core.ui.components.transactions.empty.EmptyTransactionsBlockState
|
||||||
|
import com.tangem.core.ui.components.transactions.state.TransactionState
|
||||||
|
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
|
||||||
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
|
import com.tangem.features.txhistory.entity.TxHistoryUM
|
||||||
|
|
||||||
|
private const val LOAD_ITEMS_BUFFER = 20
|
||||||
|
|
||||||
|
fun LazyListScope.txHistoryItemsLegacy(listState: LazyListState, state: TxHistoryUM) {
|
||||||
|
when (state) {
|
||||||
|
is TxHistoryUM.Content -> contentItems(listState, state)
|
||||||
|
is TxHistoryUM.Empty -> nonContentItem(
|
||||||
|
state = EmptyTransactionsBlockState.Empty(
|
||||||
|
onExplore = state.onExploreClick,
|
||||||
|
exploreIconResId = R.drawable.ic_arrow_top_right_24,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
is TxHistoryUM.Error -> nonContentItem(
|
||||||
|
state = EmptyTransactionsBlockState.FailedToLoad(
|
||||||
|
onReload = state.onReloadClick,
|
||||||
|
onExplore = state.onExploreClick,
|
||||||
|
reloadIconResId = R.drawable.ic_refresh_24,
|
||||||
|
exploreIconResId = R.drawable.ic_arrow_top_right_24,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
is TxHistoryUM.Loading -> loadingItems(state)
|
||||||
|
is TxHistoryUM.NotSupported -> {
|
||||||
|
if (state.pendingTransactions.isNotEmpty()) {
|
||||||
|
item(key = "PendingTxsBlock", contentType = "PendingTxsBlock") {
|
||||||
|
PendingTxsBlock(pendingTxs = state.pendingTransactions, isBalanceHidden = state.isBalanceHidden)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nonContentItem(
|
||||||
|
state = EmptyTransactionsBlockState.NotImplemented(
|
||||||
|
onExplore = state.onExploreClick,
|
||||||
|
exploreIconResId = R.drawable.ic_arrow_top_right_24,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun LazyListScope.nonContentItem(state: EmptyTransactionsBlockState, modifier: Modifier = Modifier) {
|
||||||
|
item(key = state::class.java, contentType = state::class.java) {
|
||||||
|
EmptyTransactionBlockLegacy(
|
||||||
|
state = state,
|
||||||
|
modifier = modifier
|
||||||
|
.animateItem(fadeInSpec = null, fadeOutSpec = null)
|
||||||
|
.padding(horizontal = TangemTheme.dimens.spacing16, vertical = TangemTheme.dimens.spacing12)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun LazyListScope.loadingItems(state: TxHistoryUM.Loading) {
|
||||||
|
itemsIndexed(
|
||||||
|
items = state.items,
|
||||||
|
key = { _, item ->
|
||||||
|
when (item) {
|
||||||
|
is TxHistoryUM.TxHistoryItemUM.GroupTitle -> item.itemKey
|
||||||
|
is TxHistoryUM.TxHistoryItemUM.Title -> item.onExploreClick.hashCode()
|
||||||
|
is TxHistoryUM.TxHistoryItemUM.Transaction ->
|
||||||
|
item.state.txHash + (item.state as? TransactionState.Content)?.hashCode()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
contentType = { _, item -> item::class.java },
|
||||||
|
itemContent = { index, item ->
|
||||||
|
TxHistoryListItemLegacy(
|
||||||
|
state = item,
|
||||||
|
isBalanceHidden = true,
|
||||||
|
modifier = Modifier.roundedShapeItemDecoration(
|
||||||
|
currentIndex = index,
|
||||||
|
lastIndex = state.items.lastIndex,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun LazyListScope.contentItems(listState: LazyListState, state: TxHistoryUM.Content) {
|
||||||
|
itemsIndexed(
|
||||||
|
items = state.items,
|
||||||
|
key = { _, item ->
|
||||||
|
when (item) {
|
||||||
|
is TxHistoryUM.TxHistoryItemUM.GroupTitle -> item.itemKey
|
||||||
|
is TxHistoryUM.TxHistoryItemUM.Title -> item.onExploreClick.hashCode()
|
||||||
|
is TxHistoryUM.TxHistoryItemUM.Transaction ->
|
||||||
|
item.state.txHash + (item.state as? TransactionState.Content)?.hashCode()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
contentType = { _, item -> item::class.java },
|
||||||
|
itemContent = { index, item ->
|
||||||
|
TxHistoryListItemLegacy(
|
||||||
|
state = item,
|
||||||
|
isBalanceHidden = state.isBalanceHidden,
|
||||||
|
modifier = Modifier.roundedShapeItemDecoration(
|
||||||
|
currentIndex = index,
|
||||||
|
lastIndex = state.items.lastIndex,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
item {
|
||||||
|
InfiniteListHandler(
|
||||||
|
listState = listState,
|
||||||
|
buffer = LOAD_ITEMS_BUFFER,
|
||||||
|
onLoadMore = state.loadMore,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun TxHistoryListItemLegacy(
|
||||||
|
state: TxHistoryUM.TxHistoryItemUM,
|
||||||
|
isBalanceHidden: Boolean,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
when (state) {
|
||||||
|
is TxHistoryUM.TxHistoryItemUM.GroupTitle -> {
|
||||||
|
TxHistoryGroupTitle(config = state.legacyGroupTitle, modifier = modifier)
|
||||||
|
}
|
||||||
|
is TxHistoryUM.TxHistoryItemUM.Title -> {
|
||||||
|
TxHistoryTitle(onExploreClick = state.onExploreClick, modifier = modifier)
|
||||||
|
}
|
||||||
|
is TxHistoryUM.TxHistoryItemUM.Transaction -> {
|
||||||
|
Transaction(
|
||||||
|
state = state.state,
|
||||||
|
isBalanceHidden = isBalanceHidden,
|
||||||
|
modifier = modifier,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,7 @@ import com.tangem.core.decompose.model.getOrCreateModel
|
||||||
import com.tangem.features.txhistory.entity.TxHistoryUM
|
import com.tangem.features.txhistory.entity.TxHistoryUM
|
||||||
import com.tangem.features.txhistory.model.TxHistoryModel
|
import com.tangem.features.txhistory.model.TxHistoryModel
|
||||||
import com.tangem.features.txhistory.ui.txHistoryItems
|
import com.tangem.features.txhistory.ui.txHistoryItems
|
||||||
|
import com.tangem.features.txhistory.ui.txHistoryItemsLegacy
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
|
|
@ -22,6 +23,10 @@ internal class DefaultTxHistoryComponent @AssistedInject constructor(
|
||||||
override val txHistoryState: StateFlow<TxHistoryUM>
|
override val txHistoryState: StateFlow<TxHistoryUM>
|
||||||
get() = model.uiState
|
get() = model.uiState
|
||||||
|
|
||||||
|
override fun LazyListScope.txHistoryContentLegacy(listState: LazyListState, state: TxHistoryUM) {
|
||||||
|
txHistoryItemsLegacy(listState, state)
|
||||||
|
}
|
||||||
|
|
||||||
override fun LazyListScope.txHistoryContent(listState: LazyListState, state: TxHistoryUM) {
|
override fun LazyListScope.txHistoryContent(listState: LazyListState, state: TxHistoryUM) {
|
||||||
txHistoryItems(listState, state)
|
txHistoryItems(listState, state)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue