Updated on 2026-08-14

This commit is contained in:
Tangem 2026-04-02 10:24:41 +03:00
commit af09ce9ca4
877 changed files with 16091 additions and 6613 deletions

View file

@ -45,7 +45,6 @@ dependencies {
implementation(tangemDeps.hot.core)
implementation(tangemDeps.card.core)
implementation(tangemDeps.blockchain)
implementation(deps.timber)
implementation(deps.firebase.perf) {
exclude(group = "com.google.firebase", module = "protolite-well-known-types")
exclude(group = "com.google.protobuf", module = "protobuf-javalite")
@ -126,6 +125,7 @@ dependencies {
implementation(projects.domain.yieldSupply.models)
implementation(projects.domain.appTheme)
implementation(projects.domain.appTheme.models)
implementation(projects.domain.tokensync)
/** Feature Apis */
implementation(projects.features.details.api)
@ -149,6 +149,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

@ -0,0 +1,110 @@
package com.tangem.feature.wallet.child.tokenActions
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.DpOffset
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.ui.components.SimpleSettingsRow
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
import com.tangem.core.ui.components.getDefaultRowColors
import com.tangem.core.ui.components.getWarningRowColors
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.LocalRedesignEnabled
import com.tangem.core.ui.res.TangemTheme
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.feature.wallet.child.tokenActions.TokenActionsComponent.Params
import com.tangem.feature.wallet.presentation.wallet.ui.components.fastForEach
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.*
internal class DefaultTokenActionsComponent @AssistedInject constructor(
@Assisted appComponentContext: AppComponentContext,
@Assisted private val params: Params,
val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
) : TokenActionsComponent, AppComponentContext by appComponentContext {
val isBalanceHiddenFlow: StateFlow<Boolean>
field = MutableStateFlow(false)
init {
getBalanceHidingSettingsUseCase()
.conflate()
.distinctUntilChanged()
.onEach {
isBalanceHiddenFlow.value = it.isBalanceHidden
}
.launchIn(componentScope)
}
override fun dismiss() {
params.onDismiss()
}
@Composable
override fun BottomSheet() {
if (!LocalRedesignEnabled.current) {
TangemBottomSheet<TangemBottomSheetConfigContent.Empty>(
containerColor = TangemTheme.colors.background.primary,
config = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = ::dismiss,
content = TangemBottomSheetConfigContent.Empty,
),
) {
Column {
params.actions.fastForEach { action ->
if (action.isEnabled) {
val rowColors = if (action.isWarning) {
getWarningRowColors()
} else {
getDefaultRowColors()
}
SimpleSettingsRow(
title = action.text.resolveReference(),
icon = action.iconResId,
enabled = action.isEnabled,
rowColors = rowColors,
onItemsClick = action.onClick,
)
}
}
}
}
}
}
@Composable
override fun Content(modifier: Modifier) {
if (params.tokenRowUM == null) {
dismiss()
} else {
val isBalanceHidden by isBalanceHiddenFlow.collectAsStateWithLifecycle()
if (LocalRedesignEnabled.current) {
val offset = with(LocalDensity.current) {
DpOffset(params.offsetX.toDp(), params.offsetY.toDp())
}
TokenActionContent(
tokenRowUM = params.tokenRowUM,
isBalanceHidden = isBalanceHidden,
offset = offset,
actions = params.actions,
onDismiss = params.onDismiss,
modifier = modifier,
)
}
}
}
@AssistedFactory
interface Factory : TokenActionsComponent.Factory {
override fun create(context: AppComponentContext, params: Params): DefaultTokenActionsComponent
}
}

View file

@ -0,0 +1,210 @@
package com.tangem.feature.wallet.child.tokenActions
import android.content.res.Configuration
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEach
import com.tangem.core.ui.ds.contextmenu.CenteredContextMenuPositionProvider
import com.tangem.core.ui.ds.contextmenu.TangemContextMenu
import com.tangem.core.ui.ds.row.token.TangemTokenRow
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
import com.tangem.core.ui.ds.row.token.internal.TangemTokenRowPreviewData
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreviewRedesign
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.state.model.TokenActionButtonUM
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
@Composable
internal fun TokenActionContent(
tokenRowUM: TangemTokenRowUM,
isBalanceHidden: Boolean,
offset: DpOffset,
actions: ImmutableList<TokenActionButtonUM>,
modifier: Modifier = Modifier,
onDismiss: () -> Unit,
) {
val density = LocalDensity.current
var anchorShiftPx by remember { mutableIntStateOf(0) }
val anchorShiftDp = with(density) { anchorShiftPx.toDp() }
val animatedShift by animateDpAsState(
targetValue = anchorShiftDp,
animationSpec = tween(),
label = "AnchorShift",
)
Box(modifier.fillMaxSize()) {
Box(modifier = Modifier.offset(y = offset.y - animatedShift)) {
TangemTokenRow(
tokenRowUM = tokenRowUM,
isBalanceHidden = isBalanceHidden,
reorderableState = null,
modifier = Modifier
.padding(horizontal = TangemTheme.dimens2.x3)
.clip(RoundedCornerShape(18.dp))
.background(TangemTheme.colors2.surface.level3),
)
TangemContextMenu(
expanded = true,
onDismissRequest = onDismiss,
positionProvider = remember(density) {
CenteredContextMenuPositionProvider(
contentOffset = DpOffset(x = 0.dp, y = 12.dp),
density = density,
onAnchorShiftRequired = { shift ->
if (anchorShiftPx == 0 && shift > 0) {
anchorShiftPx = shift
}
},
)
},
) {
TokenActionContextMenuContent(
actions = actions,
onDismiss = onDismiss,
)
}
}
}
}
@Composable
private fun TokenActionContextMenuContent(actions: ImmutableList<TokenActionButtonUM>, onDismiss: () -> Unit) {
Column(
modifier = Modifier
.widthIn(min = 206.dp)
.padding(
vertical = TangemTheme.dimens2.x2_5,
horizontal = TangemTheme.dimens2.x4,
),
) {
actions.fastForEach { item ->
Column {
Row(
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2),
modifier = Modifier
.clickable(
enabled = item.isEnabled,
onClick = {
item.onClick()
onDismiss()
},
)
.padding(
start = TangemTheme.dimens2.x1_5,
end = TangemTheme.dimens2.x2,
top = TangemTheme.dimens2.x2_5,
bottom = TangemTheme.dimens2.x2_5,
),
) {
Icon(
imageVector = ImageVector.vectorResource(item.iconResId),
contentDescription = null,
tint = if (item.isWarning) {
TangemTheme.colors2.graphic.status.warning
} else {
TangemTheme.colors2.graphic.neutral.primary
},
modifier = Modifier.size(TangemTheme.dimens2.x5),
)
Text(
text = item.text.resolveReference(),
style = TangemTheme.typography2.headingRegular17,
color = if (item.isWarning) {
TangemTheme.colors2.text.status.warning
} else {
TangemTheme.colors2.text.neutral.primary
},
)
}
if (item.hasDivider) {
Spacer(
modifier = Modifier
.padding(
vertical = TangemTheme.dimens2.x2_5,
horizontal = TangemTheme.dimens2.x2,
)
.fillMaxWidth()
.height(1.dp)
.background(TangemTheme.colors2.border.neutral.primary),
)
}
}
}
}
}
// region Preview
@Composable
@Preview(showBackground = true, widthDp = 360)
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun TokenActionContent_Preview() {
TangemThemePreviewRedesign {
TokenActionContent(
tokenRowUM = TangemTokenRowPreviewData.tokenState,
offset = DpOffset(
x = 100.dp,
y = 100.dp,
),
actions = persistentListOf(
TokenActionButtonUM(
id = "Send",
text = stringReference("Send"),
iconResId = R.drawable.ic_arrow_up_24,
isEnabled = true,
isWarning = false,
hasDivider = false,
onClick = {},
),
TokenActionButtonUM(
id = "Receive",
text = stringReference("Receive"),
iconResId = R.drawable.ic_arrow_down_24,
isEnabled = true,
isWarning = false,
hasDivider = false,
onClick = {},
),
TokenActionButtonUM(
id = "Swap",
text = stringReference("Swap"),
iconResId = R.drawable.ic_exchange_vertical_24,
isEnabled = true,
isWarning = false,
hasDivider = true,
onClick = {},
),
TokenActionButtonUM(
id = "Remove",
text = stringReference("Remove"),
iconResId = R.drawable.ic_trash_24,
isEnabled = true,
isWarning = true,
hasDivider = false,
onClick = {},
),
),
isBalanceHidden = false,
onDismiss = {},
)
}
}
// endregion

View file

@ -1,64 +1,20 @@
package com.tangem.feature.wallet.child.tokenActions
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.ui.components.SimpleSettingsRow
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
import com.tangem.core.ui.components.getDefaultRowColors
import com.tangem.core.ui.components.getWarningRowColors
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
import com.tangem.feature.wallet.presentation.wallet.state.model.TokenActionButtonUM
import com.tangem.feature.wallet.presentation.wallet.ui.components.fastForEach
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
internal class TokenActionsComponent @AssistedInject constructor(
@Assisted appComponentContext: AppComponentContext,
@Assisted private val params: Params,
) : ComposableBottomSheetComponent, AppComponentContext by appComponentContext {
override fun dismiss() {
params.onDismiss()
}
@Composable
override fun BottomSheet() {
TangemBottomSheet<TangemBottomSheetConfigContent.Empty>(
containerColor = TangemTheme.colors.background.primary,
config = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = ::dismiss,
content = TangemBottomSheetConfigContent.Empty,
),
) {
Column {
params.actions.fastForEach { action ->
if (action.isEnabled) {
val rowColors = if (action.isWarning) {
getWarningRowColors()
} else {
getDefaultRowColors()
}
SimpleSettingsRow(
title = action.text.resolveReference(),
icon = action.iconResId,
enabled = action.isEnabled,
rowColors = rowColors,
onItemsClick = action.onClick,
)
}
}
}
}
}
import kotlinx.collections.immutable.ImmutableList
internal interface TokenActionsComponent : ComposableBottomSheetComponent, ComposableContentComponent {
data class Params(
val actions: List<TokenActionButtonUM>,
val actions: ImmutableList<TokenActionButtonUM>,
val tokenRowUM: TangemTokenRowUM?,
val offsetX: Float,
val offsetY: Float,
val onDismiss: () -> Unit,
)
interface Factory : ComponentFactory<Params, TokenActionsComponent>
}

View file

@ -0,0 +1,16 @@
package com.tangem.feature.wallet.child.tokenActions.di
import com.tangem.feature.wallet.child.tokenActions.DefaultTokenActionsComponent
import com.tangem.feature.wallet.child.tokenActions.TokenActionsComponent
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
@Module
@InstallIn(SingletonComponent::class)
internal interface TokenActionsModule {
@Binds
fun bindTokenActionsComponentFactory(impl: DefaultTokenActionsComponent.Factory): TokenActionsComponent.Factory
}

View file

@ -16,14 +16,15 @@ import com.tangem.core.decompose.context.child
import com.tangem.core.decompose.context.childByContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.DesignFeatureToggles
import com.tangem.features.promobanners.api.NewPromoBannersFeatureToggles
import com.tangem.features.promobanners.api.PromoBannersBlockComponent
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
import com.tangem.core.ui.components.haze.hazeEffectTangem
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.decompose.ComposableDialogComponent
import com.tangem.core.ui.utils.parseBigDecimal
import com.tangem.domain.tokens.model.details.TokenAction
import com.tangem.feature.wallet.child.organizetokens.OrganizeTokensComponent
import com.tangem.feature.wallet.child.tokenActions.DefaultTokenActionsComponent
import com.tangem.feature.wallet.child.tokenActions.TokenActionsComponent
import com.tangem.feature.wallet.child.wallet.model.WalletModel
import com.tangem.feature.wallet.navigation.WalletRoute
@ -32,12 +33,14 @@ import com.tangem.feature.wallet.presentation.wallet.ui.WalletScreen
import com.tangem.feature.wallet.presentation.wallet.ui.WalletScreen2
import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.KycRejectedComponent
import com.tangem.feature.walletsettings.component.RenameWalletComponent
import com.tangem.core.ui.utils.parseBigDecimal
import com.tangem.features.biometry.AskBiometryComponent
import com.tangem.features.feed.entry.components.FeedEntryComponent
import com.tangem.features.promobanners.api.NewPromoBannersFeatureToggles
import com.tangem.features.promobanners.api.PromoBannersBlockComponent
import com.tangem.features.pushnotifications.api.PushNotificationsBottomSheetComponent
import com.tangem.features.pushnotifications.api.PushNotificationsParams
import com.tangem.features.send.v2.api.NetworkSelectionComponent
import com.tangem.features.tangempay.component.TangemPayMainBlockComponent
import com.tangem.features.tokenreceive.TokenReceiveComponent
import com.tangem.features.yield.supply.api.YieldSupplyDepositedWarningComponent
import dagger.assisted.Assisted
@ -51,6 +54,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,
@ -59,6 +63,7 @@ internal class WalletComponent @AssistedInject constructor(
private val promoBannersBlockComponentFactory: PromoBannersBlockComponent.Factory,
private val newPromoBannersFeatureToggles: NewPromoBannersFeatureToggles,
private val networkSelectionComponentFactory: NetworkSelectionComponent.Factory,
private val tokenActionsComponentFactory: TokenActionsComponent.Factory,
private val designFeatureToggles: DesignFeatureToggles,
) : ComposableContentComponent, AppComponentContext by appComponentContext {
@ -70,6 +75,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
@ -156,11 +167,14 @@ internal class WalletComponent @AssistedInject constructor(
)
}
is WalletDialogConfig.TokenActionList -> {
TokenActionsComponent(
appComponentContext = childByContext(componentContext),
tokenActionsComponentFactory.create(
context = childByContext(componentContext),
params = TokenActionsComponent.Params(
actions = dialogConfig.actionList,
onDismiss = model.innerWalletRouter.dialogNavigation::dismiss,
tokenRowUM = dialogConfig.tokenRowUM,
offsetX = dialogConfig.offsetX,
offsetY = dialogConfig.offsetY,
),
)
}
@ -218,10 +232,12 @@ 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,
tangemPayComponent = tangemPayMainBlockComponent,
bottomSheetContent = {
BottomSheetContent(
bottomSheetState = bottomSheetState,
@ -234,8 +250,9 @@ internal class WalletComponent @AssistedInject constructor(
)
} else {
WalletScreen(
state = model.uiState.collectAsStateWithLifecycle().value,
state = uiState,
promoBannersBlockComponent = promoBannersBlockComponent,
tangemPayComponent = tangemPayMainBlockComponent,
bottomSheetContent = {
BottomSheetContent(
bottomSheetState = bottomSheetState,
@ -250,6 +267,13 @@ internal class WalletComponent @AssistedInject constructor(
when (val dialog = dialog.child?.instance) {
is ComposableDialogComponent -> dialog.Dialog()
is DefaultTokenActionsComponent -> {
if (designFeatureToggles.isRedesignEnabled) {
dialog.Content(Modifier.hazeEffectTangem())
} else {
dialog.BottomSheet()
}
}
is ComposableBottomSheetComponent -> dialog.BottomSheet()
else -> {}
}

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,22 @@ 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.tokensync.usecase.StartTokenSyncUseCase
import com.tangem.features.hotwallet.HotWalletFeatureToggles
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 +63,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
@ -68,7 +71,7 @@ import com.tangem.utils.coroutines.*
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.*
import timber.log.Timber
import com.tangem.utils.logging.TangemLogger
import javax.inject.Inject
private const val TANGEM_PAY_UPDATE_INTERVAL = 60_000L
@ -119,7 +122,11 @@ 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,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
private val startTokenSyncUseCase: StartTokenSyncUseCase,
val screenLifecycleProvider: ScreenLifecycleProvider,
val innerWalletRouter: InnerWalletRouter,
) : Model() {
@ -152,12 +159,13 @@ internal class WalletModel @Inject constructor(
subscribeTangemPayOnWalletState()
subscribeToMainScreenQrScanning()
enableNotificationsIfNeeded()
applyPendingTokenSyncs()
clickIntents.initialize(innerWalletRouter, modelScope)
modelScope.launch {
bindRefcodeWithWalletUseCase.retry()
.onLeft { Timber.e("Failed to bind refcode with wallets: $it") }
.onLeft { TangemLogger.e("Failed to bind refcode with wallets: $it") }
}
}
@ -308,7 +316,7 @@ internal class WalletModel @Inject constructor(
val shouldAskNotificationPermissionsViaBs = notificationsRepository.shouldAskNotificationPermissionsViaBs()
val shouldShow = notificationsRepository.shouldShowSubscribeOnNotificationsAfterUpdate()
val isHuaweiDevice = getIsHuaweiDeviceWithoutGoogleServicesUseCase()
Timber.d(
TangemLogger.d(
"push BS afterUpdate: $shouldShow," +
"isHuaweiDevice $isHuaweiDevice",
)
@ -427,14 +435,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)
}
@ -454,7 +465,7 @@ internal class WalletModel @Inject constructor(
awaitAll(
async {
refreshMultiCurrencyWalletQuotesUseCase(wallet.walletCardState.id).getOrElse {
Timber.e("Failed to refreshMultiCurrencyWalletQuotesUseCase $it")
TangemLogger.e("Failed to refreshMultiCurrencyWalletQuotesUseCase $it")
}
},
async {
@ -497,10 +508,10 @@ internal class WalletModel @Inject constructor(
}
is WalletsUpdateActionResolver.Action.ReorderWallets -> reorderWallets(action)
WalletsUpdateActionResolver.Action.EmptyWallets -> {
Timber.w("Wallets list is empty!")
TangemLogger.w("Wallets list is empty!")
}
is WalletsUpdateActionResolver.Action.Unknown -> {
Timber.w("Unable to perform action: $action")
TangemLogger.w("Unable to perform action: $action")
}
}
}
@ -543,6 +554,7 @@ internal class WalletModel @Inject constructor(
walletImageResolver = walletImageResolver,
isMainScreenQrScanningEnabled = walletFeatureToggles.isMainScreenQrScanningEnabled,
getWalletIconUseCase = getWalletIconUseCase,
isTangemPayRefactorEnabled = tangemPayFeatureToggles.isTangemPayAccountsRefactorEnabled,
),
)
@ -589,6 +601,7 @@ internal class WalletModel @Inject constructor(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isTangemPayRefactorEnabled = tangemPayFeatureToggles.isTangemPayAccountsRefactorEnabled,
),
)
}
@ -610,6 +623,7 @@ internal class WalletModel @Inject constructor(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isTangemPayRefactorEnabled = tangemPayFeatureToggles.isTangemPayAccountsRefactorEnabled,
),
)
}
@ -624,6 +638,7 @@ internal class WalletModel @Inject constructor(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isTangemPayRefactorEnabled = tangemPayFeatureToggles.isTangemPayAccountsRefactorEnabled,
),
)
@ -685,6 +700,7 @@ internal class WalletModel @Inject constructor(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isTangemPayRefactorEnabled = tangemPayFeatureToggles.isTangemPayAccountsRefactorEnabled,
),
)
@ -824,6 +840,12 @@ internal class WalletModel @Inject constructor(
}
}
private fun applyPendingTokenSyncs() {
if (hotWalletFeatureToggles.isTokenSyncEnabled) {
startTokenSyncUseCase.applyPendingSyncs()
}
}
private fun enableNotificationsIfNeeded() {
modelScope.launch {
val isUserAllowToEnableNotifications = notificationsRepository.isUserAllowToSubscribeOnPushNotifications()
@ -836,7 +858,7 @@ internal class WalletModel @Inject constructor(
setNotificationsEnabledUseCase(userWalletId, true).onRight {
notificationsRepository.setNotificationsWasEnabledAutomatically(userWalletId.stringValue)
}.onLeft {
Timber.e(it)
TangemLogger.e("Error", it)
}
}
}

View file

@ -13,7 +13,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotificat
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletType
import timber.log.Timber
import com.tangem.utils.logging.TangemLogger
import javax.inject.Inject
/**
@ -42,7 +42,7 @@ internal class WalletsUpdateActionResolver @Inject constructor(
getUpdateContentAction(currentState, wallets, selectedWallet)
}
Timber.i("Resolved action: $action")
TangemLogger.i("Resolved action: $action")
return action
}

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

@ -1,6 +1,8 @@
package com.tangem.feature.wallet.child.wallet.model.intents
import androidx.compose.ui.geometry.Offset
import arrow.core.getOrElse
import com.tangem.utils.logging.TangemLogger
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
import com.tangem.common.ui.tokens.TokenItemStateConverter.ApySource
import com.tangem.core.analytics.api.AnalyticsEventHandler
@ -8,6 +10,7 @@ import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.AccountId
@ -37,10 +40,10 @@ import com.tangem.feature.wallet.presentation.wallet.state.transformers.OpenBott
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.MultiWalletCurrencyActionsConverter
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.collections.immutable.toPersistentList
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@Suppress("TooManyFunctions")
@ -56,6 +59,13 @@ internal interface WalletContentClickIntents {
fun onTokenItemLongClick(accountId: AccountId, cryptoCurrencyStatus: CryptoCurrencyStatus)
fun onTokenItemLongClickV2(
accountId: AccountId,
cryptoCurrencyStatus: CryptoCurrencyStatus,
offset: Offset,
tokenRowUM: TangemTokenRowUM,
)
fun onApyLabelClick(accountId: AccountId, currencyStatus: CryptoCurrencyStatus, apySource: ApySource, apy: String)
fun onYieldPromoCloseClick()
@ -131,12 +141,12 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
override fun onTokenItemLongClick(accountId: AccountId, cryptoCurrencyStatus: CryptoCurrencyStatus) {
modelScope.launch(dispatchers.main) {
val userWalletId = accountId.userWalletId
val userWallet = getUserWalletUseCase(userWalletId).getOrElse {
Timber.e(
val userWallet = getUserWalletUseCase(userWalletId).getOrElse { error ->
TangemLogger.e(
"""
Unable to get user wallet
|- ID: $userWalletId
|- Exception: $it
|- Exception: $error
""".trimIndent(),
)
@ -153,6 +163,47 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
accountId = accountId,
clickIntents = currencyActionsClickIntents,
).convert(actionsState),
offset = Offset.Zero,
tokenRowUM = null,
)
}
}
}
override fun onTokenItemLongClickV2(
accountId: AccountId,
cryptoCurrencyStatus: CryptoCurrencyStatus,
offset: Offset,
tokenRowUM: TangemTokenRowUM,
) {
modelScope.launch {
val userWalletId = accountId.userWalletId
val userWallet = getUserWalletUseCase(userWalletId).getOrElse { exception ->
TangemLogger.e(
"""
Unable to get user wallet
|- ID: $userWalletId
|- Exception: $exception
""".trimIndent(),
)
return@launch
}
getCryptoCurrencyActionsUseCase(userWallet = userWallet, cryptoCurrencyStatus = cryptoCurrencyStatus)
.take(count = 1)
.collectLatest { actionsState ->
router.openTokenActionSheet(
userWallet = userWallet,
tokenActionList = MultiWalletCurrencyActionsConverter(
userWallet = userWallet,
accountId = accountId,
clickIntents = currencyActionsClickIntents,
).convert(actionsState)
.filter { it.isEnabled }
.toPersistentList(),
offset = offset,
tokenRowUM = tokenRowUM,
)
}
}
@ -287,7 +338,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
txHash = txHash,
currency = currency,
).fold(
ifLeft = { Timber.e(it.toString()) },
ifLeft = { TangemLogger.e(it.toString()) },
ifRight = { router.openUrl(url = it) },
)
}

View file

@ -1,7 +1,7 @@
package com.tangem.feature.wallet.child.wallet.model.intents
import arrow.core.getOrElse
import com.tangem.common.TangemBlogUrlBuilder
import com.tangem.utils.logging.TangemLogger
import com.tangem.common.routing.AppRoute.*
import com.tangem.common.routing.AppRouter
import com.tangem.common.ui.notifications.NotificationId
@ -11,14 +11,15 @@ import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.Basic.ButtonSupport
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.navigation.review.ReviewManager
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.domain.card.SetCardWasScannedUseCase
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.hotwallet.CloseHotWalletUpgradeBannerUseCase
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
@ -39,12 +40,12 @@ import com.tangem.domain.tokens.model.analytics.PromoAnalyticsEvent
import com.tangem.domain.tokens.model.analytics.PromoAnalyticsEvent.Program
import com.tangem.domain.tokens.model.analytics.PromoAnalyticsEvent.PromotionBannerClicked
import com.tangem.domain.tokens.model.details.NavigationAction
import com.tangem.domain.tokensync.usecase.AcknowledgeTokenSyncCompletionUseCase
import com.tangem.domain.wallets.usecase.*
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.Basic
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAlertUM
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
import com.tangem.features.pushnotifications.api.analytics.PushNotificationAnalyticEvents
@ -53,7 +54,6 @@ import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@Suppress("TooManyFunctions")
@ -85,14 +85,6 @@ internal interface WalletWarningsClickIntents {
fun onNoteMigrationButtonClick(url: String)
fun onSeedPhraseNotificationConfirm()
fun onSeedPhraseNotificationDecline()
fun onSeedPhraseSecondNotificationAccept()
fun onSeedPhraseSecondNotificationReject()
fun onAllowPermissions()
fun onDenyPermissions()
@ -104,6 +96,10 @@ internal interface WalletWarningsClickIntents {
fun onUpgradeHotWalletClick(userWalletId: UserWalletId)
fun onCloseUpgradeBannerClick(userWalletId: UserWalletId)
fun onDismissTokenSyncNotification(userWalletId: UserWalletId)
fun onTokenSyncManageClick(userWalletId: UserWalletId)
}
@Suppress("LargeClass", "LongParameterList", "TooManyFunctions")
@ -122,7 +118,6 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
private val shouldShowPromoWalletUseCase: ShouldShowPromoWalletUseCase,
private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val seedPhraseNotificationUseCase: SeedPhraseNotificationUseCase,
private val urlOpener: UrlOpener,
private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
private val multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
@ -137,6 +132,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
private val uiMessageSender: UiMessageSender,
private val reviewManager: ReviewManager,
private val closeHotWalletUpgradeBannerUseCase: CloseHotWalletUpgradeBannerUseCase,
private val acknowledgeTokenSyncCompletionUseCase: AcknowledgeTokenSyncCompletionUseCase,
) : BaseWalletClickIntents(), WalletWarningsClickIntents {
override fun onAddBackupCardClick() {
@ -187,7 +183,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
userWalletId = userWallet.walletId,
currencies = missedAddressCurrencies,
).fold(
ifLeft = { Timber.e(it, "Failed to derive public keys") },
ifLeft = { TangemLogger.e("Failed to derive public keys", it) },
ifRight = {
fetchCryptoCurrencies(userWalletId = userWallet.walletId, currencies = missedAddressCurrencies)
},
@ -360,66 +356,6 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
}
}
override fun onSeedPhraseNotificationConfirm() {
val userWallet = getSelectedUserWallet() ?: return
analyticsEventHandler.send(MainScreen.NoticeSeedPhraseSupportButtonYes())
uiMessageSender.send(
WalletAlertUM.seedPhraseConfirm {
modelScope.launch {
seedPhraseNotificationUseCase.confirm(userWalletId = userWallet.walletId)
urlOpener.openUrl(
url = TangemBlogUrlBuilder.build(post = TangemBlogUrlBuilder.Post.SeedNotify),
)
}
},
)
}
override fun onSeedPhraseNotificationDecline() {
val userWallet = getSelectedUserWallet() ?: return
analyticsEventHandler.send(MainScreen.NoticeSeedPhraseSupportButtonNo())
uiMessageSender.send(
WalletAlertUM.seedPhraseDismiss {
modelScope.launch {
seedPhraseNotificationUseCase.decline(userWalletId = userWallet.walletId)
}
},
)
}
override fun onSeedPhraseSecondNotificationAccept() {
val userWallet = getSelectedUserWallet() ?: return
analyticsEventHandler.send(MainScreen.NoticeSeedPhraseSupportButtonUsed())
uiMessageSender.send(
WalletAlertUM.seedPhraseConfirm {
modelScope.launch {
seedPhraseNotificationUseCase.acceptSecond(userWalletId = userWallet.walletId)
urlOpener.openUrl(
url = TangemBlogUrlBuilder.build(post = TangemBlogUrlBuilder.Post.SeedNotifySecond),
)
}
},
)
}
override fun onSeedPhraseSecondNotificationReject() {
val userWallet = getSelectedUserWallet() ?: return
analyticsEventHandler.send(MainScreen.NoticeSeedPhraseSupportButtonDeclined())
modelScope.launch {
seedPhraseNotificationUseCase.rejectSecond(userWalletId = userWallet.walletId)
}
}
override fun onFinishWalletActivationClick(isBackupExists: Boolean) {
analyticsEventHandler.send(MainScreen.ButtonFinalizeActivation())
val userWalletId = stateHolder.getSelectedWalletId()
@ -485,7 +421,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
networks = currencies.map(CryptoCurrency::network).toSet(),
),
)
.onLeft { Timber.e("Unable to fetch networks: $it") }
.onLeft { TangemLogger.e("Unable to fetch networks: $it") }
},
async {
multiQuoteStatusFetcher(
@ -494,7 +430,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
appCurrencyId = null,
),
)
.onLeft { Timber.e("Unable to fetch quotes: $it") }
.onLeft { TangemLogger.e("Unable to fetch quotes: $it") }
},
async {
val stakingIds = currencies.mapNotNullTo(hashSetOf()) {
@ -507,7 +443,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
stakingIds = stakingIds,
),
)
.onLeft { Timber.e("Unable to fetch yield balances: $it") }
.onLeft { TangemLogger.e("Unable to fetch yield balances: $it") }
},
)
.awaitAll()
@ -516,12 +452,12 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
private fun getSelectedUserWallet(): UserWallet? {
val userWalletId = stateHolder.getSelectedWalletId()
return getUserWalletUseCase(userWalletId).getOrElse {
Timber.e(
return getUserWalletUseCase(userWalletId).getOrElse { error ->
TangemLogger.e(
"""
Unable to get user wallet
|- ID: $userWalletId
|- Exception: $it
|- Exception: $error
""".trimIndent(),
)
@ -538,7 +474,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
setNotificationsEnabledUseCase(userWalletId, true).onRight {
notificationsRepository.setNotificationsWasEnabledAutomatically(userWalletId.stringValue)
}.onLeft {
Timber.e(it)
TangemLogger.e("Error", it)
}
}
}
@ -572,6 +508,17 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
}
}
override fun onDismissTokenSyncNotification(userWalletId: UserWalletId) {
acknowledgeTokenSyncCompletionUseCase(userWalletId)
}
override fun onTokenSyncManageClick(userWalletId: UserWalletId) {
acknowledgeTokenSyncCompletionUseCase(userWalletId)
router.openManageTokensScreen(
AccountId.forMainCryptoPortfolio(userWalletId),
)
}
private companion object {
const val VISA_PROMO_LINK = "https://tangem.com/en/cardwaitlist/?utm_source=tangem-app-banner" +
"&utm_medium=banner" +

View file

@ -36,7 +36,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.withTimeoutOrNull
import timber.log.Timber
import com.tangem.utils.logging.TangemLogger
import kotlin.time.Duration.Companion.seconds
@Suppress("LongParameterList")
@ -72,12 +72,12 @@ internal class DefaultPromoDeeplinkHandler @AssistedInject constructor(
private fun findSelectedWallet(promoCode: String) {
getSelectedWalletSyncUseCase().fold(
ifLeft = {
Timber.tag(LOG_TAG).e("Error on getting user wallet: $it")
ifLeft = { error ->
TangemLogger.withTag(LOG_TAG).e("Error on getting user wallet: $error")
showAlert(Failed)
},
ifRight = { userWallet ->
Timber.tag(LOG_TAG).d("SelectedUserWallet ${userWallet.walletId.stringValue.mask()}")
TangemLogger.withTag(LOG_TAG).d("SelectedUserWallet ${userWallet.walletId.stringValue.mask()}")
findBitcoinAddress(userWallet = userWallet, promoCode = promoCode)
},
)
@ -96,24 +96,24 @@ internal class DefaultPromoDeeplinkHandler @AssistedInject constructor(
},
)
Timber.tag(LOG_TAG).d("All user network statuses ${networkStatuses?.size}")
TangemLogger.withTag(LOG_TAG).d("All user network statuses ${networkStatuses?.size}")
val cryptoCurrencies = multiWalletCryptoCurrenciesSupplier.getSyncOrNull(
MultiWalletCryptoCurrenciesProducer.Params(userWallet.walletId),
)
Timber.tag(LOG_TAG).d("All user cryptoCurrencies on main ${cryptoCurrencies?.size}")
TangemLogger.withTag(LOG_TAG).d("All user cryptoCurrencies on main ${cryptoCurrencies?.size}")
val bitcoinCurrency = cryptoCurrencies?.firstOrNull { it.id.rawNetworkId == Blockchain.Bitcoin.id }
Timber.tag(LOG_TAG).d("BitcoinCurrency $bitcoinCurrency")
TangemLogger.withTag(LOG_TAG).d("BitcoinCurrency $bitcoinCurrency")
val bitcoinStatus = networkStatuses?.firstOrNull { status ->
status.network.id == bitcoinCurrency?.network?.id
}
Timber.tag(LOG_TAG).d("BitcoinStatus $bitcoinStatus")
TangemLogger.withTag(LOG_TAG).d("BitcoinStatus $bitcoinStatus")
if (bitcoinStatus == null) {
Timber.tag(LOG_TAG).d("No bitcoin, bitcoin network status == null")
TangemLogger.withTag(LOG_TAG).d("No bitcoin, bitcoin network status == null")
showAlert(NoBitcoinAddress)
} else {
val networkAddress = when (bitcoinStatus.value) {
@ -126,7 +126,7 @@ internal class DefaultPromoDeeplinkHandler @AssistedInject constructor(
?.defaultAddress?.value
if (bitcoinAddress != null) {
Timber.tag(LOG_TAG).d(
TangemLogger.withTag(LOG_TAG).d(
"Start activation promoCode ${promoCode.mask()} address ${bitcoinAddress.mask()}",
)
@ -138,7 +138,7 @@ internal class DefaultPromoDeeplinkHandler @AssistedInject constructor(
} else {
uiMessageSender.send(GlobalLoadingMessage(false))
delay(DEFAULT_MESSAGE_SENDER_DELAY)
Timber.tag(LOG_TAG).d("No Bitcoin address $bitcoinStatus.value")
TangemLogger.withTag(LOG_TAG).d("No Bitcoin address $bitcoinStatus.value")
showAlert(NoBitcoinAddress)
}
}
@ -155,13 +155,15 @@ internal class DefaultPromoDeeplinkHandler @AssistedInject constructor(
delay(DEFAULT_MESSAGE_SENDER_DELAY)
uiMessageSender.send(GlobalLoadingMessage(false))
delay(DEFAULT_MESSAGE_SENDER_DELAY)
Timber.tag(LOG_TAG).d("${promoCode.mask()} activation success on address ${bitcoinAddress.mask()}")
TangemLogger.withTag(
LOG_TAG,
).d("${promoCode.mask()} activation success on address ${bitcoinAddress.mask()}")
showAlert(Activated)
}.onLeft { error ->
delay(DEFAULT_MESSAGE_SENDER_DELAY)
uiMessageSender.send(GlobalLoadingMessage(false))
delay(DEFAULT_MESSAGE_SENDER_DELAY)
Timber.tag(LOG_TAG).d("${promoCode.mask()} activation failed $error")
TangemLogger.withTag(LOG_TAG).d("${promoCode.mask()} activation failed $error")
val alertType = when (error) {
ActivatePromoCodeError.ActivationFailed -> Failed
ActivatePromoCodeError.InvalidPromoCode -> InvalidPromoCode
@ -203,7 +205,7 @@ internal class DefaultPromoDeeplinkHandler @AssistedInject constructor(
@Suppress("NullableToStringCall")
private fun saveAndBindRefcode() {
scope.launch(dispatchers.default) {
Timber.i("saveAndBindRefcode: refcode = $refcode, campaign = $campaign")
TangemLogger.i("saveAndBindRefcode: refcode = $refcode, campaign = $campaign")
if (!refcode.isNullOrBlank()) {
val conversionData = AppsFlyerConversionData(refcode = refcode, campaign = campaign)
@ -211,7 +213,7 @@ internal class DefaultPromoDeeplinkHandler @AssistedInject constructor(
appsFlyerStore.storeIfAbsent(value = conversionData)
bindRefcodeWithWalletUseCase(conversionData)
.onLeft { Timber.e("Failed to bind refcode with wallets: $it") }
.onLeft { TangemLogger.e("Failed to bind refcode with wallets: $it") }
}
}
}

View file

@ -20,7 +20,9 @@ internal object WalletPreviewDataLegacy {
balance = "8923,05312312312312312312331231231233432423423424234 $",
additionalInfo = WalletAdditionalInfo(
hideable = false,
content = TextReference.Str("3 cards • Seed phrase3 cards • Seed phrasephrasephrasephrase"),
content = WalletAdditionalInfo.Content.Text(
TextReference.Str("3 cards • Seed phrase3 cards • Seed phrasephrasephrasephrase"),
),
),
imageResId = R.drawable.ill_wallet2_cards3_120_106,
dropDownItems = persistentListOf(),

View file

@ -17,6 +17,7 @@ import com.tangem.feature.wallet.presentation.common.WalletPreviewDataLegacy.top
import com.tangem.feature.wallet.presentation.preview.WalletBalancePreview
import com.tangem.feature.wallet.presentation.preview.WalletPreviewData
import com.tangem.feature.wallet.presentation.wallet.state.model.*
import com.tangem.features.tangempay.entity.TangemPayMainUM
import kotlinx.collections.immutable.persistentListOf
internal object WalletScreenPreviewData {
@ -39,7 +40,7 @@ internal object WalletScreenPreviewData {
promoBannerUM = TangemTokenRowUM.PromoBannerUM.Empty,
tailUM = TangemRowTailUM.Empty,
onItemClick = {},
onItemLongClick = {},
onItemLongClick = { _, _ -> },
)
private val accountRowDefault = TangemTokenRowUM.Content(
@ -60,7 +61,7 @@ internal object WalletScreenPreviewData {
promoBannerUM = TangemTokenRowUM.PromoBannerUM.Empty,
tailUM = TangemRowTailUM.Empty,
onItemClick = {},
onItemLongClick = {},
onItemLongClick = { _, _ -> },
)
private val tokenListDefault = WalletTokensListUM.Content(
@ -167,7 +168,7 @@ internal object WalletScreenPreviewData {
isFlickering = false,
onItemClick = {},
),
tangemPayState = TangemPayState.Loading,
tangemPayMainUM = TangemPayMainUM.Loading,
)
private val walletEmpty = WalletUM.Content(
@ -182,7 +183,7 @@ internal object WalletScreenPreviewData {
notificationsCarousel = persistentListOf(),
tokensListUM = WalletTokensListUM.Empty(onEmptyClick = {}),
nftState = WalletNFTItemUM.Hidden,
tangemPayState = TangemPayState.Empty,
tangemPayMainUM = TangemPayMainUM.Empty,
)
private val walletAccountDefault = walletDefault.copy(

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
@ -159,7 +160,7 @@ internal object WalletScreenPreviewDataLegacy {
title = "Note",
additionalInfo = WalletAdditionalInfo(
hideable = false,
content = TextReference.Str("Locked"),
content = WalletAdditionalInfo.Content.Text(TextReference.Str("Locked")),
),
imageResId = R.drawable.ill_note_btc_120_106,
dropDownItems = persistentListOf(),
@ -171,7 +172,7 @@ internal object WalletScreenPreviewDataLegacy {
title = "Wallet 1",
additionalInfo = WalletAdditionalInfo(
hideable = false,
content = TextReference.Str("Seed phrase"),
content = WalletAdditionalInfo.Content.Text(TextReference.Str("Seed phrase")),
),
imageResId = R.drawable.ill_wallet2_cards3_120_106,
cardCount = 3,
@ -217,6 +218,8 @@ internal object WalletScreenPreviewDataLegacy {
onClick = {},
),
type = WalletType.Cold,
tangemPayMainUM = TangemPayMainUM.Empty,
isTangemPayRefactorEnabled = false,
)
}

View file

@ -1,5 +1,6 @@
package com.tangem.feature.wallet.presentation.router
import androidx.compose.ui.geometry.Offset
import com.arkivanov.decompose.router.slot.SlotNavigation
import com.arkivanov.decompose.router.slot.activate
import com.arkivanov.decompose.router.slot.dismiss
@ -8,6 +9,7 @@ import com.tangem.common.routing.AppRouter
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.DesignFeatureToggles
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
import com.tangem.domain.models.TokenReceiveConfig
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.currency.CryptoCurrency
@ -17,8 +19,6 @@ import com.tangem.domain.qrscanning.models.QrSendTarget
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.TangemPayDetailsConfig
import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.redux.StateDialog
import com.tangem.domain.tokens.model.details.NavigationAction
import com.tangem.domain.tokens.model.details.TokenAction
import com.tangem.feature.wallet.child.organizetokens.OrganizeTokensComponent
@ -35,7 +35,6 @@ import javax.inject.Inject
internal class DefaultWalletRouter @Inject constructor(
private val router: AppRouter,
private val urlOpener: UrlOpener,
private val reduxStateHolder: ReduxStateHolder,
private val designFeatureToggles: DesignFeatureToggles,
) : InnerWalletRouter {
@ -117,10 +116,6 @@ internal class DefaultWalletRouter @Inject constructor(
return router.stack.lastOrNull() is AppRoute.Wallet
}
override fun openScanFailedDialog(onTryAgain: () -> Unit) {
reduxStateHolder.dispatchDialogShow(StateDialog.ScanFailsDialog(StateDialog.ScanFailsSource.MAIN, onTryAgain))
}
override fun openNFT(userWallet: UserWallet) {
router.push(
AppRoute.NFT(
@ -168,10 +163,18 @@ internal class DefaultWalletRouter @Inject constructor(
)
}
override fun openTokenActionSheet(userWallet: UserWallet, tokenActionList: ImmutableList<TokenActionButtonUM>) {
override fun openTokenActionSheet(
userWallet: UserWallet,
tokenActionList: ImmutableList<TokenActionButtonUM>,
offset: Offset,
tokenRowUM: TangemTokenRowUM?,
) {
dialogNavigation.activate(
configuration = WalletDialogConfig.TokenActionList(
actionList = tokenActionList,
offsetX = offset.x,
offsetY = offset.y,
tokenRowUM = tokenRowUM,
),
)
}

View file

@ -1,8 +1,10 @@
package com.tangem.feature.wallet.presentation.router
import androidx.compose.runtime.Stable
import androidx.compose.ui.geometry.Offset
import com.arkivanov.decompose.router.slot.SlotNavigation
import com.tangem.common.routing.AppRoute
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
import com.tangem.domain.models.TokenReceiveConfig
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.currency.CryptoCurrency
@ -66,9 +68,6 @@ internal interface InnerWalletRouter {
/** Is wallet last screen */
fun isWalletLastScreen(): Boolean
/** Open scan failed dialog */
fun openScanFailedDialog(onTryAgain: () -> Unit)
/** Open NFT collections screen */
fun openNFT(userWallet: UserWallet)
@ -89,7 +88,12 @@ internal interface InnerWalletRouter {
fun openYieldSupplyEntryScreen(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, apy: String)
/** Open token action sheet */
fun openTokenActionSheet(userWallet: UserWallet, tokenActionList: ImmutableList<TokenActionButtonUM>)
fun openTokenActionSheet(
userWallet: UserWallet,
tokenActionList: ImmutableList<TokenActionButtonUM>,
offset: Offset,
tokenRowUM: TangemTokenRowUM?,
)
/** Open QR scanner screen */
fun openQrScanner()

View file

@ -155,18 +155,6 @@ sealed class WalletScreenAnalyticsEvent {
class DeleteWalletTapped : MainScreen(event = "Button - Delete Wallet Tapped")
class NoticeSeedPhraseSupport : MainScreen(event = "Notice - Seed Phrase Support")
class NoticeSeedPhraseSupportSecond : MainScreen(event = "Notice - Seed Phrase Support2")
class NoticeSeedPhraseSupportButtonNo : MainScreen(event = "Button - Support No")
class NoticeSeedPhraseSupportButtonYes : MainScreen(event = "Button - Support Yes")
class NoticeSeedPhraseSupportButtonUsed : MainScreen(event = "Button - Support Used")
class NoticeSeedPhraseSupportButtonDeclined : MainScreen(event = "Button - Support Declined")
class NoticeUnrecognizedQr : MainScreen(
event = "Notice - Unrecognized QR",
)

View file

@ -116,13 +116,12 @@ internal class WalletWarningsAnalyticsSender @Inject constructor(
balanceState = balanceState,
)
}
is WalletNotification.Critical.SeedPhraseNotification -> NoticeSeedPhraseSupport()
is WalletNotification.Critical.SeedPhraseSecondNotification -> NoticeSeedPhraseSupportSecond()
is WalletNotification.PushNotifications -> PushBanner()
is WalletNotification.Warning.TangemPayRefreshNeeded -> null
is WalletNotification.Warning.TangemPayUnreachable -> null
is WalletNotification.UpgradeHotWalletPromo -> null
is WalletNotification.TokenSyncCompleted -> null
is WalletNotification.CreateTangemPayAccount -> null
}
}
@ -162,8 +161,6 @@ internal class WalletWarningsAnalyticsSender @Inject constructor(
balanceState = balanceState,
)
}
is WalletNotificationUM.SeedPhraseNotification -> NoticeSeedPhraseSupport()
is WalletNotificationUM.SeedPhraseSecondNotification -> NoticeSeedPhraseSupportSecond()
is WalletNotificationUM.PushNotifications -> PushBanner()
is WalletNotificationUM.UnlockWallets,
is WalletNotificationUM.NoAccount,
@ -171,6 +168,8 @@ internal class WalletWarningsAnalyticsSender @Inject constructor(
WalletNotificationUM.SomeNetworksUnreachable,
is WalletNotificationUM.UsedOutdatedData,
is WalletNotificationUM.CloreMigration,
is WalletNotificationUM.TangemPayRefreshNeeded,
WalletNotificationUM.TangemPayUnreachable,
-> null
}
}

View file

@ -11,7 +11,6 @@ import com.tangem.core.ui.message.bottomSheetMessage
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.wallets.usecase.SeedPhraseNotificationUseCase
import com.tangem.feature.wallet.child.wallet.model.WalletActivationBannerType
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
@ -24,7 +23,6 @@ import javax.inject.Inject
@ModelScoped
internal class WalletWarningsSingleEventSender @Inject constructor(
private val seedPhraseNotificationUseCase: SeedPhraseNotificationUseCase,
private val screenLifecycleProvider: ScreenLifecycleProvider,
private val uiMessageSender: UiMessageSender,
private val getUserWalletUseCase: GetUserWalletUseCase,
@ -32,11 +30,7 @@ internal class WalletWarningsSingleEventSender @Inject constructor(
) {
private val isActivationBottomSheetShown: ConcurrentHashMap<UserWalletId, Boolean> = ConcurrentHashMap()
suspend fun send(
userWalletId: UserWalletId,
displayedUiState: WalletState?,
newWarnings: List<WalletNotification>,
) {
fun send(userWalletId: UserWalletId, displayedUiState: WalletState?, newWarnings: List<WalletNotification>) {
if (screenLifecycleProvider.isBackgroundState.value) return
if (newWarnings.isEmpty()) return
if (displayedUiState == null || displayedUiState.pullToRefreshConfig.isRefreshing) return
@ -51,9 +45,6 @@ internal class WalletWarningsSingleEventSender @Inject constructor(
events.forEach { event ->
when (event) {
is WalletNotification.Critical.SeedPhraseNotification -> {
seedPhraseNotificationUseCase.notified(userWalletId = userWalletId)
}
is WalletNotification.FinishWalletActivation -> {
// We check that map contains the first seen wallet (will return null instead false/true otherwise)
// and for this wallet we haven't shown the activation bs yet (check that returns false, not true)
@ -69,11 +60,7 @@ internal class WalletWarningsSingleEventSender @Inject constructor(
}
}
suspend fun send(
userWalletId: UserWalletId,
displayedWalletUM: WalletUM?,
newNotifications: List<WalletNotificationUM>,
) {
fun send(userWalletId: UserWalletId, displayedWalletUM: WalletUM?, newNotifications: List<WalletNotificationUM>) {
if (screenLifecycleProvider.isBackgroundState.value) return
if (newNotifications.isEmpty()) return
if (displayedWalletUM == null || displayedWalletUM.pullToRefreshConfig.isRefreshing) return
@ -89,9 +76,6 @@ internal class WalletWarningsSingleEventSender @Inject constructor(
events.forEach { event ->
when (event) {
is WalletNotificationUM.SeedPhraseNotification -> {
seedPhraseNotificationUseCase.notified(userWalletId = userWalletId)
}
is WalletNotificationUM.FinishWalletActivation -> {
// We check that map contains the first seen wallet (will return null instead false/true otherwise)
// and for this wallet we haven't shown the activation bs yet (check that returns false, not true)

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,10 +27,10 @@ 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.tokensync.model.TokenSyncProgress
import com.tangem.domain.tokensync.usecase.ObserveTokenSyncUseCase
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase
import com.tangem.domain.wallets.usecase.SeedPhraseNotificationUseCase
import com.tangem.feature.wallet.child.wallet.model.WalletActivationBannerType
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.impl.R
@ -44,8 +46,8 @@ import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import javax.inject.Inject
@Deprecated("Remove with main toggle [DesignFeatureToggles.isRedesignEnabled]")
@ -56,7 +58,6 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
private val isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase,
private val isNeedToBackupUseCase: IsNeedToBackupUseCase,
private val backupValidator: BackupValidator,
private val seedPhraseNotificationUseCase: SeedPhraseNotificationUseCase,
private val shouldShowPromoWalletUseCase: ShouldShowPromoWalletUseCase,
private val notificationsRepository: NotificationsRepository,
private val accountDependencies: AccountDependencies,
@ -64,24 +65,26 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
private val shouldShowUpgradeHotWalletBannerUseCase: ShouldShowUpgradeHotWalletBannerUseCase,
private val getUpgradeBannerClosureTimestampUseCase: GetUpgradeBannerClosureTimestampUseCase,
private val checkHotWalletUpgradeBannerUseCase: CheckHotWalletUpgradeBannerUseCase,
private val observeTokenSyncUseCase: ObserveTokenSyncUseCase,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
) {
@Suppress("UNCHECKED_CAST", "MagicNumber", "LongMethod", "CastNullableToNonNullableType")
fun create(userWallet: UserWallet, clickIntents: WalletClickIntents): Flow<ImmutableList<WalletNotification>> {
val cardTypesResolver = (userWallet as? UserWallet.Cold)?.scanResponse?.cardTypesResolver
val params = SingleAccountStatusListProducer.Params(userWallet.walletId)
val accountStatusListFlow = accountDependencies.singleAccountStatusListSupplier(params)
val accountStatusListFlow by lazy {
val params = SingleAccountStatusListProducer.Params(userWallet.walletId)
accountDependencies.singleAccountStatusListSupplier(params)
.map { it.totalFiatBalance to it.flattenCurrencies() }
.map { Lce.Content(it) }
val tokenSyncProgressFlow = if (hotWalletFeatureToggles.isTokenSyncEnabled && userWallet is UserWallet.Hot) {
observeTokenSyncUseCase(userWallet.walletId).distinctUntilChanged()
} else {
flowOf(TokenSyncProgress.Idle)
}
return combine(
accountStatusListFlow,
isReadyToShowRateAppUseCase().distinctUntilChanged(),
isNeedToBackupUseCase(userWallet.walletId).distinctUntilChanged(),
seedPhraseNotificationUseCase(userWalletId = userWallet.walletId).distinctUntilChanged(),
shouldShowPromoWalletUseCase(userWalletId = userWallet.walletId, promoId = PromoId.OnePlusOne)
.distinctUntilChanged(),
notificationsRepository.getShouldShowNotification(NotificationId.EnablePushesReminderNotification.key)
@ -93,25 +96,29 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
.distinctUntilChanged(),
getUpgradeBannerClosureTimestampUseCase(userWallet.walletId)
.distinctUntilChanged(),
tokenSyncProgressFlow,
) { 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
val shouldShowOnePlusOnePromo = array[4] as Boolean
val shouldShowEnablePushesReminderNotification = array[5] as Boolean
val shouldAccessCodeSkipped = array[6] as Boolean
val shouldShowYieldPromo = array[7] as Boolean
val shouldShowUpgradeBanner = array[8] as Boolean
val closureTimestamp = array[9] as? Long
val shouldShowOnePlusOnePromo = array[3] as Boolean
val shouldShowEnablePushesReminderNotification = array[4] as Boolean
val shouldAccessCodeSkipped = array[5] as Boolean
val shouldShowYieldPromo = array[6] as Boolean
val shouldShowUpgradeBanner = array[7] as Boolean
val closureTimestamp = array[8] as? Long
val tokenSyncProgress = array[9] as TokenSyncProgress
val flattenCurrencies = accountStatusList.flattenCurrencies()
val paymentAccountStatus = accountStatusList.accountStatuses
.filterIsInstance<AccountStatus.Payment>()
.firstOrNull()
buildList {
addUsedOutdatedDataNotification(totalFiatBalance)
addUsedOutdatedDataNotification(accountStatusList.totalFiatBalance)
addCriticalNotifications(userWallet, seedPhraseIssueStatus, clickIntents)
addCriticalNotifications(userWallet, clickIntents)
addUpgradeHotWalletPromoNotification(
userWallet = userWallet,
@ -146,6 +153,12 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
clickIntents = clickIntents,
)
addTokenSyncCompletedNotification(
userWallet = userWallet,
tokenSyncProgress = tokenSyncProgress,
clickIntents = clickIntents,
)
addPushReminderNotification(
clickIntents = clickIntents,
shouldShowPushReminderBanner = shouldShowEnablePushesReminderNotification &&
@ -162,38 +175,65 @@ 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,
)
}
private fun MutableList<WalletNotification>.addCriticalNotifications(
userWallet: UserWallet,
seedPhraseIssueStatus: SeedPhraseNotificationsStatus,
clickIntents: WalletClickIntents,
) {
if (userWallet !is UserWallet.Cold) {
return
}
addSeedNotificationIfNeeded(userWallet, seedPhraseIssueStatus, clickIntents)
val cardTypesResolver = userWallet.scanResponse.cardTypesResolver
addIf(
element = WalletNotification.Critical.BackupError { clickIntents.onBackupErrorClick() },
@ -218,43 +258,10 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
}
}
private fun MutableList<WalletNotification>.addSeedNotificationIfNeeded(
userWallet: UserWallet.Cold,
seedPhraseIssueStatus: SeedPhraseNotificationsStatus,
clickIntents: WalletClickIntents,
) {
val isNotificationAvailable = with(userWallet) {
val isDemo = isDemoCardUseCase(cardId = userWallet.cardId)
val isWalletWithSeedPhrase = scanResponse.cardTypesResolver.isWallet2() && userWallet.isImported
!isDemo && isWalletWithSeedPhrase
}
when (seedPhraseIssueStatus) {
SeedPhraseNotificationsStatus.SHOW_FIRST -> addIf(
element = WalletNotification.Critical.SeedPhraseNotification(
onDeclineClick = clickIntents::onSeedPhraseNotificationDecline,
onConfirmClick = clickIntents::onSeedPhraseNotificationConfirm,
),
condition = isNotificationAvailable,
)
SeedPhraseNotificationsStatus.SHOW_SECOND -> addIf(
element = WalletNotification.Critical.SeedPhraseSecondNotification(
onDeclineClick = clickIntents::onSeedPhraseSecondNotificationReject,
onConfirmClick = clickIntents::onSeedPhraseSecondNotificationAccept,
),
condition = isNotificationAvailable,
)
SeedPhraseNotificationsStatus.NOT_NEEDED -> {
// do nothing
}
}
}
private fun MutableList<WalletNotification>.addInformationalNotifications(
userWallet: UserWallet,
cardTypesResolver: CardTypesResolver?,
flattenCurrencies: Lce<TokenListError, List<CryptoCurrencyStatus>>,
flattenCurrencies: List<CryptoCurrencyStatus>,
clickIntents: WalletClickIntents,
) {
addIf(
@ -267,7 +274,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 +292,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 +335,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 +360,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 +372,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 +391,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
@ -403,6 +404,20 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
// }
// }
private fun MutableList<WalletNotification>.addTokenSyncCompletedNotification(
userWallet: UserWallet,
tokenSyncProgress: TokenSyncProgress,
clickIntents: WalletClickIntents,
) {
addIf(
element = WalletNotification.TokenSyncCompleted(
onCloseClick = { clickIntents.onDismissTokenSyncNotification(userWallet.walletId) },
onManageTokensClick = { clickIntents.onTokenSyncManageClick(userWallet.walletId) },
),
condition = tokenSyncProgress is TokenSyncProgress.Completed,
)
}
private fun MutableList<WalletNotification>.addRateTheAppNotification(
isReadyToShowRating: Boolean,
clickIntents: WalletClickIntents,
@ -427,7 +442,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 +452,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 +475,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

@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.domain
import com.tangem.common.ui.userwallet.ext.walletInterationIcon
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.ui.ds.message.TangemMessageEffect
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
import com.tangem.domain.card.CardTypesResolver
import com.tangem.domain.card.common.util.cardTypesResolver
@ -10,14 +11,15 @@ import com.tangem.domain.demo.IsDemoCardUseCase
import com.tangem.domain.hotwallet.GetAccessCodeSkippedUseCase
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
import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.domain.wallets.models.SeedPhraseNotificationsStatus
import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase
import com.tangem.domain.wallets.usecase.SeedPhraseNotificationUseCase
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.account.AccountDependencies
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotificationUM
import com.tangem.hot.sdk.model.HotWalletId
@ -40,7 +42,6 @@ internal class GetWalletNotificationsFactory @Inject constructor(
private val isDemoCardUseCase: IsDemoCardUseCase,
private val isNeedToBackupUseCase: IsNeedToBackupUseCase,
private val backupValidator: BackupValidator,
private val seedPhraseNotificationUseCase: SeedPhraseNotificationUseCase,
private val accountDependencies: AccountDependencies,
private val getAccessCodeSkippedUseCase: GetAccessCodeSkippedUseCase,
private val hasSingleWalletSignedHashesUseCase: HasSingleWalletSignedHashesUseCase,
@ -54,16 +55,19 @@ internal class GetWalletNotificationsFactory @Inject constructor(
return combine(
flow = accountStatusListFlow,
flow2 = isNeedToBackupUseCase(userWallet.walletId).distinctUntilChanged(),
flow3 = seedPhraseNotificationUseCase(userWalletId = userWallet.walletId).distinctUntilChanged(),
flow4 = getAccessCodeSkippedUseCase(userWallet.walletId).distinctUntilChanged(),
) { accountList, isNeedToBackup, seedPhraseIssueStatus, shouldAccessCodeSkipped ->
flow3 = getAccessCodeSkippedUseCase(userWallet.walletId).distinctUntilChanged(),
) { accountList, isNeedToBackup, shouldAccessCodeSkipped ->
val totalFiatBalance = accountList.totalFiatBalance
val flattenCurrencies = accountList.flattenCurrencies()
val paymentAccountStatus = accountList.accountStatuses
.filterIsInstance<AccountStatus.Payment>()
.firstOrNull()
buildList {
addUsedOutdatedDataNotification(totalFiatBalance)
addCriticalNotifications(userWallet, seedPhraseIssueStatus, clickIntents)
addCriticalNotifications(userWallet, clickIntents)
addFinishWalletActivationNotification(
userWallet = userWallet,
@ -86,6 +90,14 @@ internal class GetWalletNotificationsFactory @Inject constructor(
isNeedToBackup = isNeedToBackup,
clickIntents = clickIntents,
)
if (paymentAccountStatus != null) {
addTangemPayWarnings(
status = paymentAccountStatus,
userWallet = userWallet,
walletClickIntents = clickIntents,
)
}
}.sortedBy { it.type.ordinal }.toImmutableList()
}
}
@ -99,15 +111,12 @@ internal class GetWalletNotificationsFactory @Inject constructor(
private fun MutableList<WalletNotificationUM>.addCriticalNotifications(
userWallet: UserWallet,
seedPhraseIssueStatus: SeedPhraseNotificationsStatus,
clickIntents: WalletClickIntents,
) {
if (userWallet !is UserWallet.Cold) {
return
}
addSeedNotificationIfNeeded(userWallet, seedPhraseIssueStatus, clickIntents)
val cardTypesResolver = userWallet.scanResponse.cardTypesResolver
addIf(
element = WalletNotificationUM.BackupError { clickIntents.onSupportClick() },
@ -207,6 +216,34 @@ internal class GetWalletNotificationsFactory @Inject constructor(
)
}
private fun MutableList<WalletNotificationUM>.addTangemPayWarnings(
status: AccountStatus.Payment,
userWallet: UserWallet,
walletClickIntents: WalletClickIntents,
) {
val notification = when (status.value) {
is PaymentAccountStatusValue.Error.NotSynced -> WalletNotificationUM.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 -> null // TODO(Main redesign)
is PaymentAccountStatusValue.Error.Unavailable -> WalletNotificationUM.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<WalletNotificationUM>.addNoAccountWarning(cryptoCurrencyStatus: CryptoCurrencyStatus?) {
val noAccountStatus = cryptoCurrencyStatus?.value as? CryptoCurrencyStatus.NoAccount
if (noAccountStatus != null) {
@ -279,37 +316,6 @@ internal class GetWalletNotificationsFactory @Inject constructor(
)
}
private fun MutableList<WalletNotificationUM>.addSeedNotificationIfNeeded(
userWallet: UserWallet.Cold,
seedPhraseIssueStatus: SeedPhraseNotificationsStatus,
clickIntents: WalletClickIntents,
) {
val isNotificationAvailable = with(userWallet) {
val isDemo = isDemoCardUseCase(cardId = userWallet.cardId)
val isWalletWithSeedPhrase = scanResponse.cardTypesResolver.isWallet2() && userWallet.isImported
!isDemo && isWalletWithSeedPhrase
}
when (seedPhraseIssueStatus) {
SeedPhraseNotificationsStatus.SHOW_FIRST -> addIf(
element = WalletNotificationUM.SeedPhraseNotification(
onDeclineClick = clickIntents::onSeedPhraseNotificationDecline,
onConfirmClick = clickIntents::onSeedPhraseNotificationConfirm,
),
condition = isNotificationAvailable,
)
SeedPhraseNotificationsStatus.SHOW_SECOND -> addIf(
element = WalletNotificationUM.SeedPhraseSecondNotification(
onDeclineClick = clickIntents::onSeedPhraseSecondNotificationReject,
onConfirmClick = clickIntents::onSeedPhraseSecondNotificationAccept,
),
condition = isNotificationAvailable,
)
SeedPhraseNotificationsStatus.NOT_NEEDED -> Unit
}
}
private suspend fun hasSignedHashes(
selectedWallet: UserWallet,
cryptoCurrencyStatus: CryptoCurrencyStatus?,

View file

@ -7,9 +7,9 @@ import com.tangem.domain.demo.models.DemoConfig
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.utils.logging.TangemLogger
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import timber.log.Timber
import javax.inject.Inject
@ModelScoped
@ -42,7 +42,7 @@ class HasSingleWalletSignedHashesUseCase @Inject constructor(
},
)
} catch (e: IllegalArgumentException) {
Timber.w(e, "Unable to validate signature count: user wallet not found")
TangemLogger.w("Unable to validate signature count: user wallet not found", e)
false
}
}

View file

@ -18,7 +18,7 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.withContext
import timber.log.Timber
import com.tangem.utils.logging.TangemLogger
import javax.inject.Inject
@ModelScoped
@ -58,7 +58,7 @@ internal class OnrampStatusFactory @Inject constructor(
if (!onrampTx.activeStatus.isTerminal) {
getOnrampStatusUseCase(userWallet = userWallet, onrampTx.info.txId).fold(
ifLeft = {
Timber.e("Couldn't update onramp status. $it")
TangemLogger.e("Couldn't update onramp status. $it")
},
ifRight = { statusModel ->
val txId = statusModel.txId

View file

@ -1,17 +1,17 @@
package com.tangem.feature.wallet.presentation.wallet.domain
import com.tangem.utils.logging.TangemLogger
import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
import timber.log.Timber
internal fun GetSelectedWalletSyncUseCase.unwrap(): UserWallet? {
return this().fold(
ifLeft = {
Timber.e("Impossible to get selected wallet $it")
ifLeft = { error ->
TangemLogger.e("Impossible to get selected wallet $error")
null
},
ifRight = { it },

View file

@ -10,6 +10,7 @@ import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.card.common.util.getCardsCount
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.state.model.TokenSyncProgressUM
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAdditionalInfo
import java.math.BigDecimal
@ -28,7 +29,11 @@ internal object WalletAdditionalInfoFactory {
* @param wallet current wallet
* @param currencyAmount amount of currency
*/
fun resolve(wallet: UserWallet, currencyAmount: BigDecimal? = null): WalletAdditionalInfo {
fun resolve(
wallet: UserWallet,
currencyAmount: BigDecimal? = null,
syncProgress: TokenSyncProgressUM = TokenSyncProgressUM.Idle,
): WalletAdditionalInfo {
return when (wallet) {
is UserWallet.Cold -> {
if (wallet.isMultiCurrency) {
@ -37,19 +42,26 @@ internal object WalletAdditionalInfoFactory {
wallet.resolveSingleCurrencyInfo(currencyAmount)
}
}
is UserWallet.Hot -> wallet.resolveAdditionalInfo()
is UserWallet.Hot -> wallet.resolveAdditionalInfo(syncProgress)
}
}
private fun UserWallet.Hot.resolveAdditionalInfo(): WalletAdditionalInfo {
private fun UserWallet.Hot.resolveAdditionalInfo(syncProgress: TokenSyncProgressUM): WalletAdditionalInfo {
val content = if (syncProgress is TokenSyncProgressUM.InProgress) {
WalletAdditionalInfo.Content.SyncProgress(syncProgress.progressPercent)
} else {
WalletAdditionalInfo.Content.Text(
TextReference.Res(R.string.hw_mobile_wallet) +
when {
isLocked -> DIVIDER + TextReference.Res(R.string.common_locked)
backedUp.not() -> DIVIDER + TextReference.Res(R.string.hw_backup_no_backup)
else -> TextReference.Str("")
},
)
}
return WalletAdditionalInfo(
hideable = false,
content = TextReference.Res(R.string.hw_mobile_wallet) +
when {
isLocked -> DIVIDER + TextReference.Res(R.string.common_locked)
backedUp.not() -> DIVIDER + TextReference.Res(R.string.hw_backup_no_backup)
else -> TextReference.Str("")
},
content = content,
isHotBackedUp = backedUp,
)
}
@ -58,9 +70,11 @@ internal object WalletAdditionalInfoFactory {
return if (isLocked) {
WalletAdditionalInfo(
hideable = false,
content = getBackupInfoWithDivider(
backupCardsCount = getCardsCount(),
) + TextReference.Res(R.string.common_locked),
content = WalletAdditionalInfo.Content.Text(
getBackupInfoWithDivider(
backupCardsCount = getCardsCount(),
) + TextReference.Res(R.string.common_locked),
),
)
} else {
val cardTypeResolver = scanResponse.cardTypesResolver
@ -76,8 +90,10 @@ internal object WalletAdditionalInfoFactory {
return if (isImported) {
WalletAdditionalInfo(
hideable = false,
content = getBackupInfoWithDivider(backupCardsCount = getCardsCount()) + TextReference.Res(
id = R.string.common_seed_phrase,
content = WalletAdditionalInfo.Content.Text(
getBackupInfoWithDivider(backupCardsCount = getCardsCount()) + TextReference.Res(
id = R.string.common_seed_phrase,
),
),
)
} else {
@ -94,7 +110,7 @@ internal object WalletAdditionalInfoFactory {
}
private fun getBackupInfo(backupCardsCount: Int?): WalletAdditionalInfo {
val content = if (backupCardsCount != null) {
val ref = if (backupCardsCount != null) {
getBackupInfoTextReference(count = backupCardsCount)
} else {
TextReference.EMPTY
@ -102,7 +118,7 @@ internal object WalletAdditionalInfoFactory {
return WalletAdditionalInfo(
hideable = false,
content = content,
content = WalletAdditionalInfo.Content.Text(ref),
)
}
@ -118,7 +134,7 @@ internal object WalletAdditionalInfoFactory {
return if (isLocked) {
WalletAdditionalInfo(
hideable = false,
content = TextReference.Res(R.string.common_locked),
content = WalletAdditionalInfo.Content.Text(TextReference.Res(R.string.common_locked)),
)
} else {
val blockchain = scanResponse.cardTypesResolver.getBlockchain()
@ -126,7 +142,7 @@ internal object WalletAdditionalInfoFactory {
WalletAdditionalInfo(
hideable = true,
content = TextReference.Str(value = amount.orEmpty()),
content = WalletAdditionalInfo.Content.Text(TextReference.Str(value = amount.orEmpty())),
)
}
}

View file

@ -11,7 +11,7 @@ import kotlinx.coroutines.supervisorScope
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import timber.log.Timber
import com.tangem.utils.logging.TangemLogger
import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
import javax.inject.Singleton
@ -45,7 +45,7 @@ internal class WalletContentFetcher @Inject constructor(
* (doesn't matter if it is active or not), then skip the update process.
*/
if (!forceUpdate && savedJobHolder != null && !savedJobHolder.isEmpty()) {
Timber.d("Skip fetching for $userWalletId")
TangemLogger.d("Skip fetching for $userWalletId")
return@withContext
}
@ -55,7 +55,7 @@ internal class WalletContentFetcher @Inject constructor(
* then cancel the previous update.
*/
if (forceUpdate && savedJobHolder?.isActive == true) {
Timber.d("Cancel old fetching for $userWalletId")
TangemLogger.d("Cancel old fetching for $userWalletId")
savedJobHolder.cancel()
}
@ -63,7 +63,7 @@ internal class WalletContentFetcher @Inject constructor(
JobHolder().also { fetchingJobMap[userWalletId] = it }
}
Timber.d("Start fetching for $userWalletId")
TangemLogger.d("Start fetching for $userWalletId")
val maybeResult = launch {
walletBalanceFetcher(
@ -71,11 +71,11 @@ internal class WalletContentFetcher @Inject constructor(
userWalletId = userWalletId,
isPaymentAccountRefactorEnabled = tangemPayFeatureToggles.isTangemPayAccountsRefactorEnabled,
),
).onLeft(Timber::e)
).onLeft { TangemLogger.e("Error", it) }
}
.saveInAndJoin(jobHolder)
Timber.d("Finish fetching with result $maybeResult for $userWalletId")
TangemLogger.d("Finish fetching with result $maybeResult for $userWalletId")
}
}
}

View file

@ -3,7 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.domain
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.models.wallet.copy
import com.tangem.domain.wallets.repository.WalletNamesMigrationRepository
import timber.log.Timber
import com.tangem.utils.logging.TangemLogger
class WalletNameMigrationUseCase(
private val userWalletsListRepository: UserWalletsListRepository,
@ -17,13 +17,13 @@ class WalletNameMigrationUseCase(
val wallets = userWalletsListRepository.userWalletsSync()
val existingNames: MutableSet<String> = mutableSetOf()
wallets.forEach {
val defaultName = it.name
wallets.forEach { wallet ->
val defaultName = wallet.name
val suggestedWalletName = suggestedWalletName(defaultName, existingNames)
if (defaultName != suggestedWalletName) {
userWalletsListRepository.saveWithoutLock(it.copy(name = suggestedWalletName), canOverride = true)
userWalletsListRepository.saveWithoutLock(wallet.copy(name = suggestedWalletName), canOverride = true)
}
Timber.tag("Migrated names").e(it.walletId.toString() + " " + suggestedWalletName)
TangemLogger.withTag("Migrated names").e(wallet.walletId.toString() + " " + suggestedWalletName)
}
walletNamesMigrationRepository.setMigrationDone()

View file

@ -7,7 +7,7 @@ import com.tangem.domain.models.wallet.isLocked
import kotlinx.coroutines.CloseableCoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.newSingleThreadContext
import timber.log.Timber
import com.tangem.utils.logging.TangemLogger
import javax.inject.Inject
/**
@ -45,19 +45,19 @@ internal class WalletScreenContentLoader @Inject constructor(
storage.remove(id)
loadInternal(userWallet, coroutineScope, isRefresh = true)
} else {
Timber.d("$id content loading has already started")
TangemLogger.d("$id content loading has already started")
}
}
}
/** Cancel loading by [id] */
fun cancel(id: UserWalletId) {
Timber.d("$id content loading is canceled")
TangemLogger.d("$id content loading is canceled")
storage.remove(id)
}
fun cancelAll() {
Timber.d("All content loading is canceled")
TangemLogger.d("All content loading is canceled")
storage.clear()
singleBackgroundDispatcher.close()
}
@ -69,11 +69,11 @@ internal class WalletScreenContentLoader @Inject constructor(
)
if (loader == null) {
Timber.e("Impossible to create loader for $userWallet")
TangemLogger.e("Impossible to create loader for $userWallet")
return
}
Timber.d("${userWallet.walletId} content loading is ${if (isRefresh) "re" else ""}started")
TangemLogger.d("${userWallet.walletId} content loading is ${if (isRefresh) "re" else ""}started")
loader.subscribers
.map { it.subscribe(coroutineScope, singleBackgroundDispatcher) }

View file

@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
import com.tangem.core.ui.DesignFeatureToggles
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.feature.wallet.presentation.wallet.subscribers.*
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@ -17,11 +18,13 @@ internal class MultiWalletContentLoader @AssistedInject constructor(
private val walletNotificationsSubscriberFactory: WalletNotificationsSubscriber.Factory,
private val multiWalletActionButtonsSubscriberFactory: MultiWalletActionButtonsSubscriber.Factory,
private val tangemPayMainSubscriberFactory: TangemPayMainSubscriber.Factory,
private val tokenSyncSubscriberFactory: TokenSyncSubscriber.Factory,
private val tokenListAnalyticsSubscriberFactory: TokenListAnalyticsSubscriber.Factory,
private val designFeatureToggles: DesignFeatureToggles,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
) : WalletContentLoader(id = userWallet.walletId) {
override fun create(): List<WalletSubscriber> = listOf(
override fun create(): List<WalletSubscriber> = listOfNotNull(
accountListSubscriberFactory.create(userWallet),
walletNFTListSubscriberFactory.create(userWallet),
checkWalletWithFundsSubscriberFactory.create(userWallet),
@ -33,6 +36,11 @@ internal class MultiWalletContentLoader @AssistedInject constructor(
multiWalletActionButtonsSubscriberFactory.create(userWallet),
tangemPayMainSubscriberFactory.create(userWallet),
tokenListAnalyticsSubscriberFactory.create(userWallet),
if (hotWalletFeatureToggles.isTokenSyncEnabled && userWallet is UserWallet.Hot) {
tokenSyncSubscriberFactory.create(userWallet)
} else {
null
},
)
@AssistedFactory

View file

@ -13,7 +13,7 @@ import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import timber.log.Timber
import com.tangem.utils.logging.TangemLogger
import javax.inject.Inject
import javax.inject.Singleton
@ -41,7 +41,7 @@ internal class WalletStateController @Inject constructor(
}
fun update(transformer: WalletScreenStateTransformer) {
Timber.d("Applying: ${transformer::class.simpleName}")
TangemLogger.d("Applying: ${transformer::class.simpleName}")
mutableUiState.update(function = transformer::transform)
}

View file

@ -1,6 +1,7 @@
package com.tangem.feature.wallet.presentation.wallet.state.model
import androidx.annotation.DrawableRes
import androidx.compose.runtime.Stable
import com.tangem.core.ui.extensions.TextReference
import kotlinx.serialization.Serializable
@ -13,11 +14,14 @@ import kotlinx.serialization.Serializable
* @property isWarning if warning row
* @property isEnabled enabled
*/
@Stable
@Serializable
data class TokenActionButtonUM(
val id: String,
val text: TextReference,
@DrawableRes val iconResId: Int,
val onClick: () -> Unit,
val isWarning: Boolean,
val isEnabled: Boolean = true,
val hasDivider: Boolean = false,
)

View file

@ -0,0 +1,13 @@
package com.tangem.feature.wallet.presentation.wallet.state.model
import androidx.compose.runtime.Immutable
@Immutable
internal sealed class TokenSyncProgressUM {
data object Idle : TokenSyncProgressUM()
data class InProgress(val progressPercent: Int) : TokenSyncProgressUM()
data object Completed : TokenSyncProgressUM()
}

View file

@ -6,7 +6,12 @@ import com.tangem.core.ui.extensions.TextReference
@Immutable
data class WalletAdditionalInfo(
val hideable: Boolean,
val content: TextReference,
val content: Content,
val isHotBackedUp: Boolean = false,
val shouldShowProgress: Boolean = false,
)
) {
@Immutable
sealed interface Content {
data class Text(val text: TextReference) : Content
data class SyncProgress(val progressPercent: Int) : Content
}
}

View file

@ -9,24 +9,6 @@ import com.tangem.feature.wallet.impl.R
internal object WalletAlertUM {
fun seedPhraseConfirm(onClick: () -> Unit): DialogMessage {
return DialogMessage(
message = resourceReference(R.string.warning_seedphrase_issue_answer_yes),
firstActionBuilder = {
okAction(onClick = onClick)
},
)
}
fun seedPhraseDismiss(onClick: () -> Unit): DialogMessage {
return DialogMessage(
message = resourceReference(R.string.warning_seedphrase_issue_answer_no),
firstActionBuilder = {
okAction(onClick = onClick)
},
)
}
fun unableHideToken(cryptoCurrency: CryptoCurrency): DialogMessage {
return DialogMessage(
title = resourceReference(

View file

@ -85,7 +85,10 @@ internal sealed interface WalletCardState {
private companion object {
val defaultAdditionalInfo: WalletAdditionalInfo
get() = WalletAdditionalInfo(hideable = true, content = EMPTY_BALANCE_TEXT)
get() = WalletAdditionalInfo(
hideable = true,
content = WalletAdditionalInfo.Content.Text(EMPTY_BALANCE_TEXT),
)
}
}

View file

@ -1,5 +1,6 @@
package com.tangem.feature.wallet.presentation.wallet.state.model
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
import com.tangem.domain.models.TokenReceiveConfig
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.account.AccountName
@ -34,6 +35,9 @@ internal sealed interface WalletDialogConfig {
@Serializable
data class TokenActionList(
val actionList: ImmutableList<TokenActionButtonUM>,
val tokenRowUM: TangemTokenRowUM?,
val offsetY: Float,
val offsetX: Float,
) : WalletDialogConfig
@Serializable

View file

@ -55,34 +55,6 @@ sealed class WalletNotification(val config: NotificationConfig) {
onClick = onSupportClick,
),
)
data class SeedPhraseNotification(
val onDeclineClick: () -> Unit,
val onConfirmClick: () -> Unit,
) : Critical(
title = resourceReference(R.string.warning_seedphrase_issue_title),
subtitle = resourceReference(R.string.warning_seedphrase_issue_message),
buttonsState = NotificationConfig.ButtonsState.SecondaryPairButtonsConfig(
leftText = resourceReference(R.string.common_no),
onLeftClick = onDeclineClick,
rightText = resourceReference(R.string.common_yes),
onRightClick = onConfirmClick,
),
)
data class SeedPhraseSecondNotification(
val onDeclineClick: () -> Unit,
val onConfirmClick: () -> Unit,
) : Critical(
title = resourceReference(R.string.warning_seedphrase_action_required_title),
subtitle = resourceReference(R.string.warning_seedphrase_contacted_support),
buttonsState = NotificationConfig.ButtonsState.SecondaryPairButtonsConfig(
leftText = resourceReference(R.string.seed_warning_no),
onLeftClick = onDeclineClick,
rightText = resourceReference(R.string.seed_warning_yes),
onRightClick = onConfirmClick,
),
)
}
sealed class Warning(
@ -151,7 +123,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 +131,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 +451,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

@ -7,10 +7,7 @@ import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.ds.message.TangemMessageButtonUM
import com.tangem.core.ui.ds.message.TangemMessageEffect
import com.tangem.core.ui.ds.message.TangemMessageUM
import com.tangem.core.ui.extensions.pluralReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.wallet.impl.R
import kotlinx.collections.immutable.persistentListOf
@ -143,64 +140,6 @@ internal sealed class WalletNotificationUM(val messageUM: TangemMessageUM, val t
type = WalletNotificationType.Critical,
)
data class SeedPhraseNotification(
val onDeclineClick: () -> Unit,
val onConfirmClick: () -> Unit,
) : WalletNotificationUM(
messageUM = TangemMessageUM(
id = "SeedPhraseIssueNotification",
title = resourceReference(id = R.string.warning_seedphrase_issue_title),
subtitle = resourceReference(id = R.string.warning_seedphrase_issue_message),
messageEffect = TangemMessageEffect.Warning,
buttonsUM = persistentListOf(
TangemMessageButtonUM(
text = resourceReference(id = R.string.common_no),
type = TangemButtonType.PrimaryInverse,
onClick = onDeclineClick,
),
TangemMessageButtonUM(
text = resourceReference(id = R.string.common_yes),
type = TangemButtonType.PrimaryInverse,
onClick = onConfirmClick,
),
),
iconUM = TangemIconUM.Icon(
iconRes = R.drawable.ic_attention_default_24,
tintReference = { TangemTheme.colors2.graphic.neutral.primary },
),
),
type = WalletNotificationType.Critical,
)
data class SeedPhraseSecondNotification(
val onDeclineClick: () -> Unit,
val onConfirmClick: () -> Unit,
) : WalletNotificationUM(
messageUM = TangemMessageUM(
id = "SeedPhraseSecondIssueNotification",
title = resourceReference(id = R.string.warning_seedphrase_action_required_title),
subtitle = resourceReference(id = R.string.warning_seedphrase_contacted_support),
messageEffect = TangemMessageEffect.Warning,
iconUM = TangemIconUM.Icon(
iconRes = R.drawable.ic_attention_default_24,
tintReference = { TangemTheme.colors2.graphic.neutral.primary },
),
buttonsUM = persistentListOf(
TangemMessageButtonUM(
text = resourceReference(id = R.string.seed_warning_no),
type = TangemButtonType.PrimaryInverse,
onClick = onDeclineClick,
),
TangemMessageButtonUM(
text = resourceReference(id = R.string.seed_warning_yes),
type = TangemButtonType.PrimaryInverse,
onClick = onConfirmClick,
),
),
),
type = WalletNotificationType.Critical,
)
data class MissingBackup(val onClick: () -> Unit) : WalletNotificationUM(
messageUM = TangemMessageUM(
id = "MissingBackupNotification",
@ -355,6 +294,43 @@ internal sealed class WalletNotificationUM(val messageUM: TangemMessageUM, val t
),
type = WalletNotificationType.Warning,
)
data class TangemPayRefreshNeeded(
private val onRefreshClick: () -> Unit,
private val buttonText: TextReference,
private val shouldShowProgress: Boolean,
) : WalletNotificationUM(
messageUM = TangemMessageUM(
id = "TangemPayRefreshNeeded",
title = resourceReference(id = R.string.tangempay_payment_account_sync_needed),
subtitle = resourceReference(id = R.string.tangempay_use_tangem_device_to_restore_payment_account),
buttonsUM = persistentListOf(
TangemMessageButtonUM(
text = buttonText,
iconRes = R.drawable.ic_tangem_24,
onClick = onRefreshClick,
type = TangemButtonType.Primary,
isLoading = shouldShowProgress,
),
),
messageEffect = TangemMessageEffect.Card,
isCentered = true,
),
type = WalletNotificationType.Warning,
)
data object TangemPayUnreachable : WalletNotificationUM(
messageUM = TangemMessageUM(
id = "TangemPayUnreachable",
title = resourceReference(id = R.string.tangempay_temporarily_unavailable),
subtitle = resourceReference(id = R.string.tangempay_service_unreachable_try_later),
iconUM = TangemIconUM.Icon(
iconRes = R.drawable.ic_attention_default_24,
tintReference = { TangemTheme.colors2.graphic.status.attention },
),
),
type = WalletNotificationType.Warning,
)
// endregion
// region Promo

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,9 @@ 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
abstract val tokenSyncProgressUM: TokenSyncProgressUM
data class Content(
override val pullToRefreshConfig: PullToRefreshConfig,
@ -35,6 +39,9 @@ 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,
override val tokenSyncProgressUM: TokenSyncProgressUM = TokenSyncProgressUM.Idle,
) : MultiCurrency()
data class Locked(
@ -54,6 +61,9 @@ 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 tokenSyncProgressUM: TokenSyncProgressUM = TokenSyncProgressUM.Idle
}
}

View file

@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.state.model
import androidx.compose.runtime.Immutable
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
import com.tangem.core.ui.ds.button.TangemButtonUM
import com.tangem.features.tangempay.entity.TangemPayMainUM
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentListOf
@ -23,7 +24,7 @@ internal sealed interface WalletUM {
val type: WalletType
val tangemPayState: TangemPayState
val tangemPayMainUM: TangemPayMainUM
data class Content(
override val pullToRefreshConfig: PullToRefreshConfig,
@ -34,7 +35,7 @@ internal sealed interface WalletUM {
override val tokensListUM: WalletTokensListUM,
override val nftState: WalletNFTItemUM,
override val type: WalletType,
override val tangemPayState: TangemPayState,
override val tangemPayMainUM: TangemPayMainUM,
) : WalletUM
data class Locked(
@ -47,6 +48,6 @@ internal sealed interface WalletUM {
override val pullToRefreshConfig = PullToRefreshConfig(false, {})
override val tokensListUM: WalletTokensListUM = WalletTokensListUM.Locked
override val nftState: WalletNFTItemUM = WalletNFTItemUM.Hidden
override val tangemPayState: TangemPayState = TangemPayState.Empty
override val tangemPayMainUM: TangemPayMainUM = TangemPayMainUM.Empty
}
}

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

@ -5,7 +5,7 @@ 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 kotlinx.collections.immutable.toImmutableList
import timber.log.Timber
import com.tangem.utils.logging.TangemLogger
internal class DeleteWalletTransformer(
private val selectedWalletIndex: Int,
@ -31,7 +31,7 @@ internal class DeleteWalletTransformer(
wallets = (prevState.wallets - deletedWalletState).toImmutableList(),
)
else -> {
Timber.e("Wallets does not contain deleted wallet")
TangemLogger.e("Wallets does not contain deleted wallet")
prevState
}
}

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

@ -6,7 +6,7 @@ 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
import kotlinx.collections.immutable.toPersistentList
import timber.log.Timber
import com.tangem.utils.logging.TangemLogger
/**
* Transformer that renames wallets
@ -51,7 +51,7 @@ internal class RenameWalletsTransformer(
is WalletState.MultiCurrency.Locked,
is WalletState.SingleCurrency.Locked,
-> {
Timber.e("Impossible to rename wallet in locked state")
TangemLogger.e("Impossible to rename wallet in locked state")
prevState
}
}
@ -63,7 +63,7 @@ internal class RenameWalletsTransformer(
prevState.copy(walletsBalanceUM = prevState.walletsBalanceUM.copySealed(name = newName))
}
is WalletUM.Locked -> {
Timber.e("Impossible to rename wallet in locked state")
TangemLogger.e("Impossible to rename wallet in locked state")
prevState
}
}

View file

@ -11,7 +11,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.toPersistentList
import timber.log.Timber
import com.tangem.utils.logging.TangemLogger
internal class SetCryptoCurrencyActionsTransformer(
private val tokenActionsState: TokenActionsState,
@ -26,11 +26,11 @@ internal class SetCryptoCurrencyActionsTransformer(
prevState.copy(buttons = tokenActionsState.toManageButtons())
}
is WalletState.SingleCurrency.Locked -> {
Timber.w("Impossible to load primary currency status for locked wallet")
TangemLogger.w("Impossible to load primary currency status for locked wallet")
prevState
}
is WalletState.MultiCurrency -> {
Timber.w("Impossible to load crypto currency actions for multi-currency wallet")
TangemLogger.w("Impossible to load crypto currency actions for multi-currency wallet")
prevState
}
}

View file

@ -13,7 +13,7 @@ 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.transformers.converter.SingleWalletOnrampTransactionConverter
import kotlinx.collections.immutable.toPersistentList
import timber.log.Timber
import com.tangem.utils.logging.TangemLogger
internal class SetExpressStatusesTransformer(
userWalletId: UserWalletId,
@ -47,11 +47,11 @@ internal class SetExpressStatusesTransformer(
)
}
is WalletState.SingleCurrency.Locked -> {
Timber.w("Impossible to load express statuses for locked wallet")
TangemLogger.w("Impossible to load express statuses for locked wallet")
prevState
}
is WalletState.MultiCurrency -> {
Timber.w("Impossible to load express statuses for multi-currency wallet")
TangemLogger.w("Impossible to load express statuses for multi-currency wallet")
prevState
}
}

View file

@ -9,7 +9,7 @@ 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.transformers.converter.SingleWalletCardStateConverter
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.SingleWalletMarketPriceConverter
import timber.log.Timber
import com.tangem.utils.logging.TangemLogger
@Deprecated("Remove with main toggle [DesignFeatureToggles.isRedesignEnabled]")
internal class SetPrimaryCurrencyTransformer(
@ -27,11 +27,11 @@ internal class SetPrimaryCurrencyTransformer(
)
}
is WalletState.SingleCurrency.Locked -> {
Timber.w("Impossible to load primary currency status for locked wallet")
TangemLogger.w("Impossible to load primary currency status for locked wallet")
prevState
}
is WalletState.MultiCurrency -> {
Timber.w("Impossible to load primary currency status for multi-currency wallet")
TangemLogger.w("Impossible to load primary currency status for multi-currency wallet")
prevState
}
}

View file

@ -13,7 +13,7 @@ import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory
import com.tangem.feature.wallet.presentation.wallet.state.model.*
import com.tangem.feature.wallet.presentation.wallet.state.utils.disableButtons
import timber.log.Timber
import com.tangem.utils.logging.TangemLogger
import java.math.BigDecimal
internal class SetTokenListErrorTransformer(
@ -39,11 +39,11 @@ internal class SetTokenListErrorTransformer(
)
}
is WalletState.MultiCurrency.Locked -> {
Timber.w("Impossible to load tokens list for locked wallet")
TangemLogger.w("Impossible to load tokens list for locked wallet")
prevState
}
is WalletState.SingleCurrency -> {
Timber.w("Impossible to load tokens list for single-currency wallet")
TangemLogger.w("Impossible to load tokens list for single-currency wallet")
prevState
}
}
@ -61,14 +61,14 @@ internal class SetTokenListErrorTransformer(
walletsBalanceUM = walletUM.walletsBalanceUM.toLoadedState(),
tokensListUM = WalletTokensListUM.Empty(
onEmptyClick = {
clickIntents.onManageTokensClick(walletUM.walletsBalanceUM.id)
clickIntents.onTokenSyncManageClick(walletUM.walletsBalanceUM.id)
},
),
buttons = walletUM.disableButtons(),
)
}
is WalletUM.Locked -> {
Timber.w("Impossible to load tokens list for locked wallet")
TangemLogger.w("Impossible to load tokens list for locked wallet")
walletUM
}
}

View file

@ -1,19 +1,19 @@
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
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.TokenListStateConverter
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.WalletTokensListUMConverter
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.*
import com.tangem.feature.wallet.presentation.wallet.state.utils.enableButtons
import timber.log.Timber
import com.tangem.features.tangempay.entity.TangemPayMainUM
import com.tangem.utils.logging.TangemLogger
import java.math.BigDecimal
@Suppress("LongParameterList")
internal class SetTokenListTransformer(
private val params: TokenConverterParams,
private val userWallet: UserWallet,
@ -23,24 +23,33 @@ internal class SetTokenListTransformer(
private val stakingAvailabilityMap: Map<CryptoCurrency, StakingAvailability> = emptyMap(),
private val shouldShowMainPromo: Boolean,
private val isAccountsModeEnabled: Boolean,
private val isRedesignEnabled: Boolean,
) : WalletStateTransformer(userWallet.walletId) {
private val tangemPayConverter by lazy {
TangemPayMainBlockConverter(
tangemPayClickIntents = clickIntents,
isRedesignEnabled = isRedesignEnabled,
)
}
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(),
)
}
is WalletState.MultiCurrency.Locked -> {
Timber.w("Impossible to load tokens list for locked wallet")
TangemLogger.w("Impossible to load tokens list for locked wallet")
prevState
}
is WalletState.SingleCurrency,
-> {
Timber.w("Impossible to load tokens list for single-currency wallet")
TangemLogger.w("Impossible to load tokens list for single-currency wallet")
prevState
}
}
@ -51,12 +60,13 @@ internal class SetTokenListTransformer(
is WalletUM.Content -> {
walletUM.copy(
walletsBalanceUM = walletUM.walletsBalanceUM.toLoadedState2(),
tangemPayMainUM = walletUM.tangemPayMainUM.toLoadedState(),
tokensListUM = toLoadedState(),
buttons = walletUM.enableButtons(),
)
}
is WalletUM.Locked -> {
Timber.w("Impossible to load tokens list for locked wallet")
TangemLogger.w("Impossible to load tokens list for locked wallet")
walletUM
}
}
@ -97,11 +107,22 @@ 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(
onEmptyClick = {
clickIntents.onManageTokensClick(userWallet.walletId)
clickIntents.onTokenSyncManageClick(userWallet.walletId)
},
)
}

View file

@ -1,51 +1,34 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAdditionalInfo
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory
import com.tangem.feature.wallet.presentation.wallet.state.model.TokenSyncProgressUM
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
internal class SetTokenSyncProgressTransformer(
userWalletId: UserWalletId,
private val progressPercent: Int,
) : WalletStateTransformer(userWalletId) {
private val userWallet: UserWallet,
private val progress: TokenSyncProgressUM,
) : WalletStateTransformer(userWallet.walletId) {
override fun transform(prevState: WalletState): WalletState {
return when (prevState) {
is WalletState.MultiCurrency.Content -> {
val updatedCardState = updateCardState(prevState.walletCardState)
prevState.copy(walletCardState = updatedCardState)
}
else -> {
prevState
}
is WalletState.MultiCurrency.Content -> prevState.copy(
walletCardState = updateCardState(prevState.walletCardState),
tokenSyncProgressUM = progress,
)
else -> prevState
}
}
override fun transform(walletUM: WalletUM): WalletUM {
return walletUM
}
override fun transform(walletUM: WalletUM): WalletUM = walletUM
private fun updateCardState(cardState: WalletCardState): WalletCardState {
val additionalInfo = WalletAdditionalInfo(
hideable = false,
content = resourceReference(
id = R.string.initial_wallet_sync_restore_progress,
formatArgs = wrappedList(progressPercent),
),
shouldShowProgress = true,
)
val additionalInfo = WalletAdditionalInfoFactory.resolve(wallet = userWallet, syncProgress = progress)
return when (cardState) {
is WalletCardState.Loading -> {
cardState.copy(additionalInfo = additionalInfo)
}
is WalletCardState.Content -> {
cardState.copy(additionalInfo = additionalInfo)
}
is WalletCardState.Loading -> cardState.copy(additionalInfo = additionalInfo)
is WalletCardState.Content -> cardState.copy(additionalInfo = additionalInfo)
else -> cardState
}
}

View file

@ -12,7 +12,7 @@ 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.transformers.converter.TxHistoryItemStateConverter
import kotlinx.collections.immutable.toImmutableList
import timber.log.Timber
import com.tangem.utils.logging.TangemLogger
internal class SetTxHistoryCountErrorTransformer(
private val userWallet: UserWallet,
@ -46,7 +46,7 @@ internal class SetTxHistoryCountErrorTransformer(
is WalletState.SingleCurrency.Locked,
is WalletState.MultiCurrency,
-> {
Timber.w("Impossible to load transactions history for multi-currency wallet")
TangemLogger.w("Impossible to load transactions history for multi-currency wallet")
prevState
}
}

View file

@ -9,7 +9,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
import timber.log.Timber
import com.tangem.utils.logging.TangemLogger
internal class SetTxHistoryCountTransformer(
userWalletId: UserWalletId,
@ -24,11 +24,11 @@ internal class SetTxHistoryCountTransformer(
)
is WalletState.SingleCurrency.Locked,
-> {
Timber.w("Impossible to load transactions history for locked wallet")
TangemLogger.w("Impossible to load transactions history for locked wallet")
prevState
}
is WalletState.MultiCurrency -> {
Timber.w("Impossible to load transactions history for multi-currency wallet")
TangemLogger.w("Impossible to load transactions history for multi-currency wallet")
prevState
}
}
@ -40,7 +40,7 @@ internal class SetTxHistoryCountTransformer(
private fun TxHistoryState.toLoadingState(): TxHistoryState {
return if (this is TxHistoryState.Content) {
Timber.d("Load transactions history: $transactionsCount")
TangemLogger.d("Load transactions history: $transactionsCount")
copy(
contentItems = contentItems.apply {

View file

@ -6,7 +6,7 @@ import com.tangem.domain.txhistory.models.TxHistoryListError
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
import timber.log.Timber
import com.tangem.utils.logging.TangemLogger
internal class SetTxHistoryItemsErrorTransformer(
userWalletId: UserWalletId,
@ -18,11 +18,11 @@ internal class SetTxHistoryItemsErrorTransformer(
return when (prevState) {
is WalletState.SingleCurrency.Content -> prevState.copy(txHistoryState = createErrorState())
is WalletState.SingleCurrency.Locked -> {
Timber.w("Impossible to load transactions history for locked wallet")
TangemLogger.w("Impossible to load transactions history for locked wallet")
prevState
}
is WalletState.MultiCurrency -> {
Timber.w("Impossible to load transactions history for multi-currency wallet")
TangemLogger.w("Impossible to load transactions history for multi-currency wallet")
prevState
}
}

View file

@ -9,7 +9,7 @@ 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.transformers.converter.TxHistoryItemFlowConverter
import kotlinx.coroutines.flow.Flow
import timber.log.Timber
import com.tangem.utils.logging.TangemLogger
internal class SetTxHistoryItemsTransformer(
userWallet: UserWallet,
@ -23,11 +23,11 @@ internal class SetTxHistoryItemsTransformer(
txHistoryState = prevState.txHistoryState.toContentState(),
)
is WalletState.SingleCurrency.Locked -> {
Timber.w("Impossible to load transactions history for locked wallet")
TangemLogger.w("Impossible to load transactions history for locked wallet")
prevState
}
is WalletState.MultiCurrency -> {
Timber.w("Impossible to load transactions history for multi-currency wallet")
TangemLogger.w("Impossible to load transactions history for multi-currency wallet")
prevState
}
}

View file

@ -7,7 +7,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import timber.log.Timber
import com.tangem.utils.logging.TangemLogger
internal class SetWarningsTransformer(
userWalletId: UserWalletId,
@ -23,7 +23,7 @@ internal class SetWarningsTransformer(
is WalletState.MultiCurrency.Locked,
is WalletState.SingleCurrency.Locked,
-> {
Timber.w("Impossible to update notifications for locked wallet")
TangemLogger.w("Impossible to update notifications for locked wallet")
prevState
}
}
@ -36,7 +36,7 @@ internal class SetWarningsTransformer(
notificationsCarousel = notificationsCarousel,
)
is WalletUM.Locked -> {
Timber.w("Impossible to update notifications for locked wallet")
TangemLogger.w("Impossible to update notifications for locked wallet")
walletUM
}
}

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

@ -11,13 +11,14 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletLoadingStateFactory
import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toPersistentList
import timber.log.Timber
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 {
@ -25,6 +26,7 @@ internal class UnlockWalletTransformer(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isTangemPayRefactorEnabled = isTangemPayRefactorEnabled,
)
}
@ -65,7 +67,7 @@ internal class UnlockWalletTransformer(
is WalletState.MultiCurrency.Content,
is WalletState.SingleCurrency.Content,
-> {
Timber.e("Impossible to unlock wallet with not locked state")
TangemLogger.e("Impossible to unlock wallet with not locked state")
prevState
}
}
@ -77,7 +79,7 @@ internal class UnlockWalletTransformer(
userWallet = unlockedWallet,
)
is WalletUM.Content -> {
Timber.e("Impossible to unlock wallet with not locked state")
TangemLogger.e("Impossible to unlock wallet with not locked state")
walletUM
}
}

View file

@ -4,10 +4,11 @@ import com.tangem.domain.card.common.util.getCardsCount
import com.tangem.domain.models.wallet.UserWallet
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.TokenSyncProgressUM
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
import timber.log.Timber
import com.tangem.utils.logging.TangemLogger
internal class UpdateWalletCardsCountTransformer(
private val userWallet: UserWallet,
@ -17,7 +18,9 @@ internal class UpdateWalletCardsCountTransformer(
override fun transform(prevState: WalletState): WalletState {
return when (prevState) {
is WalletState.MultiCurrency.Content -> {
prevState.copy(walletCardState = prevState.walletCardState.toUpdatedState())
prevState.copy(
walletCardState = prevState.walletCardState.toUpdatedState(prevState.tokenSyncProgressUM),
)
}
is WalletState.SingleCurrency.Content -> {
prevState.copy(walletCardState = prevState.walletCardState.toUpdatedState())
@ -25,7 +28,7 @@ internal class UpdateWalletCardsCountTransformer(
is WalletState.MultiCurrency.Locked,
is WalletState.SingleCurrency.Locked,
-> {
Timber.e("Impossible to update wallet cards count for locked wallet")
TangemLogger.e("Impossible to update wallet cards count for locked wallet")
prevState
}
}
@ -35,10 +38,12 @@ internal class UpdateWalletCardsCountTransformer(
return walletUM // todo redesign main
}
private fun WalletCardState.toUpdatedState(): WalletCardState {
private fun WalletCardState.toUpdatedState(
syncProgress: TokenSyncProgressUM = TokenSyncProgressUM.Idle,
): WalletCardState {
return when (this) {
is WalletCardState.Content -> copy(
additionalInfo = WalletAdditionalInfoFactory.resolve(wallet = userWallet),
additionalInfo = WalletAdditionalInfoFactory.resolve(wallet = userWallet, syncProgress = syncProgress),
imageResId = walletImageResolver.resolve(userWallet = userWallet),
cardCount = (userWallet as? UserWallet.Cold)?.getCardsCount(),
)

View file

@ -23,11 +23,25 @@ internal class MultiWalletCurrencyActionsConverter(
) : Converter<TokenActionsState, ImmutableList<TokenActionButtonUM>> {
override fun convert(value: TokenActionsState): ImmutableList<TokenActionButtonUM> {
return value.states
.filterIfSingleWithToken()
val actionList = value.states.filterIfSingleWithToken()
.mapNotNull {
mapTokenActionState(actionsState = it, cryptoCurrencyStatus = value.cryptoCurrencyStatus)
}
return actionList
.mapIndexed { index, action ->
val analyticsAction = TokenActionsState.ActionState.Analytics::class.java.simpleName
val hideTokenAction = TokenActionsState.ActionState.HideToken::class.java.simpleName
if (
action.id == analyticsAction ||
index != actionList.lastIndex && actionList[index + 1].id == hideTokenAction
) {
action.copy(hasDivider = true)
} else {
action
}
}
.toImmutableList()
}
@ -111,6 +125,7 @@ internal class MultiWalletCurrencyActionsConverter(
}
return TokenActionButtonUM(
id = actionsState::class.java.simpleName,
text = title,
iconResId = icon,
onClick = action,

View file

@ -0,0 +1,126 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter
import androidx.compose.ui.text.SpanStyle
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.core.ui.format.bigdecimal.formatStyled
import com.tangem.core.ui.res.TangemTheme
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,
private val isRedesignEnabled: Boolean,
) : 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 = if (isRedesignEnabled) {
balance.formatStyled {
fiat(
fiatCurrencyCode = currency.currencyCode,
fiatCurrencySymbol = currency.symbol,
spanStyleReference = { SpanStyle(color = TangemTheme.colors2.text.neutral.secondary) },
)
}
} else {
stringReference(
balance.format {
fiat(fiatCurrencyCode = currency.currencyCode, fiatCurrencySymbol = currency.symbol)
},
)
}
return formattedBalance
}
}

View file

@ -76,8 +76,13 @@ internal class WalletTokenCurrencyItemConverter(
onItemLongClick = when (value.value) {
CryptoCurrencyStatus.Loading -> null
else -> {
{
clickIntents.onTokenItemLongClick(accountId, value)
{ offset, tokenRowUM ->
clickIntents.onTokenItemLongClickV2(
accountId = accountId,
cryptoCurrencyStatus = value,
offset = offset,
tokenRowUM = tokenRowUM,
)
}
}
},

View file

@ -76,20 +76,17 @@ internal class WalletTokensListUMConverter(
onEmptyClick = { clickIntents.onManageTokensClick(value.mainAccount.accountId) },
)
} else {
val isCollapsable = value.accountStatuses.count {
it is AccountStatus.CryptoPortfolio && it.account.tokensCount > 0
} > 1
val tokenListUM = value.accountStatuses
.filterIsInstance<AccountStatus.CryptoPortfolio>()
.asSequence()
.flatMap { accountStatus ->
if (isAccountsModeEnabled) {
val isCollapsable = accountStatus.tokenList.flattenCurrencies().isNotEmpty()
val isExpanded = expandedAccounts.contains(accountStatus.account.accountId)
sequenceOf(
TokensListItemUM2.Portfolio(
tokenRowUM = accountRowConverter.convert(accountStatus),
isExpanded = isExpanded || !isCollapsable,
isExpanded = isExpanded,
isCollapsable = isCollapsable,
onEmptyClick = { clickIntents.onManageTokensClick(accountStatus.account.accountId) },
tokenList = getTokenListItems(
@ -166,7 +163,7 @@ internal class WalletTokensListUMConverter(
return if (accountList.flattenCurrencies().size > 1 && !selectedWallet.isSingleWalletWithToken()) {
TangemButtonUM(
text = resourceReference(R.string.organize_tokens_title),
isEnabled = accountList.totalFiatBalance is TotalFiatBalance.Loading,
isEnabled = accountList.totalFiatBalance !is TotalFiatBalance.Loading,
size = TangemButtonSize.X9,
shape = TangemButtonShape.Rounded,
type = TangemButtonType.PrimaryInverse,

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 {
@ -67,7 +69,7 @@ internal class WalletLoadingStateFactory(
is UserWallet.Cold -> WalletType.Cold
is UserWallet.Hot -> WalletType.Hot
},
tangemPayState = TangemPayState.Empty,
tangemPayMainUM = TangemPayMainUM.Empty,
)
}
@ -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

@ -17,9 +17,9 @@ import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTokenListErrorTransformer
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTokenListTransformer
import com.tangem.feature.wallet.presentation.wallet.state.transformers.TokenConverterParams
import com.tangem.utils.logging.TangemLogger
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import timber.log.Timber
import java.math.BigDecimal
/**
@ -103,6 +103,7 @@ internal abstract class BasicAccountListSubscriber : BasicWalletSubscriber() {
stakingAvailabilityMap = stakingAvailabilityMap,
shouldShowMainPromo = shouldShowMainPromo,
isAccountsModeEnabled = isAccountMode,
isRedesignEnabled = true,
),
)
}
@ -126,7 +127,7 @@ internal abstract class BasicAccountListSubscriber : BasicWalletSubscriber() {
?: return
},
ifError = { e ->
Timber.e("Failed to load token list: $e")
TangemLogger.e("Failed to load token list: $e")
stateController.update(
SetTokenListErrorTransformer(
selectedWallet = userWallet,
@ -165,6 +166,7 @@ internal abstract class BasicAccountListSubscriber : BasicWalletSubscriber() {
stakingAvailabilityMap = stakingAvailabilityMap,
shouldShowMainPromo = shouldShowMainPromo,
isAccountsModeEnabled = false,
isRedesignEnabled = false,
),
)
}

View file

@ -21,7 +21,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import timber.log.Timber
import com.tangem.utils.logging.TangemLogger
@Suppress("LongParameterList")
internal class TangemPayMainSubscriber @AssistedInject constructor(
@ -67,7 +67,7 @@ internal class TangemPayMainSubscriber @AssistedInject constructor(
}
TangemPayCustomerInfoError.UnknownError -> {
// hide TangemPay block
Timber.e("Failed when loading main screen TangemPay info: $tangemPayError")
TangemLogger.e("Failed when loading main screen TangemPay info: $tangemPayError")
stateController.update(
transformer = TangemPayHiddenStateTransformer(userWalletId),
)

View file

@ -0,0 +1,45 @@
package com.tangem.feature.wallet.presentation.wallet.subscribers
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.tokensync.model.TokenSyncProgress
import com.tangem.domain.tokensync.usecase.ObserveTokenSyncUseCase
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.model.TokenSyncProgressUM
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTokenSyncProgressTransformer
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.onEach
internal class TokenSyncSubscriber @AssistedInject constructor(
@Assisted private val userWallet: UserWallet,
private val stateController: WalletStateController,
private val observeTokenSyncUseCase: ObserveTokenSyncUseCase,
) : WalletSubscriber() {
override fun create(coroutineScope: CoroutineScope): Flow<*> {
return observeTokenSyncUseCase(userWallet.walletId)
.onEach { current -> handleProgress(userWallet, current) }
}
private fun handleProgress(userWallet: UserWallet, current: TokenSyncProgress) {
val progressUM = when (current) {
is TokenSyncProgress.InProgress -> TokenSyncProgressUM.InProgress(current.progressPercent)
is TokenSyncProgress.Completed -> TokenSyncProgressUM.Completed
is TokenSyncProgress.Idle -> TokenSyncProgressUM.Idle
}
stateController.update(
SetTokenSyncProgressTransformer(
userWallet = userWallet,
progress = progressUM,
),
)
}
@AssistedFactory
interface Factory {
fun create(userWallet: UserWallet): TokenSyncSubscriber
}
}

View file

@ -6,7 +6,7 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import timber.log.Timber
import com.tangem.utils.logging.TangemLogger
/**
* Component for implementation of flow subscription
@ -18,7 +18,7 @@ internal abstract class WalletSubscriber {
protected abstract fun create(coroutineScope: CoroutineScope): Flow<*>
fun subscribe(coroutineScope: CoroutineScope, dispatchers: CoroutineDispatcher): Job {
Timber.d("Subscribe on ${this::class.simpleName}")
TangemLogger.d("Subscribe on ${this::class.simpleName}")
return create(coroutineScope)
.flowOn(dispatchers)

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

@ -12,6 +12,7 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.pager.HorizontalPager
@ -49,6 +50,7 @@ import com.tangem.core.ui.components.background.northernlights.NorthernLightsBac
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheetDraggableHeader
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
import com.tangem.core.ui.components.containers.pullToRefresh.TangemPullToRefreshSlidingContainer
import com.tangem.core.ui.components.haze.hazeEffectTangem
import com.tangem.core.ui.components.haze.hazeSourceTangem
import com.tangem.core.ui.components.rememberIsKeyboardVisible
import com.tangem.core.ui.components.sheetscaffold.*
@ -57,6 +59,7 @@ import com.tangem.core.ui.ds.topbar.collapsing.TangemCollapsingTopBar
import com.tangem.core.ui.ds.topbar.collapsing.rememberTangemExitUntilCollapsedScrollBehavior
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.res.*
import com.tangem.core.ui.utils.TangemSharedTransitionLayout
import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewData
import com.tangem.feature.wallet.presentation.wallet.state.model.NOT_INITIALIZED_WALLET_INDEX
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletBalanceUM
@ -68,6 +71,10 @@ import com.tangem.feature.wallet.presentation.wallet.ui.components.common.Wallet
import com.tangem.feature.wallet.presentation.wallet.ui.components.common.WalletPagerIndicator
import com.tangem.feature.wallet.presentation.wallet.ui.components.common.WalletTopBar
import com.tangem.feature.wallet.presentation.wallet.ui.utils.lazyListStateMapSaver
import com.tangem.features.tangempay.component.TangemPayMainBlockComponent
import com.tangem.features.tangempay.entity.TangemPayMainUM
import dev.chrisbanes.haze.HazeProgressive
import dev.chrisbanes.haze.HazeTint
import kotlinx.coroutines.launch
import kotlin.math.abs
@ -77,6 +84,8 @@ private const val MARKET_HINT_THRESHOLD = 0.5f
@Composable
internal fun WalletScreen2(
state: WalletScreenState,
tangemPayComponent: TangemPayMainBlockComponent,
modifier: Modifier = Modifier,
bottomSheetContent: @Composable (() -> Unit),
bottomSheetHeaderHeightProvider: () -> Dp,
onBottomSheetStateChange: (BottomSheetState) -> Unit,
@ -91,12 +100,28 @@ internal fun WalletScreen2(
pageCount = { state.wallets2.size },
)
val listStates = rememberSaveable(saver = lazyListStateMapSaver(walletsPagerState.pageCount)) {
mutableMapOf<Int, LazyListState>().apply {
repeat(walletsPagerState.pageCount) { index -> put(index, LazyListState()) }
}
}
val isTopOverscrollEnabled by remember {
derivedStateOf {
val listState = listStates[walletsPagerState.currentPage] ?: return@derivedStateOf false
listState.layoutInfo.totalItemsCount > 0 &&
!listState.canScrollBackward && !listState.canScrollForward ||
listState.canScrollBackward && !listState.canScrollForward
}
}
val partialCollapsedHeight = 64.dp + statusBarHeight
val balanceBlockHeight = 320.dp + partialCollapsedHeight
val behavior = rememberTangemExitUntilCollapsedScrollBehavior(
expandedHeight = balanceBlockHeight,
partialCollapsedHeight = partialCollapsedHeight,
snapAnimationSpec = spring(stiffness = Spring.StiffnessMedium),
isTopOverscrollEnabled = isTopOverscrollEnabled,
)
val coroutineScope = rememberCoroutineScope()
@ -104,10 +129,13 @@ internal fun WalletScreen2(
WalletContent2(
state = state,
walletsPagerState = walletsPagerState,
tangemPayComponent = tangemPayComponent,
behavior = behavior,
bottomSheetContent = bottomSheetContent,
bottomSheetHeaderHeightProvider = bottomSheetHeaderHeightProvider,
onBottomSheetStateChange = onBottomSheetStateChange,
modifier = modifier,
listStates = listStates,
)
WalletEventEffect(
@ -128,7 +156,10 @@ internal fun WalletScreen2(
private fun WalletContent2(
state: WalletScreenState,
walletsPagerState: PagerState,
tangemPayComponent: TangemPayMainBlockComponent,
behavior: TangemCollapsingAppBarBehavior,
listStates: Map<Int, LazyListState>,
modifier: Modifier = Modifier,
bottomSheetHeaderHeightProvider: () -> Dp,
onBottomSheetStateChange: (BottomSheetState) -> Unit,
bottomSheetContent: @Composable (() -> Unit),
@ -144,6 +175,7 @@ private fun WalletContent2(
}
BaseScaffoldWithMarkets(
modifier = modifier,
state = state,
bottomSheetHeaderHeightProvider = bottomSheetHeaderHeightProvider,
onBottomSheetStateChange = onBottomSheetStateChange,
@ -168,12 +200,6 @@ private fun WalletContent2(
}
}
val listStates = rememberSaveable(saver = lazyListStateMapSaver(walletsPagerState.pageCount)) {
mutableMapOf<Int, LazyListState>().apply {
repeat(walletsPagerState.pageCount) { index -> put(index, LazyListState()) }
}
}
val canPagerScroll by remember { derivedStateOf { behavior.state.heightOffset == 0f } }
val pullToRefreshState = rememberPullToRefreshState()
@ -181,7 +207,7 @@ private fun WalletContent2(
Box(
modifier = Modifier
.fillMaxSize()
.hazeSourceTangem(zIndex = -1f),
.hazeSourceTangem(zIndex = -2f),
) {
NorthernLightsBackground(
containerColor = if (LocalIsInDarkTheme.current) {
@ -201,10 +227,20 @@ private fun WalletContent2(
behavior = behavior,
)
val overlay = TangemTheme.colors2.overlay.overlayPrimary
HorizontalPager(
state = walletsPagerState,
userScrollEnabled = canPagerScroll,
beyondViewportPageCount = 1,
modifier = Modifier.hazeEffectTangem {
fallbackTint = HazeTint(color = overlay)
progressive = HazeProgressive.verticalGradient(
startIntensity = 1f,
endIntensity = 1f,
preferPerformance = true,
)
},
) { currentWalletIndex ->
val listState = listStates[currentWalletIndex] ?: rememberLazyListState()
@ -214,7 +250,8 @@ private fun WalletContent2(
LaunchedEffect(walletsPagerState.currentPage, currentWallet.walletsBalanceUM) {
if (walletsPagerState.currentPage == currentWalletIndex) {
walletBalance = (currentWallet.walletsBalanceUM as? WalletBalanceUM.Content)?.balanceInAppBar
walletBalance =
(currentWallet.walletsBalanceUM as? WalletBalanceUM.Content)?.balanceInAppBar
}
}
LaunchedEffect(walletsPagerState.currentPage, currentWallet.pullToRefreshConfig) {
@ -234,38 +271,45 @@ private fun WalletContent2(
val pageSlideAlpha by rememberPageAlpha(walletsPagerState, currentWalletIndex)
TangemPullToRefreshSlidingContainer(
state = pullToRefreshState,
config = currentWallet.pullToRefreshConfig,
modifier = Modifier.alpha(pageSlideAlpha),
indicatorOffset = with(LocalDensity.current) {
behavior.state.partialHeightLimit.toDp()
},
TangemSharedTransitionLayout(
modifier = Modifier
.fillMaxSize()
.alpha(pageSlideAlpha),
) {
TangemCollapsingTopBar(
state = behavior.state,
collapsingPart = {
WalletBalance(
behavior = behavior,
walletBalanceUM = currentWallet.walletsBalanceUM,
buttons = currentWallet.buttons,
isBalanceHidden = state.isHidingMode,
)
TangemPullToRefreshSlidingContainer(
state = pullToRefreshState,
config = currentWallet.pullToRefreshConfig,
indicatorOffset = with(LocalDensity.current) {
behavior.state.partialHeightLimit.toDp()
},
body = {
WalletListContent(
currentWallet = currentWallet,
listState = listState,
isBalanceHidden = state.isHidingMode,
contentPadding = contentPadding,
modifier = Modifier
.fillMaxSize()
.nestedScroll(behavior.nestedScrollConnection),
)
},
)
) {
TangemCollapsingTopBar(
state = behavior.state,
collapsingPart = {
WalletBalance(
behavior = behavior,
walletBalanceUM = currentWallet.walletsBalanceUM,
buttons = currentWallet.buttons,
isBalanceHidden = state.isHidingMode,
)
},
body = {
WalletListContent(
currentWallet = currentWallet,
listState = listState,
isBalanceHidden = state.isHidingMode,
contentPadding = contentPadding,
tangemPayComponent = tangemPayComponent,
modifier = Modifier
.fillMaxSize()
.nestedScroll(behavior.nestedScrollConnection),
)
},
)
}
val peekHeight = bottomSheetHeaderHeightProvider() + handComposableComponentHeight + bottomBarHeight
val peekHeight =
bottomSheetHeaderHeightProvider() + handComposableComponentHeight + bottomBarHeight
MarketsHint(
modifier = Modifier
.align(Alignment.BottomCenter)
@ -565,6 +609,14 @@ private fun WalletScreen2_Preview(@PreviewParameter(WalletScreen2PreviewProvider
TangemThemePreviewRedesign {
WalletScreen2(
state = data,
tangemPayComponent = object : TangemPayMainBlockComponent {
override fun LazyListScope.tangemPayMainContent(
state: TangemPayMainUM,
isBalanceHidden: Boolean,
modifier: Modifier,
) {
}
},
bottomSheetContent = {
Text("Markets Content")
},

View file

@ -3,9 +3,9 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.ui.Modifier
import com.tangem.core.ui.ds.button.TangemButton
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.ui.components.visa.TangemPayMainScreenBlock
import com.tangem.features.tangempay.component.TangemPayMainBlockComponent
import com.tangem.features.tangempay.entity.TangemPayMainUM
internal fun LazyListScope.nftCollections2(state: WalletUM, itemModifier: Modifier) {
(state as? WalletUM.Content)?.let { content ->
@ -33,17 +33,13 @@ internal fun LazyListScope.organizeTokens2(state: WalletUM, itemModifier: Modifi
}
}
internal fun LazyListScope.tangemPay(walletUM: WalletUM, isBalanceHiding: Boolean, modifier: Modifier = Modifier) {
if (walletUM is WalletState.MultiCurrency) {
item(
key = "TangemPayMainScreenBlock",
contentType = walletUM.tangemPayState::class.java,
) {
TangemPayMainScreenBlock(
state = walletUM.tangemPayState,
isBalanceHidden = isBalanceHiding,
modifier = modifier,
)
}
internal fun LazyListScope.tangemPay(
tangemPayComponent: TangemPayMainBlockComponent,
tangemPayUM: TangemPayMainUM,
isBalanceHidden: Boolean,
modifier: Modifier = Modifier,
) {
with(tangemPayComponent) {
tangemPayMainContent(modifier = modifier, state = tangemPayUM, isBalanceHidden = isBalanceHidden)
}
}

View file

@ -41,6 +41,9 @@ import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.conditional
import com.tangem.core.ui.extensions.orMaskWithStars
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.feature.wallet.impl.R
import com.tangem.core.ui.res.TangemDimens
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
@ -156,16 +159,10 @@ private fun CardContainer(state: WalletCardState, isBalanceHidden: Boolean, item
.padding(vertical = TangemTheme.dimens.spacing8),
)
val additionalText by remember(state.additionalInfo, isBalanceHidden) {
mutableStateOf(
state.additionalInfo?.content?.orMaskWithStars(
maskWithStars = state.additionalInfo?.hideable == true && isBalanceHidden,
),
)
}
AdditionalInfo(
text = additionalText,
showProgress = state.additionalInfo?.shouldShowProgress == true,
content = state.additionalInfo?.content,
hideable = state.additionalInfo?.hideable == true,
isBalanceHidden = isBalanceHidden,
modifier = Modifier.conditional(
state.imageResId == null,
) { fillMaxWidth() },
@ -300,28 +297,53 @@ private fun Modifier.nonContentBalanceSize(dimens: TangemDimens): Modifier {
}
@Composable
private fun AdditionalInfo(text: TextReference?, showProgress: Boolean, modifier: Modifier = Modifier) {
private fun AdditionalInfo(
content: WalletAdditionalInfo.Content?,
hideable: Boolean,
isBalanceHidden: Boolean,
modifier: Modifier = Modifier,
) {
AnimatedContent(
targetState = text,
targetState = content,
contentKey = { con ->
when (con) {
is WalletAdditionalInfo.Content.Text -> con
is WalletAdditionalInfo.Content.SyncProgress -> WalletAdditionalInfo.Content.SyncProgress::class
null -> null
}
},
label = "Update the additional text",
modifier = modifier,
transitionSpec = {
fadeIn(animationSpec = tween(durationMillis = 220, delayMillis = 90)) togetherWith
fadeOut(animationSpec = tween(durationMillis = 90))
},
) { animatedText ->
if (animatedText != null) {
) { animatedContent ->
if (animatedContent != null) {
Row(
horizontalArrangement = Arrangement.spacedBy(6.dp),
) {
AdditionalInfoText(text = animatedText)
if (showProgress) {
CircularProgressIndicator(
modifier = Modifier
.size(TangemTheme.dimens.size16),
color = TangemTheme.colors.icon.accent,
strokeWidth = TangemTheme.dimens.size2,
)
when (animatedContent) {
is WalletAdditionalInfo.Content.Text -> {
AdditionalInfoText(
text = animatedContent.text.orMaskWithStars(
maskWithStars = hideable && isBalanceHidden,
),
)
}
is WalletAdditionalInfo.Content.SyncProgress -> {
AdditionalInfoText(
text = resourceReference(
id = R.string.initial_wallet_sync_restore_progress,
formatArgs = wrappedList(animatedContent.progressPercent),
),
)
CircularProgressIndicator(
modifier = Modifier.size(TangemTheme.dimens.size16),
color = TangemTheme.colors.icon.accent,
strokeWidth = TangemTheme.dimens.size2,
)
}
}
}
} else {
@ -396,7 +418,7 @@ private class WalletCardStateProvider : CollectionPreviewParameterProvider<Walle
title = "Title",
additionalInfo = WalletAdditionalInfo(
hideable = false,
content = TextReference.Str("3 cards"),
content = WalletAdditionalInfo.Content.Text(TextReference.Str("3 cards")),
),
),
WalletPreviewDataLegacy.walletCardContentState.copy(

View file

@ -15,7 +15,6 @@ import com.tangem.common.ui.notifications.notificationsCarousel
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.core.ui.components.transactions.txHistoryItems
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.utils.TangemSharedTransitionLayout
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.ui.components.multicurrency.tokensListItems
@ -23,6 +22,7 @@ import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency
import com.tangem.feature.wallet.presentation.wallet.ui.components.nftCollections2
import com.tangem.feature.wallet.presentation.wallet.ui.components.organizeTokens2
import com.tangem.feature.wallet.presentation.wallet.ui.components.tangemPay
import com.tangem.features.tangempay.component.TangemPayMainBlockComponent
import kotlinx.collections.immutable.toPersistentList
@Composable
@ -30,6 +30,7 @@ internal fun WalletListContent(
currentWallet: WalletUM,
isBalanceHidden: Boolean,
listState: LazyListState,
tangemPayComponent: TangemPayMainBlockComponent,
contentPadding: PaddingValues,
modifier: Modifier = Modifier,
) {
@ -38,40 +39,40 @@ internal fun WalletListContent(
val movableItemModifier = Modifier.padding(horizontal = TangemTheme.dimens2.x3)
val itemModifier = movableItemModifier.padding(top = TangemTheme.dimens2.x3)
TangemSharedTransitionLayout(modifier) {
LazyColumn(
state = listState,
contentPadding = contentPadding,
horizontalAlignment = Alignment.CenterHorizontally,
overscrollEffect = rememberOverscrollEffect(),
) {
notifications(
notifications = currentWallet.notifications.map { it.messageUM }.toPersistentList(),
contentColor = containerColor,
modifier = movableItemModifier,
)
notificationsCarousel(
containerColor = containerColor,
modifier = movableItemModifier,
notifications = currentWallet.notificationsCarousel.map { it.messageUM }.toPersistentList(),
)
LazyColumn(
modifier = modifier,
state = listState,
contentPadding = contentPadding,
horizontalAlignment = Alignment.CenterHorizontally,
overscrollEffect = rememberOverscrollEffect(),
) {
notifications(
notifications = currentWallet.notifications.map { it.messageUM }.toPersistentList(),
contentColor = containerColor,
modifier = movableItemModifier,
)
notificationsCarousel(
containerColor = containerColor,
modifier = movableItemModifier,
notifications = currentWallet.notificationsCarousel.map { it.messageUM }.toPersistentList(),
)
tangemPay(
walletUM = currentWallet,
isBalanceHiding = isBalanceHidden,
modifier = itemModifier,
)
tangemPay(
tangemPayComponent = tangemPayComponent,
tangemPayUM = currentWallet.tangemPayMainUM,
isBalanceHidden = isBalanceHidden,
modifier = itemModifier,
)
tokensListItems2(
walletTokensListUM = currentWallet.tokensListUM,
modifier = movableItemModifier,
isBalanceHidden = isBalanceHidden,
)
tokensListItems2(
walletTokensListUM = currentWallet.tokensListUM,
modifier = movableItemModifier,
isBalanceHidden = isBalanceHidden,
)
nftCollections2(state = currentWallet, itemModifier = itemModifier)
nftCollections2(state = currentWallet, itemModifier = itemModifier)
organizeTokens2(state = currentWallet, itemModifier = itemModifier)
}
organizeTokens2(state = currentWallet, itemModifier = itemModifier)
}
}

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

@ -6,6 +6,7 @@ import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.animateIntAsState
import androidx.compose.animation.core.snap
import androidx.compose.animation.core.tween
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
@ -17,8 +18,12 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.positionInWindow
import androidx.compose.ui.layout.positionOnScreen
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.semantics.semantics
@ -155,12 +160,19 @@ private fun LazyListScope.tokenItem(
backgroundColor = TangemTheme.colors2.surface.level3,
)
var position by remember { mutableStateOf(Offset.Zero) }
when (val tokenRowUM = listItem.tokenRowUM) {
is TangemTokenRowUM -> TangemTokenRow(
tokenRowUM = tokenRowUM,
isBalanceHidden = isBalanceHidden,
reorderableState = null,
modifier = itemModifier,
modifier = itemModifier
.onGloballyPositioned { position = it.positionOnScreen() }
.combinedClickable(
enabled = tokenRowUM.onItemClick != null || tokenRowUM.onItemLongClick != null,
onClick = tokenRowUM.onItemClick ?: {},
onLongClick = { tokenRowUM.onItemLongClick?.invoke(position, tokenRowUM) },
),
)
is TangemHeaderRowUM -> TangemHeaderRow(
headerRowUM = tokenRowUM,
@ -170,6 +182,7 @@ private fun LazyListScope.tokenItem(
}
}
@Suppress("LongMethod")
private fun LazyListScope.portfolioItem(
listItem: TokensListItemUM2.Portfolio,
index: Int,
@ -220,12 +233,23 @@ private fun LazyListScope.portfolioItem(
.testTag(MainScreenTestTags.TOKEN_LIST_ITEM)
.semantics { lazyListItemPosition = tokenIndex + 1 }
var position by remember { mutableStateOf(Offset.Zero) }
when (val tokenRowUM = item.tokenRowUM) {
is TangemTokenRowUM -> TangemTokenRow(
tokenRowUM = tokenRowUM,
isBalanceHidden = isBalanceHidden,
reorderableState = null,
modifier = itemModifier,
modifier = itemModifier
.onGloballyPositioned {
position = it.positionInWindow()
}
.combinedClickable(
enabled = tokenRowUM.onItemClick != null || tokenRowUM.onItemLongClick != null,
onClick = tokenRowUM.onItemClick ?: {},
onLongClick = {
tokenRowUM.onItemLongClick?.invoke(position, tokenRowUM)
},
),
)
is TangemHeaderRowUM -> TangemHeaderRow(
headerRowUM = tokenRowUM,
@ -343,7 +367,8 @@ internal fun PortfolioRowItem(
val composables = remember {
SharedTokenRowComposables(
icon = { modifier ->
val size = if (isExpandedWrapped) AccountIconSize.ExtraSmall else AccountIconSize.Default
val size =
if (isExpandedWrapped) AccountIconSize.ExtraSmall else AccountIconSize.Default
val headIcon = item.tokenRowUM.headIconUM
val sizedHeadIcon = if (headIcon is TangemIconUM.Currency) {
headIcon.copy(

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,

View file

@ -45,7 +45,6 @@ import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import timber.log.Timber
@OptIn(ExperimentalCoroutinesApi::class)
class DefaultPromoDeeplinkHandlerTest {
@ -83,7 +82,6 @@ class DefaultPromoDeeplinkHandlerTest {
messages = mutableListOf()
every { uiMessageSender.send(capture(messages)) } just runs
Timber.uprootAll()
}
@Test