Updated on 2026-08-14
This commit is contained in:
parent
46cf9b8b08
commit
210ba5ca59
22 changed files with 303 additions and 90 deletions
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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>
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
|
|
@ -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?,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
override suspend fun validateDeeplink(link: String): Either<UniversalError, Boolean> {
|
||||
return requestHelper.runWithErrorLogs(TAG) {
|
||||
val result = requestHelper.request {
|
||||
tangemPayApi.validateDeeplink(DeeplinkValidityRequest(link))
|
||||
}.map { it.result?.status == VALID_STATUS }
|
||||
}.result
|
||||
result?.status == VALID_STATUS
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getCustomerInfo(): Either<UniversalError, CustomerInfo> = either {
|
||||
return requestHelper.request { authHeader ->
|
||||
override suspend fun getCustomerInfo(): Either<UniversalError, CustomerInfo> {
|
||||
return requestHelper.runWithErrorLogs(TAG) {
|
||||
val result = requestHelper.request { authHeader ->
|
||||
tangemPayApi.getCustomerMe(authHeader)
|
||||
}.map { getCustomerInfo(it.result) }
|
||||
}.result
|
||||
getCustomerInfo(result)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getMainScreenCustomerInfo(): Either<UniversalError, CustomerInfo> = either {
|
||||
return requestHelper.requestWithPersistedToken { authHeader ->
|
||||
override suspend fun getMainScreenCustomerInfo(): Either<UniversalError, MainScreenCustomerInfo> {
|
||||
return requestHelper.runWithErrorLogs(TAG) {
|
||||
val result = requestHelper.requestWithPersistedToken { authHeader ->
|
||||
tangemPayApi.getCustomerMe(authHeader)
|
||||
}.map { getCustomerInfo(it.result) }
|
||||
}.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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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"),
|
||||
}
|
||||
|
|
@ -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>
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
private suspend fun refreshTangemPayInfo() {
|
||||
val newState = withContext(dispatchers.io) {
|
||||
tangemPayMainScreenCustomerInfoUseCase()?.let { info ->
|
||||
stateHolder.update {
|
||||
it.copy(tangemPayState = tangemPayStateConverter.convert(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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,4 +107,8 @@ internal class DefaultWalletRouter @Inject constructor(
|
|||
configuration = WalletDialogConfig.TokenReceive(tokenReceiveConfig),
|
||||
)
|
||||
}
|
||||
|
||||
override fun openTangemPayOnboarding() {
|
||||
router.push(AppRoute.TangemPayOnboarding(AppRoute.TangemPayOnboarding.Mode.ContinueOnboarding))
|
||||
}
|
||||
}
|
||||
|
|
@ -58,4 +58,6 @@ internal interface InnerWalletRouter {
|
|||
fun openNFT(userWallet: UserWallet)
|
||||
|
||||
fun openTokenReceiveBottomSheet(tokenReceiveConfig: TokenReceiveConfig)
|
||||
|
||||
fun openTangemPayOnboarding()
|
||||
}
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -1,47 +1,63 @@
|
|||
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(
|
||||
!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 = { 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) },
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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,13 +15,13 @@ 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 -> {
|
||||
is Progress -> {
|
||||
Notification(
|
||||
modifier = modifier
|
||||
.fillMaxWidth(),
|
||||
|
|
@ -34,6 +33,7 @@ internal fun TangemPayMainScreenBlock(state: TangemPayState, modifier: Modifier
|
|||
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = state.buttonText,
|
||||
onClick = state.onButtonClick,
|
||||
showProgress = state.showProgress,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
@ -42,7 +42,6 @@ internal fun TangemPayMainScreenBlock(state: TangemPayState, modifier: Modifier
|
|||
is TangemPayState.Empty -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
|
|
@ -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"),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue