Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-24 12:05:48 +05:00
parent f3c2d98214
commit f192c85914
59 changed files with 1679 additions and 432 deletions

View file

@ -184,7 +184,7 @@ internal class PortfolioSelectorModel @Inject constructor(
val account = accountStatus.account
val accountBalance = when (accountStatus) {
is AccountStatus.CryptoPortfolio -> accountStatus.tokenList.totalFiatBalance
is AccountStatus.Payment -> accountStatus.totalFiatBalance
is AccountStatus.Payment -> accountStatus.value.totalFiatBalance
}
val accountItemUM = AccountPortfolioItemUMConverter(
onClick = { selectorController.selectAccount(account.accountId) },

View file

@ -19,6 +19,7 @@ import com.tangem.domain.exchange.RampStateManager
import com.tangem.domain.models.TotalFiatBalance
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.account.filterCryptoPortfolio
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
import com.tangem.domain.settings.usercountry.models.UserCountry
@ -105,7 +106,7 @@ internal class OnrampTokenListModel @Inject constructor(
updateTokenListUM(
SetLoadingAccountTokenListTransformer(
appCurrency = appCurrency,
accountList = accountList.accountStatuses.toList(),
accountList = accountList.accountStatuses.filterCryptoPortfolio().toList(),
isAccountsMode = isAccountsMode,
),
)
@ -237,6 +238,7 @@ internal class OnrampTokenListModel @Inject constructor(
private fun AccountStatusList.filterAccountsByQuery(
query: String,
): Map<Account.CryptoPortfolio, List<CryptoCurrencyStatus>> = accountStatuses.asSequence()
.filterCryptoPortfolio()
.associate { accountStatus ->
when (accountStatus) {
is AccountStatus.CryptoPortfolio -> {

View file

@ -31,6 +31,7 @@ import com.tangem.domain.exchange.RampStateManager
import com.tangem.domain.express.models.ExpressOperationType
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.account.filterCryptoPortfolio
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.network.Network
@ -131,7 +132,7 @@ internal class SwapInteractorImpl @AssistedInject constructor(
private suspend fun getAccountCurrencyTokensDataState(currency: CryptoCurrency): TokensDataStateExpress {
val walletAccountCurrencyStatuses = singleAccountStatusListSupplier.getSyncOrNull(
SingleAccountStatusListProducer.Params(userWalletId),
)?.accountStatuses.orEmpty()
)?.accountStatuses.orEmpty().filterCryptoPortfolio()
val walletAccountCurrencyStatusesExceptInitial: Map<Account, List<CryptoCurrencyStatus>> =
walletAccountCurrencyStatuses.mapNotNull { accountStatus ->

View file

@ -15,4 +15,6 @@ dependencies {
/** Compose */
implementation(deps.compose.runtime)
implementation(deps.compose.foundation)
implementation(deps.compose.ui)
}

View file

@ -0,0 +1,19 @@
package com.tangem.features.tangempay.component
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.runtime.Stable
import androidx.compose.ui.Modifier
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.features.tangempay.entity.TangemPayMainUM
@Stable
interface TangemPayMainBlockComponent {
fun LazyListScope.tangemPayMainContent(
state: TangemPayMainUM,
isBalanceHidden: Boolean,
modifier: Modifier = Modifier,
)
interface Factory : ComponentFactory<Unit, TangemPayMainBlockComponent>
}

View file

@ -0,0 +1,26 @@
package com.tangem.features.tangempay.entity
import androidx.compose.runtime.Immutable
import com.tangem.core.ui.extensions.TextReference
@Immutable
sealed class TangemPayMainUM {
data object Empty : TangemPayMainUM()
data object Loading : TangemPayMainUM()
data class UnderReview(val subtitle: TextReference, val onClick: () -> Unit) : TangemPayMainUM()
data class IssuingCard(val onClick: () -> Unit) : TangemPayMainUM()
data class FailedToIssue(val onClick: () -> Unit) : TangemPayMainUM()
data class Content(
val subtitle: TextReference,
val isBalanceFlickering: Boolean,
val balance: TextReference,
val balanceSubtitle: TextReference,
val onClick: () -> Unit,
val shouldShowOnlyCacheWarning: Boolean,
) : TangemPayMainUM()
data object TemporaryUnavailable : TangemPayMainUM()
data object SyncNeeded : TangemPayMainUM()
data object ExposedDevice : TangemPayMainUM()
}

View file

@ -15,10 +15,9 @@ dependencies {
/** Core */
implementation(projects.core.decompose)
implementation(projects.core.ui)
implementation(projects.core.configToggles)
/** Features api */
implementation(projects.features.tangempay.details.api)
implementation(projects.features.tangempay.main.api)
/** Compose */
implementation(deps.compose.foundation)

View file

@ -0,0 +1,37 @@
package com.tangem.features.tangempay.component
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.ui.Modifier
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.features.tangempay.entity.TangemPayMainUM
import com.tangem.features.tangempay.ui.TangemPayMainBlockItem
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
private const val TANGEM_PAY_ACCOUNT_CONTENT_TYPE = "TangemPayAccount"
@Suppress("UnusedPrivateProperty")
internal class DefaultTangemPayMainBlockComponent @AssistedInject constructor(
@Assisted context: AppComponentContext,
@Assisted params: Unit,
) : TangemPayMainBlockComponent, AppComponentContext by context {
override fun LazyListScope.tangemPayMainContent(
state: TangemPayMainUM,
isBalanceHidden: Boolean,
modifier: Modifier,
) {
item(
key = TANGEM_PAY_ACCOUNT_CONTENT_TYPE,
contentType = TANGEM_PAY_ACCOUNT_CONTENT_TYPE,
) {
TangemPayMainBlockItem(state, isBalanceHidden, modifier)
}
}
@AssistedFactory
interface Factory : TangemPayMainBlockComponent.Factory {
override fun create(context: AppComponentContext, params: Unit): DefaultTangemPayMainBlockComponent
}
}

View file

@ -0,0 +1,17 @@
package com.tangem.features.tangempay.di
import com.tangem.features.tangempay.component.DefaultTangemPayMainBlockComponent
import com.tangem.features.tangempay.component.TangemPayMainBlockComponent
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
@Module
@InstallIn(SingletonComponent::class)
internal interface TangemPayMainModule {
@Binds
fun bindTangemPayMainBlockComponent(
factory: DefaultTangemPayMainBlockComponent.Factory,
): TangemPayMainBlockComponent.Factory
}

View file

@ -0,0 +1,355 @@
package com.tangem.features.tangempay.ui
import android.content.res.Configuration
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
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.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
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.components.block.BlockCard
import com.tangem.core.ui.components.inputrow.InputRowImageBase
import com.tangem.core.ui.components.text.applyBladeBrush
import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.tangempay.entity.TangemPayMainUM
import com.tangem.features.tangempay.main.impl.R
import com.tangem.utils.StringsSigns.DASH_SIGN
private const val DISABLED_ALPHA = 0.6F
@Composable
internal fun TangemPayMainBlockItem(state: TangemPayMainUM, isBalanceHidden: Boolean, modifier: Modifier = Modifier) {
when (state) {
is TangemPayMainUM.Empty -> Unit
is TangemPayMainUM.Loading -> TangemPayMainLoadingItem(modifier)
is TangemPayMainUM.UnderReview -> TangemPayMainUnderReviewItem(state, modifier)
is TangemPayMainUM.IssuingCard -> TangemPayMainIssuingCardItem(state, modifier)
is TangemPayMainUM.FailedToIssue -> TangemPayMainFailedIssueItem(state, modifier)
is TangemPayMainUM.Content -> TangemPayMainBlockContent(state, isBalanceHidden, modifier)
is TangemPayMainUM.TemporaryUnavailable -> TangemPayMainTempUnavailableItem(modifier)
is TangemPayMainUM.SyncNeeded -> TangemPayMainSyncNeededItem(modifier)
is TangemPayMainUM.ExposedDevice -> TangemPayMainExposedDeviceItem(modifier)
}
}
@Composable
private fun TangemPayMainBlockContent(
state: TangemPayMainUM.Content,
isBalanceHidden: Boolean,
modifier: Modifier = Modifier,
) {
Surface(
modifier = modifier,
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.subtitle.resolveReference(),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
)
}
Column(
modifier = Modifier.fillMaxHeight(),
verticalArrangement = Arrangement.spacedBy(2.dp),
horizontalAlignment = Alignment.End,
) {
TangemPayFiatAmount(
text = state.balance.resolveReference(),
isBalanceFlickering = state.isBalanceFlickering,
isBalanceFromCache = state.shouldShowOnlyCacheWarning,
isBalanceHidden = isBalanceHidden,
)
Text(
text = state.balanceSubtitle.resolveReference(),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
textAlign = TextAlign.End,
)
}
}
}
}
@Composable
private fun TangemPayFiatAmount(
text: String,
isBalanceFlickering: Boolean,
isBalanceFromCache: Boolean,
isBalanceHidden: Boolean,
modifier: Modifier = Modifier,
) {
Row(
modifier = modifier,
verticalAlignment = Alignment.CenterVertically,
) {
AnimatedVisibility(isBalanceFromCache) {
Row(
modifier = Modifier.padding(horizontal = 4.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
Icon(
modifier = Modifier.size(12.dp),
painter = painterResource(R.drawable.ic_error_sync_24),
tint = TangemTheme.colors.icon.inactive,
contentDescription = null,
)
}
}
Text(
text = text.orMaskWithStars(isBalanceHidden),
style = TangemTheme.typography.body2.applyBladeBrush(
isEnabled = isBalanceFlickering,
textColor = TangemTheme.colors.text.primary1,
),
textAlign = TextAlign.End,
)
}
}
@Composable
private fun TangemPayMainUnderReviewItem(state: TangemPayMainUM.UnderReview, modifier: Modifier = Modifier) {
BlockCard(
modifier = modifier
.clip(RoundedCornerShape(size = TangemTheme.dimens.radius14))
.background(TangemTheme.colors.background.primary),
onClick = state.onClick,
) {
InputRowImageBase(
modifier = Modifier
.padding(
all = TangemTheme.dimens.spacing12,
),
subtitle = resourceReference(R.string.tangempay_payment_account),
caption = state.subtitle,
subtitleColor = TangemTheme.colors.text.primary1,
captionColor = TangemTheme.colors.text.tertiary,
iconResWebp = R.drawable.img_visa_36,
)
}
}
@Composable
private fun TangemPayMainTempUnavailableItem(modifier: Modifier = Modifier) {
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,
)
}
}
@Composable
private fun TangemPayMainIssuingCardItem(state: TangemPayMainUM.IssuingCard, modifier: Modifier = Modifier) {
BlockCard(
modifier = modifier
.clip(RoundedCornerShape(size = TangemTheme.dimens.radius14))
.background(TangemTheme.colors.background.primary),
onClick = state.onClick,
) {
InputRowImageBase(
modifier = Modifier
.padding(all = TangemTheme.dimens.spacing12),
subtitle = resourceReference(R.string.tangempay_payment_account),
caption = resourceReference(R.string.tangempay_issuing_your_card),
subtitleColor = TangemTheme.colors.text.primary1,
captionColor = TangemTheme.colors.text.tertiary,
iconResWebp = R.drawable.img_visa_36,
)
}
}
@Composable
private fun TangemPayMainFailedIssueItem(state: TangemPayMainUM.FailedToIssue, modifier: Modifier = Modifier) {
BlockCard(
modifier = modifier
.clip(RoundedCornerShape(size = TangemTheme.dimens.radius14))
.background(TangemTheme.colors.background.primary),
onClick = state.onClick,
) {
InputRowImageBase(
modifier = Modifier
.padding(
all = TangemTheme.dimens.spacing12,
),
subtitle = TextReference.Res(R.string.tangempay_payment_account),
caption = TextReference.Res(R.string.tangempay_failed_to_issue_card),
subtitleColor = TangemTheme.colors.text.primary1,
captionColor = TangemTheme.colors.text.tertiary,
iconResWebp = com.tangem.core.ui.R.drawable.img_visa_36,
iconEndRes = R.drawable.ic_alert_24,
endIconTint = TangemTheme.colors.icon.warning,
)
}
}
@Composable
private fun TangemPayMainSyncNeededItem(modifier: Modifier = Modifier) {
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,
)
}
}
@Composable
private fun TangemPayMainExposedDeviceItem(modifier: Modifier = Modifier) {
BlockCard(
modifier = modifier
.clip(RoundedCornerShape(size = TangemTheme.dimens.radius14))
.background(TangemTheme.colors.background.primary)
.alpha(DISABLED_ALPHA),
enabled = false,
) {
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,
)
}
}
@Composable
private fun TangemPayMainLoadingItem(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),
)
}
}
}
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview
@Composable
private fun TangemPayMainItemsPreview(
@PreviewParameter(TangemPayMainUMPreviewParameterProvider::class)
state: TangemPayMainUM,
) {
TangemThemePreview {
TangemPayMainBlockItem(state = state, isBalanceHidden = false)
}
}
private class TangemPayMainUMPreviewParameterProvider : CollectionPreviewParameterProvider<TangemPayMainUM>(
collection = listOf(
TangemPayMainUM.Loading,
TangemPayMainUM.SyncNeeded,
TangemPayMainUM.TemporaryUnavailable,
TangemPayMainUM.ExposedDevice,
TangemPayMainUM.FailedToIssue(onClick = {}),
TangemPayMainUM.UnderReview(subtitle = resourceReference(R.string.tangempay_kyc_in_progress), onClick = {}),
TangemPayMainUM.IssuingCard(onClick = {}),
TangemPayMainUM.Content(
subtitle = TextReference.Str("*1234"),
isBalanceFlickering = true,
balance = TextReference.Str("$ 101.56"),
balanceSubtitle = TextReference.Str("USDC"),
onClick = {},
shouldShowOnlyCacheWarning = true,
),
),
)

