Updated on 2026-08-14
This commit is contained in:
parent
f1f9dff5ce
commit
aefaf636e8
8 changed files with 142 additions and 26 deletions
|
|
@ -86,6 +86,6 @@ internal class DefaultTangemPayTxHistoryRepository @Inject constructor(
|
|||
}.result
|
||||
val items = TangemPayTxHistoryItemConverter.convertList(result.transactions).filterNotNull()
|
||||
txHistoryItemsStore.store(key = customerWalletAddress, cursor = cursor ?: INITIAL_CURSOR, value = items)
|
||||
}
|
||||
}.onLeft { error(it.toString()) }
|
||||
}
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@ internal class PreviewTangemPayTxHistoryComponent(txHistoryUM: TangemPayTxHistor
|
|||
companion object {
|
||||
val loadingUM = TangemPayTxHistoryUM.Loading(isBalanceHidden = true)
|
||||
val emptyUM = TangemPayTxHistoryUM.Empty(isBalanceHidden = true)
|
||||
val errorUM = TangemPayTxHistoryUM.Error(isBalanceHidden = true, onReload = {})
|
||||
val contentUM = TangemPayTxHistoryUM.Content(
|
||||
isBalanceHidden = false,
|
||||
loadMore = { false },
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
package com.tangem.features.tangempay.entity
|
||||
|
||||
import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.features.tangempay.details.impl.R
|
||||
|
||||
internal sealed class TangemPayEmptyTransactionHistoryState {
|
||||
|
||||
abstract val iconRes: Int
|
||||
abstract val text: TextReference
|
||||
|
||||
data class FailedToLoad(
|
||||
private val onReload: () -> Unit,
|
||||
) : TangemPayEmptyTransactionHistoryState() {
|
||||
override val iconRes: Int = R.drawable.ic_alert_history_64
|
||||
override val text: TextReference = resourceReference(R.string.transaction_history_error_failed_to_load)
|
||||
val actionButtonConfig = ActionButtonConfig(
|
||||
text = resourceReference(R.string.common_reload),
|
||||
iconResId = R.drawable.ic_refresh_24,
|
||||
onClick = onReload,
|
||||
)
|
||||
}
|
||||
|
||||
data object Empty : TangemPayEmptyTransactionHistoryState() {
|
||||
override val iconRes: Int = R.drawable.ic_empty_token_64
|
||||
override val text: TextReference = resourceReference(R.string.transaction_history_empty_transactions)
|
||||
}
|
||||
}
|
||||
|
|
@ -54,11 +54,16 @@ internal class TangemPayTxHistoryModel @Inject constructor(
|
|||
.onEach(::updateState)
|
||||
.launchIn(modelScope)
|
||||
listManager.paginationStatus
|
||||
.onEach { paginationStatus -> handlePaginationStatus(paginationStatus) }
|
||||
.onEach(::handlePaginationStatus)
|
||||
.launchIn(modelScope)
|
||||
listManager.emptyStatus
|
||||
.onEach(::handleEmptyState)
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun updateState(items: ImmutableList<TangemPayTxHistoryUM.TangemPayTxHistoryItemUM>) {
|
||||
if (items.isEmpty()) return // fast exit. If items is empty, no need to update ui items
|
||||
|
||||
uiState.update { state ->
|
||||
if (state is TangemPayTxHistoryUM.Content) {
|
||||
state.copy(items = items)
|
||||
|
|
@ -72,6 +77,12 @@ internal class TangemPayTxHistoryModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleEmptyState(isEmpty: Boolean) {
|
||||
if (isEmpty) {
|
||||
uiState.update { getEmptyState(it.isBalanceHidden) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun handlePaginationStatus(status: PaginationStatus<*>) {
|
||||
uiState.update { state ->
|
||||
when (status) {
|
||||
|
|
@ -108,6 +119,10 @@ internal class TangemPayTxHistoryModel @Inject constructor(
|
|||
Timber.d("onTransactionClick: $item")
|
||||
}
|
||||
|
||||
private fun getEmptyState(isBalanceHidden: Boolean): TangemPayTxHistoryUM.Empty {
|
||||
return TangemPayTxHistoryUM.Empty(isBalanceHidden = isBalanceHidden)
|
||||
}
|
||||
|
||||
private fun getErrorState(isBalanceHidden: Boolean): TangemPayTxHistoryUM.Error {
|
||||
return TangemPayTxHistoryUM.Error(isBalanceHidden = isBalanceHidden, onReload = ::reload)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,7 @@ package com.tangem.features.tangempay.ui
|
|||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.LinearEasing
|
||||
import androidx.compose.animation.core.RepeatMode
|
||||
import androidx.compose.animation.core.animateFloat
|
||||
import androidx.compose.animation.core.infiniteRepeatable
|
||||
import androidx.compose.animation.core.rememberInfiniteTransition
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
|
|
@ -15,12 +10,8 @@ import androidx.compose.foundation.lazy.LazyColumn
|
|||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
|
|
@ -44,21 +35,14 @@ import com.tangem.core.ui.components.dropdownmenu.TangemDropdownItem
|
|||
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenu
|
||||
import com.tangem.core.ui.components.snackbar.TangemSnackbarHost
|
||||
import com.tangem.core.ui.components.text.applyBladeBrush
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.orMaskWithStars
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.TokenDetailsTopBarTestTags
|
||||
import com.tangem.features.tangempay.components.txHistory.PreviewTangemPayTxHistoryComponent
|
||||
import com.tangem.features.tangempay.components.txHistory.TangemPayTxHistoryComponent
|
||||
import com.tangem.features.tangempay.details.impl.R
|
||||
import com.tangem.features.tangempay.entity.TangemPayCardDetailsUM
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsBalanceBlockState
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsTopBarConfig
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsUM
|
||||
import com.tangem.features.tangempay.entity.*
|
||||
import com.tangem.utils.StringsSigns.DASH_SIGN
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
|
|
@ -415,8 +399,8 @@ private fun TangemPayDetailsTopAppBar(config: TangemPayDetailsTopBarConfig, modi
|
|||
)
|
||||
}
|
||||
|
||||
@Preview(device = Devices.PIXEL_7_PRO, group = "day")
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, device = Devices.PIXEL_7_PRO, group = "night")
|
||||
@Preview(device = Devices.PIXEL_7_PRO)
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, device = Devices.PIXEL_7_PRO)
|
||||
@Composable
|
||||
private fun TangemPayDetailsScreenPreview(
|
||||
@PreviewParameter(TangemPayDetailsUMProvider::class) state: TangemPayDetailsUM,
|
||||
|
|
@ -473,4 +457,26 @@ private class TangemPayDetailsUMProvider : CollectionPreviewParameterProvider<Ta
|
|||
isBalanceHidden = false,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@Preview(device = Devices.PIXEL_7_PRO)
|
||||
@Composable
|
||||
private fun TangemPayDetailsTxHistoryScreenPreview(
|
||||
@PreviewParameter(TangemPayDetailsTxHistoryProvider::class) state: TangemPayTxHistoryUM,
|
||||
) {
|
||||
TangemThemePreview {
|
||||
TangemPayDetailsScreen(
|
||||
state = TangemPayDetailsUMProvider().values.first(),
|
||||
txHistoryComponent = PreviewTangemPayTxHistoryComponent(txHistoryUM = state),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class TangemPayDetailsTxHistoryProvider : CollectionPreviewParameterProvider<TangemPayTxHistoryUM>(
|
||||
collection = listOf(
|
||||
PreviewTangemPayTxHistoryComponent.loadingUM,
|
||||
PreviewTangemPayTxHistoryComponent.contentUM,
|
||||
PreviewTangemPayTxHistoryComponent.emptyUM,
|
||||
PreviewTangemPayTxHistoryComponent.errorUM,
|
||||
),
|
||||
)
|
||||
|
|
@ -20,6 +20,7 @@ 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.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.constraintlayout.compose.ChainStyle
|
||||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import androidx.constraintlayout.compose.Dimension
|
||||
|
|
@ -27,6 +28,7 @@ import coil.compose.rememberAsyncImagePainter
|
|||
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.buttons.actions.ActionButton
|
||||
import com.tangem.core.ui.components.list.InfiniteListHandler
|
||||
import com.tangem.core.ui.components.transactions.TxHistoryGroupTitle
|
||||
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
|
||||
|
|
@ -34,7 +36,9 @@ import com.tangem.core.ui.extensions.orMaskWithStars
|
|||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.test.EmptyTransactionBlockTestTags
|
||||
import com.tangem.core.ui.test.TransactionHistoryBlockTestTags
|
||||
import com.tangem.features.tangempay.entity.TangemPayEmptyTransactionHistoryState
|
||||
import com.tangem.features.tangempay.entity.TangemPayTransactionState
|
||||
import com.tangem.features.tangempay.entity.TangemPayTxHistoryUM
|
||||
|
||||
|
|
@ -43,12 +47,25 @@ private const val LOAD_ITEMS_BUFFER = 20
|
|||
internal fun LazyListScope.tangemPayTxHistoryItems(listState: LazyListState, state: TangemPayTxHistoryUM) {
|
||||
when (state) {
|
||||
is TangemPayTxHistoryUM.Content -> contentItems(listState = listState, state = state)
|
||||
is TangemPayTxHistoryUM.Empty -> TODO("[REDACTED_JIRA]")
|
||||
is TangemPayTxHistoryUM.Error -> TODO("[REDACTED_JIRA]")
|
||||
is TangemPayTxHistoryUM.Empty -> nonContentItem(state = TangemPayEmptyTransactionHistoryState.Empty)
|
||||
is TangemPayTxHistoryUM.Error -> nonContentItem(
|
||||
state = TangemPayEmptyTransactionHistoryState.FailedToLoad(onReload = state.onReload),
|
||||
)
|
||||
is TangemPayTxHistoryUM.Loading -> loadingItems(state = state)
|
||||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.nonContentItem(state: TangemPayEmptyTransactionHistoryState, modifier: Modifier = Modifier) {
|
||||
item(key = state::class.java, contentType = state::class.java) {
|
||||
TangemPayEmptyTransactionBlock(
|
||||
state = state,
|
||||
modifier = modifier
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16, vertical = TangemTheme.dimens.spacing12)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.contentItems(listState: LazyListState, state: TangemPayTxHistoryUM.Content) {
|
||||
itemsIndexed(
|
||||
items = state.items,
|
||||
|
|
@ -370,4 +387,49 @@ private fun Timestamp(state: TangemPayTransactionState, modifier: Modifier = Mod
|
|||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TangemPayEmptyTransactionBlock(
|
||||
state: TangemPayEmptyTransactionHistoryState,
|
||||
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,
|
||||
)
|
||||
|
||||
when (state) {
|
||||
is TangemPayEmptyTransactionHistoryState.Empty -> Unit
|
||||
is TangemPayEmptyTransactionHistoryState.FailedToLoad -> ActionButton(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 24.dp)
|
||||
.fillMaxWidth(),
|
||||
config = state.actionButtonConfig,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -33,6 +33,7 @@ internal class TangemPayTxHistoryListManager(
|
|||
private val uiManager = TangemPayTxHistoryUiManager(state = state, txHistoryUiActions = txHistoryUiActions)
|
||||
|
||||
val uiItems: Flow<ImmutableList<TangemPayTxHistoryUM.TangemPayTxHistoryItemUM>> = uiManager.items
|
||||
val emptyStatus: Flow<Boolean> = state.map { it.isEmpty }.distinctUntilChanged()
|
||||
val paginationStatus: Flow<PaginationStatus<*>> = state.map { it.status }.distinctUntilChanged()
|
||||
|
||||
suspend fun launchPagination() = coroutineScope {
|
||||
|
|
@ -80,6 +81,7 @@ internal class TangemPayTxHistoryListManager(
|
|||
newCurrencyBatches = batchListState.data,
|
||||
clearUiBatches = clearUiBatches,
|
||||
),
|
||||
isEmpty = batchListState.status is PaginationStatus.EndOfPagination && batchListState.data.isEmpty(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,4 +7,5 @@ import com.tangem.pagination.PaginationStatus
|
|||
internal data class TangemPayTxHistoryState(
|
||||
val status: PaginationStatus<*> = PaginationStatus.None,
|
||||
val uiBatches: List<Batch<Int, List<TangemPayTxHistoryUM.TangemPayTxHistoryItemUM>>> = listOf(),
|
||||
val isEmpty: Boolean = false,
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue