Updated on 2026-08-14

This commit is contained in:
Tangem 2025-09-30 11:59:23 +04:00
parent 418fbf710d
commit 8c47e9483c
20 changed files with 53 additions and 36 deletions

View file

@ -593,7 +593,7 @@ internal class ChildFactory @Inject constructor(
is AppRoute.TangemPayDetails -> {
createComponentChild(
context = context,
params = TangemPayDetailsComponent.Params(userWalletId = route.userWalletId),
params = TangemPayDetailsComponent.Params(customerWalletAddress = route.customerWalletAddress),
componentFactory = tangemPayDetailsComponentFactory,
)
}

View file

@ -439,7 +439,7 @@ sealed class AppRoute(val path: String) : Route {
) : AppRoute(path = "/archived_account/${userWalletId.stringValue}")
@Serializable
data class TangemPayDetails(val userWalletId: UserWalletId) : AppRoute(path = "/tangem_pay_details")
data class TangemPayDetails(val customerWalletAddress: String) : AppRoute(path = "/tangem_pay_details")
@Serializable
data class TangemPayOnboarding(

View file

@ -2,21 +2,20 @@ package com.tangem.datasource.local.visa
import com.tangem.datasource.local.datastore.core.StringKeyDataStore
import com.tangem.datasource.local.datastore.core.StringKeyDataStoreDecorator
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
internal class DefaultTangemPayTxHistoryItemsStore(
dataStore: StringKeyDataStore<Map<String, List<TangemPayTxHistoryItem>>>,
) : TangemPayTxHistoryItemsStore,
StringKeyDataStoreDecorator<UserWalletId, Map<String, List<TangemPayTxHistoryItem>>>(dataStore) {
override fun provideStringKey(key: UserWalletId): String = key.stringValue
StringKeyDataStoreDecorator<String, Map<String, List<TangemPayTxHistoryItem>>>(dataStore) {
override fun provideStringKey(key: String): String = key
override suspend fun getSyncOrNull(key: UserWalletId, cursor: String): List<TangemPayTxHistoryItem>? {
override suspend fun getSyncOrNull(key: String, cursor: String): List<TangemPayTxHistoryItem>? {
val storedValue = getSyncOrNull(key)
return storedValue?.get(cursor)
}
override suspend fun store(key: UserWalletId, cursor: String, value: List<TangemPayTxHistoryItem>) {
override suspend fun store(key: String, cursor: String, value: List<TangemPayTxHistoryItem>) {
val oldValue = getSyncOrNull(key).orEmpty()
val newValue = oldValue.toMutableMap().apply { put(cursor, value) }
store(key, newValue)

View file

@ -1,13 +1,12 @@
package com.tangem.datasource.local.visa
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
interface TangemPayTxHistoryItemsStore {
suspend fun getSyncOrNull(key: UserWalletId, cursor: String): List<TangemPayTxHistoryItem>?
suspend fun getSyncOrNull(key: String, cursor: String): List<TangemPayTxHistoryItem>?
suspend fun remove(key: UserWalletId)
suspend fun remove(key: String)
suspend fun store(key: UserWalletId, cursor: String, value: List<TangemPayTxHistoryItem>)
suspend fun store(key: String, cursor: String, value: List<TangemPayTxHistoryItem>)
}

View file

@ -68,11 +68,14 @@ internal class DefaultOnboardingRepository @Inject constructor(
private fun getCustomerInfo(response: CustomerMeResponse.Result?): CustomerInfo {
val card = response?.card
val balance = response?.balance
val cardInfo = if (card != null && balance != null) {
val productInstance = response?.productInstance
val cardInfo = if (productInstance != null && card != null && balance != null) {
CardInfo(
lastFourDigits = card.cardNumberEnd,
balance = balance.availableBalance,
currencyCode = balance.currency,
customerWalletAddress = productInstance.cardWalletAddress,
)
} else {
null

View file

@ -4,7 +4,6 @@ import com.tangem.data.common.cache.CacheRegistry
import com.tangem.data.visa.utils.TangemPayTxHistoryItemConverter
import com.tangem.datasource.api.pay.TangemPayApi
import com.tangem.datasource.local.visa.TangemPayTxHistoryItemsStore
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.tangempay.model.TangemPayTxHistoryListBatchFlow
import com.tangem.domain.tangempay.model.TangemPayTxHistoryListBatchingContext
import com.tangem.domain.tangempay.model.TangemPayTxHistoryListConfig
@ -65,28 +64,28 @@ internal class DefaultTangemPayTxHistoryRepository @Inject constructor(
limit: Int,
): List<TangemPayTxHistoryItem> {
cacheRegistry.invokeOnExpire(
key = getCacheKey(userWalletId = config.userWalletId, cursor = cursor),
key = getCacheKey(customerWalletAddress = config.customerWalletAddress, cursor = cursor),
skipCache = config.refresh,
block = { fetch(userWalletId = config.userWalletId, cursor = cursor, pageSize = limit) },
block = { fetch(customerWalletAddress = config.customerWalletAddress, cursor = cursor, pageSize = limit) },
)
return txHistoryItemsStore.getSyncOrNull(
key = config.userWalletId,
key = config.customerWalletAddress,
cursor = cursor ?: INITIAL_CURSOR,
).orEmpty()
}
private fun getCacheKey(userWalletId: UserWalletId, cursor: String?): String {
return "tangem_pay_tx_history_${userWalletId}_${cursor ?: INITIAL_CURSOR}"
private fun getCacheKey(customerWalletAddress: String, cursor: String?): String {
return "tangem_pay_tx_history_${customerWalletAddress}_${cursor ?: INITIAL_CURSOR}"
}
private suspend fun fetch(userWalletId: UserWalletId, cursor: String?, pageSize: Int) {
private suspend fun fetch(customerWalletAddress: String, cursor: String?, pageSize: Int) {
requestPerformer.runWithErrorLogs(TAG) {
val result = requestPerformer.request { authHeader ->
visaApi.getTangemPayTxHistory(authHeader = authHeader, limit = pageSize, cursor = cursor)
}.result
val items = TangemPayTxHistoryItemConverter.convertList(result.transactions)
txHistoryItemsStore.store(key = userWalletId, cursor = cursor ?: INITIAL_CURSOR, value = items)
txHistoryItemsStore.store(key = customerWalletAddress, cursor = cursor ?: INITIAL_CURSOR, value = items)
}
}
}

View file

@ -24,6 +24,7 @@ data class CustomerInfo(
val lastFourDigits: String,
val balance: BigDecimal,
val currencyCode: String,
val customerWalletAddress: String,
)
fun isKycApproved() = kycStatus == APPROVED_KYC_STATUS

View file

@ -1,5 +1,3 @@
package com.tangem.domain.tangempay.model
import com.tangem.domain.models.wallet.UserWalletId
data class TangemPayTxHistoryListConfig(val userWalletId: UserWalletId, val refresh: Boolean)
data class TangemPayTxHistoryListConfig(val customerWalletAddress: String, val refresh: Boolean)

View file

@ -2,9 +2,8 @@ package com.tangem.features.tangempay.components
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.models.wallet.UserWalletId
interface TangemPayDetailsComponent : ComposableContentComponent {
data class Params(val userWalletId: UserWalletId)
data class Params(val customerWalletAddress: String)
interface Factory : ComponentFactory<Params, TangemPayDetailsComponent>
}

View file

@ -23,7 +23,7 @@ internal class DefaultTangemPayDetailsComponent @AssistedInject constructor(
private val model: TangemPayDetailsModel = getOrCreateModel(params = params)
private val txHistoryComponent = DefaultTangemPayTxHistoryComponent(
appComponentContext = child("txHistoryComponent"),
params = DefaultTangemPayTxHistoryComponent.Params(userWalletId = params.userWalletId),
params = DefaultTangemPayTxHistoryComponent.Params(customerWalletAddress = params.customerWalletAddress),
)
@Composable

View file

@ -4,7 +4,6 @@ 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.domain.models.wallet.UserWalletId
import com.tangem.features.tangempay.model.TangemPayTxHistoryModel
import com.tangem.features.txhistory.entity.TxHistoryUM
import com.tangem.features.txhistory.ui.txHistoryItems
@ -22,5 +21,5 @@ internal class DefaultTangemPayTxHistoryComponent(
txHistoryItems(listState, state)
}
data class Params(val userWalletId: UserWalletId)
data class Params(val customerWalletAddress: String)
}

View file

@ -31,7 +31,7 @@ internal class TangemPayTxHistoryModel @Inject constructor(
private val listManager = TangemPayTxHistoryListManager(
repository = tangemPayTxHistoryRepository,
dispatchers = dispatchers,
userWalletId = params.userWalletId,
customerWalletAddress = params.customerWalletAddress,
txHistoryUiActions = this,
)
@ -86,7 +86,7 @@ internal class TangemPayTxHistoryModel @Inject constructor(
}
private fun loadMoreItems(): Boolean {
modelScope.launch { listManager.loadMore(params.userWalletId) }
modelScope.launch { listManager.loadMore(params.customerWalletAddress) }
return true
}

View file

@ -1,6 +1,5 @@
package com.tangem.features.tangempay.utils
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.tangempay.model.TangemPayTxHistoryListBatchingContext
import com.tangem.domain.tangempay.model.TangemPayTxHistoryListConfig
import com.tangem.domain.tangempay.repository.TangemPayTxHistoryRepository
@ -23,7 +22,7 @@ private typealias TangemPayTxHistoryBatchAction = BatchAction<Int, TangemPayTxHi
internal class TangemPayTxHistoryListManager(
private val repository: TangemPayTxHistoryRepository,
private val dispatchers: CoroutineDispatcherProvider,
private val userWalletId: UserWalletId,
private val customerWalletAddress: String,
private val txHistoryUiActions: TxHistoryUiActions,
) {
private val jobHolder = JobHolder()
@ -56,15 +55,18 @@ internal class TangemPayTxHistoryListManager(
suspend fun reload() {
actionsFlow.emit(
BatchAction.Reload(
requestParams = TangemPayTxHistoryListConfig(userWalletId = userWalletId, refresh = true),
requestParams = TangemPayTxHistoryListConfig(
customerWalletAddress = customerWalletAddress,
refresh = true,
),
),
)
}
suspend fun loadMore(userWalletId: UserWalletId) {
suspend fun loadMore(customerWalletAddress: String) {
actionsFlow.emit(
BatchAction.LoadMore(
requestParams = TangemPayTxHistoryListConfig(userWalletId, refresh = false),
requestParams = TangemPayTxHistoryListConfig(customerWalletAddress, refresh = false),
),
)
}

View file

@ -349,6 +349,7 @@ internal class WalletModel @Inject constructor(
value = info,
onIssueOrderClick = ::issueOrder,
onContinueKycClick = innerWalletRouter::openTangemPayOnboarding,
cardOnClick = ::tangemPayCardOnClick,
).transform(stateHolder.uiState.value.tangemPayState)
}
} ?: return
@ -374,6 +375,10 @@ internal class WalletModel @Inject constructor(
}
}
private fun tangemPayCardOnClick(customerWalletAddress: String) {
innerWalletRouter.openTangemPayDetails(customerWalletAddress)
}
private fun needToRefreshTimer() {
modelScope.launch {
delay(REFRESH_WALLET_BACKGROUND_TIMER_MILLIS)

View file

@ -111,4 +111,8 @@ internal class DefaultWalletRouter @Inject constructor(
override fun openTangemPayOnboarding() {
router.push(AppRoute.TangemPayOnboarding(AppRoute.TangemPayOnboarding.Mode.ContinueOnboarding))
}
override fun openTangemPayDetails(custoemrWalletAddress: String) {
router.push(AppRoute.TangemPayDetails(custoemrWalletAddress))
}
}

View file

@ -60,4 +60,6 @@ internal interface InnerWalletRouter {
fun openTokenReceiveBottomSheet(tokenReceiveConfig: TokenReceiveConfig)
fun openTangemPayOnboarding()
fun openTangemPayDetails(custoemrWalletAddress: String)
}

View file

@ -20,5 +20,6 @@ internal sealed class TangemPayState {
data class Card(
val lastFourDigits: TextReference,
val balanceText: TextReference,
val onClick: () -> Unit,
) : TangemPayState()
}

View file

@ -17,6 +17,7 @@ internal class TangemPayStateTransformer(
private val value: MainScreenCustomerInfo? = null,
private val onIssueOrderClick: () -> Unit = {},
private val onContinueKycClick: () -> Unit = {},
private val cardOnClick: (String) -> Unit = {},
private val issueProgressState: Boolean = false,
private val issueState: Boolean = false,
) : Transformer<TangemPayState> {
@ -63,6 +64,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) },
)
private fun getBalanceText(cardInfo: CardInfo): String {

View file

@ -1,6 +1,7 @@
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
@ -21,7 +22,9 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
@Composable
internal fun TangemPayCardMainBlock(state: TangemPayState.Card, modifier: Modifier = Modifier) {
Surface(
modifier = modifier.fillMaxWidth(),
modifier = modifier
.fillMaxWidth()
.clickable { state.onClick() },
shape = TangemTheme.shapes.roundedCornersXMedium,
color = TangemTheme.colors.background.primary,
) {

View file

@ -81,6 +81,7 @@ private fun ResetCardScreenPreview() {
TangemPayState.Card(
lastFourDigits = TextReference.Str("*1234"),
balanceText = TextReference.Str("$ 0.00"),
onClick = {},
),
)
}