Updated on 2026-08-14

This commit is contained in:
Tangem 2025-09-29 12:34:23 +04:00
parent 46cf9b8b08
commit 210ba5ca59
22 changed files with 303 additions and 90 deletions

View file

@ -14,6 +14,7 @@ import javax.inject.Singleton
import kotlin.text.encodeToByteArray
private const val DEFAULT_KEY = "tangem_pay_default_key"
private const val ORDER_ID_KEY = "tangem_pay_order_id_key"
@Singleton
internal class DefaultTangemPayStorage @Inject constructor(
@ -53,9 +54,25 @@ internal class DefaultTangemPayStorage @Inject constructor(
?.let(tokensAdapter::fromJson)
}
override suspend fun clear(customerWalletAddress: String) = withContext(dispatcherProvider.io) {
secureStorage.delete(createKey(customerWalletAddress))
override suspend fun storeOrderId(customerWalletAddress: String, orderId: String) {
withContext(dispatcherProvider.io) {
secureStorage.store(
orderId.encodeToByteArray(throwOnInvalidSequence = true),
createOrderIdKey(customerWalletAddress),
)
}
}
private fun createKey(cardId: String): String = "${DEFAULT_KEY}_$cardId"
override suspend fun getOrderId(customerWalletAddress: String): String? = withContext(dispatcherProvider.io) {
secureStorage.get(createOrderIdKey(customerWalletAddress))?.decodeToString(throwOnInvalidSequence = true)
}
override suspend fun clear(customerWalletAddress: String) = withContext(dispatcherProvider.io) {
secureStorage.delete(createKey(customerWalletAddress))
secureStorage.delete(createOrderIdKey(customerWalletAddress))
}
private fun createKey(address: String): String = "${DEFAULT_KEY}_$address"
private fun createOrderIdKey(address: String): String = "${ORDER_ID_KEY}_$address"
}

View file

@ -7,6 +7,7 @@ import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.POST
import retrofit2.http.Path
import retrofit2.http.Query
private const val TX_HISTORY_PAGING_DEFAULT_LIMIT = 20
@ -124,4 +125,16 @@ interface TangemPayApi {
@POST("v1/deeplink/validate")
suspend fun validateDeeplink(@Body body: DeeplinkValidityRequest): ApiResponse<DeeplinkValidityResponse>
@GET("v1/order/{order_id}")
suspend fun getOrder(
@Header("Authorization") authHeader: String,
@Path("order_id") orderId: String,
): ApiResponse<OrderResponse>
@POST("v1/order")
suspend fun createOrder(
@Header("Authorization") authHeader: String,
@Body body: OrderRequest,
): ApiResponse<OrderResponse>
}

View file

@ -0,0 +1,9 @@
package com.tangem.datasource.api.pay.models.request
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class OrderRequest(
@Json(name = "wallet_address") val walletAddress: String,
)

View file

@ -0,0 +1,33 @@
package com.tangem.datasource.api.pay.models.response
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class OrderResponse(
@Json(name = "result") val result: Result?,
@Json(name = "error") val error: String?,
) {
@JsonClass(generateAdapter = true)
data class Result(
@Json(name = "id") val id: String,
@Json(name = "customer_id") val customerId: String?,
@Json(name = "type") val type: String?,
@Json(name = "status") val status: String,
@Json(name = "step") val step: String?,
@Json(name = "data") val data: Data,
@Json(name = "step_change_code") val stepChangeCode: Int?,
@Json(name = "created_at") val createdAt: String?,
@Json(name = "updated_at") val updatedAt: String?,
) {
@JsonClass(generateAdapter = true)
data class Data(
@Json(name = "type") val type: String?,
@Json(name = "specification_name") val specificationName: String?,
@Json(name = "customer_wallet_address") val customerWalletAddress: String,
@Json(name = "emboss_name") val embossName: String?,
@Json(name = "product_instance_id") val productInstanceId: String?,
@Json(name = "payment_account_id") val paymentAccountId: String?,
)
}
}

View file

@ -8,5 +8,9 @@ interface TangemPayStorage {
suspend fun getAuthTokens(customerWalletAddress: String): VisaAuthTokens?
suspend fun storeOrderId(customerWalletAddress: String, orderId: String)
suspend fun getOrderId(customerWalletAddress: String): String?
suspend fun clear(customerWalletAddress: String)
}

View file

@ -256,6 +256,7 @@ private fun SingleSecondaryButton(config: NotificationButtonsState.SecondaryButt
modifier = Modifier.fillMaxWidth(),
iconResId = config.iconResId,
enabled = isEnabled,
showProgress = config.showProgress,
)
} else {
SecondaryButton(
@ -264,6 +265,7 @@ private fun SingleSecondaryButton(config: NotificationButtonsState.SecondaryButt
modifier = Modifier.fillMaxWidth(),
size = TangemButtonSize.WideAction,
enabled = isEnabled,
showProgress = config.showProgress,
)
}
}

View file

@ -44,6 +44,7 @@ data class NotificationConfig(
val text: TextReference,
@DrawableRes val iconResId: Int? = null,
val onClick: () -> Unit,
val showProgress: Boolean = false,
) : ButtonsState()
data class PairButtonsConfig(

View file

@ -5,6 +5,7 @@ import com.tangem.data.pay.repository.DefaultTangemPayTxHistoryRepository
import com.tangem.data.pay.repository.DefaultOnboardingRepository
import com.tangem.domain.pay.repository.KycRepository
import com.tangem.domain.pay.repository.OnboardingRepository
import com.tangem.domain.pay.usecase.TangemPayIssueOrderUseCase
import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase
import com.tangem.domain.tangempay.repository.TangemPayTxHistoryRepository
import dagger.Binds
@ -38,5 +39,11 @@ internal interface TangemPayDataModule {
): TangemPayMainScreenCustomerInfoUseCase {
return TangemPayMainScreenCustomerInfoUseCase(repository = repository)
}
@Provides
@Singleton
fun provideTangemPayIssueOrderUseCase(repository: OnboardingRepository): TangemPayIssueOrderUseCase {
return TangemPayIssueOrderUseCase(repository = repository)
}
}
}

View file

@ -1,40 +1,68 @@
package com.tangem.data.pay.repository
import arrow.core.Either
import arrow.core.raise.either
import com.tangem.core.error.UniversalError
import com.tangem.datasource.api.pay.TangemPayApi
import com.tangem.datasource.api.pay.models.request.DeeplinkValidityRequest
import com.tangem.datasource.api.pay.models.request.OrderRequest
import com.tangem.datasource.api.pay.models.response.CustomerMeResponse
import com.tangem.domain.pay.model.CustomerInfo.CardInfo
import com.tangem.datasource.local.visa.TangemPayStorage
import com.tangem.domain.pay.model.CustomerInfo
import com.tangem.domain.pay.model.CustomerInfo.CardInfo
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 javax.inject.Inject
private const val VALID_STATUS = "valid"
private const val TAG = "TangemPay: OnboardingRepository"
internal class DefaultOnboardingRepository @Inject constructor(
private val tangemPayApi: TangemPayApi,
private val requestHelper: TangemPayRequestPerformer,
private val tangemPayStorage: TangemPayStorage,
) : OnboardingRepository {
override suspend fun validateDeeplink(link: String): Either<UniversalError, Boolean> = either {
return requestHelper.request {
tangemPayApi.validateDeeplink(DeeplinkValidityRequest(link))
}.map { it.result?.status == VALID_STATUS }
override suspend fun validateDeeplink(link: String): Either<UniversalError, Boolean> {
return requestHelper.runWithErrorLogs(TAG) {
val result = requestHelper.request {
tangemPayApi.validateDeeplink(DeeplinkValidityRequest(link))
}.result
result?.status == VALID_STATUS
}
}
override suspend fun getCustomerInfo(): Either<UniversalError, CustomerInfo> = either {
return requestHelper.request { authHeader ->
tangemPayApi.getCustomerMe(authHeader)
}.map { getCustomerInfo(it.result) }
override suspend fun getCustomerInfo(): Either<UniversalError, CustomerInfo> {
return requestHelper.runWithErrorLogs(TAG) {
val result = requestHelper.request { authHeader ->
tangemPayApi.getCustomerMe(authHeader)
}.result
getCustomerInfo(result)
}
}
override suspend fun getMainScreenCustomerInfo(): Either<UniversalError, CustomerInfo> = either {
return requestHelper.requestWithPersistedToken { authHeader ->
tangemPayApi.getCustomerMe(authHeader)
}.map { getCustomerInfo(it.result) }
override suspend fun getMainScreenCustomerInfo(): Either<UniversalError, MainScreenCustomerInfo> {
return requestHelper.runWithErrorLogs(TAG) {
val result = requestHelper.requestWithPersistedToken { authHeader ->
tangemPayApi.getCustomerMe(authHeader)
}.result
val orderStatus = getOrderStatus().getOrNull() ?: error("Order status is null")
MainScreenCustomerInfo(info = getCustomerInfo(result), orderStatus = orderStatus)
}
}
override suspend fun createOrder(): Either<UniversalError, Unit> {
return requestHelper.runWithErrorLogs(TAG) {
val walletAddress = requestHelper.getCustomerWalletAddress()
val result = requestHelper.requestWithPersistedToken { authHeader ->
tangemPayApi.createOrder(authHeader, body = OrderRequest(walletAddress))
}.result ?: error("Create order result is null")
tangemPayStorage.storeOrderId(result.data.customerWalletAddress, result.id)
}
}
private fun getCustomerInfo(response: CustomerMeResponse.Result?): CustomerInfo {
@ -55,4 +83,23 @@ internal class DefaultOnboardingRepository @Inject constructor(
cardInfo = cardInfo,
)
}
private suspend fun getOrderStatus(): Either<UniversalError, OrderStatus> {
return requestHelper.runWithErrorLogs(TAG) {
val walletAddress = requestHelper.getCustomerWalletAddress()
val orderId: String = tangemPayStorage.getOrderId(walletAddress)
?: return@runWithErrorLogs OrderStatus.NOT_ISSUED
val result = requestHelper.request { authHeader ->
tangemPayApi.getOrder(authHeader, orderId)
}.result ?: error("Order result is null")
when (result.status) {
OrderStatus.NEW.apiName -> OrderStatus.NEW
OrderStatus.PROCESSING.apiName -> OrderStatus.PROCESSING
OrderStatus.COMPLETED.apiName -> OrderStatus.COMPLETED
else -> OrderStatus.CANCELED
}
}
}
}

View file

@ -19,6 +19,7 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import javax.inject.Inject
private const val INITIAL_CURSOR = "initial_cursor_key"
private const val TAG = "TangemPay: TangemPayTxHistoryRepository:"
internal class DefaultTangemPayTxHistoryRepository @Inject constructor(
private val requestPerformer: TangemPayRequestPerformer,
@ -80,15 +81,11 @@ internal class DefaultTangemPayTxHistoryRepository @Inject constructor(
}
private suspend fun fetch(userWalletId: UserWalletId, cursor: String?, pageSize: Int) {
val response = requestPerformer.request { authHeader ->
visaApi.getTangemPayTxHistory(
authHeader = authHeader,
limit = pageSize,
cursor = cursor,
)
}.getOrNull()
response?.let {
val items = TangemPayTxHistoryItemConverter.convertList(response.result.transactions)
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)
}
}

View file

@ -3,7 +3,11 @@ package com.tangem.domain.pay.model
import java.math.BigDecimal
private const val APPROVED_KYC_STATUS = "APPROVED"
private const val ACTIVE_PI_STATUS = "active"
data class MainScreenCustomerInfo(
val info: CustomerInfo,
val orderStatus: OrderStatus,
)
data class CustomerInfo(
val productInstance: ProductInstance?,
@ -23,6 +27,4 @@ data class CustomerInfo(
)
fun isKycApproved() = kycStatus == APPROVED_KYC_STATUS
fun isProductInstanceActive() = productInstance?.status == ACTIVE_PI_STATUS
}

View file

@ -0,0 +1,9 @@
package com.tangem.domain.pay.model
enum class OrderStatus(val apiName: String) {
NOT_ISSUED(""),
NEW("NEW"),
PROCESSING("PROCESSING"),
COMPLETED("COMPLETED"),
CANCELED("CANCELED"),
}

View file

@ -3,6 +3,7 @@ package com.tangem.domain.pay.repository
import arrow.core.Either
import com.tangem.core.error.UniversalError
import com.tangem.domain.pay.model.CustomerInfo
import com.tangem.domain.pay.model.MainScreenCustomerInfo
interface OnboardingRepository {
@ -10,8 +11,10 @@ interface OnboardingRepository {
suspend fun getCustomerInfo(): Either<UniversalError, CustomerInfo>
suspend fun createOrder(): Either<UniversalError, Unit>
/**
* Returns only if the user already authorised at least once
*/
suspend fun getMainScreenCustomerInfo(): Either<UniversalError, CustomerInfo>
suspend fun getMainScreenCustomerInfo(): Either<UniversalError, MainScreenCustomerInfo>
}

View file

@ -0,0 +1,10 @@
package com.tangem.domain.pay.usecase
import com.tangem.domain.pay.repository.OnboardingRepository
class TangemPayIssueOrderUseCase(
private val repository: OnboardingRepository,
) {
suspend operator fun invoke(): Unit? = repository.createOrder().getOrNull()
}

View file

@ -1,6 +1,6 @@
package com.tangem.domain.pay.usecase
import com.tangem.domain.pay.model.CustomerInfo
import com.tangem.domain.pay.model.MainScreenCustomerInfo
import com.tangem.domain.pay.repository.OnboardingRepository
/**
@ -11,5 +11,5 @@ class TangemPayMainScreenCustomerInfoUseCase(
private val repository: OnboardingRepository,
) {
suspend operator fun invoke(): CustomerInfo? = repository.getMainScreenCustomerInfo().getOrNull()
suspend operator fun invoke(): MainScreenCustomerInfo? = repository.getMainScreenCustomerInfo().getOrNull()
}

View file

@ -63,9 +63,6 @@ internal class TangemPayOnboardingModel @Inject constructor(
else -> openKyc()
}
}
!customerInfo.isProductInstanceActive() -> {
// TODO [REDACTED_TASK_KEY]: create order and poll order status (API is not ready yet)
}
else -> back()
}
}

View file

@ -18,6 +18,7 @@ import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.domain.nft.ObserveAndClearNFTCacheIfNeedUseCase
import com.tangem.domain.notifications.GetIsHuaweiDeviceWithoutGoogleServicesUseCase
import com.tangem.domain.notifications.repository.NotificationsRepository
import com.tangem.domain.pay.usecase.TangemPayIssueOrderUseCase
import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase
import com.tangem.domain.settings.*
import com.tangem.domain.tokens.RefreshMultiCurrencyWalletQuotesUseCase
@ -51,6 +52,8 @@ import kotlinx.coroutines.flow.*
import timber.log.Timber
import javax.inject.Inject
private const val TANGEM_PAY_UPDATE_INTERVAL = 60000L
@Suppress("LongParameterList", "LargeClass")
@Stable
@ModelScoped
@ -88,6 +91,7 @@ internal class WalletModel @Inject constructor(
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
private val userWalletsListRepository: UserWalletsListRepository,
private val tangemPayMainScreenCustomerInfoUseCase: TangemPayMainScreenCustomerInfoUseCase,
private val tangemPayIssueOrderUseCase: TangemPayIssueOrderUseCase,
private val tangemPayStateConverter: TangemPayStateConverter,
private val tangemPayFeatureToggles: TangemPayFeatureToggles,
val screenLifecycleProvider: ScreenLifecycleProvider,
@ -105,6 +109,8 @@ internal class WalletModel @Inject constructor(
private var expressTxStatusTaskScheduler = SingleTaskScheduler<Unit>()
private var updateTangemPayJob: Job? = null
init {
analyticsEventsHandler.send(WalletScreenAnalyticsEvent.MainScreen.ScreenOpened)
@ -323,20 +329,44 @@ internal class WalletModel @Inject constructor(
private fun subscribeToTangemPayInfo() {
/**
* Update state each time a user opens/returns to wallet screen
* and every minute while user stays on the main screen
*/
screenLifecycleProvider.isBackgroundState.onEach { isBackground ->
if (!isBackground) { updateTangemPayInfo() }
screenLifecycleProvider.isBackgroundState.onEach { inBackground ->
if (!inBackground && tangemPayFeatureToggles.isTangemPayEnabled) {
updateTangemPayJob = modelScope.launch {
refreshTangemPayInfo()
while (isActive) {
delay(TANGEM_PAY_UPDATE_INTERVAL)
refreshTangemPayInfo()
}
}
} else {
updateTangemPayJob?.cancel()
updateTangemPayJob = null
}
}.launchIn(modelScope)
}
private fun updateTangemPayInfo() {
if (tangemPayFeatureToggles.isTangemPayEnabled) {
modelScope.launch {
tangemPayMainScreenCustomerInfoUseCase()?.let { info ->
stateHolder.update {
it.copy(tangemPayState = tangemPayStateConverter.convert(info))
}
}
private suspend fun refreshTangemPayInfo() {
val newState = withContext(dispatchers.io) {
tangemPayMainScreenCustomerInfoUseCase()?.let { info ->
tangemPayStateConverter.convert(
value = info,
onIssueOrderClick = ::issueOrder,
onContinueKycClick = innerWalletRouter::openTangemPayOnboarding,
)
}
} ?: return
stateHolder.update { it.copy(tangemPayState = newState) }
}
private fun issueOrder() {
modelScope.launch {
stateHolder.update { it.copy(tangemPayState = tangemPayStateConverter.getIssueProgress()) }
withContext(dispatchers.io) {
tangemPayIssueOrderUseCase()
} ?: stateHolder.update {
it.copy(tangemPayState = tangemPayStateConverter.getIssueState(::issueOrder))
}
}
}

View file

@ -107,4 +107,8 @@ internal class DefaultWalletRouter @Inject constructor(
configuration = WalletDialogConfig.TokenReceive(tokenReceiveConfig),
)
}
override fun openTangemPayOnboarding() {
router.push(AppRoute.TangemPayOnboarding(AppRoute.TangemPayOnboarding.Mode.ContinueOnboarding))
}
}

View file

@ -58,4 +58,6 @@ internal interface InnerWalletRouter {
fun openNFT(userWallet: UserWallet)
fun openTokenReceiveBottomSheet(tokenReceiveConfig: TokenReceiveConfig)
fun openTangemPayOnboarding()
}

View file

@ -14,6 +14,7 @@ internal sealed class TangemPayState {
val buttonText: TextReference,
@DrawableRes val iconRes: Int,
val onButtonClick: () -> Unit,
val showProgress: Boolean = false,
) : TangemPayState()
data class Card(

View file

@ -1,48 +1,64 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter
import com.tangem.common.routing.AppRoute
import com.tangem.common.ui.R
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.ui.extensions.TextReference
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.CustomerInfo
import com.tangem.domain.pay.model.MainScreenCustomerInfo
import com.tangem.domain.pay.model.OrderStatus
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
import com.tangem.utils.converter.Converter
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState.Progress
import java.util.Currency
import javax.inject.Inject
internal class TangemPayStateConverter @Inject constructor(
private val router: Router,
) : Converter<CustomerInfo, TangemPayState> {
internal class TangemPayStateConverter @Inject constructor() {
override fun convert(value: CustomerInfo): TangemPayState {
val route = AppRoute.TangemPayOnboarding(AppRoute.TangemPayOnboarding.Mode.ContinueOnboarding)
val cardInfo = value.cardInfo
fun convert(
value: MainScreenCustomerInfo,
onIssueOrderClick: () -> Unit,
onContinueKycClick: () -> Unit,
): TangemPayState {
val cardInfo = value.info.cardInfo
return when {
!value.isKycApproved() -> TangemPayState.Progress(
title = TextReference.Res(R.string.tangempay_kyc_in_progress_notification_title),
buttonText = TextReference.Res(R.string.tangempay_kyc_in_progress_notification_button),
iconRes = R.drawable.ic_promo_kyc_36,
onButtonClick = { router.push(route) },
)
!value.isProductInstanceActive() -> TangemPayState.Progress(
title = TextReference.Res(R.string.tangempay_issue_card_notification_title),
buttonText = TextReference.Res(R.string.common_continue),
iconRes = R.drawable.ic_tangem_pay_promo_card_36,
onButtonClick = { router.push(route) },
)
!value.info.isKycApproved() -> {
Progress(
title = TextReference.Res(R.string.tangempay_kyc_in_progress_notification_title),
buttonText = TextReference.Res(R.string.tangempay_kyc_in_progress_notification_button),
iconRes = R.drawable.ic_promo_kyc_36,
onButtonClick = onContinueKycClick,
)
}
value.orderStatus == OrderStatus.NOT_ISSUED || value.orderStatus == OrderStatus.CANCELED -> {
getIssueState(onIssueOrderClick)
}
cardInfo != null -> {
TangemPayState.Card(
lastFourDigits = TextReference.Str("*${cardInfo.lastFourDigits}"),
balanceText = TextReference.Str(getBalanceText(cardInfo)),
)
}
else -> TangemPayState.Empty
else -> {
getIssueProgress()
}
}
}
fun getIssueProgress(): TangemPayState = Progress(
title = TextReference.Res(R.string.tangempay_issue_card_notification_title),
buttonText = TextReference.EMPTY,
iconRes = R.drawable.ic_tangem_pay_promo_card_36,
onButtonClick = {},
showProgress = true,
)
fun getIssueState(onIssueOrderClick: () -> Unit) = Progress(
title = TextReference.Res(R.string.tangempay_issue_card_notification_title),
buttonText = TextReference.Res(R.string.common_continue),
iconRes = R.drawable.ic_tangem_pay_promo_card_36,
onButtonClick = onIssueOrderClick,
)
private fun getBalanceText(cardInfo: CardInfo): String {
val currency = Currency.getInstance(cardInfo.currencyCode)
return cardInfo.balance.format {

View file

@ -1,7 +1,6 @@
package com.tangem.feature.wallet.presentation.wallet.ui.components.visa
import android.content.res.Configuration
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
@ -16,31 +15,31 @@ import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState.Progress
import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.TangemPayCardMainBlock
@Composable
internal fun TangemPayMainScreenBlock(state: TangemPayState, modifier: Modifier = Modifier) {
AnimatedVisibility(state !is TangemPayState.Empty) {
when (state) {
is TangemPayState.Progress -> {
Notification(
modifier = modifier
.fillMaxWidth(),
config = NotificationConfig(
title = state.title,
iconSize = 36.dp,
subtitle = TextReference.EMPTY,
iconResId = state.iconRes,
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
text = state.buttonText,
onClick = state.onButtonClick,
),
when (state) {
is Progress -> {
Notification(
modifier = modifier
.fillMaxWidth(),
config = NotificationConfig(
title = state.title,
iconSize = 36.dp,
subtitle = TextReference.EMPTY,
iconResId = state.iconRes,
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
text = state.buttonText,
onClick = state.onButtonClick,
showProgress = state.showProgress,
),
)
}
is TangemPayState.Card -> TangemPayCardMainBlock(state, modifier)
is TangemPayState.Empty -> Unit
),
)
}
is TangemPayState.Card -> TangemPayCardMainBlock(state, modifier)
is TangemPayState.Empty -> Unit
}
}
@ -51,7 +50,7 @@ private fun ResetCardScreenPreview() {
TangemThemePreview {
Column(verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) {
TangemPayMainScreenBlock(
TangemPayState.Progress(
Progress(
title = TextReference.Res(R.string.tangempay_kyc_in_progress_notification_title),
buttonText = TextReference.Res(R.string.tangempay_kyc_in_progress_notification_button),
iconRes = R.drawable.ic_promo_kyc_36,
@ -60,7 +59,7 @@ private fun ResetCardScreenPreview() {
)
TangemPayMainScreenBlock(
TangemPayState.Progress(
Progress(
title = TextReference.Res(R.string.tangempay_issue_card_notification_title),
buttonText = TextReference.Res(R.string.common_continue),
iconRes = R.drawable.ic_tangem_pay_promo_card_36,
@ -68,6 +67,16 @@ private fun ResetCardScreenPreview() {
),
)
TangemPayMainScreenBlock(
Progress(
title = TextReference.Res(R.string.tangempay_issue_card_notification_title),
buttonText = TextReference.EMPTY,
iconRes = R.drawable.ic_tangem_pay_promo_card_36,
onButtonClick = {},
showProgress = true,
),
)
TangemPayMainScreenBlock(
TangemPayState.Card(
lastFourDigits = TextReference.Str("*1234"),