View file

@ -148,6 +148,7 @@ dependencies {
implementation(projects.features.tangempay.details.api)
implementation(projects.features.feed.api)
implementation(projects.features.promoBanners.api)
implementation(projects.features.tangempay.main.api)
/** Common modules */
implementation(projects.common)

View file

@ -37,6 +37,7 @@ import com.tangem.features.biometry.AskBiometryComponent
import com.tangem.features.feed.entry.components.FeedEntryComponent
import com.tangem.features.pushnotifications.api.PushNotificationsBottomSheetComponent
import com.tangem.features.pushnotifications.api.PushNotificationsParams
import com.tangem.features.tangempay.component.TangemPayMainBlockComponent
import com.tangem.features.send.v2.api.NetworkSelectionComponent
import com.tangem.features.tokenreceive.TokenReceiveComponent
import com.tangem.features.yield.supply.api.YieldSupplyDepositedWarningComponent
@ -51,6 +52,7 @@ internal class WalletComponent @AssistedInject constructor(
@Assisted appComponentContext: AppComponentContext,
@Assisted navigate: (WalletRoute) -> Unit,
feedEntryComponentFactory: FeedEntryComponent.Factory,
tangemPayMainBlockComponentFactory: TangemPayMainBlockComponent.Factory,
private val renameWalletComponentFactory: RenameWalletComponent.Factory,
private val askBiometryComponentFactory: AskBiometryComponent.Factory,
private val pushNotificationsBottomSheetComponent: PushNotificationsBottomSheetComponent.Factory,
@ -70,6 +72,12 @@ internal class WalletComponent @AssistedInject constructor(
entryRoute = null,
)
}
private val tangemPayMainBlockComponent by lazy {
tangemPayMainBlockComponentFactory.create(
context = child("tangemPayMainBlockComponent"),
params = Unit,
)
}
private val promoBannersBlockComponent: PromoBannersBlockComponent? by lazy {
if (!newPromoBannersFeatureToggles.isNewPromoBannersEnabled) return@lazy null
@ -218,10 +226,11 @@ internal class WalletComponent @AssistedInject constructor(
val bottomSheetState = remember { mutableStateOf(BottomSheetState.COLLAPSED) }
var headerSize by remember { mutableStateOf(0.dp) }
val dialog by dialog.subscribeAsState()
val uiState by model.uiState.collectAsStateWithLifecycle()
if (designFeatureToggles.isRedesignEnabled) {
WalletScreen2(
state = model.uiState.collectAsStateWithLifecycle().value,
state = uiState,
bottomSheetContent = {
BottomSheetContent(
bottomSheetState = bottomSheetState,
@ -234,8 +243,9 @@ internal class WalletComponent @AssistedInject constructor(
)
} else {
WalletScreen(
state = model.uiState.collectAsStateWithLifecycle().value,
state = uiState,
promoBannersBlockComponent = promoBannersBlockComponent,
tangemPayComponent = tangemPayMainBlockComponent,
bottomSheetContent = {
BottomSheetContent(
bottomSheetState = bottomSheetState,

View file

@ -6,13 +6,13 @@ import com.arkivanov.decompose.router.slot.activate
import com.arkivanov.decompose.router.slot.dismiss
import com.tangem.common.routing.AppRoute
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.ui.utils.parseBigDecimal
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
import com.tangem.core.analytics.utils.TrackingContextProxy
import com.tangem.core.decompose.di.ModelScoped
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.usecase.IsAccountsModeEnabledUseCase
@ -25,20 +25,20 @@ import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.models.wallet.*
import com.tangem.domain.notifications.GetIsHuaweiDeviceWithoutGoogleServicesUseCase
import com.tangem.domain.notifications.repository.NotificationsRepository
import com.tangem.domain.qrscanning.models.QrResultSource
import com.tangem.domain.qrscanning.models.SourceType
import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase
import com.tangem.domain.qrscanning.models.ClassifiedQrContent
import com.tangem.domain.qrscanning.models.QrSendTarget
import com.tangem.domain.walletconnect.WcPairService
import com.tangem.domain.walletconnect.model.WcPairRequest
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
import com.tangem.domain.qrscanning.models.SourceType
import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase
import com.tangem.domain.qrscanning.usecases.ResolveQrSendTargetsUseCase
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.wallets.usecase.*
import com.tangem.domain.wallets.usecase.GetWalletIconUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyUpdateUseCase
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
@ -61,6 +61,7 @@ import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.KycRejec
import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider
import com.tangem.features.biometry.AskBiometryComponent
import com.tangem.features.pushnotifications.api.PushNotificationsModelCallbacks
import com.tangem.features.tangempay.TangemPayFeatureToggles
import com.tangem.features.wallet.deeplink.WalletDeepLinkActionListener
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
import com.tangem.utils.Provider
@ -119,6 +120,8 @@ internal class WalletModel @Inject constructor(
private val listenToQrScanningUseCase: ListenToQrScanningUseCase,
private val wcPairService: WcPairService,
private val resolveQrSendTargetsUseCase: ResolveQrSendTargetsUseCase,
private val paymentAccountStatusFetcher: PaymentAccountStatusFetcher,
private val tangemPayFeatureToggles: TangemPayFeatureToggles,
private val uiMessageSender: UiMessageSender,
val screenLifecycleProvider: ScreenLifecycleProvider,
val innerWalletRouter: InnerWalletRouter,
@ -427,14 +430,17 @@ internal class WalletModel @Inject constructor(
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)
}
@ -543,6 +549,7 @@ internal class WalletModel @Inject constructor(
walletImageResolver = walletImageResolver,
isMainScreenQrScanningEnabled = walletFeatureToggles.isMainScreenQrScanningEnabled,
getWalletIconUseCase = getWalletIconUseCase,
isTangemPayRefactorEnabled = tangemPayFeatureToggles.isTangemPayAccountsRefactorEnabled,
),
)
@ -589,6 +596,7 @@ internal class WalletModel @Inject constructor(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isTangemPayRefactorEnabled = tangemPayFeatureToggles.isTangemPayAccountsRefactorEnabled,
),
)
}
@ -610,6 +618,7 @@ internal class WalletModel @Inject constructor(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isTangemPayRefactorEnabled = tangemPayFeatureToggles.isTangemPayAccountsRefactorEnabled,
),
)
}
@ -624,6 +633,7 @@ internal class WalletModel @Inject constructor(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isTangemPayRefactorEnabled = tangemPayFeatureToggles.isTangemPayAccountsRefactorEnabled,
),
)
@ -685,6 +695,7 @@ internal class WalletModel @Inject constructor(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isTangemPayRefactorEnabled = tangemPayFeatureToggles.isTangemPayAccountsRefactorEnabled,
),
)

View file

@ -23,6 +23,7 @@ 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.repository.OnboardingRepository
import com.tangem.domain.pay.usecase.ProduceTangemPayInitialDataUseCase
import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase
@ -30,7 +31,6 @@ 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
import com.tangem.feature.wallet.presentation.wallet.state.transformers.TangemPayHideOnboardingStateTransformer
import com.tangem.feature.wallet.presentation.wallet.state.transformers.TangemPayRefreshNeededStateTransformer
import com.tangem.feature.wallet.presentation.wallet.state.transformers.TangemPayRefreshShowProgressTransformer
import kotlinx.coroutines.launch
import javax.inject.Inject
@ -77,6 +77,7 @@ internal class TangemPayClickIntentsImplementor @Inject constructor(
private val tangemPayEligibilityManager: TangemPayEligibilityManager,
private val uiMessageSender: UiMessageSender,
private val analyticsEventHandler: AnalyticsEventHandler,
private val paymentAccountStatusFetcher: PaymentAccountStatusFetcher,
) : BaseWalletClickIntents(), TangemPayIntents {
override suspend fun onPullToRefresh() {
@ -85,20 +86,28 @@ internal class TangemPayClickIntentsImplementor @Inject constructor(
return
}
tangemPayMainScreenCustomerInfoUseCase.fetch(userWalletId)
paymentAccountStatusFetcher.invoke(PaymentAccountStatusFetcher.Params(userWalletId))
}
override fun onRefreshPayToken(userWallet: UserWallet) {
stateHolder.update(TangemPayRefreshShowProgressTransformer(userWallet.walletId))
stateHolder.update(
TangemPayRefreshShowProgressTransformer(
userWalletId = userWallet.walletId,
shouldShowProgress = true,
),
)
modelScope.launch {
produceInitialDataTangemPay.invoke(userWallet.walletId)
.onRight { tangemPayMainScreenCustomerInfoUseCase.fetch(userWallet.walletId) }
.onRight {
tangemPayMainScreenCustomerInfoUseCase.fetch(userWallet.walletId)
paymentAccountStatusFetcher.invoke(PaymentAccountStatusFetcher.Params(userWallet.walletId))
}
.onLeft {
stateHolder.update(
transformer = TangemPayRefreshNeededStateTransformer(
userWallet = userWallet,
TangemPayRefreshShowProgressTransformer(
userWalletId = userWallet.walletId,
onRefreshClick = { onRefreshPayToken(userWallet) },
shouldShowProgress = false,
),
)
}
@ -267,7 +276,10 @@ internal class TangemPayClickIntentsImplementor @Inject constructor(
analyticsEventHandler.send(TangemPayAnalyticsEvents.KycCancelled())
modelScope.launch {
tangemPayOnboardingRepository.disableTangemPay(userWalletId)
.onRight { tangemPayMainScreenCustomerInfoUseCase.fetch(userWalletId) }
.onRight {
tangemPayMainScreenCustomerInfoUseCase.fetch(userWalletId)
paymentAccountStatusFetcher.invoke(PaymentAccountStatusFetcher.Params(userWalletId))
}
.onLeft { uiMessageSender.send(ToastMessage(resourceReference(R.string.common_something_went_wrong))) }
}
}

View file

@ -18,6 +18,7 @@ import com.tangem.feature.wallet.child.wallet.model.WalletActivationBannerType
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.common.WalletPreviewDataLegacy.topBarConfig
import com.tangem.feature.wallet.presentation.wallet.state.model.*
import com.tangem.features.tangempay.entity.TangemPayMainUM
import com.tangem.utils.StringsSigns.DASH_SIGN
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList
@ -217,6 +218,8 @@ internal object WalletScreenPreviewDataLegacy {
onClick = {},
),
type = WalletType.Cold,
tangemPayMainUM = TangemPayMainUM.Empty,
isTangemPayRefactorEnabled = false,
)
}

View file

@ -123,6 +123,7 @@ internal class WalletWarningsAnalyticsSender @Inject constructor(
is WalletNotification.Warning.TangemPayUnreachable -> null
is WalletNotification.UpgradeHotWalletPromo -> null
is WalletNotification.TokenSyncCompleted -> null
is WalletNotification.CreateTangemPayAccount -> null
}
}

View file

@ -7,10 +7,10 @@ import com.tangem.common.ui.userwallet.ext.walletInterationIcon
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.ui.components.notifications.NotificationConfig.ButtonsState
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.account.models.AccountStatusList
import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
import com.tangem.domain.card.CardTypesResolver
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.core.lce.Lce
import com.tangem.domain.demo.IsDemoCardUseCase
import com.tangem.domain.hotwallet.CheckHotWalletUpgradeBannerUseCase
import com.tangem.domain.hotwallet.GetAccessCodeSkippedUseCase
@ -18,6 +18,8 @@ import com.tangem.domain.hotwallet.GetUpgradeBannerClosureTimestampUseCase
import com.tangem.domain.hotwallet.ShouldShowUpgradeHotWalletBannerUseCase
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.TotalFiatBalance
import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.account.PaymentAccountStatusValue
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
@ -25,7 +27,6 @@ import com.tangem.domain.notifications.repository.NotificationsRepository
import com.tangem.domain.promo.ShouldShowPromoWalletUseCase
import com.tangem.domain.promo.models.PromoId
import com.tangem.domain.settings.IsReadyToShowRateAppUseCase
import com.tangem.domain.tokens.error.TokenListError
import com.tangem.domain.wallets.models.SeedPhraseNotificationsStatus
import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase
import com.tangem.domain.wallets.usecase.SeedPhraseNotificationUseCase
@ -45,7 +46,6 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import javax.inject.Inject
@Deprecated("Remove with main toggle [DesignFeatureToggles.isRedesignEnabled]")
@ -69,13 +69,8 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
@Suppress("UNCHECKED_CAST", "MagicNumber", "LongMethod", "CastNullableToNonNullableType")
fun create(userWallet: UserWallet, clickIntents: WalletClickIntents): Flow<ImmutableList<WalletNotification>> {
val cardTypesResolver = (userWallet as? UserWallet.Cold)?.scanResponse?.cardTypesResolver
val accountStatusListFlow by lazy {
val params = SingleAccountStatusListProducer.Params(userWallet.walletId)
accountDependencies.singleAccountStatusListSupplier(params)
.map { it.totalFiatBalance to it.flattenCurrencies() }
.map { Lce.Content(it) }
}
val params = SingleAccountStatusListProducer.Params(userWallet.walletId)
val accountStatusListFlow = accountDependencies.singleAccountStatusListSupplier(params)
return combine(
accountStatusListFlow,
@ -95,9 +90,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
.distinctUntilChanged(),
) { array -> array }
.map { array ->
val lceTokens = array[0] as Lce<TokenListError, Pair<TotalFiatBalance, List<CryptoCurrencyStatus>>>
val totalFiatBalance = lceTokens.map { it.first }
val flattenCurrencies = lceTokens.map { it.second }
val accountStatusList = array[0] as AccountStatusList
val isReadyToShowRating = array[1] as Boolean
val isNeedToBackup = array[2] as Boolean
val seedPhraseIssueStatus = array[3] as SeedPhraseNotificationsStatus
@ -108,8 +101,13 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
val shouldShowUpgradeBanner = array[8] as Boolean
val closureTimestamp = array[9] as? Long
val flattenCurrencies = accountStatusList.flattenCurrencies()
val paymentAccountStatus = accountStatusList.accountStatuses
.filterIsInstance<AccountStatus.Payment>()
.firstOrNull()
buildList {
addUsedOutdatedDataNotification(totalFiatBalance)
addUsedOutdatedDataNotification(accountStatusList.totalFiatBalance)
addCriticalNotifications(userWallet, seedPhraseIssueStatus, clickIntents)
@ -162,24 +160,54 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
if (!hasCriticalOrWarning) {
addRateTheAppNotification(isReadyToShowRating, clickIntents)
}
// add as last warning
paymentAccountStatus?.let { paymentAccountStatus ->
addTangemPayWarnings(
status = paymentAccountStatus,
userWallet = userWallet,
walletClickIntents = clickIntents,
)
}
}.toImmutableList()
}
}
private fun MutableList<WalletNotification>.addUsedOutdatedDataNotification(
totalFiatBalance: Lce<TokenListError, TotalFiatBalance>,
private fun MutableList<WalletNotification>.addTangemPayWarnings(
status: AccountStatus.Payment,
userWallet: UserWallet,
walletClickIntents: WalletClickIntents,
) {
val notification = when (status.value) {
is PaymentAccountStatusValue.Error.NotSynced -> WalletNotification.Warning.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 = { walletClickIntents.onRefreshPayToken(userWallet) },
shouldShowProgress = false,
)
is PaymentAccountStatusValue.NotCreated -> WalletNotification.CreateTangemPayAccount(
onClick = { walletClickIntents.onOnboardingBannerClick(userWallet.walletId) },
onCloseClick = { walletClickIntents.onOnboardingBannerCloseClick(userWallet.walletId) },
)
is PaymentAccountStatusValue.Error.Unavailable -> WalletNotification.Warning.TangemPayUnreachable
is PaymentAccountStatusValue.Error.CardIssueFailed,
is PaymentAccountStatusValue.Error.ExposedDevice,
is PaymentAccountStatusValue.IssuingCard,
is PaymentAccountStatusValue.Loaded,
is PaymentAccountStatusValue.Loading,
is PaymentAccountStatusValue.Locked,
is PaymentAccountStatusValue.UnderReview,
-> null
}
notification?.let(::add)
}
private fun MutableList<WalletNotification>.addUsedOutdatedDataNotification(totalFiatBalance: TotalFiatBalance) {
addIf(
element = WalletNotification.UsedOutdatedData,
condition = totalFiatBalance.fold(
ifLoading = {
(it as? TotalFiatBalance.Loaded)?.source == StatusSource.ONLY_CACHE
},
ifContent = {
(it as? TotalFiatBalance.Loaded)?.source == StatusSource.ONLY_CACHE
},
ifError = { false },
),
condition = (totalFiatBalance as? TotalFiatBalance.Loaded)?.source == StatusSource.ONLY_CACHE,
)
}
@ -254,7 +282,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
private fun MutableList<WalletNotification>.addInformationalNotifications(
userWallet: UserWallet,
cardTypesResolver: CardTypesResolver?,
flattenCurrencies: Lce<TokenListError, List<CryptoCurrencyStatus>>,
flattenCurrencies: List<CryptoCurrencyStatus>,
clickIntents: WalletClickIntents,
) {
addIf(
@ -267,7 +295,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
private fun MutableList<WalletNotification>.addMissingAddressesNotification(
userWallet: UserWallet,
flattenCurrencies: Lce<TokenListError, List<CryptoCurrencyStatus>>,
flattenCurrencies: List<CryptoCurrencyStatus>,
clickIntents: WalletClickIntents,
) {
val currencies = flattenCurrencies.getMissingAddressCurrencies()
@ -285,10 +313,8 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
)
}
private fun Lce<TokenListError, List<CryptoCurrencyStatus>>.getMissingAddressCurrencies(): List<CryptoCurrency> {
val flattenCurrencies = getOrNull(isPartialContentAccepted = true) ?: return emptyList()
return flattenCurrencies
private fun List<CryptoCurrencyStatus>.getMissingAddressCurrencies(): List<CryptoCurrency> {
return this
.filter { it.value is CryptoCurrencyStatus.MissedDerivation }
.map(CryptoCurrencyStatus::currency)
}
@ -330,7 +356,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
private fun MutableList<WalletNotification>.addWarningNotifications(
cardTypesResolver: CardTypesResolver?,
flattenCurrencies: Lce<TokenListError, List<CryptoCurrencyStatus>>,
flattenCurrencies: List<CryptoCurrencyStatus>,
isNeedToBackup: Boolean,
clickIntents: WalletClickIntents,
) {
@ -355,7 +381,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
}
private fun MutableList<WalletNotification>.addCloreMigrationNotification(
flattenCurrencies: Lce<TokenListError, List<CryptoCurrencyStatus>>,
flattenCurrencies: List<CryptoCurrencyStatus>,
clickIntents: WalletClickIntents,
) {
val cloreCurrency = flattenCurrencies.findCloreCurrency() ?: return
@ -367,10 +393,8 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
)
}
private fun Lce<TokenListError, List<CryptoCurrencyStatus>>.findCloreCurrency(): CryptoCurrencyStatus? {
val currencies = getOrNull(isPartialContentAccepted = true) ?: return null
return currencies.find { currencyStatus ->
private fun List<CryptoCurrencyStatus>.findCloreCurrency(): CryptoCurrencyStatus? {
return this.find { currencyStatus ->
BlockchainUtils.isClore(currencyStatus.currency.network.rawId)
}
}
@ -388,10 +412,8 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
)
}
private fun Lce<TokenListError, List<CryptoCurrencyStatus>>.hasUnreachableNetworks(): Boolean {
val flattenCurrencies = getOrNull(isPartialContentAccepted = false) ?: return false
return flattenCurrencies.any { it.value is CryptoCurrencyStatus.Unreachable }
private fun List<CryptoCurrencyStatus>.hasUnreachableNetworks(): Boolean {
return this.any { it.value is CryptoCurrencyStatus.Unreachable }
}
// Remove in first iteration of yield supply feature
@ -427,7 +449,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
private fun MutableList<WalletNotification>.addFinishWalletActivationNotification(
userWallet: UserWallet,
flattenCurrencies: Lce<TokenListError, List<CryptoCurrencyStatus>>,
flattenCurrencies: List<CryptoCurrencyStatus>,
clickIntents: WalletClickIntents,
shouldAccessCodeSkipped: Boolean,
) {
@ -437,12 +459,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
val isAccessCodeRequired = userWallet.hotWalletId.authType == HotWalletId.AuthType.NoPassword &&
!shouldAccessCodeSkipped
val shouldShowFinishActivation = !isBackupExists || isAccessCodeRequired
val type = flattenCurrencies.fold(
ifLoading = { return },
ifContent = { it.getFinishWalletActivationType() },
ifError = { WalletActivationBannerType.Attention },
)
val type = flattenCurrencies.getFinishWalletActivationType()
addIf(
element = WalletNotification.FinishWalletActivation(
@ -465,15 +482,14 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
private suspend fun MutableList<WalletNotification>.addUpgradeHotWalletPromoNotification(
userWallet: UserWallet,
flattenCurrencies: Lce<TokenListError, List<CryptoCurrencyStatus>>,
flattenCurrencies: List<CryptoCurrencyStatus>,
clickIntents: WalletClickIntents,
shouldShowUpgradeBanner: Boolean,
closureTimestamp: Long?,
) {
if (userWallet !is UserWallet.Hot) return
val currencies = flattenCurrencies.getOrNull(isPartialContentAccepted = true).orEmpty()
val hasBalance = currencies.any { it.value.amount.orZero().isPositive() }
val hasBalance = flattenCurrencies.any { it.value.amount.orZero().isPositive() }
val shouldShow = checkHotWalletUpgradeBannerUseCase(
walletId = userWallet.walletId,

View file

@ -151,7 +151,6 @@ sealed class WalletNotification(val config: NotificationConfig) {
)
data class TangemPayRefreshNeeded(
@DrawableRes private val tangemIcon: Int?,
private val onRefreshClick: () -> Unit,
private val buttonText: TextReference,
private val shouldShowProgress: Boolean,
@ -160,7 +159,7 @@ sealed class WalletNotification(val config: NotificationConfig) {
subtitle = resourceReference(id = R.string.tangempay_use_tangem_device_to_restore_payment_account),
buttonsState = ButtonsState.PrimaryButtonConfig(
text = buttonText,
iconResId = tangemIcon,
iconResId = R.drawable.ic_tangem_24,
onClick = onRefreshClick,
shouldShowProgress = shouldShowProgress,
),
@ -480,4 +479,11 @@ sealed class WalletNotification(val config: NotificationConfig) {
),
),
)
data class CreateTangemPayAccount(val onClick: () -> Unit, val onCloseClick: () -> Unit) : WalletNotification(
config = NotificationConfig(
subtitle = resourceReference(R.string.tangempay_onboarding_banner_description),
iconResId = R.drawable.img_tangem_pay_visa_banner,
),
)
}

View file

@ -10,6 +10,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.holder.LockedTx
import com.tangem.feature.wallet.presentation.wallet.state.model.holder.LockedWalletStateHolder
import com.tangem.feature.wallet.presentation.wallet.state.model.holder.TxHistoryStateHolder
import com.tangem.feature.wallet.presentation.wallet.state.model.holder.WalletStateHolder
import com.tangem.features.tangempay.entity.TangemPayMainUM
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.PersistentList
@ -24,6 +25,8 @@ internal sealed interface WalletState : WalletStateHolder {
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
data class Content(
override val pullToRefreshConfig: PullToRefreshConfig,
@ -35,6 +38,8 @@ internal sealed interface WalletState : WalletStateHolder {
override val nftState: WalletNFTItemUM,
override val type: WalletType,
override val tangemPayState: TangemPayState,
override val tangemPayMainUM: TangemPayMainUM,
override val isTangemPayRefactorEnabled: Boolean,
) : MultiCurrency()
data class Locked(
@ -54,6 +59,8 @@ 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
}
}

View file

@ -13,6 +13,7 @@ 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 {
@ -20,6 +21,7 @@ internal class AddWalletTransformer(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isTangemPayRefactorEnabled = isTangemPayRefactorEnabled,
)
}

View file

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

View file

@ -24,6 +24,7 @@ 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 {
@ -31,6 +32,7 @@ internal class ReinitializeNewWalletTransformer(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isTangemPayRefactorEnabled = isTangemPayRefactorEnabled,
)
}

View file

@ -20,6 +20,7 @@ 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 {
@ -27,6 +28,7 @@ internal class ReinitializeWalletTransformer(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isTangemPayRefactorEnabled = isTangemPayRefactorEnabled,
)
}

View file

@ -1,6 +1,7 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.staking.model.StakingAvailability
@ -8,10 +9,12 @@ import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.state.model.*
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.MultiWalletBalanceUMTransformer
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.MultiWalletCardStateConverter
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.TangemPayMainBlockConverter
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.TokenListStateConverter
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.WalletTokensListUMConverter
import com.tangem.feature.wallet.presentation.wallet.state.utils.enableButtons
import com.tangem.utils.logging.TangemLogger
import com.tangem.features.tangempay.entity.TangemPayMainUM
import java.math.BigDecimal
internal class SetTokenListTransformer(
@ -25,12 +28,17 @@ internal class SetTokenListTransformer(
private val isAccountsModeEnabled: Boolean,
) : WalletStateTransformer(userWallet.walletId) {
private val tangemPayConverter by lazy {
TangemPayMainBlockConverter(tangemPayClickIntents = clickIntents)
}
override fun transform(prevState: WalletState): WalletState {
return when (prevState) {
is WalletState.MultiCurrency.Content -> {
prevState.copy(
walletCardState = prevState.walletCardState.toLoadedState(),
tokensListState = prevState.tokensListState.toLoadedState(),
tangemPayMainUM = prevState.tangemPayMainUM.toLoadedState(),
buttons = prevState.enableButtons(),
)
}
@ -97,6 +105,17 @@ internal class SetTokenListTransformer(
).convert(value = this)
}
private fun TangemPayMainUM.toLoadedState(): TangemPayMainUM {
val paymentAccountStatus = when (params) {
is TokenConverterParams.Account -> params.accountList.accountStatuses
.filterIsInstance<AccountStatus.Payment>()
.firstOrNull()
is TokenConverterParams.Wallet -> null
} ?: return this
return tangemPayConverter.convert(paymentAccountStatus)
}
private fun toLoadedState(): WalletTokensListUM {
if (params !is TokenConverterParams.Account) {
return WalletTokensListUM.Empty(

View file

@ -18,7 +18,6 @@ internal class TangemPayRefreshNeededStateTransformer(
override fun transform(prevState: WalletState): WalletState {
val tangemPayState = TangemPayState.RefreshNeeded(
notification = TangemPayRefreshNeeded(
tangemIcon = R.drawable.ic_tangem_24,
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)

View file

@ -5,9 +5,11 @@ 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
import kotlinx.collections.immutable.toImmutableList
internal class TangemPayRefreshShowProgressTransformer(
userWalletId: UserWalletId,
private val shouldShowProgress: Boolean,
) : WalletStateTransformer(userWalletId) {
override fun transform(prevState: WalletState): WalletState {
@ -15,11 +17,19 @@ internal class TangemPayRefreshShowProgressTransformer(
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)
} else {
warning
}
}
return multiContentState.copy(
tangemPayState = refreshNeededState.copy(
notification = refreshNotification.copy(shouldShowProgress = true),
notification = refreshNotification.copy(shouldShowProgress = shouldShowProgress),
),
warnings = newWarnings.toImmutableList(),
)
}

View file

@ -18,6 +18,7 @@ internal class UnlockWalletTransformer(
private val clickIntents: WalletClickIntents,
private val walletImageResolver: WalletImageResolver,
private val getWalletIconUseCase: GetWalletIconUseCase,
private val isTangemPayRefactorEnabled: Boolean,
) : WalletScreenStateTransformer {
private val walletLoadingStateFactory by lazy {
@ -25,6 +26,7 @@ internal class UnlockWalletTransformer(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isTangemPayRefactorEnabled = isTangemPayRefactorEnabled,
)
}

View file

@ -0,0 +1,110 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter
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.StatusSource
import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.account.PaymentAccountStatusValue
import com.tangem.domain.models.kyc.KycStatus
import com.tangem.domain.pay.TangemPayDetailsConfig
import com.tangem.domain.visa.model.TangemPayCardFrozenState
import com.tangem.feature.wallet.child.wallet.model.intents.TangemPayIntents
import com.tangem.features.tangempay.entity.TangemPayMainUM
import com.tangem.utils.converter.Converter
import java.math.BigDecimal
import java.util.Currency
private const val POLYGON_CHAIN_ID = 137
internal class TangemPayMainBlockConverter(
private val tangemPayClickIntents: TangemPayIntents,
) : Converter<AccountStatus.Payment, TangemPayMainUM> {
@Suppress("LongMethod", "CyclomaticComplexMethod")
override fun convert(value: AccountStatus.Payment): TangemPayMainUM {
return when (val statusValue = value.value) {
is PaymentAccountStatusValue.Error.CardIssueFailed -> TangemPayMainUM.FailedToIssue(
onClick = { tangemPayClickIntents.onIssuingFailedClicked(statusValue.customerId) },
)
is PaymentAccountStatusValue.Error.ExposedDevice -> TangemPayMainUM.ExposedDevice
is PaymentAccountStatusValue.Error.NotSynced -> TangemPayMainUM.SyncNeeded
is PaymentAccountStatusValue.Error.Unavailable -> TangemPayMainUM.TemporaryUnavailable
is PaymentAccountStatusValue.IssuingCard -> TangemPayMainUM.IssuingCard(
onClick = { tangemPayClickIntents.onIssuingCardClicked() },
)
is PaymentAccountStatusValue.UnderReview -> TangemPayMainUM.UnderReview(
subtitle = when (statusValue.kycStatus) {
KycStatus.REJECTED -> TextReference.Res(R.string.tangempay_kyc_has_failed)
else -> TextReference.Res(R.string.tangempay_kyc_in_progress)
},
onClick = {
when (statusValue.kycStatus) {
KycStatus.REJECTED -> tangemPayClickIntents.onKycRejectedClicked(
userWalletId = value.account.userWalletId,
customerId = statusValue.customerId,
)
else -> tangemPayClickIntents.onKycProgressClicked(value.account.userWalletId)
}
},
)
is PaymentAccountStatusValue.NotCreated -> TangemPayMainUM.Empty
is PaymentAccountStatusValue.Loading -> TangemPayMainUM.Loading
is PaymentAccountStatusValue.Locked -> TangemPayMainUM.Content(
subtitle = stringReference("*${statusValue.lastFourDigits}"),
isBalanceFlickering = statusValue.source == StatusSource.CACHE,
balance = getBalanceText(
currencyCode = statusValue.currencyCode,
balance = statusValue.fiatBalance.availableBalance,
),
balanceSubtitle = stringReference("USDC"), // TODO hardcode for now
shouldShowOnlyCacheWarning = statusValue.source == StatusSource.ONLY_CACHE,
onClick = {
tangemPayClickIntents.openDetails(
value.account.userWalletId,
TangemPayDetailsConfig(
customerId = statusValue.customerId,
cardId = statusValue.cardId,
isPinSet = statusValue.isPinSet,
cardFrozenState = TangemPayCardFrozenState.Frozen,
cardNumberEnd = statusValue.lastFourDigits,
chainId = POLYGON_CHAIN_ID,
),
)
},
)
is PaymentAccountStatusValue.Loaded -> TangemPayMainUM.Content(
subtitle = stringReference("*${statusValue.lastFourDigits}"),
isBalanceFlickering = statusValue.source == StatusSource.CACHE,
balance = getBalanceText(
currencyCode = statusValue.currencyCode,
balance = statusValue.fiatBalance.availableBalance,
),
balanceSubtitle = stringReference("USDC"), // TODO hardcode for now
shouldShowOnlyCacheWarning = statusValue.source == StatusSource.ONLY_CACHE,
onClick = {
tangemPayClickIntents.openDetails(
value.account.userWalletId,
TangemPayDetailsConfig(
customerId = statusValue.customerId,
cardId = statusValue.cardId,
isPinSet = statusValue.isPinSet,
cardFrozenState = TangemPayCardFrozenState.Unfrozen,
cardNumberEnd = statusValue.lastFourDigits,
chainId = POLYGON_CHAIN_ID,
),
)
},
)
}
}
private fun getBalanceText(currencyCode: String, balance: BigDecimal): TextReference {
val currency = Currency.getInstance(currencyCode)
val formattedBalance = balance.format {
fiat(fiatCurrencyCode = currency.currencyCode, fiatCurrencySymbol = currency.symbol)
}
return stringReference(formattedBalance)
}
}

View file

@ -17,6 +17,7 @@ import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory
import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver
import com.tangem.feature.wallet.presentation.wallet.state.model.*
import com.tangem.features.tangempay.entity.TangemPayMainUM
import com.tangem.utils.extensions.addIf
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.PersistentList
@ -33,6 +34,7 @@ internal class WalletLoadingStateFactory(
private val clickIntents: WalletClickIntents,
private val walletImageResolver: WalletImageResolver,
private val getWalletIconUseCase: GetWalletIconUseCase,
private val isTangemPayRefactorEnabled: Boolean,
) {
fun create(userWallet: UserWallet): WalletState {
@ -82,6 +84,8 @@ internal class WalletLoadingStateFactory(
nftState = WalletNFTItemUM.Hidden,
type = WalletType.Hot,
tangemPayState = TangemPayState.Empty,
tangemPayMainUM = TangemPayMainUM.Empty,
isTangemPayRefactorEnabled = isTangemPayRefactorEnabled,
)
}
@ -96,6 +100,8 @@ internal class WalletLoadingStateFactory(
nftState = WalletNFTItemUM.Hidden,
type = WalletType.Cold,
tangemPayState = TangemPayState.Empty,
tangemPayMainUM = TangemPayMainUM.Empty,
isTangemPayRefactorEnabled = isTangemPayRefactorEnabled,
)
}

View file

@ -49,7 +49,6 @@ import com.tangem.common.ui.bottomsheet.chooseaddress.ChooseAddressBottomSheetCo
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheet
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
import com.tangem.common.ui.expressStatus.expressTransactionsItems
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.components.atoms.handComposableComponentHeight
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheetDraggableHeaderLegacy
@ -60,6 +59,7 @@ import com.tangem.core.ui.components.sheetscaffold.*
import com.tangem.core.ui.components.snackbar.CopiedTextSnackbar
import com.tangem.core.ui.components.snackbar.TangemSnackbar
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.event.StateEvent
import com.tangem.core.ui.extensions.softLayerShadow
import com.tangem.core.ui.extensions.stringResourceSafe
@ -84,6 +84,8 @@ import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency
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
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@ -92,6 +94,7 @@ import kotlin.math.roundToInt
@Composable
internal fun WalletScreen(
state: WalletScreenState,
tangemPayComponent: TangemPayMainBlockComponent,
promoBannersBlockComponent: ComposableContentComponent? = null,
bottomSheetContent: @Composable (() -> Unit),
bottomSheetHeaderHeightProvider: () -> Dp,
@ -106,6 +109,7 @@ internal fun WalletScreen(
WalletContent(
state = state,
tangemPayComponent = tangemPayComponent,
walletsListState = walletsListState,
snackbarHostState = snackbarHostState,
isAutoScroll = isAutoScroll,
@ -128,6 +132,7 @@ internal fun WalletScreen(
@Composable
private fun WalletContent(
state: WalletScreenState,
tangemPayComponent: TangemPayMainBlockComponent,
walletsListState: LazyListState,
snackbarHostState: SnackbarHostState,
isAutoScroll: State<Boolean>,
@ -220,18 +225,12 @@ private fun WalletContent(
}
}
if (selectedWallet is WalletState.MultiCurrency) {
item(
key = "TangemPayMainScreenBlock",
contentType = selectedWallet.tangemPayState::class.java,
) {
TangemPayMainScreenBlock(
state = selectedWallet.tangemPayState,
isBalanceHidden = state.isHidingMode,
modifier = itemModifier,
)
}
}
tangemPayItem(
modifier = itemModifier,
state = selectedWallet,
isHidingMode = state.isHidingMode,
tangemPayComponent = tangemPayComponent,
)
(selectedWallet as? WalletState.SingleCurrency)?.let { walletState ->
walletState.marketPriceBlockState?.let { marketPriceBlockState ->
@ -749,6 +748,25 @@ internal fun LazyListScope.nftCollections(state: WalletState, itemModifier: Modi
}
}
internal fun LazyListScope.tangemPayItem(
state: WalletState,
isHidingMode: Boolean,
tangemPayComponent: TangemPayMainBlockComponent,
modifier: Modifier = Modifier,
) {
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)
}
}
}
@Composable
private fun ShowBottomSheet(bottomSheetConfig: TangemBottomSheetConfig?) {
if (bottomSheetConfig != null) {
@ -767,6 +785,14 @@ private fun WalletScreen_Preview(@PreviewParameter(WalletScreenPreviewProvider::
TangemThemePreview {
WalletScreen(
state = data,
tangemPayComponent = object : TangemPayMainBlockComponent {
override fun LazyListScope.tangemPayMainContent(
state: TangemPayMainUM,
isBalanceHidden: Boolean,
modifier: Modifier,
) {
}
},
bottomSheetContent = {
Text("Markets Content")
},

View file

@ -3,10 +3,13 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.common
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items
import androidx.compose.ui.Modifier
import com.tangem.common.ui.notifications.CreatePaymentAccountNotification
import com.tangem.core.ui.components.notifications.NoteMigrationNotification
import com.tangem.core.ui.components.notifications.Notification
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.res.ForceDarkTheme
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
import kotlinx.collections.immutable.ImmutableList
@ -49,6 +52,16 @@ internal fun LazyListScope.notifications(configs: ImmutableList<WalletNotificati
)
}
}
is WalletNotification.CreateTangemPayAccount -> {
CreatePaymentAccountNotification(
modifier = modifier.animateItem(fadeInSpec = null, fadeOutSpec = null),
onClick = item.onClick,
onCloseClick = item.onCloseClick,
image = R.drawable.img_tangem_pay_visa_banner,
title = resourceReference(R.string.tangempay_onboarding_banner_title),
subtitle = resourceReference(R.string.tangempay_onboarding_banner_description),
)
}
else -> {
Notification(
config = item.config,

View file

@ -13,6 +13,7 @@ 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
@ -41,7 +42,6 @@ private fun TangemPayMainScreenBlockPreview() {
TangemPayMainScreenBlock(
state = TangemPayState.RefreshNeeded(
TangemPayRefreshNeeded(
tangemIcon = R.drawable.ic_tangem_24,
buttonText = resourceReference(id = R.string.home_button_scan),
onRefreshClick = {},
shouldShowProgress = false,
@ -49,13 +49,30 @@ private fun TangemPayMainScreenBlockPreview() {
),
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_kyc_in_progress_notification_title),
description = TextReference.EMPTY,
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 = {},
@ -65,19 +82,8 @@ private fun TangemPayMainScreenBlockPreview() {
TangemPayMainScreenBlock(
Progress(
title = TextReference.Res(R.string.tangempay_issue_card_notification_title),
description = TextReference.EMPTY,
buttonText = TextReference.Res(R.string.common_continue),
iconRes = R.drawable.ic_tangem_pay_promo_card_36,
onButtonClick = {},
),
isBalanceHidden = false,
)
TangemPayMainScreenBlock(
Progress(
title = TextReference.Res(R.string.tangempay_issue_card_notification_title),
description = TextReference.Res(R.string.tangempay_issue_card_notification_description),
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 = {},

View file

@ -67,7 +67,6 @@ private fun TangemPayRefreshBlockPreview() {
TangemPayRefreshBlock(
state = TangemPayState.RefreshNeeded(
TangemPayRefreshNeeded(
tangemIcon = R.drawable.ic_tangem_24,
buttonText = resourceReference(id = R.string.tangempay_sync_needed_restore_access),
onRefreshClick = {},
shouldShowProgress = true,