Updated on 2026-08-14
This commit is contained in:
parent
d943b499fa
commit
80f829a2aa
27 changed files with 831 additions and 279 deletions
|
|
@ -35,20 +35,20 @@ data class TangemPayTxHistoryResponse(
|
|||
@Json(name = "authorized_amount") val authorizedAmount: BigDecimal? = null,
|
||||
@Json(name = "authorization_method") val authorizationMethod: String? = null,
|
||||
@Json(name = "memo") val memo: String? = null,
|
||||
@Json(name = "receipt") val receipt: Boolean? = null,
|
||||
@Json(name = "merchant_name") val merchantName: String? = null,
|
||||
@Json(name = "merchant_category") val merchantCategory: String? = null,
|
||||
@Json(name = "merchant_category_code") val merchantCategoryCode: String? = null,
|
||||
@Json(name = "receipt") val receipt: Boolean,
|
||||
@Json(name = "merchant_name") val merchantName: String,
|
||||
@Json(name = "merchant_category") val merchantCategory: String,
|
||||
@Json(name = "merchant_category_code") val merchantCategoryCode: String,
|
||||
@Json(name = "merchant_id") val merchantId: String? = null,
|
||||
@Json(name = "enriched_merchant_icon") val enrichedMerchantIcon: String? = null,
|
||||
@Json(name = "enriched_merchant_name") val enrichedMerchantName: String? = null,
|
||||
@Json(name = "enriched_merchant_category") val enrichedMerchantCategory: String? = null,
|
||||
@Json(name = "card_id") val cardId: String? = null,
|
||||
@Json(name = "card_type") val cardType: String? = null,
|
||||
@Json(name = "status") val status: String? = null,
|
||||
@Json(name = "status") val status: String,
|
||||
@Json(name = "declined_reason") val declinedReason: String? = null,
|
||||
@Json(name = "authorized_at") val authorizedAt: DateTime? = null,
|
||||
@Json(name = "posted_at") val postedAt: DateTime? = null,
|
||||
@Json(name = "authorized_at") val authorizedAt: DateTime,
|
||||
@Json(name = "posted_at") val postedAt: DateTime,
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
|
|
@ -56,10 +56,10 @@ data class TangemPayTxHistoryResponse(
|
|||
@Json(name = "amount") val amount: BigDecimal,
|
||||
@Json(name = "currency") val currency: String,
|
||||
@Json(name = "memo") val memo: String? = null,
|
||||
@Json(name = "chain_id") val chainId: Long? = null,
|
||||
@Json(name = "wallet_address") val walletAddress: String? = null,
|
||||
@Json(name = "transaction_hash") val transactionHash: String? = null,
|
||||
@Json(name = "posted_at") val postedAt: DateTime? = null,
|
||||
@Json(name = "chain_id") val chainId: Long,
|
||||
@Json(name = "wallet_address") val walletAddress: String,
|
||||
@Json(name = "transaction_hash") val transactionHash: String,
|
||||
@Json(name = "posted_at") val postedAt: DateTime?,
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
|
|
@ -70,8 +70,8 @@ data class TangemPayTxHistoryResponse(
|
|||
@Json(name = "chain_id") val chainId: Long? = null,
|
||||
@Json(name = "wallet_address") val walletAddress: String? = null,
|
||||
@Json(name = "transaction_hash") val transactionHash: String? = null,
|
||||
@Json(name = "status") val status: String? = null,
|
||||
@Json(name = "posted_at") val postedAt: DateTime? = null,
|
||||
@Json(name = "status") val status: String,
|
||||
@Json(name = "posted_at") val postedAt: DateTime,
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
|
|
@ -79,6 +79,6 @@ data class TangemPayTxHistoryResponse(
|
|||
@Json(name = "amount") val amount: BigDecimal,
|
||||
@Json(name = "currency") val currency: String,
|
||||
@Json(name = "description") val description: String? = null,
|
||||
@Json(name = "posted_at") val postedAt: DateTime? = null,
|
||||
@Json(name = "posted_at") val postedAt: DateTime,
|
||||
)
|
||||
}
|
||||
12
core/ui/src/main/res/drawable/ic_category_24.xml
Normal file
12
core/ui/src/main/res/drawable/ic_category_24.xml
Normal file
File diff suppressed because one or more lines are too long
22
core/ui/src/main/res/drawable/ic_percent_24.xml
Normal file
22
core/ui/src/main/res/drawable/ic_percent_24.xml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:autoMirrored="true"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
|
||||
<path
|
||||
android:fillColor="#1E1E1E"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M16.895,12.784C19.159,12.784 20.994,14.625 20.994,16.896C20.994,19.167 19.159,21.008 16.895,21.008C14.631,21.008 12.796,19.167 12.796,16.896C12.796,14.625 14.631,12.784 16.895,12.784ZM16.895,14.534C15.594,14.534 14.54,15.592 14.54,16.896C14.54,18.201 15.594,19.259 16.895,19.259C18.195,19.259 19.25,18.201 19.25,16.896C19.25,15.592 18.195,14.534 16.895,14.534Z" />
|
||||
|
||||
<path
|
||||
android:fillColor="#1E1E1E"
|
||||
android:pathData="M18.507,3.859C18.907,3.457 19.557,3.457 19.957,3.859C20.358,4.261 20.358,4.912 19.957,5.314L5.412,19.906C5.012,20.308 4.362,20.308 3.962,19.906C3.561,19.504 3.561,18.852 3.962,18.451L18.507,3.859Z" />
|
||||
|
||||
<path
|
||||
android:fillColor="#1E1E1E"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M7.092,3.008C9.356,3.008 11.191,4.849 11.191,7.12C11.191,9.391 9.356,11.232 7.092,11.232C4.829,11.232 2.994,9.391 2.994,7.12C2.994,4.849 4.829,3.008 7.092,3.008ZM7.092,4.758C5.792,4.758 4.738,5.816 4.738,7.12C4.738,8.425 5.792,9.483 7.092,9.483C8.393,9.483 9.447,8.425 9.447,7.12C9.447,5.816 8.393,4.758 7.092,4.758Z" />
|
||||
|
||||
</vector>
|
||||
|
|
@ -13,12 +13,15 @@ import com.tangem.domain.pay.model.CustomerInfo.ProductInstance
|
|||
import com.tangem.domain.pay.model.MainScreenCustomerInfo
|
||||
import com.tangem.domain.pay.model.OrderStatus
|
||||
import com.tangem.domain.pay.repository.OnboardingRepository
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val VALID_STATUS = "valid"
|
||||
private const val TAG = "TangemPay: OnboardingRepository"
|
||||
|
||||
internal class DefaultOnboardingRepository @Inject constructor(
|
||||
private val dispatcherProvider: CoroutineDispatcherProvider,
|
||||
private val tangemPayApi: TangemPayApi,
|
||||
private val requestHelper: TangemPayRequestPerformer,
|
||||
private val tangemPayStorage: TangemPayStorage,
|
||||
|
|
@ -54,8 +57,8 @@ internal class DefaultOnboardingRepository @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun createOrder(): Either<UniversalError, Unit> {
|
||||
return requestHelper.runWithErrorLogs(TAG) {
|
||||
override suspend fun createOrder(): Either<UniversalError, Unit> = withContext(dispatcherProvider.io) {
|
||||
requestHelper.runWithErrorLogs(TAG) {
|
||||
val walletAddress = requestHelper.getCustomerWalletAddress()
|
||||
val result = requestHelper.requestWithPersistedToken { authHeader ->
|
||||
tangemPayApi.createOrder(authHeader, body = OrderRequest(walletAddress))
|
||||
|
|
@ -68,14 +71,13 @@ internal class DefaultOnboardingRepository @Inject constructor(
|
|||
private fun getCustomerInfo(response: CustomerMeResponse.Result?): CustomerInfo {
|
||||
val card = response?.card
|
||||
val balance = response?.balance
|
||||
val productInstance = response?.productInstance
|
||||
val cardInfo = if (productInstance != null && card != null && balance != null) {
|
||||
val paymentAccount = response?.paymentAccount
|
||||
val cardInfo = if (paymentAccount != null && card != null && balance != null) {
|
||||
CardInfo(
|
||||
lastFourDigits = card.cardNumberEnd,
|
||||
balance = balance.availableBalance,
|
||||
currencyCode = balance.currency,
|
||||
customerWalletAddress = productInstance.cardWalletAddress,
|
||||
|
||||
customerWalletAddress = paymentAccount.customerWalletAddress,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ internal class DefaultTangemPayTxHistoryRepository @Inject constructor(
|
|||
val result = requestPerformer.request { authHeader ->
|
||||
visaApi.getTangemPayTxHistory(authHeader = authHeader, limit = pageSize, cursor = cursor)
|
||||
}.result
|
||||
val items = TangemPayTxHistoryItemConverter.convertList(result.transactions)
|
||||
val items = TangemPayTxHistoryItemConverter.convertList(result.transactions).filterNotNull()
|
||||
txHistoryItemsStore.store(key = customerWalletAddress, cursor = cursor ?: INITIAL_CURSOR, value = items)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,49 +3,55 @@ package com.tangem.data.visa.utils
|
|||
import com.tangem.datasource.api.pay.models.response.TangemPayTxHistoryResponse
|
||||
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
|
||||
import com.tangem.utils.converter.Converter
|
||||
import timber.log.Timber
|
||||
import java.util.Currency
|
||||
|
||||
internal object TangemPayTxHistoryItemConverter :
|
||||
Converter<TangemPayTxHistoryResponse.Transaction, TangemPayTxHistoryItem> {
|
||||
Converter<TangemPayTxHistoryResponse.Transaction, TangemPayTxHistoryItem?> {
|
||||
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
override fun convert(value: TangemPayTxHistoryResponse.Transaction): TangemPayTxHistoryItem {
|
||||
val spend = value.spend
|
||||
val collateral = value.collateral
|
||||
val payment = value.payment
|
||||
val fee = value.fee
|
||||
override fun convert(value: TangemPayTxHistoryResponse.Transaction): TangemPayTxHistoryItem? {
|
||||
return value.spend?.let { convertSpend(id = value.id, spend = it) }
|
||||
?: value.payment?.let { convertPayment(id = value.id, payment = it) }
|
||||
?: value.fee?.let { convertFee(id = value.id, fee = it) }
|
||||
?: run {
|
||||
Timber.wtf("unknown type of transaction: $value")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
return TangemPayTxHistoryItem(
|
||||
id = value.id,
|
||||
date = when {
|
||||
spend != null -> spend.postedAt
|
||||
collateral != null -> collateral.postedAt
|
||||
payment != null -> payment.postedAt
|
||||
fee != null -> fee.postedAt
|
||||
else -> null
|
||||
},
|
||||
amount = when {
|
||||
spend != null -> spend.amount
|
||||
collateral != null -> collateral.amount
|
||||
payment != null -> payment.amount
|
||||
fee != null -> fee.amount
|
||||
else -> null
|
||||
},
|
||||
merchantName = when {
|
||||
spend != null -> spend.merchantName
|
||||
else -> null
|
||||
},
|
||||
status = when {
|
||||
spend != null -> spend.status
|
||||
payment != null -> payment.status
|
||||
else -> null
|
||||
},
|
||||
currency = when {
|
||||
spend != null -> spend.currency
|
||||
collateral != null -> collateral.currency
|
||||
payment != null -> payment.currency
|
||||
fee != null -> fee.currency
|
||||
else -> null
|
||||
},
|
||||
private fun convertSpend(id: String, spend: TangemPayTxHistoryResponse.Spend): TangemPayTxHistoryItem.Spend {
|
||||
return TangemPayTxHistoryItem.Spend(
|
||||
id = id,
|
||||
date = spend.postedAt,
|
||||
amount = spend.amount,
|
||||
currency = Currency.getInstance(spend.currency),
|
||||
enrichedMerchantName = spend.enrichedMerchantName,
|
||||
merchantName = spend.merchantName,
|
||||
enrichedMerchantCategory = spend.enrichedMerchantCategory,
|
||||
merchantCategory = spend.merchantCategory,
|
||||
status = spend.status,
|
||||
enrichedMerchantIconUrl = spend.enrichedMerchantIcon,
|
||||
)
|
||||
}
|
||||
|
||||
private fun convertPayment(
|
||||
id: String,
|
||||
payment: TangemPayTxHistoryResponse.Payment,
|
||||
): TangemPayTxHistoryItem.Payment {
|
||||
return TangemPayTxHistoryItem.Payment(
|
||||
id = id,
|
||||
date = payment.postedAt,
|
||||
currency = Currency.getInstance(payment.currency),
|
||||
amount = payment.amount,
|
||||
)
|
||||
}
|
||||
|
||||
private fun convertFee(id: String, fee: TangemPayTxHistoryResponse.Fee): TangemPayTxHistoryItem.Fee {
|
||||
return TangemPayTxHistoryItem.Fee(
|
||||
id = id,
|
||||
date = fee.postedAt,
|
||||
currency = Currency.getInstance(fee.currency),
|
||||
amount = fee.amount,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,14 +2,38 @@ package com.tangem.domain.visa.model
|
|||
|
||||
import org.joda.time.DateTime
|
||||
import java.math.BigDecimal
|
||||
import java.util.Currency
|
||||
|
||||
data class TangemPayTxHistoryItem(
|
||||
val id: String,
|
||||
val date: DateTime?,
|
||||
val amount: BigDecimal?,
|
||||
val merchantName: String?,
|
||||
val status: String?,
|
||||
val currency: String?,
|
||||
) {
|
||||
val timeStampInMillis: Long = date?.millis ?: 0
|
||||
sealed class TangemPayTxHistoryItem {
|
||||
abstract val id: String
|
||||
abstract val date: DateTime
|
||||
abstract val amount: BigDecimal
|
||||
abstract val currency: Currency
|
||||
|
||||
data class Spend(
|
||||
override val id: String,
|
||||
override val date: DateTime,
|
||||
override val amount: BigDecimal,
|
||||
override val currency: Currency,
|
||||
val enrichedMerchantName: String?,
|
||||
val merchantName: String,
|
||||
val enrichedMerchantCategory: String?,
|
||||
val merchantCategory: String,
|
||||
val status: String,
|
||||
val enrichedMerchantIconUrl: String?,
|
||||
) : TangemPayTxHistoryItem()
|
||||
|
||||
data class Payment(
|
||||
override val id: String,
|
||||
override val date: DateTime,
|
||||
override val amount: BigDecimal,
|
||||
override val currency: Currency,
|
||||
) : TangemPayTxHistoryItem()
|
||||
|
||||
data class Fee(
|
||||
override val id: String,
|
||||
override val date: DateTime,
|
||||
override val amount: BigDecimal,
|
||||
override val currency: Currency,
|
||||
) : TangemPayTxHistoryItem()
|
||||
}
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
package com.tangem.domain.pay.usecase
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.core.error.UniversalError
|
||||
import com.tangem.domain.pay.repository.OnboardingRepository
|
||||
|
||||
class TangemPayIssueOrderUseCase(
|
||||
private val repository: OnboardingRepository,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(): Unit? = repository.createOrder().getOrNull()
|
||||
suspend operator fun invoke(): Either<UniversalError, Unit> = repository.createOrder()
|
||||
}
|
||||
|
|
@ -442,7 +442,7 @@ private class PreviewManageTokensComponentProvider : PreviewParameterProvider<Ma
|
|||
showTangemIcon = true,
|
||||
params = ManageTokensComponent.Params(
|
||||
source = ManageTokensSource.ONBOARDING,
|
||||
userWalletId = UserWalletId("wallet_id"),
|
||||
userWalletId = UserWalletId("0x"),
|
||||
),
|
||||
),
|
||||
PreviewManageTokensComponent(
|
||||
|
|
@ -455,7 +455,7 @@ private class PreviewManageTokensComponentProvider : PreviewParameterProvider<Ma
|
|||
showTangemIcon = false,
|
||||
params = ManageTokensComponent.Params(
|
||||
source = ManageTokensSource.ONBOARDING,
|
||||
userWalletId = UserWalletId("wallet_id"),
|
||||
userWalletId = UserWalletId("0x"),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ dependencies {
|
|||
implementation(projects.domain.visa.models)
|
||||
|
||||
/** Compose */
|
||||
implementation(deps.compose.coil)
|
||||
implementation(deps.compose.constraintLayout)
|
||||
implementation(deps.compose.foundation)
|
||||
implementation(deps.compose.material3)
|
||||
implementation(deps.compose.ui)
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ import androidx.compose.foundation.lazy.LazyListScope
|
|||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.features.tangempay.entity.TangemPayTxHistoryUM
|
||||
import com.tangem.features.tangempay.model.TangemPayTxHistoryModel
|
||||
import com.tangem.features.txhistory.entity.TxHistoryUM
|
||||
import com.tangem.features.txhistory.ui.txHistoryItems
|
||||
import com.tangem.features.tangempay.ui.tangemPayTxHistoryItems
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
internal class DefaultTangemPayTxHistoryComponent(
|
||||
|
|
@ -15,10 +15,10 @@ internal class DefaultTangemPayTxHistoryComponent(
|
|||
) : AppComponentContext by appComponentContext, TangemPayTxHistoryComponent {
|
||||
|
||||
private val model: TangemPayTxHistoryModel = getOrCreateModel(params = params)
|
||||
override val state: StateFlow<TxHistoryUM> = model.uiState
|
||||
override val state: StateFlow<TangemPayTxHistoryUM> = model.uiState
|
||||
|
||||
override fun LazyListScope.txHistoryContent(listState: LazyListState, state: TxHistoryUM) {
|
||||
txHistoryItems(listState, state)
|
||||
override fun LazyListScope.txHistoryContent(listState: LazyListState, state: TangemPayTxHistoryUM) {
|
||||
tangemPayTxHistoryItems(listState, state)
|
||||
}
|
||||
|
||||
data class Params(val customerWalletAddress: String)
|
||||
|
|
|
|||
|
|
@ -2,170 +2,150 @@ package com.tangem.features.tangempay.components.txHistory
|
|||
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import com.tangem.core.ui.components.transactions.state.TransactionState
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.features.tangempay.details.impl.R
|
||||
import com.tangem.features.txhistory.entity.TxHistoryUM
|
||||
import com.tangem.features.txhistory.ui.txHistoryItems
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.tangempay.entity.TangemPayTransactionState
|
||||
import com.tangem.features.tangempay.entity.TangemPayTxHistoryUM
|
||||
import com.tangem.features.tangempay.ui.tangemPayTxHistoryItems
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
internal class PreviewTangemPayTxHistoryComponent(txHistoryUM: TxHistoryUM) : TangemPayTxHistoryComponent {
|
||||
override val state: StateFlow<TxHistoryUM> = MutableStateFlow(txHistoryUM)
|
||||
internal class PreviewTangemPayTxHistoryComponent(txHistoryUM: TangemPayTxHistoryUM) : TangemPayTxHistoryComponent {
|
||||
override val state: StateFlow<TangemPayTxHistoryUM> = MutableStateFlow(txHistoryUM)
|
||||
|
||||
override fun LazyListScope.txHistoryContent(listState: LazyListState, state: TxHistoryUM) {
|
||||
txHistoryItems(listState, state)
|
||||
override fun LazyListScope.txHistoryContent(listState: LazyListState, state: TangemPayTxHistoryUM) {
|
||||
tangemPayTxHistoryItems(listState = listState, state = state)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val loadingUM = TxHistoryUM.Loading(isBalanceHidden = true, onExploreClick = {})
|
||||
val emptyUM = TxHistoryUM.Empty(isBalanceHidden = true, onExploreClick = {})
|
||||
val contentUM = TxHistoryUM.Content(
|
||||
val loadingUM = TangemPayTxHistoryUM.Loading(isBalanceHidden = true)
|
||||
val emptyUM = TangemPayTxHistoryUM.Empty(isBalanceHidden = true)
|
||||
val contentUM = TangemPayTxHistoryUM.Content(
|
||||
isBalanceHidden = false,
|
||||
loadMore = { false },
|
||||
items = persistentListOf(
|
||||
TxHistoryUM.TxHistoryItemUM.Title(onExploreClick = {}),
|
||||
TxHistoryUM.TxHistoryItemUM.GroupTitle(title = "Today", itemKey = "Today"),
|
||||
TxHistoryUM.TxHistoryItemUM.Transaction(
|
||||
state = TransactionState.Content(
|
||||
txHash = "signiferumque",
|
||||
TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.Title,
|
||||
TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.GroupTitle(title = "Today", itemKey = "Today"),
|
||||
TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.Transaction(
|
||||
transaction = TangemPayTransactionState.Content.Spend(
|
||||
id = "signiferumque",
|
||||
onClick = {},
|
||||
amount = "-4.99 USD",
|
||||
amountColor = { TangemTheme.colors.text.primary1 },
|
||||
time = "16:41",
|
||||
status = TransactionState.Content.Status.Confirmed,
|
||||
direction = TransactionState.Content.Direction.OUTGOING,
|
||||
onClick = {},
|
||||
iconRes = R.drawable.ic_arrow_up_24,
|
||||
title = stringReference("Starbucks"),
|
||||
subtitle = stringReference("Food&Drinks"),
|
||||
timestamp = 3464,
|
||||
iconUrl = null,
|
||||
),
|
||||
),
|
||||
TxHistoryUM.TxHistoryItemUM.Transaction(
|
||||
state = TransactionState.Content(
|
||||
txHash = "signiferumque",
|
||||
TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.Transaction(
|
||||
transaction = TangemPayTransactionState.Content.Spend(
|
||||
id = "signiferumque",
|
||||
amount = "-126.20 USD",
|
||||
amountColor = { TangemTheme.colors.text.primary1 },
|
||||
time = "12:04",
|
||||
status = TransactionState.Content.Status.Confirmed,
|
||||
direction = TransactionState.Content.Direction.OUTGOING,
|
||||
onClick = {},
|
||||
iconRes = R.drawable.ic_arrow_up_24,
|
||||
title = stringReference("Wallmart"),
|
||||
subtitle = stringReference("Supermarket"),
|
||||
timestamp = 3465,
|
||||
iconUrl = null,
|
||||
),
|
||||
),
|
||||
TxHistoryUM.TxHistoryItemUM.GroupTitle(title = "Yesterday", itemKey = "Yesterday"),
|
||||
TxHistoryUM.TxHistoryItemUM.Transaction(
|
||||
state = TransactionState.Content(
|
||||
txHash = "signiferumque",
|
||||
TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.GroupTitle(title = "Yesterday", itemKey = "Yesterday"),
|
||||
TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.Transaction(
|
||||
transaction = TangemPayTransactionState.Content.Spend(
|
||||
id = "signiferumque",
|
||||
amount = "-4.99 USD",
|
||||
amountColor = { TangemTheme.colors.text.primary1 },
|
||||
time = "21:41",
|
||||
status = TransactionState.Content.Status.Confirmed,
|
||||
direction = TransactionState.Content.Direction.OUTGOING,
|
||||
onClick = {},
|
||||
iconRes = R.drawable.ic_arrow_up_24,
|
||||
title = stringReference("Starbucks"),
|
||||
subtitle = stringReference("Food&Drinks"),
|
||||
timestamp = 3464,
|
||||
iconUrl = null,
|
||||
),
|
||||
),
|
||||
TxHistoryUM.TxHistoryItemUM.Transaction(
|
||||
state = TransactionState.Content(
|
||||
txHash = "signiferumque",
|
||||
TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.Transaction(
|
||||
transaction = TangemPayTransactionState.Content.Spend(
|
||||
id = "signiferumque",
|
||||
amount = "-126.20 USD",
|
||||
amountColor = { TangemTheme.colors.text.primary1 },
|
||||
time = "10:04",
|
||||
status = TransactionState.Content.Status.Confirmed,
|
||||
direction = TransactionState.Content.Direction.OUTGOING,
|
||||
onClick = {},
|
||||
iconRes = R.drawable.ic_arrow_up_24,
|
||||
title = stringReference("Wallmart"),
|
||||
subtitle = stringReference("Supermarket"),
|
||||
timestamp = 3465,
|
||||
iconUrl = null,
|
||||
),
|
||||
),
|
||||
TxHistoryUM.TxHistoryItemUM.Transaction(
|
||||
state = TransactionState.Content(
|
||||
txHash = "signiferumque",
|
||||
TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.Transaction(
|
||||
transaction = TangemPayTransactionState.Content.Spend(
|
||||
id = "signiferumque",
|
||||
amount = "-4.99 USD",
|
||||
amountColor = { TangemTheme.colors.text.primary1 },
|
||||
time = "19:41",
|
||||
status = TransactionState.Content.Status.Confirmed,
|
||||
direction = TransactionState.Content.Direction.OUTGOING,
|
||||
onClick = {},
|
||||
iconRes = R.drawable.ic_arrow_up_24,
|
||||
title = stringReference("Starbucks"),
|
||||
subtitle = stringReference("Food&Drinks"),
|
||||
timestamp = 3464,
|
||||
iconUrl = null,
|
||||
),
|
||||
),
|
||||
TxHistoryUM.TxHistoryItemUM.Transaction(
|
||||
state = TransactionState.Content(
|
||||
txHash = "signiferumque",
|
||||
TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.Transaction(
|
||||
transaction = TangemPayTransactionState.Content.Spend(
|
||||
id = "signiferumque",
|
||||
amount = "-126.20 USD",
|
||||
amountColor = { TangemTheme.colors.text.primary1 },
|
||||
time = "18:04",
|
||||
status = TransactionState.Content.Status.Confirmed,
|
||||
direction = TransactionState.Content.Direction.OUTGOING,
|
||||
onClick = {},
|
||||
iconRes = R.drawable.ic_arrow_up_24,
|
||||
title = stringReference("Wallmart"),
|
||||
subtitle = stringReference("Supermarket"),
|
||||
timestamp = 3465,
|
||||
iconUrl = null,
|
||||
),
|
||||
),
|
||||
TxHistoryUM.TxHistoryItemUM.Transaction(
|
||||
state = TransactionState.Content(
|
||||
txHash = "signiferumque",
|
||||
TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.Transaction(
|
||||
transaction = TangemPayTransactionState.Content.Spend(
|
||||
id = "signiferumque",
|
||||
amount = "-4.99 USD",
|
||||
amountColor = { TangemTheme.colors.text.primary1 },
|
||||
time = "17:41",
|
||||
status = TransactionState.Content.Status.Confirmed,
|
||||
direction = TransactionState.Content.Direction.OUTGOING,
|
||||
onClick = {},
|
||||
iconRes = R.drawable.ic_arrow_up_24,
|
||||
title = stringReference("Starbucks"),
|
||||
subtitle = stringReference("Food&Drinks"),
|
||||
timestamp = 3464,
|
||||
iconUrl = null,
|
||||
),
|
||||
),
|
||||
TxHistoryUM.TxHistoryItemUM.Transaction(
|
||||
state = TransactionState.Content(
|
||||
txHash = "signiferumque",
|
||||
TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.Transaction(
|
||||
transaction = TangemPayTransactionState.Content.Spend(
|
||||
id = "signiferumque",
|
||||
amount = "-126.20 USD",
|
||||
amountColor = { TangemTheme.colors.text.primary1 },
|
||||
time = "16:04",
|
||||
status = TransactionState.Content.Status.Confirmed,
|
||||
direction = TransactionState.Content.Direction.OUTGOING,
|
||||
onClick = {},
|
||||
iconRes = R.drawable.ic_arrow_up_24,
|
||||
title = stringReference("Wallmart"),
|
||||
subtitle = stringReference("Supermarket"),
|
||||
timestamp = 3465,
|
||||
iconUrl = null,
|
||||
),
|
||||
),
|
||||
TxHistoryUM.TxHistoryItemUM.Transaction(
|
||||
state = TransactionState.Content(
|
||||
txHash = "signiferumque",
|
||||
TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.Transaction(
|
||||
transaction = TangemPayTransactionState.Content.Spend(
|
||||
id = "signiferumque",
|
||||
amount = "-4.99 USD",
|
||||
amountColor = { TangemTheme.colors.text.primary1 },
|
||||
time = "15:41",
|
||||
status = TransactionState.Content.Status.Confirmed,
|
||||
direction = TransactionState.Content.Direction.OUTGOING,
|
||||
onClick = {},
|
||||
iconRes = R.drawable.ic_arrow_up_24,
|
||||
title = stringReference("Starbucks"),
|
||||
subtitle = stringReference("Food&Drinks"),
|
||||
timestamp = 3464,
|
||||
iconUrl = null,
|
||||
),
|
||||
),
|
||||
TxHistoryUM.TxHistoryItemUM.Transaction(
|
||||
state = TransactionState.Content(
|
||||
txHash = "signiferumque",
|
||||
TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.Transaction(
|
||||
transaction = TangemPayTransactionState.Content.Spend(
|
||||
id = "signiferumque",
|
||||
amount = "-126.20 USD",
|
||||
amountColor = { TangemTheme.colors.text.primary1 },
|
||||
time = "14:04",
|
||||
status = TransactionState.Content.Status.Confirmed,
|
||||
direction = TransactionState.Content.Direction.OUTGOING,
|
||||
onClick = {},
|
||||
iconRes = R.drawable.ic_arrow_up_24,
|
||||
title = stringReference("Wallmart"),
|
||||
subtitle = stringReference("Supermarket"),
|
||||
timestamp = 3465,
|
||||
iconUrl = null,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@ package com.tangem.features.tangempay.components.txHistory
|
|||
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import com.tangem.features.txhistory.entity.TxHistoryUM
|
||||
import com.tangem.features.tangempay.entity.TangemPayTxHistoryUM
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
internal interface TangemPayTxHistoryComponent {
|
||||
val state: StateFlow<TxHistoryUM>
|
||||
fun LazyListScope.txHistoryContent(listState: LazyListState, state: TxHistoryUM)
|
||||
val state: StateFlow<TangemPayTxHistoryUM>
|
||||
fun LazyListScope.txHistoryContent(listState: LazyListState, state: TangemPayTxHistoryUM)
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package com.tangem.features.tangempay.entity
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
||||
internal sealed interface TangemPayTransactionState {
|
||||
|
||||
val id: String
|
||||
|
||||
data class Loading(override val id: String) : TangemPayTransactionState
|
||||
|
||||
sealed interface Content : TangemPayTransactionState {
|
||||
|
||||
val onClick: () -> Unit
|
||||
val amount: String
|
||||
val amountColor: @Composable (() -> Color)
|
||||
val title: TextReference
|
||||
val subtitle: TextReference
|
||||
val time: String
|
||||
|
||||
data class Spend(
|
||||
override val id: String,
|
||||
override val onClick: () -> Unit,
|
||||
override val amount: String,
|
||||
override val amountColor: @Composable (() -> Color),
|
||||
override val title: TextReference,
|
||||
override val subtitle: TextReference,
|
||||
override val time: String,
|
||||
val iconUrl: String?,
|
||||
) : Content
|
||||
|
||||
data class Payment(
|
||||
override val id: String,
|
||||
override val onClick: () -> Unit,
|
||||
override val amount: String,
|
||||
override val amountColor: @Composable (() -> Color),
|
||||
override val title: TextReference,
|
||||
override val subtitle: TextReference,
|
||||
override val time: String,
|
||||
val isIncome: Boolean,
|
||||
) : Content
|
||||
|
||||
data class Fee(
|
||||
override val id: String,
|
||||
override val onClick: () -> Unit,
|
||||
override val amount: String,
|
||||
override val amountColor: @Composable (() -> Color),
|
||||
override val title: TextReference,
|
||||
override val subtitle: TextReference,
|
||||
override val time: String,
|
||||
) : Content
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package com.tangem.features.tangempay.entity
|
||||
|
||||
import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
internal sealed interface TangemPayTxHistoryUM {
|
||||
|
||||
val isBalanceHidden: Boolean
|
||||
|
||||
data class Loading(override val isBalanceHidden: Boolean) : TangemPayTxHistoryUM {
|
||||
val items = persistentListOf(
|
||||
TangemPayTxHistoryItemUM.Title,
|
||||
TangemPayTxHistoryItemUM.Transaction(TangemPayTransactionState.Loading("LOADING_TX_HASH_1")),
|
||||
TangemPayTxHistoryItemUM.Transaction(TangemPayTransactionState.Loading("LOADING_TX_HASH_2")),
|
||||
TangemPayTxHistoryItemUM.Transaction(TangemPayTransactionState.Loading("LOADING_TX_HASH_3")),
|
||||
)
|
||||
}
|
||||
|
||||
data class Empty(override val isBalanceHidden: Boolean) : TangemPayTxHistoryUM
|
||||
data class Error(override val isBalanceHidden: Boolean, val onReload: () -> Unit) : TangemPayTxHistoryUM
|
||||
|
||||
data class Content(
|
||||
override val isBalanceHidden: Boolean,
|
||||
val items: ImmutableList<TangemPayTxHistoryItemUM>,
|
||||
val loadMore: () -> Boolean,
|
||||
) : TangemPayTxHistoryUM
|
||||
|
||||
fun copySealed(isBalanceHidden: Boolean): TangemPayTxHistoryUM {
|
||||
return when (this) {
|
||||
is Content -> copy(isBalanceHidden = isBalanceHidden)
|
||||
is Empty -> copy(isBalanceHidden = isBalanceHidden)
|
||||
is Error -> copy(isBalanceHidden = isBalanceHidden)
|
||||
is Loading -> copy(isBalanceHidden = isBalanceHidden)
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface TangemPayTxHistoryItemUM {
|
||||
data object Title : TangemPayTxHistoryItemUM
|
||||
data class GroupTitle(val title: String, val itemKey: String) : TangemPayTxHistoryItemUM {
|
||||
val legacyGroupTitle = TxHistoryState.TxHistoryItemState.GroupTitle(title = title, itemKey = itemKey)
|
||||
}
|
||||
data class Transaction(val transaction: TangemPayTransactionState) : TangemPayTxHistoryItemUM
|
||||
}
|
||||
}
|
||||
|
|
@ -6,10 +6,11 @@ import com.tangem.core.decompose.model.Model
|
|||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
import com.tangem.domain.tangempay.repository.TangemPayTxHistoryRepository
|
||||
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
|
||||
import com.tangem.features.tangempay.components.txHistory.DefaultTangemPayTxHistoryComponent
|
||||
import com.tangem.features.tangempay.entity.TangemPayTxHistoryUM
|
||||
import com.tangem.features.tangempay.utils.TangemPayTxHistoryListManager
|
||||
import com.tangem.features.txhistory.entity.TxHistoryUM
|
||||
import com.tangem.features.txhistory.utils.TxHistoryUiActions
|
||||
import com.tangem.features.tangempay.utils.TangemPayTxHistoryUiActions
|
||||
import com.tangem.pagination.PaginationStatus
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
|
@ -25,7 +26,7 @@ internal class TangemPayTxHistoryModel @Inject constructor(
|
|||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
tangemPayTxHistoryRepository: TangemPayTxHistoryRepository,
|
||||
paramsContainer: ParamsContainer,
|
||||
) : Model(), TxHistoryUiActions {
|
||||
) : Model(), TangemPayTxHistoryUiActions {
|
||||
|
||||
private val params: DefaultTangemPayTxHistoryComponent.Params = paramsContainer.require()
|
||||
private val listManager = TangemPayTxHistoryListManager(
|
||||
|
|
@ -35,8 +36,8 @@ internal class TangemPayTxHistoryModel @Inject constructor(
|
|||
txHistoryUiActions = this,
|
||||
)
|
||||
|
||||
val uiState: StateFlow<TxHistoryUM>
|
||||
field = MutableStateFlow<TxHistoryUM>(getLoadingState(isBalanceHidden = true))
|
||||
val uiState: StateFlow<TangemPayTxHistoryUM>
|
||||
field = MutableStateFlow<TangemPayTxHistoryUM>(getLoadingState(isBalanceHidden = true))
|
||||
|
||||
init {
|
||||
handleBalanceHiding()
|
||||
|
|
@ -50,19 +51,19 @@ internal class TangemPayTxHistoryModel @Inject constructor(
|
|||
|
||||
private fun subscribeToUiItemChanges() {
|
||||
listManager.uiItems
|
||||
.onEach { updateState(it) }
|
||||
.onEach(::updateState)
|
||||
.launchIn(modelScope)
|
||||
listManager.paginationStatus
|
||||
.onEach { paginationStatus -> handlePaginationStatus(paginationStatus) }
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun updateState(items: ImmutableList<TxHistoryUM.TxHistoryItemUM>) {
|
||||
private fun updateState(items: ImmutableList<TangemPayTxHistoryUM.TangemPayTxHistoryItemUM>) {
|
||||
uiState.update { state ->
|
||||
if (state is TxHistoryUM.Content) {
|
||||
if (state is TangemPayTxHistoryUM.Content) {
|
||||
state.copy(items = items)
|
||||
} else {
|
||||
TxHistoryUM.Content(
|
||||
TangemPayTxHistoryUM.Content(
|
||||
items = items,
|
||||
isBalanceHidden = state.isBalanceHidden,
|
||||
loadMore = ::loadMoreItems,
|
||||
|
|
@ -91,11 +92,8 @@ internal class TangemPayTxHistoryModel @Inject constructor(
|
|||
}
|
||||
|
||||
fun reload() {
|
||||
// fast exit
|
||||
if (uiState.value is TxHistoryUM.NotSupported) return
|
||||
|
||||
uiState.update { state ->
|
||||
state as? TxHistoryUM.Content ?: getLoadingState(state.isBalanceHidden)
|
||||
state as? TangemPayTxHistoryUM.Content ?: getLoadingState(state.isBalanceHidden)
|
||||
}
|
||||
modelScope.launch { listManager.reload() }
|
||||
}
|
||||
|
|
@ -106,23 +104,15 @@ internal class TangemPayTxHistoryModel @Inject constructor(
|
|||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
override fun openExplorer() {
|
||||
Timber.d("onExploreClick: open explorer")
|
||||
override fun onTransactionClick(item: TangemPayTxHistoryItem) {
|
||||
Timber.d("onTransactionClick: $item")
|
||||
}
|
||||
|
||||
override fun openTxInExplorer(txHash: String) {
|
||||
Timber.d("openTxInExplorer: $txHash")
|
||||
private fun getErrorState(isBalanceHidden: Boolean): TangemPayTxHistoryUM.Error {
|
||||
return TangemPayTxHistoryUM.Error(isBalanceHidden = isBalanceHidden, onReload = ::reload)
|
||||
}
|
||||
|
||||
private fun getErrorState(isBalanceHidden: Boolean): TxHistoryUM.Error {
|
||||
return TxHistoryUM.Error(
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
onReloadClick = ::reload,
|
||||
onExploreClick = ::openExplorer,
|
||||
)
|
||||
}
|
||||
|
||||
private fun getLoadingState(isBalanceHidden: Boolean): TxHistoryUM.Loading {
|
||||
return TxHistoryUM.Loading(isBalanceHidden = isBalanceHidden, onExploreClick = ::openExplorer)
|
||||
private fun getLoadingState(isBalanceHidden: Boolean): TangemPayTxHistoryUM.Loading {
|
||||
return TangemPayTxHistoryUM.Loading(isBalanceHidden = isBalanceHidden)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,40 +1,80 @@
|
|||
package com.tangem.features.tangempay.model.transformers
|
||||
|
||||
import com.tangem.core.ui.components.transactions.state.TransactionState
|
||||
import com.tangem.core.ui.extensions.capitalize
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.utils.DateTimeFormatters
|
||||
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
|
||||
import com.tangem.features.tangempay.details.impl.R
|
||||
import com.tangem.features.txhistory.utils.TxHistoryUiActions
|
||||
import com.tangem.utils.StringsSigns
|
||||
import com.tangem.features.tangempay.entity.TangemPayTransactionState
|
||||
import com.tangem.features.tangempay.utils.TangemPayTxHistoryUiActions
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.utils.extensions.isPositive
|
||||
import org.joda.time.DateTimeZone
|
||||
import java.util.Currency
|
||||
|
||||
internal class TangemPayTxHistoryItemsConverter(
|
||||
private val txHistoryUiActions: TxHistoryUiActions,
|
||||
) : Converter<TangemPayTxHistoryItem, TransactionState> {
|
||||
override fun convert(value: TangemPayTxHistoryItem): TransactionState {
|
||||
val localDate = value.date?.withZone(DateTimeZone.getDefault())
|
||||
val currency = Currency.getInstance(value.currency)
|
||||
val amount = value.amount.format {
|
||||
fiat(fiatCurrencyCode = currency.currencyCode, fiatCurrencySymbol = currency.symbol)
|
||||
private val txHistoryUiActions: TangemPayTxHistoryUiActions,
|
||||
) : Converter<TangemPayTxHistoryItem, TangemPayTransactionState.Content> {
|
||||
override fun convert(value: TangemPayTxHistoryItem): TangemPayTransactionState.Content {
|
||||
return when (value) {
|
||||
is TangemPayTxHistoryItem.Spend -> convertSpend(spend = value)
|
||||
is TangemPayTxHistoryItem.Payment -> convertPayment(payment = value)
|
||||
is TangemPayTxHistoryItem.Fee -> convertFee(fee = value)
|
||||
}
|
||||
}
|
||||
|
||||
return TransactionState.Content(
|
||||
txHash = value.id,
|
||||
amount = "${StringsSigns.MINUS}$amount",
|
||||
time = localDate?.let { DateTimeFormatters.formatDate(it, DateTimeFormatters.timeFormatter) } ?: "",
|
||||
status = TransactionState.Content.Status.Confirmed,
|
||||
direction = TransactionState.Content.Direction.OUTGOING,
|
||||
iconRes = R.drawable.ic_arrow_up_24,
|
||||
title = stringReference(value = value.merchantName?.capitalize() ?: "Unknown merchant"),
|
||||
subtitle = stringReference("How to get merchant type?"),
|
||||
timestamp = localDate?.millis ?: 0,
|
||||
onClick = { txHistoryUiActions.openTxInExplorer(value.id) },
|
||||
private fun convertSpend(spend: TangemPayTxHistoryItem.Spend): TangemPayTransactionState.Content.Spend {
|
||||
val localDate = spend.date.withZone(DateTimeZone.getDefault())
|
||||
val amount = spend.amount.format {
|
||||
fiat(fiatCurrencyCode = spend.currency.currencyCode, fiatCurrencySymbol = spend.currency.symbol)
|
||||
}
|
||||
return TangemPayTransactionState.Content.Spend(
|
||||
id = spend.id,
|
||||
onClick = { txHistoryUiActions.onTransactionClick(spend) },
|
||||
amount = amount,
|
||||
amountColor = { TangemTheme.colors.text.primary1 },
|
||||
title = stringReference(spend.enrichedMerchantName ?: spend.merchantName),
|
||||
subtitle = stringReference(spend.enrichedMerchantCategory ?: spend.merchantCategory),
|
||||
time = DateTimeFormatters.formatDate(localDate, DateTimeFormatters.timeFormatter),
|
||||
iconUrl = spend.enrichedMerchantIconUrl,
|
||||
)
|
||||
}
|
||||
|
||||
private fun convertPayment(payment: TangemPayTxHistoryItem.Payment): TangemPayTransactionState.Content.Payment {
|
||||
val amount = payment.amount.format {
|
||||
fiat(fiatCurrencyCode = payment.currency.currencyCode, fiatCurrencySymbol = payment.currency.symbol)
|
||||
}
|
||||
val title = if (payment.amount.isPositive()) "Deposit" else "Withdrawal"
|
||||
return TangemPayTransactionState.Content.Payment(
|
||||
id = payment.id,
|
||||
onClick = { txHistoryUiActions.onTransactionClick(payment) },
|
||||
amount = amount,
|
||||
amountColor = {
|
||||
if (payment.amount.isPositive()) {
|
||||
TangemTheme.colors.text.accent
|
||||
} else {
|
||||
TangemTheme.colors.text.primary1
|
||||
}
|
||||
},
|
||||
title = stringReference(title),
|
||||
subtitle = stringReference("Transfers"),
|
||||
time = DateTimeFormatters.formatDate(payment.date, DateTimeFormatters.timeFormatter),
|
||||
isIncome = payment.amount.isPositive(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun convertFee(fee: TangemPayTxHistoryItem.Fee): TangemPayTransactionState.Content.Fee {
|
||||
val amount = fee.amount.format {
|
||||
fiat(fiatCurrencyCode = fee.currency.currencyCode, fiatCurrencySymbol = fee.currency.symbol)
|
||||
}
|
||||
return TangemPayTransactionState.Content.Fee(
|
||||
id = fee.id,
|
||||
onClick = { txHistoryUiActions.onTransactionClick(fee) },
|
||||
amount = amount,
|
||||
amountColor = { TangemTheme.colors.text.primary1 },
|
||||
title = stringReference("Fee"),
|
||||
subtitle = stringReference("Service fees"),
|
||||
time = DateTimeFormatters.formatDate(fee.date, DateTimeFormatters.timeFormatter),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,373 @@
|
|||
package com.tangem.features.tangempay.ui
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Surface
|
||||
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.graphics.Color
|
||||
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.constraintlayout.compose.ChainStyle
|
||||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import androidx.constraintlayout.compose.Dimension
|
||||
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.list.InfiniteListHandler
|
||||
import com.tangem.core.ui.components.transactions.TxHistoryGroupTitle
|
||||
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
|
||||
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.TransactionHistoryBlockTestTags
|
||||
import com.tangem.features.tangempay.entity.TangemPayTransactionState
|
||||
import com.tangem.features.tangempay.entity.TangemPayTxHistoryUM
|
||||
|
||||
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.Loading -> loadingItems(state = state)
|
||||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.contentItems(listState: LazyListState, state: TangemPayTxHistoryUM.Content) {
|
||||
itemsIndexed(
|
||||
items = state.items,
|
||||
key = { index, item ->
|
||||
when (item) {
|
||||
is TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.GroupTitle -> item.itemKey
|
||||
is TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.Title -> index + item.hashCode()
|
||||
is TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.Transaction ->
|
||||
item.transaction.id + (item.transaction as? TangemPayTransactionState.Content)?.hashCode()
|
||||
}
|
||||
},
|
||||
contentType = { _, item -> item::class.java },
|
||||
itemContent = { index, item ->
|
||||
TangemPayTxHistoryListItem(
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.loadingItems(state: TangemPayTxHistoryUM.Loading) {
|
||||
itemsIndexed(
|
||||
items = state.items,
|
||||
key = { _, item ->
|
||||
when (item) {
|
||||
is TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.GroupTitle -> item.itemKey
|
||||
is TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.Title -> item.hashCode()
|
||||
is TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.Transaction ->
|
||||
item.transaction.id + (item.transaction as? TangemPayTransactionState.Content)?.hashCode()
|
||||
}
|
||||
},
|
||||
contentType = { _, item -> item::class.java },
|
||||
itemContent = { index, item ->
|
||||
TangemPayTxHistoryListItem(
|
||||
state = item,
|
||||
isBalanceHidden = true,
|
||||
modifier = Modifier.roundedShapeItemDecoration(
|
||||
currentIndex = index,
|
||||
lastIndex = state.items.lastIndex,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TangemPayTxHistoryListItem(
|
||||
state: TangemPayTxHistoryUM.TangemPayTxHistoryItemUM,
|
||||
isBalanceHidden: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
when (state) {
|
||||
is TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.GroupTitle -> {
|
||||
TxHistoryGroupTitle(config = state.legacyGroupTitle, modifier = modifier)
|
||||
}
|
||||
is TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.Title -> {
|
||||
TangemPayTxHistoryTitle(modifier = modifier)
|
||||
}
|
||||
is TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.Transaction -> {
|
||||
TangemPayTransaction(
|
||||
transactionState = state.transaction,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TangemPayTxHistoryTitle(modifier: Modifier = Modifier) {
|
||||
Text(
|
||||
text = stringResourceSafe(id = R.string.common_transactions),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
.padding(top = TangemTheme.dimens.spacing12)
|
||||
.padding(horizontal = TangemTheme.dimens.spacing12)
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = TangemTheme.dimens.size24)
|
||||
.testTag(TransactionHistoryBlockTestTags.TITLE_TEXT),
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun TangemPayTransaction(
|
||||
transactionState: TangemPayTransactionState,
|
||||
isBalanceHidden: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Surface(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
.clickable(
|
||||
enabled = transactionState is TangemPayTransactionState.Content,
|
||||
onClick = (transactionState as? TangemPayTransactionState.Content)?.onClick ?: {},
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = TangemTheme.dimens.size56)
|
||||
.padding(
|
||||
vertical = TangemTheme.dimens.spacing8,
|
||||
horizontal = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
color = TangemTheme.colors.background.primary,
|
||||
) {
|
||||
@Suppress("DestructuringDeclarationWithTooManyEntries")
|
||||
ConstraintLayout(modifier = Modifier.fillMaxWidth()) {
|
||||
val (iconItem, titleItem, subtitleItem, amountItem, timestampItem) = createRefs()
|
||||
|
||||
createVerticalChain(titleItem, subtitleItem, chainStyle = ChainStyle.Spread)
|
||||
createVerticalChain(amountItem, timestampItem, chainStyle = ChainStyle.Spread)
|
||||
|
||||
Icon(
|
||||
state = transactionState,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size40)
|
||||
.constrainAs(iconItem) {
|
||||
start.linkTo(parent.start)
|
||||
centerVerticallyTo(parent)
|
||||
},
|
||||
)
|
||||
|
||||
Title(
|
||||
state = transactionState,
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
start = TangemTheme.dimens.spacing12,
|
||||
end = TangemTheme.dimens.spacing4,
|
||||
)
|
||||
.constrainAs(titleItem) {
|
||||
top.linkTo(parent.top)
|
||||
bottom.linkTo(subtitleItem.top)
|
||||
start.linkTo(iconItem.end)
|
||||
end.linkTo(amountItem.start)
|
||||
width = Dimension.fillToConstraints
|
||||
},
|
||||
)
|
||||
|
||||
Subtitle(
|
||||
state = transactionState,
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
start = TangemTheme.dimens.spacing12,
|
||||
end = TangemTheme.dimens.spacing4,
|
||||
)
|
||||
.constrainAs(subtitleItem) {
|
||||
top.linkTo(titleItem.bottom)
|
||||
bottom.linkTo(parent.bottom)
|
||||
start.linkTo(iconItem.end)
|
||||
end.linkTo(timestampItem.start)
|
||||
width = Dimension.fillToConstraints
|
||||
},
|
||||
)
|
||||
|
||||
Amount(
|
||||
state = transactionState,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
modifier = Modifier.constrainAs(amountItem) {
|
||||
top.linkTo(parent.top)
|
||||
bottom.linkTo(timestampItem.top)
|
||||
start.linkTo(titleItem.end)
|
||||
end.linkTo(parent.end)
|
||||
width = Dimension.fillToConstraints
|
||||
},
|
||||
)
|
||||
|
||||
Timestamp(
|
||||
state = transactionState,
|
||||
modifier = Modifier.constrainAs(timestampItem) {
|
||||
top.linkTo(amountItem.bottom)
|
||||
bottom.linkTo(parent.bottom)
|
||||
end.linkTo(parent.end)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Icon(state: TangemPayTransactionState, modifier: Modifier = Modifier) {
|
||||
when (state) {
|
||||
is TangemPayTransactionState.Content.Spend -> {
|
||||
if (state.iconUrl != null) {
|
||||
RemoteIcon(modifier = modifier, url = state.iconUrl)
|
||||
} else {
|
||||
LocalStaticIcon(modifier = modifier, id = R.drawable.ic_category_24)
|
||||
}
|
||||
}
|
||||
is TangemPayTransactionState.Content.Fee -> LocalStaticIcon(modifier = modifier, id = R.drawable.ic_percent_24)
|
||||
is TangemPayTransactionState.Content.Payment -> LocalStaticIcon(
|
||||
modifier = modifier,
|
||||
id = if (state.isIncome) R.drawable.ic_arrow_down_24 else R.drawable.ic_arrow_up_24,
|
||||
)
|
||||
is TangemPayTransactionState.Loading -> {
|
||||
CircleShimmer(modifier = modifier.size(TangemTheme.dimens.size40))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RemoteIcon(url: String, modifier: Modifier = Modifier) {
|
||||
Icon(
|
||||
modifier = modifier
|
||||
.size(TangemTheme.dimens.size40)
|
||||
.clip(CircleShape),
|
||||
painter = rememberAsyncImagePainter(url),
|
||||
contentDescription = null,
|
||||
tint = Color.Unspecified,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LocalStaticIcon(@DrawableRes id: Int, modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.size(TangemTheme.dimens.size40)
|
||||
.background(color = TangemTheme.colors.icon.secondary.copy(alpha = 0.1F), shape = CircleShape),
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size20)
|
||||
.align(Alignment.Center),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Title(state: TangemPayTransactionState, modifier: Modifier = Modifier) {
|
||||
when (state) {
|
||||
is TangemPayTransactionState.Content -> {
|
||||
Text(
|
||||
modifier = modifier,
|
||||
text = state.title.resolveReference(),
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
is TangemPayTransactionState.Loading -> {
|
||||
RectangleShimmer(
|
||||
modifier = modifier.size(width = TangemTheme.dimens.size70, height = TangemTheme.dimens.size12),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Subtitle(state: TangemPayTransactionState, modifier: Modifier = Modifier) {
|
||||
when (state) {
|
||||
is TangemPayTransactionState.Content -> {
|
||||
Text(
|
||||
text = state.subtitle.resolveReference(),
|
||||
modifier = modifier,
|
||||
textAlign = TextAlign.Start,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.caption2,
|
||||
)
|
||||
}
|
||||
is TangemPayTransactionState.Loading -> {
|
||||
RectangleShimmer(
|
||||
modifier = modifier.size(width = TangemTheme.dimens.size52, height = TangemTheme.dimens.size12),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Amount(state: TangemPayTransactionState, isBalanceHidden: Boolean, modifier: Modifier = Modifier) {
|
||||
when (state) {
|
||||
is TangemPayTransactionState.Content -> {
|
||||
Text(
|
||||
text = state.amount.orMaskWithStars(isBalanceHidden),
|
||||
modifier = modifier,
|
||||
textAlign = TextAlign.End,
|
||||
color = state.amountColor(),
|
||||
style = TangemTheme.typography.body2,
|
||||
)
|
||||
}
|
||||
is TangemPayTransactionState.Loading -> {
|
||||
RectangleShimmer(
|
||||
modifier = modifier.size(width = TangemTheme.dimens.size40, height = TangemTheme.dimens.size12),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Timestamp(state: TangemPayTransactionState, modifier: Modifier = Modifier) {
|
||||
when (state) {
|
||||
is TangemPayTransactionState.Content -> {
|
||||
Text(
|
||||
text = state.time,
|
||||
modifier = modifier,
|
||||
textAlign = TextAlign.End,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.caption2,
|
||||
)
|
||||
}
|
||||
is TangemPayTransactionState.Loading -> {
|
||||
RectangleShimmer(
|
||||
modifier = modifier.size(width = TangemTheme.dimens.size40, height = TangemTheme.dimens.size12),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,8 +4,7 @@ import com.tangem.domain.tangempay.model.TangemPayTxHistoryListBatchingContext
|
|||
import com.tangem.domain.tangempay.model.TangemPayTxHistoryListConfig
|
||||
import com.tangem.domain.tangempay.repository.TangemPayTxHistoryRepository
|
||||
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
|
||||
import com.tangem.features.txhistory.entity.TxHistoryUM
|
||||
import com.tangem.features.txhistory.utils.TxHistoryUiActions
|
||||
import com.tangem.features.tangempay.entity.TangemPayTxHistoryUM
|
||||
import com.tangem.pagination.BatchAction
|
||||
import com.tangem.pagination.BatchListState
|
||||
import com.tangem.pagination.PaginationStatus
|
||||
|
|
@ -23,7 +22,7 @@ internal class TangemPayTxHistoryListManager(
|
|||
private val repository: TangemPayTxHistoryRepository,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val customerWalletAddress: String,
|
||||
private val txHistoryUiActions: TxHistoryUiActions,
|
||||
private val txHistoryUiActions: TangemPayTxHistoryUiActions,
|
||||
) {
|
||||
private val jobHolder = JobHolder()
|
||||
private val actionsFlow: MutableSharedFlow<TangemPayTxHistoryBatchAction> = MutableSharedFlow(
|
||||
|
|
@ -33,7 +32,7 @@ internal class TangemPayTxHistoryListManager(
|
|||
private val state: MutableStateFlow<TangemPayTxHistoryState> = MutableStateFlow(TangemPayTxHistoryState())
|
||||
private val uiManager = TangemPayTxHistoryUiManager(state = state, txHistoryUiActions = txHistoryUiActions)
|
||||
|
||||
val uiItems: Flow<ImmutableList<TxHistoryUM.TxHistoryItemUM>> = uiManager.items
|
||||
val uiItems: Flow<ImmutableList<TangemPayTxHistoryUM.TangemPayTxHistoryItemUM>> = uiManager.items
|
||||
val paginationStatus: Flow<PaginationStatus<*>> = state.map { it.status }.distinctUntilChanged()
|
||||
|
||||
suspend fun launchPagination() = coroutineScope {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
package com.tangem.features.tangempay.utils
|
||||
|
||||
import com.tangem.features.txhistory.entity.TxHistoryUM
|
||||
import com.tangem.features.tangempay.entity.TangemPayTxHistoryUM
|
||||
import com.tangem.pagination.Batch
|
||||
import com.tangem.pagination.PaginationStatus
|
||||
|
||||
data class TangemPayTxHistoryState(
|
||||
internal data class TangemPayTxHistoryState(
|
||||
val status: PaginationStatus<*> = PaginationStatus.None,
|
||||
val uiBatches: List<Batch<Int, List<TxHistoryUM.TxHistoryItemUM>>> = listOf(),
|
||||
val uiBatches: List<Batch<Int, List<TangemPayTxHistoryUM.TangemPayTxHistoryItemUM>>> = listOf(),
|
||||
)
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.tangem.features.tangempay.utils
|
||||
|
||||
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
|
||||
|
||||
internal interface TangemPayTxHistoryUiActions {
|
||||
fun onTransactionClick(item: TangemPayTxHistoryItem)
|
||||
}
|
||||
|
|
@ -2,9 +2,8 @@ package com.tangem.features.tangempay.utils
|
|||
|
||||
import com.tangem.core.ui.utils.toDateFormatWithTodayYesterday
|
||||
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
|
||||
import com.tangem.features.tangempay.entity.TangemPayTxHistoryUM
|
||||
import com.tangem.features.tangempay.model.transformers.TangemPayTxHistoryItemsConverter
|
||||
import com.tangem.features.txhistory.entity.TxHistoryUM
|
||||
import com.tangem.features.txhistory.utils.TxHistoryUiActions
|
||||
import com.tangem.pagination.Batch
|
||||
import com.tangem.pagination.PaginationStatus
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
|
@ -15,11 +14,11 @@ import java.util.UUID
|
|||
|
||||
internal class TangemPayTxHistoryUiManager(
|
||||
private val state: MutableStateFlow<TangemPayTxHistoryState>,
|
||||
private val txHistoryUiActions: TxHistoryUiActions,
|
||||
private val txHistoryUiActions: TangemPayTxHistoryUiActions,
|
||||
) {
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val items: Flow<ImmutableList<TxHistoryUM.TxHistoryItemUM>> = state
|
||||
val items: Flow<ImmutableList<TangemPayTxHistoryUM.TangemPayTxHistoryItemUM>> = state
|
||||
// filter initial states, since we dont emit loading items as UI items
|
||||
.filter {
|
||||
it.status !is PaginationStatus.None &&
|
||||
|
|
@ -38,7 +37,7 @@ internal class TangemPayTxHistoryUiManager(
|
|||
fun createOrUpdateUiBatches(
|
||||
newCurrencyBatches: List<Batch<Int, List<TangemPayTxHistoryItem>>>,
|
||||
clearUiBatches: Boolean,
|
||||
): List<Batch<Int, List<TxHistoryUM.TxHistoryItemUM>>> {
|
||||
): List<Batch<Int, List<TangemPayTxHistoryUM.TangemPayTxHistoryItemUM>>> {
|
||||
val currentUiBatches = state.value.uiBatches
|
||||
val batches = if (clearUiBatches) mutableListOf() else currentUiBatches.toMutableList()
|
||||
|
||||
|
|
@ -68,51 +67,58 @@ internal class TangemPayTxHistoryUiManager(
|
|||
return batches
|
||||
}
|
||||
|
||||
private fun generateUiItems(key: Int, data: List<TangemPayTxHistoryItem>): List<TxHistoryUM.TxHistoryItemUM> {
|
||||
val items = mutableListOf<TxHistoryUM.TxHistoryItemUM>()
|
||||
private fun generateUiItems(
|
||||
key: Int,
|
||||
data: List<TangemPayTxHistoryItem>,
|
||||
): List<TangemPayTxHistoryUM.TangemPayTxHistoryItemUM> {
|
||||
val items = mutableListOf<TangemPayTxHistoryUM.TangemPayTxHistoryItemUM>()
|
||||
|
||||
// Add title for the first batch
|
||||
if (key == 0) {
|
||||
items.add(TxHistoryUM.TxHistoryItemUM.Title(onExploreClick = txHistoryUiActions::openExplorer))
|
||||
items.add(TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.Title)
|
||||
}
|
||||
|
||||
// Process batch items only if there are any
|
||||
if (data.isNotEmpty()) {
|
||||
// Add first item with its group title
|
||||
val firstItem = data.first()
|
||||
val firstDate = firstItem.timeStampInMillis.toDateFormatWithTodayYesterday()
|
||||
val firstDate = firstItem.date.millis.toDateFormatWithTodayYesterday()
|
||||
|
||||
items.add(
|
||||
TxHistoryUM.TxHistoryItemUM.GroupTitle(
|
||||
TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.GroupTitle(
|
||||
title = firstDate,
|
||||
itemKey = UUID.randomUUID().toString(),
|
||||
),
|
||||
)
|
||||
items.add(TxHistoryUM.TxHistoryItemUM.Transaction(txHistoryItemConverter.convert(firstItem)))
|
||||
items.add(
|
||||
TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.Transaction(txHistoryItemConverter.convert(firstItem)),
|
||||
)
|
||||
|
||||
// Process remaining items with date separators when needed
|
||||
data.zipWithNext { current, next ->
|
||||
val currentDate = current.timeStampInMillis.toDateFormatWithTodayYesterday()
|
||||
val nextDate = next.timeStampInMillis.toDateFormatWithTodayYesterday()
|
||||
val currentDate = current.date.millis.toDateFormatWithTodayYesterday()
|
||||
val nextDate = next.date.millis.toDateFormatWithTodayYesterday()
|
||||
|
||||
if (currentDate != nextDate) {
|
||||
items.add(
|
||||
TxHistoryUM.TxHistoryItemUM.GroupTitle(
|
||||
TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.GroupTitle(
|
||||
title = nextDate,
|
||||
itemKey = UUID.randomUUID().toString(),
|
||||
),
|
||||
)
|
||||
}
|
||||
items.add(TxHistoryUM.TxHistoryItemUM.Transaction(txHistoryItemConverter.convert(next)))
|
||||
items.add(
|
||||
TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.Transaction(txHistoryItemConverter.convert(next)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
private fun List<TxHistoryUM.TxHistoryItemUM>.transactionItemsSizeNotEqual(
|
||||
private fun List<TangemPayTxHistoryUM.TangemPayTxHistoryItemUM>.transactionItemsSizeNotEqual(
|
||||
txInfos: List<TangemPayTxHistoryItem>,
|
||||
): Boolean {
|
||||
return this.filterIsInstance<TxHistoryUM.TxHistoryItemUM.Transaction>().size != txInfos.size
|
||||
return this.filterIsInstance<TangemPayTxHistoryUM.TangemPayTxHistoryItemUM.Transaction>().size != txInfos.size
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.features.txhistory.utils
|
||||
|
||||
interface TxHistoryUiActions {
|
||||
internal interface TxHistoryUiActions {
|
||||
|
||||
fun openExplorer()
|
||||
fun openTxInExplorer(txHash: String)
|
||||
|
|
@ -58,6 +58,7 @@ dependencies {
|
|||
implementation(projects.core.decompose)
|
||||
implementation(projects.core.datasource)
|
||||
implementation(projects.core.res)
|
||||
implementation(projects.core.error)
|
||||
|
||||
implementation(projects.libs.crypto)
|
||||
implementation(projects.libs.blockchainSdk)
|
||||
|
|
|
|||
|
|
@ -356,42 +356,28 @@ internal class WalletModel @Inject constructor(
|
|||
}
|
||||
|
||||
private suspend fun refreshTangemPayInfo() {
|
||||
val newState = withContext(dispatchers.io) {
|
||||
tangemPayMainScreenCustomerInfoUseCase()?.let { info ->
|
||||
TangemPayStateTransformer(
|
||||
val info = tangemPayMainScreenCustomerInfoUseCase()
|
||||
if (info != null) {
|
||||
stateHolder.update(
|
||||
transformer = TangemPayStateTransformer(
|
||||
value = info,
|
||||
onIssueOrderClick = ::issueOrder,
|
||||
onContinueKycClick = innerWalletRouter::openTangemPayOnboarding,
|
||||
cardOnClick = ::tangemPayCardOnClick,
|
||||
).transform(stateHolder.uiState.value.tangemPayState)
|
||||
}
|
||||
} ?: return
|
||||
stateHolder.update { it.copy(tangemPayState = newState) }
|
||||
openDetails = innerWalletRouter::openTangemPayDetails,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun issueOrder() {
|
||||
modelScope.launch {
|
||||
stateHolder.update {
|
||||
it.copy(
|
||||
tangemPayState = TangemPayStateTransformer(issueProgressState = true)
|
||||
.transform(it.tangemPayState),
|
||||
)
|
||||
}
|
||||
withContext(dispatchers.io) {
|
||||
tangemPayIssueOrderUseCase()
|
||||
} ?: stateHolder.update {
|
||||
it.copy(
|
||||
tangemPayState = TangemPayStateTransformer(issueState = true, onIssueOrderClick = ::issueOrder)
|
||||
.transform(it.tangemPayState),
|
||||
)
|
||||
stateHolder.update(TangemPayStateTransformer(issueProgressState = true))
|
||||
tangemPayIssueOrderUseCase().onLeft {
|
||||
stateHolder.update(TangemPayStateTransformer(issueState = true, onIssueOrderClick = ::issueOrder))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun tangemPayCardOnClick(customerWalletAddress: String, cardNumberEnd: String) {
|
||||
innerWalletRouter.openTangemPayDetails(customerWalletAddress, cardNumberEnd)
|
||||
}
|
||||
|
||||
private fun needToRefreshTimer() {
|
||||
modelScope.launch {
|
||||
delay(REFRESH_WALLET_BACKGROUND_TIMER_MILLIS)
|
||||
|
|
|
|||
|
|
@ -6,26 +6,29 @@ import com.tangem.core.ui.format.bigdecimal.fiat
|
|||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.domain.pay.model.CustomerInfo.CardInfo
|
||||
import com.tangem.domain.pay.model.MainScreenCustomerInfo
|
||||
import com.tangem.domain.pay.model.OrderStatus.NOT_ISSUED
|
||||
import com.tangem.domain.pay.model.OrderStatus.CANCELED
|
||||
import com.tangem.domain.pay.model.OrderStatus.NOT_ISSUED
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState.Progress
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
|
||||
import java.util.Currency
|
||||
|
||||
internal class TangemPayStateTransformer(
|
||||
private val value: MainScreenCustomerInfo? = null,
|
||||
private val onIssueOrderClick: () -> Unit = {},
|
||||
private val onContinueKycClick: () -> Unit = {},
|
||||
private val cardOnClick: (customerWalletAddress: String, cardNumberEnd: String) -> Unit = { _, _ -> },
|
||||
private val openDetails: (customerWalletAddress: String, cardNumberEnd: String) -> Unit = { _, _ -> },
|
||||
private val issueProgressState: Boolean = false,
|
||||
private val issueState: Boolean = false,
|
||||
) : Transformer<TangemPayState> {
|
||||
) : WalletScreenStateTransformer {
|
||||
|
||||
override fun transform(prevState: TangemPayState): TangemPayState = when {
|
||||
issueProgressState -> createIssueProgressState()
|
||||
issueState -> createIssueState()
|
||||
else -> createInitialState()
|
||||
override fun transform(prevState: WalletScreenState): WalletScreenState {
|
||||
val tangemPayState = when {
|
||||
issueProgressState -> createIssueProgressState()
|
||||
issueState -> createIssueState()
|
||||
else -> createInitialState()
|
||||
}
|
||||
return prevState.copy(tangemPayState = tangemPayState)
|
||||
}
|
||||
|
||||
private fun createInitialState(): TangemPayState {
|
||||
|
|
@ -64,7 +67,7 @@ internal class TangemPayStateTransformer(
|
|||
private fun getCardInfoState(cardInfo: CardInfo): TangemPayState = TangemPayState.Card(
|
||||
lastFourDigits = TextReference.Str("*${cardInfo.lastFourDigits}"),
|
||||
balanceText = TextReference.Str(getBalanceText(cardInfo)),
|
||||
onClick = { cardOnClick(cardInfo.customerWalletAddress, cardInfo.lastFourDigits) },
|
||||
onClick = { openDetails(cardInfo.customerWalletAddress, cardInfo.lastFourDigits) },
|
||||
)
|
||||
|
||||
private fun getBalanceText(cardInfo: CardInfo): String {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Surface
|
||||
|
|
@ -22,11 +21,10 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
|
|||
@Composable
|
||||
internal fun TangemPayCardMainBlock(state: TangemPayState.Card, modifier: Modifier = Modifier) {
|
||||
Surface(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { state.onClick() },
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
color = TangemTheme.colors.background.primary,
|
||||
onClick = state.onClick,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue