Updated on 2026-08-14

This commit is contained in:
Tangem 2026-04-29 20:50:41 +05:00
parent f086d6bb74
commit 40fe8bb5cb
56 changed files with 82 additions and 1491 deletions

View file

@ -147,7 +147,6 @@ dependencies {
implementation(projects.features.kyc.api)
implementation(projects.features.tokenRecieve.api)
implementation(projects.features.yieldSupply.api)
implementation(projects.features.tangempay.details.api)
implementation(projects.features.feed.api)
implementation(projects.features.promoBanners.api)
implementation(projects.features.tangempay.main.api)

View file

@ -14,12 +14,13 @@ import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.utils.parseBigDecimal
import com.tangem.datasource.local.appsflyer.AppsFlyerStore
import com.tangem.domain.account.supplier.SingleAccountListSupplier
import com.tangem.domain.account.status.usecase.IsAccountsModeEnabledUseCase
import com.tangem.domain.account.supplier.SingleAccountListSupplier
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.apptheme.GetAppThemeModeUseCase
import com.tangem.domain.apptheme.model.AppThemeMode
import com.tangem.domain.assetsdiscovery.usecase.StartAssetsDiscoveryUseCase
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.models.wallet.*
@ -27,7 +28,6 @@ import com.tangem.domain.notifications.GetIsHuaweiDeviceWithoutGoogleServicesUse
import com.tangem.domain.notifications.repository.NotificationsRepository
import com.tangem.domain.pay.flow.PaymentAccountStatusFetcher
import com.tangem.domain.pay.repository.OnboardingRepository
import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase
import com.tangem.domain.qrscanning.models.ClassifiedQrContent
import com.tangem.domain.qrscanning.models.QrResultSource
import com.tangem.domain.qrscanning.models.QrSendTarget
@ -38,8 +38,6 @@ import com.tangem.domain.settings.*
import com.tangem.domain.tokens.RefreshMultiCurrencyWalletQuotesUseCase
import com.tangem.domain.walletconnect.WcPairService
import com.tangem.domain.walletconnect.model.WcPairRequest
import com.tangem.domain.assetsdiscovery.usecase.StartAssetsDiscoveryUseCase
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.domain.wallets.usecase.*
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyUpdateUseCase
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
@ -62,15 +60,15 @@ import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSend
import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.KycRejectedCallbacks
import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider
import com.tangem.features.biometry.AskBiometryComponent
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.features.pushnotifications.api.PushNotificationsModelCallbacks
import com.tangem.features.tangempay.TangemPayFeatureToggles
import com.tangem.features.wallet.deeplink.WalletDeepLinkActionListener
import com.tangem.utils.Provider
import com.tangem.utils.coroutines.*
import com.tangem.utils.logging.TangemLogger
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.*
import com.tangem.utils.logging.TangemLogger
import javax.inject.Inject
private const val TANGEM_PAY_UPDATE_INTERVAL = 60_000L
@ -108,7 +106,6 @@ internal class WalletModel @Inject constructor(
private val userWalletsListRepository: UserWalletsListRepository,
private val yieldSupplyApyUpdateUseCase: YieldSupplyApyUpdateUseCase,
private val tangemPayOnboardingRepository: OnboardingRepository,
private val tangemPayMainScreenCustomerInfoUseCase: TangemPayMainScreenCustomerInfoUseCase,
private val getAppThemeModeUseCase: GetAppThemeModeUseCase,
private val trackingContextProxy: TrackingContextProxy,
private val singleAccountListSupplier: SingleAccountListSupplier,
@ -121,7 +118,6 @@ internal class WalletModel @Inject constructor(
private val wcPairService: WcPairService,
private val resolveQrSendTargetsUseCase: ResolveQrSendTargetsUseCase,
private val paymentAccountStatusFetcher: PaymentAccountStatusFetcher,
private val tangemPayFeatureToggles: TangemPayFeatureToggles,
private val uiMessageSender: UiMessageSender,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
private val startAssetsDiscoveryUseCase: StartAssetsDiscoveryUseCase,
@ -436,17 +432,14 @@ internal class WalletModel @Inject constructor(
if (isShouldLaunchPeriodicUpdate) {
updateTangemPayJobHolder.cancel()
modelScope.launch {
tangemPayMainScreenCustomerInfoUseCase.fetch(userWalletId)
paymentAccountStatusFetcher.invoke(PaymentAccountStatusFetcher.Params(userWalletId))
while (isActive) {
delay(TANGEM_PAY_UPDATE_INTERVAL)
tangemPayMainScreenCustomerInfoUseCase.fetch(userWalletId)
paymentAccountStatusFetcher.invoke(PaymentAccountStatusFetcher.Params(userWalletId))
}
}.saveIn(updateTangemPayJobHolder)
} else {
// Don't refresh customer info periodically if the card was already issued, only update on swipe to refresh
tangemPayMainScreenCustomerInfoUseCase.fetch(userWalletId)
paymentAccountStatusFetcher.invoke(PaymentAccountStatusFetcher.Params(userWalletId))
}
}.launchIn(modelScope)
@ -555,7 +548,6 @@ internal class WalletModel @Inject constructor(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isTangemPayRefactorEnabled = tangemPayFeatureToggles.isTangemPayAccountsRefactorEnabled,
),
)
@ -602,7 +594,6 @@ internal class WalletModel @Inject constructor(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isTangemPayRefactorEnabled = tangemPayFeatureToggles.isTangemPayAccountsRefactorEnabled,
),
)
}
@ -624,7 +615,6 @@ internal class WalletModel @Inject constructor(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isTangemPayRefactorEnabled = tangemPayFeatureToggles.isTangemPayAccountsRefactorEnabled,
),
)
}
@ -639,7 +629,6 @@ internal class WalletModel @Inject constructor(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isTangemPayRefactorEnabled = tangemPayFeatureToggles.isTangemPayAccountsRefactorEnabled,
),
)
@ -701,7 +690,6 @@ internal class WalletModel @Inject constructor(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isTangemPayRefactorEnabled = tangemPayFeatureToggles.isTangemPayAccountsRefactorEnabled,
),
)

View file

@ -22,11 +22,10 @@ import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.TangemPayDetailsConfig
import com.tangem.domain.pay.TangemPayEligibilityManager
import com.tangem.domain.pay.model.TangemPayEntryPoint
import com.tangem.domain.pay.flow.PaymentAccountStatusFetcher
import com.tangem.domain.pay.model.TangemPayEntryPoint
import com.tangem.domain.pay.repository.OnboardingRepository
import com.tangem.domain.pay.usecase.ProduceTangemPayInitialDataUseCase
import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogConfig
@ -72,7 +71,6 @@ internal class TangemPayClickIntentsImplementor @Inject constructor(
private val produceInitialDataTangemPay: ProduceTangemPayInitialDataUseCase,
private val getWalletMetainfoUseCase: GetWalletMetaInfoUseCase,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val tangemPayMainScreenCustomerInfoUseCase: TangemPayMainScreenCustomerInfoUseCase,
private val tangemPayOnboardingRepository: OnboardingRepository,
private val tangemPayEligibilityManager: TangemPayEligibilityManager,
private val uiMessageSender: UiMessageSender,
@ -85,7 +83,6 @@ internal class TangemPayClickIntentsImplementor @Inject constructor(
if (!onboardingRepository.isTangemPayInitialDataProduced(userWalletId)) {
return
}
tangemPayMainScreenCustomerInfoUseCase.fetch(userWalletId)
paymentAccountStatusFetcher.invoke(PaymentAccountStatusFetcher.Params(userWalletId))
}
@ -100,7 +97,6 @@ internal class TangemPayClickIntentsImplementor @Inject constructor(
modelScope.launch {
produceInitialDataTangemPay.invoke(userWallet.walletId)
.onRight {
tangemPayMainScreenCustomerInfoUseCase.fetch(userWallet.walletId)
paymentAccountStatusFetcher.invoke(PaymentAccountStatusFetcher.Params(userWallet.walletId))
}
.onLeft {
@ -277,7 +273,6 @@ internal class TangemPayClickIntentsImplementor @Inject constructor(
modelScope.launch {
tangemPayOnboardingRepository.disableTangemPay(userWalletId)
.onRight {
tangemPayMainScreenCustomerInfoUseCase.fetch(userWalletId)
paymentAccountStatusFetcher.invoke(PaymentAccountStatusFetcher.Params(userWalletId))
}
.onLeft { uiMessageSender.send(ToastMessage(resourceReference(R.string.common_something_went_wrong))) }

View file

@ -217,15 +217,8 @@ internal object WalletScreenPreviewDataLegacy {
isFlickering = false,
onItemClick = { },
),
tangemPayState = TangemPayState.Card(
lastFourDigits = stringReference("*1234"),
balanceText = stringReference("$10"),
balanceSymbol = stringReference("USDC"),
onClick = {},
),
type = WalletType.Cold,
tangemPayMainUM = TangemPayMainUM.Empty,
isTangemPayRefactorEnabled = false,
)
}

View file

@ -2,9 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.analytics.utils
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.models.kyc.KycStatus
import com.tangem.domain.pay.model.MainScreenCustomerInfo
import com.tangem.domain.pay.model.OrderStatus
import com.tangem.domain.models.account.PaymentAccountStatusValue
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider
import javax.inject.Inject
@ -15,26 +13,23 @@ internal class WalletTangemPayAnalyticsEventSender @Inject constructor(
private val screenLifecycleProvider: ScreenLifecycleProvider,
) {
private val sentEvents = mutableSetOf<TangemPayAnalyticsEvents>()
private val sentEvents = mutableSetOf<String>()
fun send(customerInfo: MainScreenCustomerInfo) {
fun send(statusValue: PaymentAccountStatusValue) {
if (screenLifecycleProvider.isBackgroundState.value) return
val cardInfo = customerInfo.info.cardInfo
val productInstance = customerInfo.info.productInstance
// TODO: TangemPay refactor analytics
// when statement copied from TangemPayUpdateInfoStateTransformer. Be careful when editing
val event = when {
// ignore cancelled state on analytics
customerInfo.orderStatus == OrderStatus.CANCELED -> return
// ignore kyc not approved state on analytics
customerInfo.info.kycStatus != KycStatus.APPROVED -> return
cardInfo != null && productInstance != null -> return
else -> TangemPayAnalyticsEvents.IssuingBannerDisplayed()
val event = when (statusValue) {
is PaymentAccountStatusValue.IssuingCard -> TangemPayAnalyticsEvents.IssuingBannerDisplayed()
PaymentAccountStatusValue.Empty,
is PaymentAccountStatusValue.Error,
is PaymentAccountStatusValue.Loaded,
PaymentAccountStatusValue.Loading,
PaymentAccountStatusValue.NotCreated,
is PaymentAccountStatusValue.UnderReview,
-> return
}
if (sentEvents.add(event)) {
if (sentEvents.add(event.id)) {
analyticsEventHandler.send(event)
}
}

View file

@ -2,16 +2,15 @@ package com.tangem.feature.wallet.presentation.wallet.domain
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.tokens.wallet.WalletBalanceFetcher
import com.tangem.features.tangempay.TangemPayFeatureToggles
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.JobHolder
import com.tangem.utils.coroutines.saveInAndJoin
import com.tangem.utils.logging.TangemLogger
import kotlinx.coroutines.launch
import kotlinx.coroutines.supervisorScope
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import com.tangem.utils.logging.TangemLogger
import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
import javax.inject.Singleton
@ -28,7 +27,6 @@ import javax.inject.Singleton
internal class WalletContentFetcher @Inject constructor(
private val walletBalanceFetcher: WalletBalanceFetcher,
private val dispatchers: CoroutineDispatcherProvider,
private val tangemPayFeatureToggles: TangemPayFeatureToggles,
) {
private val fetchingJobMap = ConcurrentHashMap<UserWalletId, JobHolder>()
@ -66,12 +64,8 @@ internal class WalletContentFetcher @Inject constructor(
TangemLogger.d("Start fetching for $userWalletId")
val maybeResult = launch {
walletBalanceFetcher(
params = WalletBalanceFetcher.Params(
userWalletId = userWalletId,
isPaymentAccountRefactorEnabled = tangemPayFeatureToggles.isTangemPayAccountsRefactorEnabled,
),
).onLeft { TangemLogger.e("Error", it) }
walletBalanceFetcher(params = WalletBalanceFetcher.Params(userWalletId = userWalletId))
.onLeft { TangemLogger.e("Error", it) }
}
.saveInAndJoin(jobHolder)

View file

@ -1,49 +0,0 @@
package com.tangem.feature.wallet.presentation.wallet.state.model
import androidx.annotation.DrawableRes
import androidx.compose.runtime.Immutable
import com.tangem.core.ui.extensions.TextReference
@Immutable
internal sealed class TangemPayState {
object Empty : TangemPayState()
data object Loading : TangemPayState()
data class OnboardingBanner(
val onClick: () -> Unit,
val closeOnClick: () -> Unit,
) : TangemPayState()
data class Progress(
val title: TextReference,
val description: TextReference,
val buttonText: TextReference,
@DrawableRes val iconRes: Int,
val onButtonClick: () -> Unit,
val showProgress: Boolean = false,
) : TangemPayState()
data class FailedIssue(
val title: TextReference,
val description: TextReference,
@DrawableRes val iconRes: Int,
val onButtonClick: () -> Unit,
) : TangemPayState()
data class Card(
val lastFourDigits: TextReference,
val balanceText: TextReference,
val balanceSymbol: TextReference,
val onClick: () -> Unit,
) : TangemPayState()
data class RefreshNeeded(
val notification: WalletNotification,
) : TangemPayState()
data class TemporaryUnavailable(val notification: WalletNotification) : TangemPayState()
data object ExposedDevice : TangemPayState()
}

View file

@ -24,9 +24,7 @@ internal sealed interface WalletState : WalletStateHolder {
abstract val tokensListState: WalletTokensListState
abstract val nftState: WalletNFTItemUM
abstract val type: WalletType
abstract val tangemPayState: TangemPayState
abstract val tangemPayMainUM: TangemPayMainUM
abstract val isTangemPayRefactorEnabled: Boolean // TANGEM_PAY_ACCOUNTS_REFACTOR_ENABLED
abstract val assetsDiscoveryProgressUM: AssetsDiscoveryProgressUM
data class Content(
@ -38,9 +36,7 @@ internal sealed interface WalletState : WalletStateHolder {
override val tokensListState: WalletTokensListState,
override val nftState: WalletNFTItemUM,
override val type: WalletType,
override val tangemPayState: TangemPayState,
override val tangemPayMainUM: TangemPayMainUM,
override val isTangemPayRefactorEnabled: Boolean,
override val assetsDiscoveryProgressUM: AssetsDiscoveryProgressUM = AssetsDiscoveryProgressUM.Idle,
) : MultiCurrency()
@ -60,9 +56,7 @@ internal sealed interface WalletState : WalletStateHolder {
override val tokensListState = WalletTokensListState.ContentState.Locked
override val nftState: WalletNFTItemUM = WalletNFTItemUM.Hidden
override val tangemPayState: TangemPayState = TangemPayState.Empty
override val tangemPayMainUM: TangemPayMainUM = TangemPayMainUM.Empty
override val isTangemPayRefactorEnabled: Boolean = false
override val assetsDiscoveryProgressUM: AssetsDiscoveryProgressUM = AssetsDiscoveryProgressUM.Idle
}
}

View file

@ -13,7 +13,6 @@ internal class AddWalletTransformer(
private val clickIntents: WalletClickIntents,
private val walletImageResolver: WalletImageResolver,
private val getWalletIconUseCase: GetWalletIconUseCase,
private val isTangemPayRefactorEnabled: Boolean,
) : WalletScreenStateTransformer {
private val walletLoadingStateFactory by lazy {
@ -21,7 +20,6 @@ internal class AddWalletTransformer(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isTangemPayRefactorEnabled = isTangemPayRefactorEnabled,
)
}

View file

@ -27,7 +27,6 @@ internal class InitializeWalletsTransformer(
private val clickIntents: WalletClickIntents,
private val walletImageResolver: WalletImageResolver,
private val getWalletIconUseCase: GetWalletIconUseCase,
private val isTangemPayRefactorEnabled: Boolean,
) : WalletScreenStateTransformer {
private val walletLoadingStateFactory by lazy {
@ -35,7 +34,6 @@ internal class InitializeWalletsTransformer(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isTangemPayRefactorEnabled = isTangemPayRefactorEnabled,
)
}

View file

@ -24,7 +24,6 @@ internal class ReinitializeNewWalletTransformer(
private val clickIntents: WalletClickIntents,
private val walletImageResolver: WalletImageResolver,
private val getWalletIconUseCase: GetWalletIconUseCase,
private val isTangemPayRefactorEnabled: Boolean,
) : WalletScreenStateTransformer {
private val walletLoadingStateFactory by lazy {
@ -32,7 +31,6 @@ internal class ReinitializeNewWalletTransformer(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isTangemPayRefactorEnabled = isTangemPayRefactorEnabled,
)
}

View file

@ -20,7 +20,6 @@ internal class ReinitializeWalletTransformer(
private val clickIntents: WalletClickIntents,
private val walletImageResolver: WalletImageResolver,
private val getWalletIconUseCase: GetWalletIconUseCase,
private val isTangemPayRefactorEnabled: Boolean,
) : WalletStateTransformer(userWalletId = userWallet.walletId) {
private val walletLoadingStateFactory by lazy {
@ -28,7 +27,6 @@ internal class ReinitializeWalletTransformer(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isTangemPayRefactorEnabled = isTangemPayRefactorEnabled,
)
}

View file

@ -1,22 +0,0 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
internal class TangemPayExposedDeviceTransformer(
userWalletId: UserWalletId,
) : WalletStateTransformer(userWalletId) {
override fun transform(prevState: WalletState): WalletState {
return if (prevState is WalletState.MultiCurrency.Content) {
prevState.copy(tangemPayState = TangemPayState.ExposedDevice)
} else {
prevState
}
}
override fun transform(walletUM: WalletUM): WalletUM {
return walletUM // todo redesign main
}
}

View file

@ -1,23 +0,0 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
internal class TangemPayHiddenStateTransformer(
userWalletId: UserWalletId,
) : WalletStateTransformer(userWalletId) {
override fun transform(prevState: WalletState): WalletState {
return if (prevState is WalletState.MultiCurrency.Content) {
prevState.copy(tangemPayState = TangemPayState.Empty)
} else {
prevState
}
}
override fun transform(walletUM: WalletUM): WalletUM {
return walletUM // todo redesign main
}
}

View file

@ -1,7 +1,6 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
import com.tangem.features.tangempay.entity.TangemPayMainUM
@ -12,7 +11,7 @@ internal class TangemPayHideOnboardingStateTransformer(
override fun transform(prevState: WalletState): WalletState {
return if (prevState is WalletState.MultiCurrency.Content) {
prevState.copy(tangemPayState = TangemPayState.Empty, tangemPayMainUM = TangemPayMainUM.Empty)
prevState.copy(tangemPayMainUM = TangemPayMainUM.Empty)
} else {
prevState
}

View file

@ -1,20 +0,0 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
internal class TangemPayLoadingStateTransformer(userWalletId: UserWalletId) : WalletStateTransformer(userWalletId) {
override fun transform(prevState: WalletState): WalletState {
return if (prevState is WalletState.MultiCurrency.Content) {
prevState.copy(tangemPayState = TangemPayState.Loading)
} else {
prevState
}
}
override fun transform(walletUM: WalletUM): WalletUM {
return walletUM // todo redesign main
}
}

View file

@ -1,30 +0,0 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
internal class TangemPayOnboardingBannerStateTransformer(
userWalletId: UserWalletId,
private val onClick: (UserWalletId) -> Unit,
private val closeOnClick: (UserWalletId) -> Unit,
) : WalletStateTransformer(userWalletId) {
override fun transform(prevState: WalletState): WalletState {
return if (prevState is WalletState.MultiCurrency.Content) {
prevState.copy(
tangemPayState = TangemPayState.OnboardingBanner(
onClick = { onClick(userWalletId) },
closeOnClick = { closeOnClick(userWalletId) },
),
)
} else {
prevState
}
}
override fun transform(walletUM: WalletUM): WalletUM {
return walletUM // todo redesign main
}
}

View file

@ -1,39 +0,0 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification.Warning.TangemPayRefreshNeeded
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
internal class TangemPayRefreshNeededStateTransformer(
userWalletId: UserWalletId,
private val userWallet: UserWallet,
private val onRefreshClick: () -> Unit,
) : WalletStateTransformer(userWalletId = userWalletId) {
override fun transform(prevState: WalletState): WalletState {
val tangemPayState = TangemPayState.RefreshNeeded(
notification = TangemPayRefreshNeeded(
buttonText = when (userWallet) {
is UserWallet.Cold -> resourceReference(id = R.string.home_button_scan)
is UserWallet.Hot -> resourceReference(id = R.string.tangempay_sync_needed_restore_access)
},
onRefreshClick = onRefreshClick,
shouldShowProgress = false,
),
)
return if (prevState is WalletState.MultiCurrency.Content) {
prevState.copy(tangemPayState = tangemPayState)
} else {
prevState
}
}
override fun transform(walletUM: WalletUM): WalletUM {
return walletUM // todo redesign main
}
}

View file

@ -1,7 +1,6 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
@ -14,9 +13,6 @@ internal class TangemPayRefreshShowProgressTransformer(
override fun transform(prevState: WalletState): WalletState {
val multiContentState = prevState as? WalletState.MultiCurrency.Content ?: return prevState
val refreshNeededState = multiContentState.tangemPayState as? TangemPayState.RefreshNeeded ?: return prevState
val refreshNotification =
refreshNeededState.notification as? WalletNotification.Warning.TangemPayRefreshNeeded ?: return prevState
val newWarnings = prevState.warnings.map { warning ->
if (warning is WalletNotification.Warning.TangemPayRefreshNeeded) {
warning.copy(shouldShowProgress = shouldShowProgress)
@ -25,12 +21,7 @@ internal class TangemPayRefreshShowProgressTransformer(
}
}
return multiContentState.copy(
tangemPayState = refreshNeededState.copy(
notification = refreshNotification.copy(shouldShowProgress = shouldShowProgress),
),
warnings = newWarnings.toImmutableList(),
)
return multiContentState.copy(warnings = newWarnings.toImmutableList())
}
override fun transform(walletUM: WalletUM): WalletUM {

View file

@ -1,28 +0,0 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
internal class TangemPayUnavailableStateTransformer(
userWalletId: UserWalletId,
) : WalletStateTransformer(userWalletId) {
override fun transform(prevState: WalletState): WalletState {
return if (prevState is WalletState.MultiCurrency.Content) {
prevState.copy(
tangemPayState = TangemPayState.TemporaryUnavailable(
notification = WalletNotification.Warning.TangemPayUnreachable,
),
)
} else {
prevState
}
}
override fun transform(walletUM: WalletUM): WalletUM {
return walletUM // todo redesign main
}
}

View file

@ -1,130 +0,0 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers
import com.tangem.common.ui.R
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.models.kyc.KycStatus
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.TangemPayDetailsConfig
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.visa.model.TangemPayCardFrozenState
import com.tangem.feature.wallet.child.wallet.model.intents.TangemPayIntents
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.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
import java.util.Currency
/**
* Hardcode Polygon chain id only for F&F.
* Later chain id will be fetched from BFF.
*/
private const val POLYGON_CHAIN_ID = 137
internal class TangemPayUpdateInfoStateTransformer(
userWalletId: UserWalletId,
private val value: MainScreenCustomerInfo,
private val cardFrozenState: TangemPayCardFrozenState,
private val tangemPayClickIntents: TangemPayIntents,
) : WalletStateTransformer(userWalletId = userWalletId) {
override fun transform(prevState: WalletState): WalletState {
val tangemPayState = createInitialState()
return if (prevState is WalletState.MultiCurrency.Content) {
prevState.copy(tangemPayState = tangemPayState)
} else {
prevState
}
}
override fun transform(walletUM: WalletUM): WalletUM {
return walletUM // todo redesign main
}
private fun createInitialState(): TangemPayState {
val cardInfo = value.info.cardInfo
val productInstance = value.info.productInstance
val customerId = value.info.customerId ?: "Unknown"
// when statement copied to WalletTangemPayAnalyticsEventSender. Be careful when editing.
return when {
value.info.kycStatus != KycStatus.APPROVED && !value.info.customerId.isNullOrEmpty() ->
createKycInProgressState(kycStatus = value.info.kycStatus, customerId = customerId)
value.orderStatus == OrderStatus.CANCELED -> createCancelledState(customerId)
cardInfo != null && productInstance != null && value.orderStatus == OrderStatus.COMPLETED ->
getCardInfoState(customerId, cardInfo, productInstance)
else -> createIssueProgressState()
}
}
private fun getCardInfoState(
customerId: String,
cardInfo: CardInfo,
productInstance: ProductInstance,
): TangemPayState = TangemPayState.Card(
lastFourDigits = TextReference.Str("*${cardInfo.lastFourDigits}"),
balanceText = TextReference.Str(getBalanceText(cardInfo)),
balanceSymbol = stringReference("USDC"), // TODO hardcode for now
onClick = {
tangemPayClickIntents.openDetails(
userWalletId,
TangemPayDetailsConfig(
customerId = customerId,
cardId = productInstance.cardId,
isPinSet = cardInfo.isPinSet,
cardFrozenState = cardFrozenState,
cardNumberEnd = cardInfo.lastFourDigits,
chainId = POLYGON_CHAIN_ID,
displayName = productInstance.displayName,
),
)
},
)
private fun getBalanceText(cardInfo: CardInfo): String {
val currency = Currency.getInstance(cardInfo.currencyCode)
return cardInfo.balance.format {
fiat(fiatCurrencyCode = currency.currencyCode, fiatCurrencySymbol = currency.symbol)
}
}
private fun createKycInProgressState(kycStatus: KycStatus, customerId: String): TangemPayState = Progress(
title = TextReference.Res(R.string.tangempay_payment_account),
description = when (kycStatus) {
KycStatus.REJECTED -> TextReference.Res(R.string.tangempay_kyc_has_failed)
else -> TextReference.Res(R.string.tangempay_kyc_in_progress)
},
buttonText = TextReference.Res(R.string.tangempay_kyc_in_progress_notification_button),
iconRes = R.drawable.ic_promo_kyc_36,
onButtonClick = {
when (kycStatus) {
KycStatus.REJECTED -> tangemPayClickIntents.onKycRejectedClicked(
userWalletId = userWalletId,
customerId = customerId,
)
else -> tangemPayClickIntents.onKycProgressClicked(userWalletId)
}
},
)
private fun createIssueProgressState(): TangemPayState = Progress(
title = TextReference.Res(R.string.tangempay_payment_account),
description = TextReference.Res(R.string.tangempay_issuing_your_card),
buttonText = TextReference.EMPTY,
iconRes = R.drawable.ic_tangem_pay_promo_card_36,
onButtonClick = tangemPayClickIntents::onIssuingCardClicked,
showProgress = true,
)
private fun createCancelledState(customerId: String): TangemPayState = TangemPayState.FailedIssue(
title = TextReference.Res(R.string.tangempay_payment_account),
description = TextReference.Res(R.string.tangempay_failed_to_issue_card),
iconRes = R.drawable.ic_alert_24,
onButtonClick = { tangemPayClickIntents.onIssuingFailedClicked(customerId) },
)
}

View file

@ -9,16 +9,15 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenSta
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletLoadingStateFactory
import com.tangem.utils.logging.TangemLogger
import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toPersistentList
import com.tangem.utils.logging.TangemLogger
internal class UnlockWalletTransformer(
private val unlockedWallets: List<UserWallet>,
private val clickIntents: WalletClickIntents,
private val walletImageResolver: WalletImageResolver,
private val getWalletIconUseCase: GetWalletIconUseCase,
private val isTangemPayRefactorEnabled: Boolean,
) : WalletScreenStateTransformer {
private val walletLoadingStateFactory by lazy {
@ -26,7 +25,6 @@ internal class UnlockWalletTransformer(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isTangemPayRefactorEnabled = isTangemPayRefactorEnabled,
)
}

View file

@ -34,7 +34,6 @@ internal class WalletLoadingStateFactory(
private val clickIntents: WalletClickIntents,
private val walletImageResolver: WalletImageResolver,
private val getWalletIconUseCase: GetWalletIconUseCase,
private val isTangemPayRefactorEnabled: Boolean,
) {
fun create(userWallet: UserWallet): WalletState {
@ -83,9 +82,7 @@ internal class WalletLoadingStateFactory(
tokensListState = WalletTokensListState.ContentState.Loading,
nftState = WalletNFTItemUM.Hidden,
type = WalletType.Hot,
tangemPayState = TangemPayState.Empty,
tangemPayMainUM = TangemPayMainUM.Empty,
isTangemPayRefactorEnabled = isTangemPayRefactorEnabled,
)
}
@ -99,9 +96,7 @@ internal class WalletLoadingStateFactory(
tokensListState = WalletTokensListState.ContentState.Loading,
nftState = WalletNFTItemUM.Hidden,
type = WalletType.Cold,
tangemPayState = TangemPayState.Empty,
tangemPayMainUM = TangemPayMainUM.Empty,
isTangemPayRefactorEnabled = isTangemPayRefactorEnabled,
)
}

View file

@ -1,122 +1,38 @@
package com.tangem.feature.wallet.presentation.wallet.subscribers
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.model.MainCustomerInfoContentState
import com.tangem.domain.pay.model.MainScreenCustomerInfo
import com.tangem.domain.pay.model.TangemPayCustomerInfoError
import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository
import com.tangem.domain.pay.flow.PaymentAccountStatusSupplier
import com.tangem.domain.pay.repository.TangemPayWithdrawRepository
import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase
import com.tangem.domain.visa.model.TangemPayCardFrozenState
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletTangemPayAnalyticsEventSender
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.transformers.*
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import com.tangem.utils.logging.TangemLogger
@Suppress("LongParameterList")
internal class TangemPayMainSubscriber @AssistedInject constructor(
@Assisted private val userWallet: UserWallet,
private val stateController: WalletStateController,
private val clickIntents: WalletClickIntents,
private val cardDetailsRepository: TangemPayCardDetailsRepository,
private val tangemPayMainScreenCustomerInfoUseCase: TangemPayMainScreenCustomerInfoUseCase,
private val tangemPayWithdrawRepository: TangemPayWithdrawRepository,
private val paymentAccountStatusSupplier: PaymentAccountStatusSupplier,
private val analytics: WalletTangemPayAnalyticsEventSender,
) : WalletSubscriber() {
override fun create(coroutineScope: CoroutineScope): Flow<*> {
coroutineScope.launch {
// TODO: Doston move this logic to proper place(e.g. WalletBalanceFetcher)
tangemPayWithdrawRepository.pollWithdrawOrdersIfNeeds(userWallet)
}
return subscribeOnTangemPayInfoUpdates()
subscribeToStatus(coroutineScope)
return emptyFlow<Any>()
}
private fun subscribeOnTangemPayInfoUpdates(): Flow<*> {
return tangemPayMainScreenCustomerInfoUseCase(userWalletId = userWallet.walletId)
private fun subscribeToStatus(coroutineScope: CoroutineScope) {
paymentAccountStatusSupplier.invoke(userWalletId = userWallet.walletId)
.map { it.value }
.distinctUntilChanged()
.onEach { mainInfoData ->
val userWalletId = userWallet.walletId
mainInfoData.onLeft { tangemPayError ->
when (tangemPayError) {
TangemPayCustomerInfoError.RefreshNeededError -> {
stateController.update(
transformer = TangemPayRefreshNeededStateTransformer(
userWalletId = userWalletId,
userWallet = userWallet,
onRefreshClick = { clickIntents.onRefreshPayToken(userWallet) },
),
)
}
TangemPayCustomerInfoError.UnavailableError -> {
stateController.update(
transformer = TangemPayUnavailableStateTransformer(userWalletId),
)
}
TangemPayCustomerInfoError.ExposedDeviceError -> {
stateController.update(TangemPayExposedDeviceTransformer(userWalletId))
}
TangemPayCustomerInfoError.DeactivatedError -> {
stateController.update(
transformer = TangemPayHiddenStateTransformer(userWalletId),
)
}
TangemPayCustomerInfoError.UnknownError -> {
// hide TangemPay block
TangemLogger.e("Failed when loading main screen TangemPay info: $tangemPayError")
stateController.update(
transformer = TangemPayHiddenStateTransformer(userWalletId),
)
}
}
}.onRight { contentState -> handleContentState(state = contentState) }
}
}
private suspend fun handleContentState(state: MainCustomerInfoContentState) {
val userWalletId = userWallet.walletId
when (state) {
MainCustomerInfoContentState.Loading -> stateController.update(
transformer = TangemPayLoadingStateTransformer(userWalletId),
)
is MainCustomerInfoContentState.Content -> {
updateTangemPay(data = state.info, userWalletId = userWalletId)
analytics.send(customerInfo = state.info)
}
is MainCustomerInfoContentState.OnboardingBanner -> stateController.update(
transformer = TangemPayOnboardingBannerStateTransformer(
userWalletId = userWalletId,
onClick = clickIntents::onOnboardingBannerClick,
closeOnClick = clickIntents::onOnboardingBannerCloseClick,
),
)
is MainCustomerInfoContentState.Empty -> stateController.update(
transformer = TangemPayHiddenStateTransformer(userWalletId),
)
}
}
private suspend fun updateTangemPay(data: MainScreenCustomerInfo, userWalletId: UserWalletId) {
val cardFrozenState =
data.info.productInstance?.cardId?.let { cardDetailsRepository.cardFrozenStateSync(it) }
?: TangemPayCardFrozenState.Unfrozen
stateController.update(
transformer = TangemPayUpdateInfoStateTransformer(
userWalletId = userWalletId,
value = data,
cardFrozenState = cardFrozenState,
tangemPayClickIntents = clickIntents,
),
)
.onEach(analytics::send)
.launchIn(coroutineScope)
}
@AssistedFactory

View file

@ -82,7 +82,6 @@ import com.tangem.feature.wallet.presentation.wallet.ui.components.common.*
import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency.nftCollections
import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency.organizeTokensButton
import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.marketPriceBlock
import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.TangemPayMainScreenBlock
import com.tangem.feature.wallet.presentation.wallet.ui.utils.changeWalletAnimator
import com.tangem.features.tangempay.component.TangemPayMainBlockComponent
import com.tangem.features.tangempay.entity.TangemPayMainUM
@ -752,14 +751,8 @@ internal fun LazyListScope.tangemPayItem(
) {
if (state !is WalletState.MultiCurrency) return
if (state.isTangemPayRefactorEnabled) {
with(tangemPayComponent) {
tangemPayMainContent(modifier = modifier, state = state.tangemPayMainUM, isBalanceHidden = isHidingMode)
}
} else {
item(key = "TangemPayMainScreenBlock", contentType = state.tangemPayState::class.java) {
TangemPayMainScreenBlock(modifier = modifier, state = state.tangemPayState, isBalanceHidden = isHidingMode)
}
with(tangemPayComponent) {
tangemPayMainContent(modifier = modifier, state = state.tangemPayMainUM, isBalanceHidden = isHidingMode)
}
}

View file

@ -1,103 +0,0 @@
package com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency
import android.content.res.Configuration
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.common.ui.R
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.orMaskWithStars
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.TangemPayMainScreenBlock
@Composable
internal fun TangemPayCardMainBlock(
state: TangemPayState.Card,
isBalanceHidden: Boolean,
modifier: Modifier = Modifier,
) {
Surface(
modifier = modifier.fillMaxWidth(),
shape = TangemTheme.shapes.roundedCornersXMedium,
color = TangemTheme.colors.background.primary,
onClick = state.onClick,
) {
Row(
modifier = Modifier
.fillMaxWidth()
.height(IntrinsicSize.Min)
.padding(horizontal = 12.dp, vertical = 16.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(12.dp),
) {
Image(
painter = painterResource(R.drawable.img_visa_36),
contentDescription = null,
modifier = Modifier.size(36.dp),
)
Column(
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(2.dp),
) {
Text(
text = stringResourceSafe(R.string.tangempay_payment_account),
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.primary1,
)
Text(
text = state.lastFourDigits.resolveReference(),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
)
}
Column(
modifier = Modifier.fillMaxHeight(),
verticalArrangement = Arrangement.spacedBy(2.dp),
horizontalAlignment = Alignment.End,
) {
Text(
text = state.balanceText.resolveReference().orMaskWithStars(isBalanceHidden),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.End,
)
Text(
text = state.balanceSymbol.resolveReference(),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
textAlign = TextAlign.End,
)
}
}
}
}
@Preview(showBackground = true)
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun TangemPayCardMainBlockPreview() {
TangemThemePreview {
TangemPayMainScreenBlock(
TangemPayState.Card(
lastFourDigits = TextReference.Str("*1234"),
balanceText = TextReference.Str("$ 0.00"),
balanceSymbol = TextReference.Str("USDC"),
onClick = {},
),
isBalanceHidden = false,
)
}
}

View file

@ -1,41 +0,0 @@
package com.tangem.feature.wallet.presentation.wallet.ui.components.visa
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import com.tangem.core.ui.R
import com.tangem.core.ui.components.block.BlockCard
import com.tangem.core.ui.components.inputrow.InputRowImageBase
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.res.TangemTheme
private const val DISABLED_ALPHA = 0.6F
@Composable
internal fun TangemPayExposedDeviceState(modifier: Modifier = Modifier) {
BlockCard(
modifier = modifier
.clip(RoundedCornerShape(size = TangemTheme.dimens.radius14))
.background(TangemTheme.colors.background.primary)
.alpha(DISABLED_ALPHA),
enabled = false,
onClick = {},
) {
InputRowImageBase(
modifier = Modifier
.padding(
all = TangemTheme.dimens.spacing12,
),
subtitle = resourceReference(R.string.tangempay_payment_account),
caption = resourceReference(R.string.tangem_pay_rooted_device_subtitle),
subtitleColor = TangemTheme.colors.text.primary1,
captionColor = TangemTheme.colors.text.tertiary,
iconResWebp = R.drawable.img_visa_36,
endIconTint = TangemTheme.colors.icon.warning,
)
}
}

View file

@ -1,36 +0,0 @@
package com.tangem.feature.wallet.presentation.wallet.ui.components.visa
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import com.tangem.core.ui.components.block.BlockCard
import com.tangem.core.ui.components.inputrow.InputRowImageBase
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
@Composable
internal fun TangemPayFailedIssueState(state: TangemPayState.FailedIssue, modifier: Modifier = Modifier) {
BlockCard(
modifier = modifier
.clip(RoundedCornerShape(size = TangemTheme.dimens.radius14))
.background(TangemTheme.colors.background.primary),
onClick = state.onButtonClick,
) {
InputRowImageBase(
modifier = Modifier
.padding(
all = TangemTheme.dimens.spacing12,
),
subtitle = state.title,
caption = state.description,
subtitleColor = TangemTheme.colors.text.primary1,
captionColor = TangemTheme.colors.text.tertiary,
iconResWebp = com.tangem.core.ui.R.drawable.img_visa_36,
iconEndRes = state.iconRes,
endIconTint = TangemTheme.colors.icon.warning,
)
}
}

View file

@ -1,37 +0,0 @@
package com.tangem.feature.wallet.presentation.wallet.ui.components.visa
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.CircleShimmer
import com.tangem.core.ui.components.RectangleShimmer
import com.tangem.core.ui.components.SpacerWMax
import com.tangem.core.ui.res.TangemTheme
@Composable
internal fun TangemPayLoadingScreenBlock(modifier: Modifier = Modifier) {
Row(
modifier = modifier
.fillMaxWidth()
.background(color = TangemTheme.colors.background.primary, shape = TangemTheme.shapes.roundedCornersXMedium)
.padding(horizontal = 12.dp, vertical = 16.dp),
verticalAlignment = Alignment.CenterVertically,
) {
CircleShimmer(modifier = Modifier.size(36.dp))
Column(
modifier = Modifier.padding(start = 12.dp),
verticalArrangement = Arrangement.spacedBy(2.dp),
) {
RectangleShimmer(modifier = Modifier.padding(vertical = 4.dp).sizeIn(minWidth = 70.dp, minHeight = 12.dp))
RectangleShimmer(modifier = Modifier.padding(vertical = 2.dp).sizeIn(minWidth = 52.dp, minHeight = 12.dp))
}
SpacerWMax()
Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
RectangleShimmer(modifier = Modifier.padding(vertical = 4.dp).sizeIn(minWidth = 40.dp, minHeight = 12.dp))
RectangleShimmer(modifier = Modifier.padding(vertical = 2.dp).sizeIn(minWidth = 40.dp, minHeight = 12.dp))
}
}
}

View file

@ -1,106 +0,0 @@
package com.tangem.feature.wallet.presentation.wallet.ui.components.visa
import android.content.res.Configuration
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.common.ui.R
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
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.state.model.WalletNotification
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification.Warning.TangemPayRefreshNeeded
import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.TangemPayCardMainBlock
@Composable
internal fun TangemPayMainScreenBlock(state: TangemPayState, isBalanceHidden: Boolean, modifier: Modifier = Modifier) {
when (state) {
is Progress -> TangemPayProgressState(state, modifier)
is TangemPayState.Card -> TangemPayCardMainBlock(state, isBalanceHidden, modifier)
is TangemPayState.Empty -> Unit
is TangemPayState.RefreshNeeded -> TangemPayRefreshBlock(state, modifier)
is TangemPayState.TemporaryUnavailable -> TangemPayUnavailableBlock(state, modifier)
is TangemPayState.FailedIssue -> TangemPayFailedIssueState(state, modifier)
is TangemPayState.OnboardingBanner -> TangemPayOnboardingBanner(state, modifier)
is TangemPayState.ExposedDevice -> TangemPayExposedDeviceState(modifier)
is TangemPayState.Loading -> TangemPayLoadingScreenBlock(modifier)
}
}
@Preview(showBackground = true)
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun TangemPayMainScreenBlockPreview() {
TangemThemePreview {
Column(verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) {
TangemPayMainScreenBlock(state = TangemPayState.Loading, isBalanceHidden = false)
TangemPayMainScreenBlock(
state = TangemPayState.RefreshNeeded(
TangemPayRefreshNeeded(
buttonText = resourceReference(id = R.string.home_button_scan),
onRefreshClick = {},
shouldShowProgress = false,
),
),
isBalanceHidden = false,
)
TangemPayMainScreenBlock(
state = TangemPayState.TemporaryUnavailable(WalletNotification.Warning.TangemPayUnreachable),
isBalanceHidden = false,
)
TangemPayMainScreenBlock(
state = TangemPayState.OnboardingBanner(onClick = {}, closeOnClick = {}),
isBalanceHidden = false,
)
TangemPayMainScreenBlock(state = TangemPayState.ExposedDevice, isBalanceHidden = false)
TangemPayMainScreenBlock(
state = TangemPayState.FailedIssue(
title = TextReference.Res(R.string.tangempay_payment_account),
description = TextReference.Res(R.string.tangempay_failed_to_issue_card),
iconRes = R.drawable.ic_alert_24,
onButtonClick = { },
),
isBalanceHidden = false,
)
TangemPayMainScreenBlock(
Progress(
title = TextReference.Res(R.string.tangempay_payment_account),
description = TextReference.Res(R.string.tangempay_kyc_in_progress),
buttonText = TextReference.Res(R.string.tangempay_kyc_in_progress_notification_button),
iconRes = R.drawable.ic_promo_kyc_36,
onButtonClick = {},
),
isBalanceHidden = false,
)
TangemPayMainScreenBlock(
Progress(
title = TextReference.Res(R.string.tangempay_payment_account),
description = TextReference.Res(R.string.tangempay_issuing_your_card),
buttonText = TextReference.EMPTY,
iconRes = R.drawable.ic_tangem_pay_promo_card_36,
onButtonClick = {},
showProgress = true,
),
isBalanceHidden = false,
)
TangemPayMainScreenBlock(
TangemPayState.Card(
lastFourDigits = TextReference.Str("*1234"),
balanceText = TextReference.Str("$ 0.00"),
balanceSymbol = TextReference.Str("USDC"),
onClick = {},
),
isBalanceHidden = false,
)
}
}
}

View file

@ -1,120 +0,0 @@
package com.tangem.feature.wallet.presentation.wallet.ui.components.visa
import android.content.res.Configuration
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.Dimension
import com.tangem.core.ui.components.SpacerH
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
private const val GRADIENT_START_COLOR = 0xFF252934
private const val GRADIENT_END_COLOR = 0xFF12141E
private const val GRADIENT_OFFSET_X = 0f
private const val GRADIENT_OFFSET_Y = 80F
private const val GRADIENT_RADIUS = 200F
@Composable
internal fun TangemPayOnboardingBanner(state: TangemPayState.OnboardingBanner, modifier: Modifier = Modifier) {
Box(
modifier = modifier
.fillMaxWidth()
.clip(TangemTheme.shapes.roundedCornersXMedium)
.background(
brush = Brush.radialGradient(
colors = listOf(Color(GRADIENT_START_COLOR), Color(GRADIENT_END_COLOR)),
center = Offset(GRADIENT_OFFSET_X, GRADIENT_OFFSET_Y),
radius = GRADIENT_RADIUS,
),
)
.clickable(onClick = state.onClick),
) {
ConstraintLayout(
modifier = Modifier.fillMaxWidth(),
) {
val (image, text, close) = createRefs()
Image(
painter = painterResource(R.drawable.ic_close_24),
contentDescription = null,
modifier = Modifier
.size(16.dp)
.clickable(onClick = state.closeOnClick)
.constrainAs(close) {
top.linkTo(parent.top, margin = 16.dp)
end.linkTo(parent.end, margin = 16.dp)
},
colorFilter = ColorFilter.tint(TangemTheme.colors.icon.inactive),
)
Column(
modifier = Modifier
.constrainAs(text) {
top.linkTo(parent.top)
start.linkTo(image.end, margin = 12.dp)
end.linkTo(close.start, margin = 12.dp)
width = Dimension.fillToConstraints
}
.padding(top = 16.dp, bottom = 16.dp),
) {
Text(
text = stringResourceSafe(R.string.tangempay_onboarding_banner_title),
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.constantWhite,
)
SpacerH(6.dp)
Text(
text = stringResourceSafe(R.string.tangempay_onboarding_banner_description),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
)
}
Image(
painter = painterResource(R.drawable.img_tangem_pay_onboarding_banner),
contentDescription = null,
modifier = Modifier
.padding(top = 8.dp, start = 24.dp)
.constrainAs(image) {
start.linkTo(parent.start)
top.linkTo(text.top)
bottom.linkTo(text.bottom)
height = Dimension.fillToConstraints
},
)
}
}
}
@Preview(showBackground = true)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun PreviewTangemOnboardingBanner() {
TangemThemePreview {
TangemPayOnboardingBanner(
TangemPayState.OnboardingBanner(
onClick = {},
closeOnClick = {},
),
)
}
}

View file

@ -1,35 +0,0 @@
package com.tangem.feature.wallet.presentation.wallet.ui.components.visa
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import com.tangem.core.ui.R
import com.tangem.core.ui.components.block.BlockCard
import com.tangem.core.ui.components.inputrow.InputRowImageBase
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
@Composable
internal fun TangemPayProgressState(state: TangemPayState.Progress, modifier: Modifier = Modifier) {
BlockCard(
modifier = modifier
.clip(RoundedCornerShape(size = TangemTheme.dimens.radius14))
.background(TangemTheme.colors.background.primary),
onClick = state.onButtonClick,
) {
InputRowImageBase(
modifier = Modifier
.padding(
all = TangemTheme.dimens.spacing12,
),
subtitle = state.title,
caption = state.description,
subtitleColor = TangemTheme.colors.text.primary1,
captionColor = TangemTheme.colors.text.tertiary,
iconResWebp = R.drawable.img_visa_36,
)
}
}

View file

@ -1,79 +0,0 @@
package com.tangem.feature.wallet.presentation.wallet.ui.components.visa
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.R
import com.tangem.core.ui.components.SpacerH12
import com.tangem.core.ui.components.block.BlockCard
import com.tangem.core.ui.components.inputrow.InputRowImageBase
import com.tangem.core.ui.components.notifications.Notification
import com.tangem.core.ui.extensions.resourceReference
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.WalletNotification
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification.Warning.TangemPayRefreshNeeded
@Composable
internal fun TangemPayRefreshBlock(state: TangemPayState.RefreshNeeded, modifier: Modifier = Modifier) {
Column(modifier) {
Notification(
config = state.notification.config,
iconTint = when (state.notification) {
is WalletNotification.Critical -> TangemTheme.colors.icon.warning
is WalletNotification.Informational -> TangemTheme.colors.icon.accent
is WalletNotification.RateApp -> TangemTheme.colors.icon.attention
is WalletNotification.UnlockWallets -> TangemTheme.colors.icon.primary1
is WalletNotification.UsedOutdatedData -> TangemTheme.colors.text.attention
else -> null
},
)
SpacerH12()
BlockCard(
modifier = Modifier
.clip(RoundedCornerShape(size = TangemTheme.dimens.radius14))
.background(TangemTheme.colors.background.primary),
enabled = false,
) {
InputRowImageBase(
modifier = Modifier.padding(
all = TangemTheme.dimens.spacing12,
),
subtitle = resourceReference(R.string.tangempay_payment_account),
caption = resourceReference(R.string.tangempay_payment_account_sync_needed),
subtitleColor = TangemTheme.colors.text.tertiary,
captionColor = TangemTheme.colors.text.tertiary,
iconResWebp = R.drawable.img_visa_36,
)
}
}
}
@Preview(showBackground = true)
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun TangemPayRefreshBlockPreview() {
TangemThemePreview {
Column(verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) {
TangemPayRefreshBlock(
state = TangemPayState.RefreshNeeded(
TangemPayRefreshNeeded(
buttonText = resourceReference(id = R.string.tangempay_sync_needed_restore_access),
onRefreshClick = {},
shouldShowProgress = true,
),
),
modifier = Modifier,
)
}
}
}

View file

@ -1,76 +0,0 @@
package com.tangem.feature.wallet.presentation.wallet.ui.components.visa
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.R
import com.tangem.core.ui.components.SpacerH12
import com.tangem.core.ui.components.block.BlockCard
import com.tangem.core.ui.components.inputrow.InputRowImageBase
import com.tangem.core.ui.components.notifications.Notification
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
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.WalletNotification
import com.tangem.utils.StringsSigns.DASH_SIGN
@Composable
internal fun TangemPayUnavailableBlock(state: TangemPayState.TemporaryUnavailable, modifier: Modifier = Modifier) {
Column(modifier) {
Notification(
config = state.notification.config,
iconTint = when (state.notification) {
is WalletNotification.Critical -> TangemTheme.colors.icon.warning
is WalletNotification.Informational -> TangemTheme.colors.icon.accent
is WalletNotification.RateApp -> TangemTheme.colors.icon.attention
is WalletNotification.UnlockWallets -> TangemTheme.colors.icon.primary1
is WalletNotification.UsedOutdatedData -> TangemTheme.colors.text.attention
else -> null
},
)
SpacerH12()
BlockCard(
modifier = Modifier
.clip(RoundedCornerShape(size = TangemTheme.dimens.radius14))
.background(TangemTheme.colors.background.primary),
enabled = false,
) {
InputRowImageBase(
modifier = Modifier.padding(
all = TangemTheme.dimens.spacing12,
),
subtitle = resourceReference(R.string.tangempay_payment_account),
caption = TextReference.Str(DASH_SIGN),
subtitleColor = TangemTheme.colors.text.tertiary,
captionColor = TangemTheme.colors.text.tertiary,
iconResWebp = R.drawable.img_visa_36,
)
}
}
}
@Preview(showBackground = true)
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun TangemPayUnavailableBlockPreview() {
TangemThemePreview {
Column(verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) {
TangemPayUnavailableBlock(
state = TangemPayState.TemporaryUnavailable(
WalletNotification.Warning.TangemPayUnreachable,
),
modifier = Modifier,
)
}
}
}