Updated on 2026-08-14
This commit is contained in:
parent
40c611efea
commit
a006a87f9d
27 changed files with 524 additions and 16 deletions
|
|
@ -207,6 +207,9 @@ object PreferencesKeys {
|
||||||
fun getTangemPayAddToWalletKey(customerWalletAddress: String) =
|
fun getTangemPayAddToWalletKey(customerWalletAddress: String) =
|
||||||
booleanPreferencesKey("tangem_pay_add_to_wallet_done_key_$customerWalletAddress")
|
booleanPreferencesKey("tangem_pay_add_to_wallet_done_key_$customerWalletAddress")
|
||||||
|
|
||||||
|
fun getTangemPayCashbackDeactivationDismissedKey(customerWalletAddress: String) =
|
||||||
|
booleanPreferencesKey("tangem_pay_cashback_deactivation_dismissed_key_$customerWalletAddress")
|
||||||
|
|
||||||
fun getTangemPayOrderIdKey(customerWalletAddress: String) =
|
fun getTangemPayOrderIdKey(customerWalletAddress: String) =
|
||||||
stringPreferencesKey("tangem_pay_order_id_key_$customerWalletAddress")
|
stringPreferencesKey("tangem_pay_order_id_key_$customerWalletAddress")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1919,6 +1919,10 @@
|
||||||
<item quantity="one">%d card</item>
|
<item quantity="one">%d card</item>
|
||||||
<item quantity="other">%d cards</item>
|
<item quantity="other">%d cards</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
|
<string name="tangempay_cashback_deactivated_description">It was made due to your suspicious behavior. Contact support to learn more</string>
|
||||||
|
<string name="tangempay_cashback_deactivated_title">Cashback deactivated</string>
|
||||||
|
<string name="tangempay_cashback_deposited_on">Will be deposited on %1$s</string>
|
||||||
|
<string name="tangempay_cashback_widget_title">%1$s cashback in %2$s</string>
|
||||||
<string name="tangempay_change_pin_code">Change PIN-code</string>
|
<string name="tangempay_change_pin_code">Change PIN-code</string>
|
||||||
<string name="tangempay_come_back_if_forget_pin">Come back to the app if you forget it.</string>
|
<string name="tangempay_come_back_if_forget_pin">Come back to the app if you forget it.</string>
|
||||||
<string name="tangempay_common_card">Card</string>
|
<string name="tangempay_common_card">Card</string>
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,10 @@ dependencies {
|
||||||
api(tangemDeps.hot.core)
|
api(tangemDeps.hot.core)
|
||||||
implementation(tangemDeps.blockchain)
|
implementation(tangemDeps.blockchain)
|
||||||
implementation(tangemDeps.card.core)
|
implementation(tangemDeps.card.core)
|
||||||
|
// AndroidSecureStorageV2 for TangemPay secure token storage (see com.tangem.data.pay.store).
|
||||||
|
implementation(tangemDeps.card.android) {
|
||||||
|
exclude(module = "joda-time")
|
||||||
|
}
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region DI
|
// region DI
|
||||||
|
|
|
||||||
|
|
@ -306,6 +306,20 @@ internal interface TangemPayDataModule {
|
||||||
return GetCashbackSummaryUseCase(cashbackRepository)
|
return GetCashbackSummaryUseCase(cashbackRepository)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun provideGetCashbackDeactivationDismissedUseCase(
|
||||||
|
cashbackRepository: CashbackRepository,
|
||||||
|
): GetCashbackDeactivationDismissedUseCase {
|
||||||
|
return GetCashbackDeactivationDismissedUseCase(cashbackRepository)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun provideSetCashbackDeactivationDismissedUseCase(
|
||||||
|
cashbackRepository: CashbackRepository,
|
||||||
|
): SetCashbackDeactivationDismissedUseCase {
|
||||||
|
return SetCashbackDeactivationDismissedUseCase(cashbackRepository)
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
fun provideCheckOrderConflictUseCase(
|
fun provideCheckOrderConflictUseCase(
|
||||||
customerOrderRepository: CustomerOrderRepository,
|
customerOrderRepository: CustomerOrderRepository,
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package com.tangem.data.pay.repository
|
||||||
import arrow.core.Either
|
import arrow.core.Either
|
||||||
import com.tangem.data.pay.util.CashbackSummaryConverter
|
import com.tangem.data.pay.util.CashbackSummaryConverter
|
||||||
import com.tangem.datasource.api.pay.TangemPayApi
|
import com.tangem.datasource.api.pay.TangemPayApi
|
||||||
|
import com.tangem.data.pay.store.TangemPayStorage
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import com.tangem.domain.pay.model.CashbackSummary
|
import com.tangem.domain.pay.model.CashbackSummary
|
||||||
import com.tangem.domain.pay.repository.CashbackRepository
|
import com.tangem.domain.pay.repository.CashbackRepository
|
||||||
|
|
@ -12,6 +13,7 @@ import javax.inject.Inject
|
||||||
internal class DefaultCashbackRepository @Inject constructor(
|
internal class DefaultCashbackRepository @Inject constructor(
|
||||||
private val tangemPayApi: TangemPayApi,
|
private val tangemPayApi: TangemPayApi,
|
||||||
private val requestHelper: TangemPayRequestPerformer,
|
private val requestHelper: TangemPayRequestPerformer,
|
||||||
|
private val storage: TangemPayStorage,
|
||||||
) : CashbackRepository {
|
) : CashbackRepository {
|
||||||
|
|
||||||
override suspend fun getCashbackSummary(userWalletId: UserWalletId): Either<VisaApiError, CashbackSummary> {
|
override suspend fun getCashbackSummary(userWalletId: UserWalletId): Either<VisaApiError, CashbackSummary> {
|
||||||
|
|
@ -19,4 +21,14 @@ internal class DefaultCashbackRepository @Inject constructor(
|
||||||
tangemPayApi.getCashbackSummary(authHeader = authHeader)
|
tangemPayApi.getCashbackSummary(authHeader = authHeader)
|
||||||
}.map(CashbackSummaryConverter::convert)
|
}.map(CashbackSummaryConverter::convert)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun isDeactivationBannerDismissed(userWalletId: UserWalletId): Boolean {
|
||||||
|
val customerWalletAddress = requestHelper.getCustomerWalletAddress(userWalletId)
|
||||||
|
return storage.getCashbackDeactivationDismissed(customerWalletAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun setDeactivationBannerDismissed(userWalletId: UserWalletId) {
|
||||||
|
val customerWalletAddress = requestHelper.getCustomerWalletAddress(userWalletId)
|
||||||
|
storage.storeCashbackDeactivationDismissed(customerWalletAddress = customerWalletAddress, isDismissed = true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -16,7 +16,7 @@ import com.tangem.datasource.api.pay.models.request.VirtualAccountOrderRequest
|
||||||
import com.tangem.datasource.api.pay.models.response.CustomerMeResponse
|
import com.tangem.datasource.api.pay.models.response.CustomerMeResponse
|
||||||
import com.tangem.datasource.api.pay.models.response.OrderResponse
|
import com.tangem.datasource.api.pay.models.response.OrderResponse
|
||||||
import com.tangem.datasource.local.visa.TangemPayCardFrozenStateStore
|
import com.tangem.datasource.local.visa.TangemPayCardFrozenStateStore
|
||||||
import com.tangem.datasource.local.visa.TangemPayStorage
|
import com.tangem.data.pay.store.TangemPayStorage
|
||||||
import com.tangem.datasource.local.visa.TangemPayTxHistoryItemsStore
|
import com.tangem.datasource.local.visa.TangemPayTxHistoryItemsStore
|
||||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||||
import com.tangem.domain.models.account.Account
|
import com.tangem.domain.models.account.Account
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import com.tangem.datasource.api.pay.models.request.SetPinRequest
|
||||||
import com.tangem.datasource.api.pay.models.response.FreezeUnfreezeCardResponse
|
import com.tangem.datasource.api.pay.models.response.FreezeUnfreezeCardResponse
|
||||||
import com.tangem.datasource.api.pay.models.response.OrderResponse.Result.Status
|
import com.tangem.datasource.api.pay.models.response.OrderResponse.Result.Status
|
||||||
import com.tangem.datasource.local.visa.TangemPayCardFrozenStateStore
|
import com.tangem.datasource.local.visa.TangemPayCardFrozenStateStore
|
||||||
import com.tangem.datasource.local.visa.TangemPayStorage
|
import com.tangem.data.pay.store.TangemPayStorage
|
||||||
import com.tangem.domain.models.account.CardDisplayName
|
import com.tangem.domain.models.account.CardDisplayName
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import com.tangem.domain.pay.model.OrderStatus
|
import com.tangem.domain.pay.model.OrderStatus
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import com.tangem.datasource.api.pay.TangemPayApi
|
||||||
import com.tangem.datasource.api.pay.models.request.WithdrawDataRequest
|
import com.tangem.datasource.api.pay.models.request.WithdrawDataRequest
|
||||||
import com.tangem.datasource.api.pay.models.request.WithdrawRequest
|
import com.tangem.datasource.api.pay.models.request.WithdrawRequest
|
||||||
import com.tangem.datasource.api.pay.models.response.WithdrawResponse
|
import com.tangem.datasource.api.pay.models.response.WithdrawResponse
|
||||||
import com.tangem.datasource.local.visa.TangemPayStorage
|
import com.tangem.data.pay.store.TangemPayStorage
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import com.tangem.domain.models.wallet.UserWallet
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import com.tangem.datasource.api.common.response.ApiResponse
|
||||||
import com.tangem.datasource.api.pay.TangemPayAuthApi
|
import com.tangem.datasource.api.pay.TangemPayAuthApi
|
||||||
import com.tangem.datasource.api.pay.models.request.RefreshCustomerWalletAccessTokenRequest
|
import com.tangem.datasource.api.pay.models.request.RefreshCustomerWalletAccessTokenRequest
|
||||||
import com.tangem.datasource.api.pay.models.response.TangemPayGetTokensResponse
|
import com.tangem.datasource.api.pay.models.response.TangemPayGetTokensResponse
|
||||||
import com.tangem.datasource.local.visa.TangemPayStorage
|
import com.tangem.data.pay.store.TangemPayStorage
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import com.tangem.domain.visa.error.VisaApiError
|
import com.tangem.domain.visa.error.VisaApiError
|
||||||
import com.tangem.domain.visa.model.TangemPayAuthTokens
|
import com.tangem.domain.visa.model.TangemPayAuthTokens
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.tangem.tap.data
|
package com.tangem.data.pay.store
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.squareup.moshi.JsonAdapter
|
import com.squareup.moshi.JsonAdapter
|
||||||
|
|
@ -14,7 +14,6 @@ import com.tangem.datasource.local.preferences.utils.getObjectMapSync
|
||||||
import com.tangem.datasource.local.preferences.utils.getSyncOrDefault
|
import com.tangem.datasource.local.preferences.utils.getSyncOrDefault
|
||||||
import com.tangem.datasource.local.preferences.utils.getSyncOrNull
|
import com.tangem.datasource.local.preferences.utils.getSyncOrNull
|
||||||
import com.tangem.datasource.local.preferences.utils.store
|
import com.tangem.datasource.local.preferences.utils.store
|
||||||
import com.tangem.datasource.local.visa.TangemPayStorage
|
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import com.tangem.domain.pay.TangemPayWithdrawState
|
import com.tangem.domain.pay.TangemPayWithdrawState
|
||||||
import com.tangem.domain.visa.model.TangemPayAuthTokens
|
import com.tangem.domain.visa.model.TangemPayAuthTokens
|
||||||
|
|
@ -130,6 +129,19 @@ internal class DefaultTangemPayStorage @Inject constructor(
|
||||||
appPreferencesStore.store(PreferencesKeys.getTangemPayAddToWalletKey(customerWalletAddress), isDone)
|
appPreferencesStore.store(PreferencesKeys.getTangemPayAddToWalletKey(customerWalletAddress), isDone)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun getCashbackDeactivationDismissed(customerWalletAddress: String): Boolean {
|
||||||
|
return appPreferencesStore.getSyncOrNull(
|
||||||
|
key = PreferencesKeys.getTangemPayCashbackDeactivationDismissedKey(customerWalletAddress),
|
||||||
|
) == true
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun storeCashbackDeactivationDismissed(customerWalletAddress: String, isDismissed: Boolean) {
|
||||||
|
appPreferencesStore.store(
|
||||||
|
key = PreferencesKeys.getTangemPayCashbackDeactivationDismissedKey(customerWalletAddress),
|
||||||
|
value = isDismissed,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun clearOrderId(customerWalletAddress: String) {
|
override suspend fun clearOrderId(customerWalletAddress: String) {
|
||||||
appPreferencesStore.store(PreferencesKeys.getTangemPayOrderIdKey(customerWalletAddress), "")
|
appPreferencesStore.store(PreferencesKeys.getTangemPayOrderIdKey(customerWalletAddress), "")
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.tangem.datasource.local.visa
|
package com.tangem.data.pay.store
|
||||||
|
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import com.tangem.domain.pay.TangemPayWithdrawState
|
import com.tangem.domain.pay.TangemPayWithdrawState
|
||||||
|
|
@ -32,6 +32,10 @@ interface TangemPayStorage {
|
||||||
suspend fun getAddToWalletDone(customerWalletAddress: String): Boolean
|
suspend fun getAddToWalletDone(customerWalletAddress: String): Boolean
|
||||||
|
|
||||||
suspend fun storeAddToWalletDone(customerWalletAddress: String, isDone: Boolean)
|
suspend fun storeAddToWalletDone(customerWalletAddress: String, isDone: Boolean)
|
||||||
|
|
||||||
|
suspend fun getCashbackDeactivationDismissed(customerWalletAddress: String): Boolean
|
||||||
|
|
||||||
|
suspend fun storeCashbackDeactivationDismissed(customerWalletAddress: String, isDismissed: Boolean)
|
||||||
suspend fun storeCheckCustomerWalletResult(userWalletId: UserWalletId, isPaeraCustomer: Boolean)
|
suspend fun storeCheckCustomerWalletResult(userWalletId: UserWalletId, isPaeraCustomer: Boolean)
|
||||||
suspend fun checkCustomerWalletResult(userWalletId: UserWalletId): Boolean?
|
suspend fun checkCustomerWalletResult(userWalletId: UserWalletId): Boolean?
|
||||||
|
|
||||||
|
|
@ -33,6 +33,12 @@ internal class MockAwareCashbackRepository @Inject constructor(
|
||||||
return real.getCashbackSummary(userWalletId)
|
return real.getCashbackSummary(userWalletId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun isDeactivationBannerDismissed(userWalletId: UserWalletId): Boolean =
|
||||||
|
real.isDeactivationBannerDismissed(userWalletId)
|
||||||
|
|
||||||
|
override suspend fun setDeactivationBannerDismissed(userWalletId: UserWalletId) =
|
||||||
|
real.setDeactivationBannerDismissed(userWalletId)
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
val MOCK_SUMMARY = CashbackSummary.Enabled(
|
val MOCK_SUMMARY = CashbackSummary.Enabled(
|
||||||
displayMode = CashbackDisplayMode.FULL,
|
displayMode = CashbackDisplayMode.FULL,
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
package com.tangem.tap.data
|
package com.tangem.data.pay.store
|
||||||
|
|
||||||
import com.tangem.datasource.api.common.config.ApiConfig
|
import com.tangem.datasource.api.common.config.ApiConfig
|
||||||
import com.tangem.datasource.api.common.config.ApiEnvironment
|
import com.tangem.datasource.api.common.config.ApiEnvironment
|
||||||
import com.tangem.datasource.api.common.config.managers.ApiConfigsManager
|
import com.tangem.datasource.api.common.config.managers.ApiConfigsManager
|
||||||
import com.tangem.datasource.local.visa.TangemPayStorage
|
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import com.tangem.domain.pay.TangemPayWithdrawState
|
import com.tangem.domain.pay.TangemPayWithdrawState
|
||||||
import com.tangem.domain.visa.model.TangemPayAuthTokens
|
import com.tangem.domain.visa.model.TangemPayAuthTokens
|
||||||
|
|
@ -78,6 +77,12 @@ internal class MockAwareTangemPayStorage @Inject constructor(
|
||||||
override suspend fun storeAddToWalletDone(customerWalletAddress: String, isDone: Boolean) =
|
override suspend fun storeAddToWalletDone(customerWalletAddress: String, isDone: Boolean) =
|
||||||
real.storeAddToWalletDone(customerWalletAddress, isDone)
|
real.storeAddToWalletDone(customerWalletAddress, isDone)
|
||||||
|
|
||||||
|
override suspend fun getCashbackDeactivationDismissed(customerWalletAddress: String): Boolean =
|
||||||
|
real.getCashbackDeactivationDismissed(customerWalletAddress)
|
||||||
|
|
||||||
|
override suspend fun storeCashbackDeactivationDismissed(customerWalletAddress: String, isDismissed: Boolean) =
|
||||||
|
real.storeCashbackDeactivationDismissed(customerWalletAddress, isDismissed)
|
||||||
|
|
||||||
override suspend fun clearOrderId(customerWalletAddress: String) =
|
override suspend fun clearOrderId(customerWalletAddress: String) =
|
||||||
real.clearOrderId(customerWalletAddress)
|
real.clearOrderId(customerWalletAddress)
|
||||||
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package com.tangem.tap.di.data
|
package com.tangem.data.pay.store.di
|
||||||
|
|
||||||
import com.tangem.datasource.local.visa.TangemPayStorage
|
import com.tangem.data.pay.store.MockAwareTangemPayStorage
|
||||||
import com.tangem.tap.data.MockAwareTangemPayStorage
|
import com.tangem.data.pay.store.TangemPayStorage
|
||||||
import dagger.Binds
|
import dagger.Binds
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package com.tangem.tap.di.data
|
package com.tangem.data.pay.store.di
|
||||||
|
|
||||||
import com.tangem.datasource.local.visa.TangemPayStorage
|
import com.tangem.data.pay.store.DefaultTangemPayStorage
|
||||||
import com.tangem.tap.data.DefaultTangemPayStorage
|
import com.tangem.data.pay.store.TangemPayStorage
|
||||||
import dagger.Binds
|
import dagger.Binds
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
|
|
@ -10,7 +10,7 @@ import com.tangem.datasource.api.pay.TangemPayApi
|
||||||
import com.tangem.datasource.api.pay.models.response.WithdrawDataResponse
|
import com.tangem.datasource.api.pay.models.response.WithdrawDataResponse
|
||||||
import com.tangem.datasource.api.pay.models.response.WithdrawResponse
|
import com.tangem.datasource.api.pay.models.response.WithdrawResponse
|
||||||
import com.tangem.datasource.api.tangemTech.models.QuotesResponse
|
import com.tangem.datasource.api.tangemTech.models.QuotesResponse
|
||||||
import com.tangem.datasource.local.visa.TangemPayStorage
|
import com.tangem.data.pay.store.TangemPayStorage
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import com.tangem.domain.models.wallet.UserWallet
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
|
|
||||||
|
|
@ -12,4 +12,10 @@ interface CashbackRepository {
|
||||||
|
|
||||||
/** Loads the cashback summary for the customer of [userWalletId]. */
|
/** Loads the cashback summary for the customer of [userWalletId]. */
|
||||||
suspend fun getCashbackSummary(userWalletId: UserWalletId): Either<VisaApiError, CashbackSummary>
|
suspend fun getCashbackSummary(userWalletId: UserWalletId): Either<VisaApiError, CashbackSummary>
|
||||||
|
|
||||||
|
/** Whether the "Cashback deactivated" banner was permanently dismissed for [userWalletId]. */
|
||||||
|
suspend fun isDeactivationBannerDismissed(userWalletId: UserWalletId): Boolean
|
||||||
|
|
||||||
|
/** Permanently dismisses the "Cashback deactivated" banner for [userWalletId]. */
|
||||||
|
suspend fun setDeactivationBannerDismissed(userWalletId: UserWalletId)
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.tangem.domain.pay.usecase
|
||||||
|
|
||||||
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
import com.tangem.domain.pay.repository.CashbackRepository
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads whether the "Cashback deactivated" banner was permanently dismissed for the wallet.
|
||||||
|
*
|
||||||
|
* Used by the Payment account cashback block to decide whether to show the deactivation banner.
|
||||||
|
*/
|
||||||
|
class GetCashbackDeactivationDismissedUseCase(
|
||||||
|
private val cashbackRepository: CashbackRepository,
|
||||||
|
) {
|
||||||
|
suspend operator fun invoke(userWalletId: UserWalletId): Boolean {
|
||||||
|
return cashbackRepository.isDeactivationBannerDismissed(userWalletId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.tangem.domain.pay.usecase
|
||||||
|
|
||||||
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
import com.tangem.domain.pay.repository.CashbackRepository
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permanently dismisses the "Cashback deactivated" banner for the wallet.
|
||||||
|
*
|
||||||
|
* Triggered by the "Got it" button on the Payment account cashback deactivation banner.
|
||||||
|
*/
|
||||||
|
class SetCashbackDeactivationDismissedUseCase(
|
||||||
|
private val cashbackRepository: CashbackRepository,
|
||||||
|
) {
|
||||||
|
suspend operator fun invoke(userWalletId: UserWalletId) {
|
||||||
|
cashbackRepository.setDeactivationBannerDismissed(userWalletId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -69,6 +69,7 @@ internal class TangemPayDetailsStateFactory(
|
||||||
addToWalletBlockState = null,
|
addToWalletBlockState = null,
|
||||||
errorNotificationConfig = null,
|
errorNotificationConfig = null,
|
||||||
accountDeactivatedNotificationConfig = null,
|
accountDeactivatedNotificationConfig = null,
|
||||||
|
cashbackBlockState = null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -120,6 +121,7 @@ internal class TangemPayDetailsStateFactory(
|
||||||
addToWalletBlockState = null,
|
addToWalletBlockState = null,
|
||||||
errorNotificationConfig = errorNotification ?: awaitingDepositNotification,
|
errorNotificationConfig = errorNotification ?: awaitingDepositNotification,
|
||||||
accountDeactivatedNotificationConfig = null,
|
accountDeactivatedNotificationConfig = null,
|
||||||
|
cashbackBlockState = null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -153,6 +155,7 @@ internal class TangemPayDetailsStateFactory(
|
||||||
addToWalletBlockState = null,
|
addToWalletBlockState = null,
|
||||||
errorNotificationConfig = null,
|
errorNotificationConfig = null,
|
||||||
accountDeactivatedNotificationConfig = accountDeactivatedNotification,
|
accountDeactivatedNotificationConfig = accountDeactivatedNotification,
|
||||||
|
cashbackBlockState = null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,23 @@ internal data class TangemPayDetailsUM(
|
||||||
val isBalanceHidden: Boolean,
|
val isBalanceHidden: Boolean,
|
||||||
val errorNotificationConfig: NotificationConfig?,
|
val errorNotificationConfig: NotificationConfig?,
|
||||||
val accountDeactivatedNotificationConfig: NotificationConfig?,
|
val accountDeactivatedNotificationConfig: NotificationConfig?,
|
||||||
|
val cashbackBlockState: CashbackBlockUM? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
internal sealed interface CashbackBlockUM {
|
||||||
|
|
||||||
|
data class Widget(
|
||||||
|
val title: TextReference,
|
||||||
|
val subtitle: TextReference,
|
||||||
|
val onClick: () -> Unit,
|
||||||
|
) : CashbackBlockUM
|
||||||
|
|
||||||
|
data class DeactivatedBanner(
|
||||||
|
val onGotIt: () -> Unit,
|
||||||
|
) : CashbackBlockUM
|
||||||
|
}
|
||||||
|
|
||||||
internal data class TangemPayCardDetailsUM(
|
internal data class TangemPayCardDetailsUM(
|
||||||
val number: String,
|
val number: String,
|
||||||
val numberShort: String,
|
val numberShort: String,
|
||||||
|
|
|
||||||
|
|
@ -36,12 +36,16 @@ import com.tangem.domain.pay.repository.OnboardingRepository
|
||||||
import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository
|
import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository
|
||||||
import com.tangem.domain.pay.repository.TangemPayWithdrawRepository
|
import com.tangem.domain.pay.repository.TangemPayWithdrawRepository
|
||||||
import com.tangem.domain.pay.usecase.CancelTangemPayOrderUseCase
|
import com.tangem.domain.pay.usecase.CancelTangemPayOrderUseCase
|
||||||
|
import com.tangem.domain.pay.usecase.GetCashbackDeactivationDismissedUseCase
|
||||||
|
import com.tangem.domain.pay.usecase.GetCashbackSummaryUseCase
|
||||||
import com.tangem.domain.pay.usecase.GetCustomerOffersUseCase
|
import com.tangem.domain.pay.usecase.GetCustomerOffersUseCase
|
||||||
import com.tangem.domain.pay.usecase.ProduceTangemPayInitialDataUseCase
|
import com.tangem.domain.pay.usecase.ProduceTangemPayInitialDataUseCase
|
||||||
|
import com.tangem.domain.pay.usecase.SetCashbackDeactivationDismissedUseCase
|
||||||
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
|
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
|
||||||
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
|
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
|
||||||
import com.tangem.features.tangempay.TangemPayConstants
|
import com.tangem.features.tangempay.TangemPayConstants
|
||||||
import com.tangem.features.tangempay.TangemPayFeatureToggles
|
import com.tangem.features.tangempay.TangemPayFeatureToggles
|
||||||
|
import com.tangem.features.tangempay.cashback.impl.model.TangemPayCashbackDateFormatter
|
||||||
import com.tangem.features.tangempay.components.AddFundsListener
|
import com.tangem.features.tangempay.components.AddFundsListener
|
||||||
import com.tangem.features.tangempay.components.TangemPayDetailsContainerComponent
|
import com.tangem.features.tangempay.components.TangemPayDetailsContainerComponent
|
||||||
import com.tangem.features.tangempay.components.TangemPayIssueAdditionalCardComponent
|
import com.tangem.features.tangempay.components.TangemPayIssueAdditionalCardComponent
|
||||||
|
|
@ -88,6 +92,9 @@ internal class TangemPayDetailsModel @Inject constructor(
|
||||||
private val onboardingRepository: OnboardingRepository,
|
private val onboardingRepository: OnboardingRepository,
|
||||||
private val getCustomerOffers: GetCustomerOffersUseCase,
|
private val getCustomerOffers: GetCustomerOffersUseCase,
|
||||||
private val cancelTangemPayOrderUseCase: CancelTangemPayOrderUseCase,
|
private val cancelTangemPayOrderUseCase: CancelTangemPayOrderUseCase,
|
||||||
|
private val getCashbackSummaryUseCase: GetCashbackSummaryUseCase,
|
||||||
|
private val getCashbackDeactivationDismissedUseCase: GetCashbackDeactivationDismissedUseCase,
|
||||||
|
private val setCashbackDeactivationDismissedUseCase: SetCashbackDeactivationDismissedUseCase,
|
||||||
) : Model(),
|
) : Model(),
|
||||||
TangemPayTxHistoryUiActions,
|
TangemPayTxHistoryUiActions,
|
||||||
TangemPayDetailIntents,
|
TangemPayDetailIntents,
|
||||||
|
|
@ -128,6 +135,8 @@ internal class TangemPayDetailsModel @Inject constructor(
|
||||||
|
|
||||||
private val refreshStateJobHolder = JobHolder()
|
private val refreshStateJobHolder = JobHolder()
|
||||||
private val addToWalletBannerJobHolder = JobHolder()
|
private val addToWalletBannerJobHolder = JobHolder()
|
||||||
|
private val cashbackBlockJobHolder = JobHolder()
|
||||||
|
private val cashbackDateFormatter = TangemPayCashbackDateFormatter()
|
||||||
|
|
||||||
val bottomSheetNavigation: SlotNavigation<TangemPayDetailsNavigation> = SlotNavigation()
|
val bottomSheetNavigation: SlotNavigation<TangemPayDetailsNavigation> = SlotNavigation()
|
||||||
|
|
||||||
|
|
@ -155,6 +164,7 @@ internal class TangemPayDetailsModel @Inject constructor(
|
||||||
}
|
}
|
||||||
is PaymentAccountStatusValue.Loaded -> {
|
is PaymentAccountStatusValue.Loaded -> {
|
||||||
fetchAddToWalletBanner()
|
fetchAddToWalletBanner()
|
||||||
|
fetchCashbackBlock()
|
||||||
val balanceTransformer = DetailsBalanceTransformer(
|
val balanceTransformer = DetailsBalanceTransformer(
|
||||||
fiatBalance = state.balance.fiatBalance,
|
fiatBalance = state.balance.fiatBalance,
|
||||||
isMuted = !state.isFresh,
|
isMuted = !state.isFresh,
|
||||||
|
|
@ -265,6 +275,31 @@ internal class TangemPayDetailsModel @Inject constructor(
|
||||||
}.saveIn(addToWalletBannerJobHolder)
|
}.saveIn(addToWalletBannerJobHolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun fetchCashbackBlock() {
|
||||||
|
if (!tangemPayFeatureToggles.isCashbackEnabled) return
|
||||||
|
modelScope.launch {
|
||||||
|
getCashbackSummaryUseCase(userWalletId).onRight { summary ->
|
||||||
|
val isDismissed = getCashbackDeactivationDismissedUseCase(userWalletId)
|
||||||
|
uiState.update(
|
||||||
|
transformer = CashbackBlockTransformer(
|
||||||
|
summary = summary,
|
||||||
|
isDeactivationDismissed = isDismissed,
|
||||||
|
dateFormatter = cashbackDateFormatter,
|
||||||
|
onClick = ::onClickCashback,
|
||||||
|
onGotIt = ::onDismissCashbackDeactivation,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}.saveIn(cashbackBlockJobHolder)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onDismissCashbackDeactivation() {
|
||||||
|
modelScope.launch {
|
||||||
|
setCashbackDeactivationDismissedUseCase(userWalletId)
|
||||||
|
uiState.update { it.copy(cashbackBlockState = null) }
|
||||||
|
}.saveIn(cashbackBlockJobHolder)
|
||||||
|
}
|
||||||
|
|
||||||
private fun handleBalanceHiding() {
|
private fun handleBalanceHiding() {
|
||||||
getBalanceHidingSettingsUseCase().onEach {
|
getBalanceHidingSettingsUseCase().onEach {
|
||||||
uiState.update(DetailBalanceVisibilityTransformer(isHidden = it.isBalanceHidden))
|
uiState.update(DetailBalanceVisibilityTransformer(isHidden = it.isBalanceHidden))
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
package com.tangem.features.tangempay.model.transformers
|
||||||
|
|
||||||
|
import com.tangem.core.ui.R
|
||||||
|
import com.tangem.core.ui.extensions.resourceReference
|
||||||
|
import com.tangem.core.ui.extensions.wrappedList
|
||||||
|
import com.tangem.core.ui.format.bigdecimal.defaultAmount
|
||||||
|
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||||
|
import com.tangem.core.ui.format.bigdecimal.format
|
||||||
|
import com.tangem.core.ui.format.bigdecimal.getJavaCurrencyByCode
|
||||||
|
import com.tangem.domain.pay.model.CashbackSummary
|
||||||
|
import com.tangem.features.tangempay.cashback.impl.model.TangemPayCashbackDateFormatter
|
||||||
|
import com.tangem.features.tangempay.entity.CashbackBlockUM
|
||||||
|
import com.tangem.features.tangempay.entity.TangemPayDetailsUM
|
||||||
|
import com.tangem.utils.transformer.Transformer
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the Payment account cashback block from a [CashbackSummary]:
|
||||||
|
* - [CashbackSummary.Enabled] -> tappable [CashbackBlockUM.Widget];
|
||||||
|
* - [CashbackSummary.Deactivated] (unless dismissed) -> [CashbackBlockUM.DeactivatedBanner];
|
||||||
|
* - otherwise -> hidden (`null`).
|
||||||
|
*/
|
||||||
|
internal class CashbackBlockTransformer(
|
||||||
|
private val summary: CashbackSummary,
|
||||||
|
private val isDeactivationDismissed: Boolean,
|
||||||
|
private val dateFormatter: TangemPayCashbackDateFormatter,
|
||||||
|
private val onClick: () -> Unit,
|
||||||
|
private val onGotIt: () -> Unit,
|
||||||
|
) : Transformer<TangemPayDetailsUM> {
|
||||||
|
|
||||||
|
override fun transform(prevState: TangemPayDetailsUM): TangemPayDetailsUM {
|
||||||
|
val block: CashbackBlockUM? = when (summary) {
|
||||||
|
is CashbackSummary.Enabled -> buildWidget(summary)
|
||||||
|
CashbackSummary.Deactivated -> if (isDeactivationDismissed) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
CashbackBlockUM.DeactivatedBanner(onGotIt = onGotIt)
|
||||||
|
}
|
||||||
|
CashbackSummary.Disabled,
|
||||||
|
CashbackSummary.Unknown,
|
||||||
|
-> null
|
||||||
|
}
|
||||||
|
return prevState.copy(cashbackBlockState = block)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildWidget(enabled: CashbackSummary.Enabled): CashbackBlockUM.Widget {
|
||||||
|
val cashback = enabled.cashback
|
||||||
|
val currency = getJavaCurrencyByCode(cashback.currency)
|
||||||
|
val amount = cashback.confirmedAmount.format {
|
||||||
|
fiat(currency.currencyCode, currency.symbol).defaultAmount()
|
||||||
|
}
|
||||||
|
val month = dateFormatter.formatMonth(cashback.period.year, cashback.period.month)
|
||||||
|
val window = dateFormatter.formatWindow(cashback.period.payoutStart, cashback.period.payoutEnd)
|
||||||
|
return CashbackBlockUM.Widget(
|
||||||
|
title = resourceReference(R.string.tangempay_cashback_widget_title, wrappedList(amount, month)),
|
||||||
|
subtitle = resourceReference(R.string.tangempay_cashback_deposited_on, wrappedList(window)),
|
||||||
|
onClick = onClick,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
package com.tangem.features.tangempay.ui
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.res.vectorResource
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.tangem.core.ui.ds.button.*
|
||||||
|
import com.tangem.core.ui.ds.message.TangemMessage
|
||||||
|
import com.tangem.core.ui.ds.message.TangemMessageEffect
|
||||||
|
import com.tangem.core.ui.extensions.clickableSingle
|
||||||
|
import com.tangem.core.ui.extensions.resourceReference
|
||||||
|
import com.tangem.core.ui.extensions.stringReference
|
||||||
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
|
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||||
|
import com.tangem.features.tangempay.entity.CashbackBlockUM
|
||||||
|
import com.tangem.core.ui.R as CoreUiR
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cashback block shown on the Payment account screen V2. Renders either a tappable widget with the
|
||||||
|
* accrued cashback amount or a warning banner when cashback has been deactivated.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
internal fun CashbackBlock(state: CashbackBlockUM, modifier: Modifier = Modifier) {
|
||||||
|
when (state) {
|
||||||
|
is CashbackBlockUM.Widget -> {
|
||||||
|
TangemMessage(
|
||||||
|
modifier = modifier.clickableSingle(onClick = state.onClick),
|
||||||
|
title = state.title,
|
||||||
|
subtitle = state.subtitle,
|
||||||
|
messageEffect = TangemMessageEffect.Magic,
|
||||||
|
trailingContent = {
|
||||||
|
Icon(
|
||||||
|
imageVector = ImageVector.vectorResource(CoreUiR.drawable.ic_chevron_right_24),
|
||||||
|
contentDescription = null,
|
||||||
|
tint = TangemTheme.colors3.icon.secondary,
|
||||||
|
modifier = Modifier.size(20.dp),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is CashbackBlockUM.DeactivatedBanner -> {
|
||||||
|
TangemMessage(
|
||||||
|
modifier = modifier,
|
||||||
|
title = resourceReference(CoreUiR.string.tangempay_cashback_deactivated_title),
|
||||||
|
subtitle = resourceReference(CoreUiR.string.tangempay_cashback_deactivated_description),
|
||||||
|
messageEffect = TangemMessageEffect.Warning,
|
||||||
|
buttons = {
|
||||||
|
TangemButton(
|
||||||
|
buttonUM = TangemButtonUM(
|
||||||
|
text = resourceReference(CoreUiR.string.common_got_it),
|
||||||
|
onClick = state.onGotIt,
|
||||||
|
iconPosition = TangemButtonIconPosition.End,
|
||||||
|
size = TangemButtonSize.X9,
|
||||||
|
type = TangemButtonType.Primary,
|
||||||
|
shape = TangemButtonShape.Rounded,
|
||||||
|
),
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// region preview
|
||||||
|
|
||||||
|
@Preview(showBackground = true)
|
||||||
|
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
@Composable
|
||||||
|
private fun CashbackBlockWidgetPreview() {
|
||||||
|
TangemThemePreviewRedesign {
|
||||||
|
CashbackBlock(
|
||||||
|
state = CashbackBlockUM.Widget(
|
||||||
|
title = stringReference("$32.15 cashback in June"),
|
||||||
|
subtitle = stringReference("Will be deposited on July 2–5"),
|
||||||
|
onClick = {},
|
||||||
|
),
|
||||||
|
modifier = Modifier
|
||||||
|
.background(TangemTheme.colors3.bg.primary)
|
||||||
|
.padding(16.dp)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(showBackground = true)
|
||||||
|
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
@Composable
|
||||||
|
private fun CashbackBlockDeactivatedBannerPreview() {
|
||||||
|
TangemThemePreviewRedesign {
|
||||||
|
CashbackBlock(
|
||||||
|
state = CashbackBlockUM.DeactivatedBanner(onGotIt = {}),
|
||||||
|
modifier = Modifier
|
||||||
|
.background(TangemTheme.colors3.bg.primary)
|
||||||
|
.padding(16.dp)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion preview
|
||||||
|
|
@ -118,6 +118,15 @@ internal fun TangemPayDetailsScreenV2(
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
payDetailsBody(state)
|
payDetailsBody(state)
|
||||||
|
state.cashbackBlockState?.let { cashbackState ->
|
||||||
|
item("cashbackBlock") {
|
||||||
|
SpacerH12()
|
||||||
|
CashbackBlock(
|
||||||
|
state = cashbackState,
|
||||||
|
modifier = Modifier.padding(horizontal = TangemTheme.dimens2.x4),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
item("promoBannersBlock") {
|
item("promoBannersBlock") {
|
||||||
promoBannersBlockComponent.Content(
|
promoBannersBlockComponent.Content(
|
||||||
modifier = Modifier.padding(vertical = 12.dp),
|
modifier = Modifier.padding(vertical = 12.dp),
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,9 @@ internal class TangemPayDetailsModelTest {
|
||||||
onboardingRepository = mockk(relaxed = true),
|
onboardingRepository = mockk(relaxed = true),
|
||||||
getCustomerOffers = mockk(relaxed = true),
|
getCustomerOffers = mockk(relaxed = true),
|
||||||
cancelTangemPayOrderUseCase = mockk(relaxed = true),
|
cancelTangemPayOrderUseCase = mockk(relaxed = true),
|
||||||
|
getCashbackSummaryUseCase = mockk(relaxed = true),
|
||||||
|
getCashbackDeactivationDismissedUseCase = mockk(relaxed = true),
|
||||||
|
setCashbackDeactivationDismissedUseCase = mockk(relaxed = true),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,171 @@
|
||||||
|
package com.tangem.features.tangempay.model.transformers
|
||||||
|
|
||||||
|
import android.text.format.DateFormat
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
|
||||||
|
import com.tangem.core.ui.extensions.TextReference
|
||||||
|
import com.tangem.domain.pay.model.CashbackDisplayMode
|
||||||
|
import com.tangem.domain.pay.model.CashbackSummary
|
||||||
|
import com.tangem.domain.pay.model.TangemPayCashback
|
||||||
|
import com.tangem.features.tangempay.cashback.impl.model.TangemPayCashbackDateFormatter
|
||||||
|
import com.tangem.features.tangempay.entity.CashbackBlockUM
|
||||||
|
import com.tangem.features.tangempay.entity.TangemPayDetailsBalanceBlockState
|
||||||
|
import com.tangem.features.tangempay.entity.TangemPayDetailsTopBarConfig
|
||||||
|
import com.tangem.features.tangempay.entity.TangemPayDetailsUM
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockkStatic
|
||||||
|
import io.mockk.unmockkStatic
|
||||||
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
import org.joda.time.DateTime
|
||||||
|
import org.junit.jupiter.api.AfterEach
|
||||||
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.TestInstance
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource
|
||||||
|
import java.math.BigDecimal
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
|
internal class CashbackBlockTransformerTest {
|
||||||
|
|
||||||
|
private val defaultLocale = Locale.getDefault()
|
||||||
|
|
||||||
|
private val onClick: () -> Unit = {}
|
||||||
|
private val onGotIt: () -> Unit = {}
|
||||||
|
private val dateFormatter = TangemPayCashbackDateFormatter()
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
fun setup() {
|
||||||
|
// TangemPayCashbackDateFormatter -> DateTimeFormatters.dateMMMM -> android.text.format.DateFormat
|
||||||
|
Locale.setDefault(Locale.US)
|
||||||
|
mockkStatic(DateFormat::class)
|
||||||
|
every { DateFormat.getBestDateTimePattern(any(), any()) } answers { secondArg() }
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
fun tearDown() {
|
||||||
|
unmockkStatic(DateFormat::class)
|
||||||
|
Locale.setDefault(defaultLocale)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN enabled summary WHEN transform THEN cashback block is widget with content`() {
|
||||||
|
// Arrange
|
||||||
|
val transformer = createTransformer(summary = enabledSummary())
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val block = transformer.transform(contentState()).cashbackBlockState
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(block).isInstanceOf(CashbackBlockUM.Widget::class.java)
|
||||||
|
val widget = block as CashbackBlockUM.Widget
|
||||||
|
assertThat(widget.title).isInstanceOf(TextReference.Res::class.java)
|
||||||
|
assertThat(widget.title).isNotEqualTo(TextReference.EMPTY)
|
||||||
|
assertThat(widget.subtitle).isInstanceOf(TextReference.Res::class.java)
|
||||||
|
assertThat(widget.subtitle).isNotEqualTo(TextReference.EMPTY)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("provideTestModels")
|
||||||
|
fun `GIVEN summary and dismissed flag WHEN transform THEN resolves expected cashback block`(model: BlockCase) {
|
||||||
|
// Arrange
|
||||||
|
val transformer = createTransformer(summary = model.summary, isDismissed = model.isDismissed)
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val block = transformer.transform(contentState()).cashbackBlockState
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(block).isEqualTo(model.expectedBlock)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun provideTestModels(): List<BlockCase> = listOf(
|
||||||
|
BlockCase(
|
||||||
|
description = "deactivated & not dismissed -> banner",
|
||||||
|
summary = CashbackSummary.Deactivated,
|
||||||
|
isDismissed = false,
|
||||||
|
expectedBlock = CashbackBlockUM.DeactivatedBanner(onGotIt = onGotIt),
|
||||||
|
),
|
||||||
|
BlockCase(
|
||||||
|
description = "deactivated & dismissed -> hidden",
|
||||||
|
summary = CashbackSummary.Deactivated,
|
||||||
|
isDismissed = true,
|
||||||
|
expectedBlock = null,
|
||||||
|
),
|
||||||
|
BlockCase(
|
||||||
|
description = "disabled -> hidden",
|
||||||
|
summary = CashbackSummary.Disabled,
|
||||||
|
isDismissed = false,
|
||||||
|
expectedBlock = null,
|
||||||
|
),
|
||||||
|
BlockCase(
|
||||||
|
description = "unknown -> hidden",
|
||||||
|
summary = CashbackSummary.Unknown,
|
||||||
|
isDismissed = false,
|
||||||
|
expectedBlock = null,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun createTransformer(
|
||||||
|
summary: CashbackSummary,
|
||||||
|
isDismissed: Boolean = false,
|
||||||
|
): CashbackBlockTransformer = CashbackBlockTransformer(
|
||||||
|
summary = summary,
|
||||||
|
isDeactivationDismissed = isDismissed,
|
||||||
|
dateFormatter = dateFormatter,
|
||||||
|
onClick = onClick,
|
||||||
|
onGotIt = onGotIt,
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun enabledSummary(
|
||||||
|
confirmedAmount: BigDecimal = BigDecimal("32.15"),
|
||||||
|
currency: String = "USD",
|
||||||
|
year: Int = 2026,
|
||||||
|
month: Int = 6,
|
||||||
|
payoutStart: DateTime = DateTime.parse("2026-07-02"),
|
||||||
|
payoutEnd: DateTime = DateTime.parse("2026-07-05"),
|
||||||
|
): CashbackSummary.Enabled = CashbackSummary.Enabled(
|
||||||
|
displayMode = CashbackDisplayMode.FULL,
|
||||||
|
cashback = TangemPayCashback(
|
||||||
|
confirmedAmount = confirmedAmount,
|
||||||
|
currency = currency,
|
||||||
|
period = TangemPayCashback.Period(
|
||||||
|
year = year,
|
||||||
|
month = month,
|
||||||
|
payoutStart = payoutStart,
|
||||||
|
payoutEnd = payoutEnd,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pendingAmount = BigDecimal("13.65"),
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun contentState(): TangemPayDetailsUM = TangemPayDetailsUM(
|
||||||
|
topBarConfig = TangemPayDetailsTopBarConfig(
|
||||||
|
onBackClick = {},
|
||||||
|
onOpenMenu = {},
|
||||||
|
items = persistentListOf(),
|
||||||
|
itemsV2 = persistentListOf(),
|
||||||
|
),
|
||||||
|
pullToRefreshConfig = PullToRefreshConfig(isRefreshing = false, onRefresh = {}),
|
||||||
|
balanceBlockState = TangemPayDetailsBalanceBlockState.Content(
|
||||||
|
actionButtons = persistentListOf(),
|
||||||
|
cardsBlockState = null,
|
||||||
|
fiatBalance = TextReference.EMPTY,
|
||||||
|
isBalanceFlickering = false,
|
||||||
|
isNegative = false,
|
||||||
|
),
|
||||||
|
addToWalletBlockState = null,
|
||||||
|
isBalanceHidden = false,
|
||||||
|
errorNotificationConfig = null,
|
||||||
|
accountDeactivatedNotificationConfig = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
internal class BlockCase(
|
||||||
|
val summary: CashbackSummary,
|
||||||
|
val isDismissed: Boolean,
|
||||||
|
val expectedBlock: CashbackBlockUM?,
|
||||||
|
private val description: String,
|
||||||
|
) {
|
||||||
|
override fun toString(): String = description
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue