Updated on 2026-08-14

This commit is contained in:
Tangem 2026-02-06 12:40:48 +03:00
commit 870c3fa9d3
291 changed files with 4985 additions and 5870 deletions

View file

@ -44,7 +44,6 @@ 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.feed.entry.featuretoggle.FeedFeatureToggle
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.features.pushnotifications.api.PushNotificationsModelCallbacks
import com.tangem.features.tangempay.TangemPayFeatureToggles
import com.tangem.features.wallet.deeplink.WalletDeepLinkActionListener
@ -89,9 +88,7 @@ internal class WalletModel @Inject constructor(
private val notificationsRepository: NotificationsRepository,
private val getWalletsListForEnablingUseCase: GetWalletsForAutomaticallyPushEnablingUseCase,
private val setNotificationsEnabledUseCase: SetNotificationsEnabledUseCase,
private val shouldSaveUserWalletsSyncUseCase: ShouldSaveUserWalletsSyncUseCase,
private val getIsHuaweiDeviceWithoutGoogleServicesUseCase: GetIsHuaweiDeviceWithoutGoogleServicesUseCase,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
private val userWalletsListRepository: UserWalletsListRepository,
private val tangemPayFeatureToggles: TangemPayFeatureToggles,
private val yieldSupplyApyUpdateUseCase: YieldSupplyApyUpdateUseCase,
@ -212,50 +209,40 @@ internal class WalletModel @Inject constructor(
}
private fun trackScreenOpened() {
if (hotWalletFeatureToggles.isHotWalletEnabled) {
modelScope.launch {
userWalletsListRepository
.selectedUserWalletSync()
?.let { selectedWallet ->
val hasMobileWallet = userWalletsListRepository.userWalletsSync()
.any { it is UserWallet.Hot }
modelScope.launch {
userWalletsListRepository
.selectedUserWalletSync()
?.let { selectedWallet ->
val hasMobileWallet = userWalletsListRepository.userWalletsSync()
.any { it is UserWallet.Hot }
val accountsCount = if (isAccountsModeEnabledUseCase.invokeSync()) {
singleAccountListSupplier(selectedWallet.walletId)
.first()
.accounts
.size
} else {
null
}
val result = getAppThemeModeUseCase().firstOrNull()
val theme = result?.getOrElse { AppThemeMode.FOLLOW_SYSTEM } ?: AppThemeMode.FOLLOW_SYSTEM
analyticsEventsHandler.send(
WalletScreenAnalyticsEvent.MainScreen.ScreenOpened(
hasMobileWallet = hasMobileWallet,
accountsCount = accountsCount,
theme = theme.value,
isImported = selectedWallet.isImported(),
referralId = appsFlyerStore.get()?.refcode,
),
)
val accountsCount = if (isAccountsModeEnabledUseCase.invokeSync()) {
singleAccountListSupplier(selectedWallet.walletId)
.first()
.accounts
.size
} else {
null
}
}
} else {
analyticsEventsHandler.send(
WalletScreenAnalyticsEvent.MainScreen.ScreenOpenedLegacy(),
)
val result = getAppThemeModeUseCase().firstOrNull()
val theme = result?.getOrElse { AppThemeMode.FOLLOW_SYSTEM } ?: AppThemeMode.FOLLOW_SYSTEM
analyticsEventsHandler.send(
WalletScreenAnalyticsEvent.MainScreen.ScreenOpened(
hasMobileWallet = hasMobileWallet,
accountsCount = accountsCount,
theme = theme.value,
isImported = selectedWallet.isImported(),
referralId = appsFlyerStore.get()?.refcode,
),
)
}
}
}
private suspend fun shouldShowAskBiometryBottomSheet(): Boolean {
return if (hotWalletFeatureToggles.isHotWalletEnabled) {
userWalletsListRepository.userWalletsSync().any { it is UserWallet.Cold } &&
shouldShowAskBiometryUseCase() &&
canUseBiometryUseCase()
} else {
innerWalletRouter.isWalletLastScreen() && shouldShowAskBiometryUseCase() && canUseBiometryUseCase()
}
return userWalletsListRepository.userWalletsSync().any { it is UserWallet.Cold } &&
shouldShowAskBiometryUseCase() &&
canUseBiometryUseCase()
}
private fun subscribeToUserWalletsUpdates() = channelFlow<Unit> {
@ -301,20 +288,16 @@ internal class WalletModel @Inject constructor(
modelScope.launch {
val shouldAskNotificationPermissionsViaBs = notificationsRepository.shouldAskNotificationPermissionsViaBs()
val shouldShow = notificationsRepository.shouldShowSubscribeOnNotificationsAfterUpdate()
val isBiometricsEnabled = shouldSaveUserWalletsSyncUseCase()
val isHuaweiDevice = getIsHuaweiDeviceWithoutGoogleServicesUseCase()
Timber.d(
"push BS afterUpdate: $shouldShow," +
"isBiometricsEnabled $isBiometricsEnabled," +
"isHuaweiDevice $isHuaweiDevice",
)
if (!shouldAskNotificationPermissionsViaBs) {
notificationsRepository.setShouldAskNotificationPermissionsViaBs(true)
return@launch
}
if (!hotWalletFeatureToggles.isHotWalletEnabled && !isBiometricsEnabled) {
return@launch
}
if (!shouldShow) {
return@launch
}

View file

@ -1,56 +1,23 @@
package com.tangem.feature.wallet.child.wallet.model.intents
import arrow.core.getOrElse
import com.arkivanov.decompose.router.slot.activate
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.card.DeleteSavedAccessCodesUseCase
import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.usecase.DeleteWalletUseCase
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen
import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore
import com.tangem.feature.wallet.presentation.wallet.loaders.WalletScreenContentLoader
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAlertState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogConfig
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
internal interface WalletCardClickIntents {
fun onRenameBeforeConfirmationClick(userWalletId: UserWalletId)
fun onDeleteBeforeConfirmationClick(userWalletId: UserWalletId)
fun onDeleteAfterConfirmationClick(userWalletId: UserWalletId)
}
// TODO: Refactor
@Suppress("LongParameterList")
@ModelScoped
internal class WalletCardClickIntentsImplementor @Inject constructor(
private val stateHolder: WalletStateController,
private val tokenListStore: MultiWalletTokenListStore,
private val walletEventSender: WalletEventSender,
private val walletScreenContentLoader: WalletScreenContentLoader,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
private val deleteWalletUseCase: DeleteWalletUseCase,
private val deleteSavedAccessCodesUseCase: DeleteSavedAccessCodesUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
private val reduxStateHolder: ReduxStateHolder,
private val appRouter: AppRouter,
private val dispatchers: CoroutineDispatcherProvider,
) : BaseWalletClickIntents(), WalletCardClickIntents {
override fun onRenameBeforeConfirmationClick(userWalletId: UserWalletId) {
@ -63,47 +30,4 @@ internal class WalletCardClickIntentsImplementor @Inject constructor(
),
)
}
override fun onDeleteBeforeConfirmationClick(userWalletId: UserWalletId) {
analyticsEventHandler.send(MainScreen.DeleteWalletTapped())
walletEventSender.send(
event = WalletEvent.ShowAlert(
state = WalletAlertState.RemoveWalletAlert(
onConfirmClick = { onDeleteAfterConfirmationClick(userWalletId) },
),
),
)
}
override fun onDeleteAfterConfirmationClick(userWalletId: UserWalletId) {
modelScope.launch(dispatchers.main) {
walletScreenContentLoader.cancel(userWalletId)
tokenListStore.remove(userWalletId)
val walletToDelete = getUserWalletUseCase(userWalletId).getOrNull() ?: return@launch
val hasUserWallets = deleteWalletUseCase(userWalletId).getOrElse {
Timber.e("Unable to delete user wallet: $it")
return@launch
}
if (walletToDelete is UserWallet.Cold) {
deleteSavedAccessCodesUseCase(cardId = walletToDelete.cardId).onLeft {
Timber.e("Unable to delete user wallet access code: $it")
}
}
if (hasUserWallets) {
val selectedWallet = getSelectedWalletSyncUseCase().getOrElse {
error("Unable to find selected wallet: $it")
}
reduxStateHolder.onUserWalletSelected(selectedWallet)
} else {
tokenListStore.clear()
stateHolder.clear()
appRouter.replaceAll(AppRoute.Home())
}
}
}
}

View file

@ -7,13 +7,13 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
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.domain.models.account.Account
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.isLocked
import com.tangem.domain.nft.analytics.NFTAnalyticsEvent
import com.tangem.domain.settings.ShouldShowMarketsTooltipUseCase
import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
@ -33,7 +33,6 @@ import com.tangem.feature.wallet.presentation.wallet.state.transformers.CloseBot
import com.tangem.feature.wallet.presentation.wallet.state.transformers.OpenBottomSheetTransformer
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.MultiWalletCurrencyActionsConverter
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.take
@ -90,7 +89,6 @@ internal interface WalletContentClickIntents {
internal class WalletContentClickIntentsImplementor @Inject constructor(
private val stateHolder: WalletStateController,
private val currencyActionsClickIntents: WalletCurrencyActionsClickIntentsImplementor,
private val walletWarningsClickIntents: WalletWarningsClickIntentsImplementor,
private val onrampStatusFactory: OnrampStatusFactory,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
@ -100,44 +98,14 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
private val dispatchers: CoroutineDispatcherProvider,
private val walletEventSender: WalletEventSender,
private val analyticsEventHandler: AnalyticsEventHandler,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
private val accountDependencies: AccountDependencies,
private val yieldSupplySetShouldShowMainPromoUseCase: YieldSupplySetShouldShowMainPromoUseCase,
private val tokenListAnalyticsSender: TokenListAnalyticsSender,
private val uiMessageSender: UiMessageSender,
) : BaseWalletClickIntents(), WalletContentClickIntents {
override fun onDetailsClick() {
if (hotWalletFeatureToggles.isHotWalletEnabled) {
router.openDetailsScreen(stateHolder.getSelectedWalletId())
return
}
// Will be removed after Hot Wallet release
modelScope.launch(dispatchers.main) {
val userWalletId = stateHolder.getSelectedWalletId()
val userWallet = getUserWalletUseCase(userWalletId).getOrElse {
Timber.e(
"""
Unable to get user wallet
|- ID: $userWalletId
|- Exception: $it
""".trimIndent(),
)
return@launch
}
if (userWallet.isLocked) {
stateHolder.showBottomSheet(
WalletBottomSheetConfig.UnlockWallets(
onUnlockClick = walletWarningsClickIntents::onUnlockWalletClick,
onScanClick = walletWarningsClickIntents::onScanToUnlockWalletClick,
),
)
} else {
router.openDetailsScreen(stateHolder.getSelectedWalletId())
}
}
router.openDetailsScreen(stateHolder.getSelectedWalletId())
}
override fun onOrganizeTokensClick() {
@ -345,15 +313,12 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
}
override fun onConfirmDisposeExpressStatus() {
walletEventSender.send(
WalletEvent.ShowAlert(
WalletAlertState.ConfirmExpressStatusHide(
onConfirmClick = {
walletEventSender.onConsume()
onDisposeExpressStatus()
},
onCancelClick = walletEventSender::onConsume,
),
uiMessageSender.send(
WalletAlertUM.confirmExpressStatusHide(
onConfirmClick = {
walletEventSender.onConsume()
onDisposeExpressStatus()
},
),
)
}

View file

@ -12,13 +12,14 @@ import com.tangem.core.analytics.models.AnalyticsEvent
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.clipboard.ClipboardManager
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.WrappedList
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.haptic.TangemHapticEffect
import com.tangem.core.ui.haptic.VibratorHapticManager
import com.tangem.core.ui.message.DialogMessage
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase
import com.tangem.domain.account.status.usecase.ManageCryptoCurrenciesUseCase
@ -56,7 +57,7 @@ import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.domain.unwrap
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAlertState
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.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState
@ -150,6 +151,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
private val accountsFeatureToggles: AccountsFeatureToggles,
private val getAccountCurrencyStatusUseCase: GetAccountCurrencyStatusUseCase,
private val manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase,
private val uiMessageSender: UiMessageSender,
) : BaseWalletClickIntents(), WalletCurrencyActionsClickIntents {
override fun onSendClick(
@ -269,48 +271,19 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
)
modelScope.launch(dispatchers.main) {
walletEventSender.send(
event = WalletEvent.ShowAlert(
state = getHideTokeAlertConfig(userWalletId, cryptoCurrencyStatus),
),
)
}
}
val currency = cryptoCurrencyStatus.currency
val isCryptoCurrencyCoinCouldHide = currency is CryptoCurrency.Coin &&
!isCryptoCurrencyCoinCouldHide(userWalletId = userWalletId, cryptoCurrencyCoin = currency)
private suspend fun getHideTokeAlertConfig(
userWalletId: UserWalletId,
cryptoCurrencyStatus: CryptoCurrencyStatus,
): WalletAlertState.DefaultAlert {
val currency = cryptoCurrencyStatus.currency
val isCryptoCurrencyCoinCouldHide = currency is CryptoCurrency.Coin &&
!isCryptoCurrencyCoinCouldHide(userWalletId = userWalletId, cryptoCurrencyCoin = currency)
return if (isCryptoCurrencyCoinCouldHide) {
WalletAlertState.DefaultAlert(
title = resourceReference(
id = R.string.token_details_unable_hide_alert_title,
formatArgs = WrappedList(listOf(cryptoCurrencyStatus.currency.name)),
),
message = resourceReference(
id = R.string.token_details_unable_hide_alert_message,
formatArgs = WrappedList(
listOf(
cryptoCurrencyStatus.currency.name,
cryptoCurrencyStatus.currency.symbol,
cryptoCurrencyStatus.currency.network.name,
),
),
),
onConfirmClick = null,
)
} else {
WalletAlertState.DefaultAlert(
title = resourceReference(
id = R.string.token_details_hide_alert_title,
formatArgs = WrappedList(listOf(cryptoCurrencyStatus.currency.name)),
),
message = resourceReference(R.string.token_details_hide_alert_message),
onConfirmClick = { onPerformHideToken(userWalletId, cryptoCurrencyStatus) },
)
if (isCryptoCurrencyCoinCouldHide) {
uiMessageSender.send(WalletAlertUM.unableHideToken(cryptoCurrency = cryptoCurrencyStatus.currency))
} else {
uiMessageSender.send(
WalletAlertUM.hideTokenConfirm(cryptoCurrency = cryptoCurrencyStatus.currency) {
onPerformHideToken(userWalletId, cryptoCurrencyStatus)
},
)
}
}
}
@ -618,17 +591,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
private fun handleUnavailabilityReason(unavailabilityReason: ScenarioUnavailabilityReason): Boolean {
if (unavailabilityReason == ScenarioUnavailabilityReason.None) return false
modelScope.launch(dispatchers.main) {
walletEventSender.send(
event = WalletEvent.ShowAlert(
state = WalletAlertState.DefaultAlert(
title = null,
message = unavailabilityReason.getUnavailabilityReasonText(),
onConfirmClick = null,
),
),
)
}
uiMessageSender.send(DialogMessage(message = unavailabilityReason.getUnavailabilityReasonText()))
return true
}
@ -641,7 +604,10 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
modelScope.launch {
statusFlow.foldStatus(
onContent = { handleContent(route, eventCreator) },
onError = { handleError(eventCreator = eventCreator) },
onError = {
analyticsEventHandler.send(event = eventCreator(AnalyticsParam.Status.Error))
uiMessageSender.send(WalletAlertUM.unavailableOperation())
},
onLoading = { handleLoading(eventCreator) },
)
}
@ -667,21 +633,10 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
appRouter.push(route = route)
}
private fun handleError(
alertState: WalletAlertState = WalletAlertState.UnavailableOperation,
eventCreator: (AnalyticsParam.Status) -> MainScreenAnalyticsEvent,
) {
analyticsEventHandler.send(event = eventCreator(AnalyticsParam.Status.Error))
walletEventSender.send(event = WalletEvent.ShowAlert(state = alertState))
}
private fun handleLoading(eventCreator: (AnalyticsParam.Status) -> MainScreenAnalyticsEvent) {
analyticsEventHandler.send(event = eventCreator(AnalyticsParam.Status.Pending))
walletEventSender.send(
event = WalletEvent.ShowAlert(state = WalletAlertState.ProvidersStillLoading),
)
uiMessageSender.send(WalletAlertUM.providersStillLoading())
}
private suspend fun getSwapRoute(targetRoute: AppRoute): AppRoute {
@ -743,12 +698,8 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
private fun checkSwapCryptoAvailability(tokenCount: Int) {
if (tokenCount < 2) {
handleError(
alertState = WalletAlertState.InsufficientTokensCountForSwapping,
eventCreator = MainScreenAnalyticsEvent::ButtonSwap,
)
return
analyticsEventHandler.send(event = MainScreenAnalyticsEvent.ButtonSwap(AnalyticsParam.Status.Error))
uiMessageSender.send(WalletAlertUM.insufficientTokensCountForSwapping())
}
}
}

View file

@ -11,7 +11,7 @@ import com.tangem.core.analytics.models.AnalyticsParam
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.ui.extensions.resourceReference
import com.tangem.core.navigation.review.ReviewManager
import com.tangem.domain.card.SetCardWasScannedUseCase
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
@ -37,22 +37,14 @@ 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.wallets.legacy.UserWalletsListManager.Lockable.UnlockType
import com.tangem.domain.wallets.models.UnlockWalletsError
import com.tangem.domain.wallets.usecase.*
import com.tangem.feature.wallet.impl.R
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.domain.ScanCardToUnlockWalletClickHandler
import com.tangem.feature.wallet.presentation.wallet.domain.ScanCardToUnlockWalletError
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAlertState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletBottomSheetConfig
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.transformers.CloseBottomSheetTransformer
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.features.pushnotifications.api.analytics.PushNotificationAnalyticEvents
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.async
@ -75,12 +67,6 @@ internal interface WalletWarningsClickIntents {
fun onOpenUnlockWalletsBottomSheetClick()
fun onUnlockWalletClick()
fun onUnlockVisaAccessClick()
fun onScanToUnlockWalletClick()
fun onLikeAppClick()
fun onDislikeAppClick()
@ -93,6 +79,8 @@ internal interface WalletWarningsClickIntents {
fun onSupportClick()
fun onBackupErrorClick()
fun onNoteMigrationButtonClick(url: String)
fun onSeedPhraseNotificationConfirm()
@ -122,8 +110,6 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
private val neverToSuggestRateAppUseCase: NeverToSuggestRateAppUseCase,
private val remindToRateAppLaterUseCase: RemindToRateAppLaterUseCase,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val scanCardToUnlockWalletClickHandler: ScanCardToUnlockWalletClickHandler,
private val unlockWalletsUseCase: UnlockWalletsUseCase,
private val nonBiometricUnlockWalletUseCase: NonBiometricUnlockWalletUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
private val dispatchers: CoroutineDispatcherProvider,
@ -137,13 +123,13 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
private val multiStakingBalanceFetcher: MultiStakingBalanceFetcher,
private val stakingIdFactory: StakingIdFactory,
private val appRouter: AppRouter,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
private val userWalletsListRepository: UserWalletsListRepository,
private val setShouldShowNotificationUseCase: SetShouldShowNotificationUseCase,
private val notificationsRepository: NotificationsRepository,
private val setNotificationsEnabledUseCase: SetNotificationsEnabledUseCase,
private val getWalletsListForEnablingUseCase: GetWalletsForAutomaticallyPushEnablingUseCase,
private val uiMessageSender: UiMessageSender,
private val reviewManager: ReviewManager,
) : BaseWalletClickIntents(), WalletWarningsClickIntents {
override fun onAddBackupCardClick() {
@ -205,81 +191,20 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
override fun onOpenUnlockWalletsBottomSheetClick() {
analyticsEventHandler.send(MainScreen.WalletUnlockTapped())
if (hotWalletFeatureToggles.isHotWalletEnabled) {
modelScope.launch {
userWalletsListRepository.unlockAllWallets()
.onLeft {
val selectedUserWalletId = stateHolder.getSelectedWalletId()
nonBiometricUnlockWalletUseCase(selectedUserWalletId)
.onLeft { error ->
error.handle(
onAlreadyUnlocked = {},
onUserCancelled = {},
analyticsEventHandler = analyticsEventHandler,
isFromUnlockAll = true,
showMessage = uiMessageSender::send,
)
}
}
}
return
}
// Will be removed after hot wallet release
stateHolder.showBottomSheet(
WalletBottomSheetConfig.UnlockWallets(
onUnlockClick = this::onUnlockWalletClick,
onScanClick = this::onScanToUnlockWalletClick,
),
)
}
@Deprecated("Will be removed with hot wallet release")
override fun onUnlockWalletClick() {
analyticsEventHandler.send(MainScreen.UnlockAllWithBiometrics())
modelScope.launch(dispatchers.main) {
unlockWalletsUseCase(type = UnlockType.ALL_WITHOUT_SELECT)
.onRight { stateHolder.update(CloseBottomSheetTransformer(stateHolder.getSelectedWalletId())) }
.onLeft(::handleUnlockWalletsError)
}
}
override fun onUnlockVisaAccessClick() {
openScanCardDialog()
}
private fun handleUnlockWalletsError(error: UnlockWalletsError) {
val event = when (error) {
is UnlockWalletsError.DataError,
is UnlockWalletsError.UnableToUnlockWallets,
-> WalletEvent.ShowError(resourceReference(R.string.user_wallet_list_error_unable_to_unlock))
is UnlockWalletsError.NoUserWalletSelected,
is UnlockWalletsError.NotAllUserWalletsUnlocked,
-> WalletEvent.ShowAlert(WalletAlertState.RescanWallets)
}
walletEventSender.send(event)
}
@Deprecated("Will be removed with hot wallet release")
override fun onScanToUnlockWalletClick() {
analyticsEventHandler.send(MainScreen.UnlockWithCardScan())
openScanCardDialog()
}
private fun openScanCardDialog() {
modelScope.launch(dispatchers.main) {
scanCardToUnlockWalletClickHandler(walletId = stateHolder.getSelectedWalletId())
.onLeft { error ->
when (error) {
ScanCardToUnlockWalletError.WrongCardIsScanned -> {
walletEventSender.send(
event = WalletEvent.ShowAlert(WalletAlertState.WrongCardIsScanned),
modelScope.launch {
userWalletsListRepository.unlockAllWallets()
.onLeft {
val selectedUserWalletId = stateHolder.getSelectedWalletId()
nonBiometricUnlockWalletUseCase(selectedUserWalletId)
.onLeft { error ->
error.handle(
onAlreadyUnlocked = {},
onUserCancelled = {},
analyticsEventHandler = analyticsEventHandler,
isFromUnlockAll = true,
showMessage = uiMessageSender::send,
)
}
ScanCardToUnlockWalletError.ManyScanFails -> router.openScanFailedDialog(::openScanCardDialog)
}
}
}
}
@ -287,15 +212,11 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
override fun onLikeAppClick() {
analyticsEventHandler.send(MainScreen.NoticeRateAppButton(AnalyticsParam.RateApp.Liked))
walletEventSender.send(
event = WalletEvent.RateApp(
onDismissClick = {
modelScope.launch(dispatchers.main) {
neverToSuggestRateAppUseCase()
}
},
),
)
reviewManager.request {
modelScope.launch(dispatchers.main) {
neverToSuggestRateAppUseCase()
}
}
}
override fun onDislikeAppClick() {
@ -345,7 +266,6 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
action = PromotionBannerClicked.BannerAction.Closed(),
)
},
)
modelScope.launch(dispatchers.main) {
shouldShowPromoWalletUseCase.neverToShow(promoId)
@ -414,6 +334,15 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
}
}
override fun onBackupErrorClick() {
val userWallet = getSelectedUserWallet() ?: return
modelScope.launch {
val metaInfo = getWalletMetaInfoUseCase(userWallet.walletId).getOrNull() ?: return@launch
sendFeedbackEmailUseCase(type = FeedbackEmailType.BackupProblem(walletMetaInfo = metaInfo))
}
}
override fun onNoteMigrationButtonClick(url: String) {
analyticsEventHandler.send(MainScreen.NotePromoButton())
modelScope.launch(dispatchers.main) {
@ -426,21 +355,16 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
analyticsEventHandler.send(MainScreen.NoticeSeedPhraseSupportButtonYes())
walletEventSender.send(
event = WalletEvent.ShowAlert(
state = WalletAlertState.SimpleOkAlert(
message = resourceReference(R.string.warning_seedphrase_issue_answer_yes),
onOkClick = {
modelScope.launch {
seedPhraseNotificationUseCase.confirm(userWalletId = userWallet.walletId)
uiMessageSender.send(
WalletAlertUM.seedPhraseConfirm {
modelScope.launch {
seedPhraseNotificationUseCase.confirm(userWalletId = userWallet.walletId)
urlOpener.openUrl(
url = TangemBlogUrlBuilder.build(post = TangemBlogUrlBuilder.Post.SeedNotify),
)
}
},
),
),
urlOpener.openUrl(
url = TangemBlogUrlBuilder.build(post = TangemBlogUrlBuilder.Post.SeedNotify),
)
}
},
)
}
@ -449,17 +373,12 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
analyticsEventHandler.send(MainScreen.NoticeSeedPhraseSupportButtonNo())
walletEventSender.send(
event = WalletEvent.ShowAlert(
state = WalletAlertState.SimpleOkAlert(
message = resourceReference(R.string.warning_seedphrase_issue_answer_no),
onOkClick = {
modelScope.launch {
seedPhraseNotificationUseCase.decline(userWalletId = userWallet.walletId)
}
},
),
),
uiMessageSender.send(
WalletAlertUM.seedPhraseDismiss {
modelScope.launch {
seedPhraseNotificationUseCase.decline(userWalletId = userWallet.walletId)
}
},
)
}
@ -468,21 +387,16 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
analyticsEventHandler.send(MainScreen.NoticeSeedPhraseSupportButtonUsed())
walletEventSender.send(
event = WalletEvent.ShowAlert(
state = WalletAlertState.SimpleOkAlert(
message = resourceReference(R.string.warning_seedphrase_issue_answer_yes),
onOkClick = {
modelScope.launch {
seedPhraseNotificationUseCase.acceptSecond(userWalletId = userWallet.walletId)
uiMessageSender.send(
WalletAlertUM.seedPhraseConfirm {
modelScope.launch {
seedPhraseNotificationUseCase.acceptSecond(userWalletId = userWallet.walletId)
urlOpener.openUrl(
url = TangemBlogUrlBuilder.build(post = TangemBlogUrlBuilder.Post.SeedNotifySecond),
)
}
},
),
),
urlOpener.openUrl(
url = TangemBlogUrlBuilder.build(post = TangemBlogUrlBuilder.Post.SeedNotifySecond),
)
}
},
)
}

View file

@ -1,6 +1,13 @@
package com.tangem.feature.wallet.featuretoggles
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
import javax.inject.Inject
internal class DefaultWalletFeatureToggles @Inject constructor() : WalletFeatureToggles
internal class DefaultWalletFeatureToggles @Inject constructor(
private val featureToggles: FeatureTogglesManager,
) : WalletFeatureToggles {
override val isWalletReorderFeatureEnabled: Boolean
get() = featureToggles.isFeatureEnabled("WALLET_REORDER_FEATURE_ENABLED")
}

View file

@ -1,5 +1,6 @@
package com.tangem.feature.wallet.presentation.organizetokens.utils.converter.items
import com.tangem.common.getTotalWithRewardsStakingBalance
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
import com.tangem.core.ui.components.token.state.TokenItemState
import com.tangem.core.ui.extensions.stringReference
@ -9,7 +10,6 @@ import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance
import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem
import com.tangem.feature.wallet.presentation.organizetokens.utils.common.getGroupHeaderId
import com.tangem.feature.wallet.presentation.organizetokens.utils.common.getTokenItemId

View file

@ -10,7 +10,7 @@ import com.tangem.domain.account.status.model.AccountCryptoCurrencyStatus
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance
import com.tangem.common.getTotalWithRewardsStakingBalance
import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem
import com.tangem.feature.wallet.presentation.organizetokens.utils.common.getGroupHeaderId
import com.tangem.feature.wallet.presentation.organizetokens.utils.common.getTokenItemId

View file

@ -182,7 +182,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
val cardTypesResolver = userWallet.scanResponse.cardTypesResolver
addIf(
element = WalletNotification.Critical.BackupError { clickIntents.onSupportClick() },
element = WalletNotification.Critical.BackupError { clickIntents.onBackupErrorClick() },
condition = !backupValidator.isValidBackupStatus(userWallet.scanResponse.card) || userWallet.hasBackupError,
)

View file

@ -1,56 +0,0 @@
package com.tangem.feature.wallet.presentation.wallet.domain
import arrow.core.Either
import arrow.core.raise.either
import arrow.core.raise.ensure
import com.tangem.common.CompletionResult
import com.tangem.common.core.TangemSdkError
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.domain.card.ScanCardProcessor
import com.tangem.domain.wallets.builder.ColdUserWalletBuilder
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.usecase.SaveWalletUseCase
import javax.inject.Inject
internal class ScanCardToUnlockWalletClickHandler @Inject constructor(
private val scanCardProcessor: ScanCardProcessor,
private val saveWalletUseCase: SaveWalletUseCase,
private val coldUserWalletBuilderFactory: ColdUserWalletBuilder.Factory,
) {
private var scanFailsCounter = 0
suspend operator fun invoke(walletId: UserWalletId): Either<ScanCardToUnlockWalletError, Unit> {
return either {
when (val result = scanCardProcessor.scan(analyticsSource = AnalyticsParam.ScreensSources.SignIn)) {
is CompletionResult.Failure -> {
if (result.error is TangemSdkError.UserCancelled) {
scanFailsCounter++
ensure(scanFailsCounter < 2) { ScanCardToUnlockWalletError.ManyScanFails }
}
}
is CompletionResult.Success -> {
scanFailsCounter = 0
// If card's public key is null then user wallet will be null
val scannedWallet = coldUserWalletBuilderFactory.create(scanResponse = result.data).build()
ensure(walletId == scannedWallet?.walletId) {
ScanCardToUnlockWalletError.WrongCardIsScanned
}
if (scannedWallet != null) {
saveWalletUseCase(userWallet = scannedWallet, canOverride = true)
}
}
}
}
}
}
internal sealed class ScanCardToUnlockWalletError {
object WrongCardIsScanned : ScanCardToUnlockWalletError()
object ManyScanFails : ScanCardToUnlockWalletError()
}

View file

@ -2,14 +2,11 @@ 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.legacy.UserWalletsListManager
import com.tangem.domain.wallets.repository.WalletNamesMigrationRepository
import timber.log.Timber
class WalletNameMigrationUseCase(
private val userWalletsListManager: UserWalletsListManager,
private val userWalletsListRepository: UserWalletsListRepository,
private val useNewListRepository: Boolean,
private val walletNamesMigrationRepository: WalletNamesMigrationRepository,
) {
@ -18,28 +15,15 @@ class WalletNameMigrationUseCase(
return
}
if (useNewListRepository) {
val wallets = userWalletsListRepository.userWalletsSync()
val existingNames: MutableSet<String> = mutableSetOf()
wallets.forEach {
val defaultName = it.name
val suggestedWalletName = suggestedWalletName(defaultName, existingNames)
if (defaultName != suggestedWalletName) {
userWalletsListRepository.saveWithoutLock(it.copy(name = suggestedWalletName), canOverride = true)
}
Timber.tag("Migrated names").e(it.walletId.toString() + " " + suggestedWalletName)
}
} else {
val wallets = userWalletsListManager.userWalletsSync
val existingNames: MutableSet<String> = mutableSetOf()
wallets.indices.forEach { i ->
val defaultName = wallets[i].name
val suggestedWalletName = suggestedWalletName(defaultName, existingNames)
if (defaultName != suggestedWalletName) {
userWalletsListManager.update(wallets[i].walletId) { it.copy(name = suggestedWalletName) }
}
Timber.tag("Migrated names").e(i.toString() + " " + suggestedWalletName)
val wallets = userWalletsListRepository.userWalletsSync()
val existingNames: MutableSet<String> = mutableSetOf()
wallets.forEach {
val defaultName = it.name
val suggestedWalletName = suggestedWalletName(defaultName, existingNames)
if (defaultName != suggestedWalletName) {
userWalletsListRepository.saveWithoutLock(it.copy(name = suggestedWalletName), canOverride = true)
}
Timber.tag("Migrated names").e(it.walletId.toString() + " " + suggestedWalletName)
}
walletNamesMigrationRepository.setMigrationDone()

View file

@ -9,7 +9,6 @@ import com.tangem.domain.staking.usecase.StakingAvailabilityListUseCase
import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.wallets.repository.WalletsRepository
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyGetShouldShowMainPromoUseCase
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
@ -21,7 +20,6 @@ import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenList
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.subscribers.*
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.features.tangempay.TangemPayFeatureToggles
@Suppress("LongParameterList")
@ -40,14 +38,12 @@ internal class MultiWalletContentLoader(
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase,
private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory,
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
private val getStoryContentUseCase: GetStoryContentUseCase,
private val walletsRepository: WalletsRepository,
private val currenciesRepository: CurrenciesRepository,
private val yieldSupplyApyFlowUseCase: YieldSupplyApyFlowUseCase,
private val stakingAvailabilityListUseCase: StakingAvailabilityListUseCase,
private val yieldSupplyGetShouldShowMainPromoUseCase: YieldSupplyGetShouldShowMainPromoUseCase,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
private val tangemPayFeatureToggles: TangemPayFeatureToggles,
private val tangemPayMainSubscriberFactory: TangemPayMainSubscriber.Factory,
) : WalletContentLoader(id = userWallet.walletId) {
@ -92,13 +88,6 @@ internal class MultiWalletContentLoader(
getStoryContentUseCase = getStoryContentUseCase,
).let(::add)
WalletDropDownItemsSubscriber(
stateHolder = stateHolder,
shouldSaveUserWalletsUseCase = shouldSaveUserWalletsUseCase,
clickIntents = clickIntents,
hotWalletFeatureToggles = hotWalletFeatureToggles,
).let(::add)
if (tangemPayFeatureToggles.isTangemPayEnabled) {
add(tangemPayMainSubscriberFactory.create(userWallet))
}

View file

@ -9,7 +9,6 @@ import com.tangem.domain.staking.usecase.StakingAvailabilityListUseCase
import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.wallets.repository.WalletsRepository
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyGetShouldShowMainPromoUseCase
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
@ -21,7 +20,6 @@ import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenList
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.subscribers.TangemPayMainSubscriber
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.features.tangempay.TangemPayFeatureToggles
import javax.inject.Inject
@ -38,7 +36,6 @@ internal class MultiWalletContentLoaderFactory @Inject constructor(
private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase,
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
private val walletWarningsSingleEventSender: WalletWarningsSingleEventSender,
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
private val getStoryContentUseCase: GetStoryContentUseCase,
private val walletsRepository: WalletsRepository,
private val getNFTCollectionsUseCase: GetNFTCollectionsUseCase,
@ -46,7 +43,6 @@ internal class MultiWalletContentLoaderFactory @Inject constructor(
private val yieldSupplyApyFlowUseCase: YieldSupplyApyFlowUseCase,
private val stakingAvailabilityListUseCase: StakingAvailabilityListUseCase,
private val yieldSupplyGetShouldShowMainPromoUseCase: YieldSupplyGetShouldShowMainPromoUseCase,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
private val tangemPayFeatureToggles: TangemPayFeatureToggles,
private val tangemPayMainSubscriberFactory: TangemPayMainSubscriber.Factory,
) {
@ -65,13 +61,11 @@ internal class MultiWalletContentLoaderFactory @Inject constructor(
walletWarningsSingleEventSender = walletWarningsSingleEventSender,
applyTokenListSortingUseCase = applyTokenListSortingUseCase,
getStoryContentUseCase = getStoryContentUseCase,
shouldSaveUserWalletsUseCase = shouldSaveUserWalletsUseCase,
walletsRepository = walletsRepository,
getNFTCollectionsUseCase = getNFTCollectionsUseCase,
currenciesRepository = currenciesRepository,
yieldSupplyApyFlowUseCase = yieldSupplyApyFlowUseCase,
stakingAvailabilityListUseCase = stakingAvailabilityListUseCase,
hotWalletFeatureToggles = hotWalletFeatureToggles,
tangemPayFeatureToggles = tangemPayFeatureToggles,
tangemPayMainSubscriberFactory = tangemPayMainSubscriberFactory,
yieldSupplyGetShouldShowMainPromoUseCase = yieldSupplyGetShouldShowMainPromoUseCase,

View file

@ -2,14 +2,12 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.promo.GetStoryContentUseCase
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsSingleEventSender
import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarningsFactory
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.subscribers.*
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.features.tangempay.TangemPayFeatureToggles
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@ -25,10 +23,8 @@ internal class MultiWalletContentLoaderV2 @AssistedInject constructor(
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
private val walletWarningsSingleEventSender: WalletWarningsSingleEventSender,
private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory,
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
private val getStoryContentUseCase: GetStoryContentUseCase,
private val checkWalletWithFundsSubscriberFactory: CheckWalletWithFundsSubscriber.Factory,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
private val tangemPayFeatureToggles: TangemPayFeatureToggles,
private val tangemPayMainSubscriberFactory: TangemPayMainSubscriber.Factory,
) : WalletContentLoader(id = userWallet.walletId) {
@ -50,12 +46,6 @@ internal class MultiWalletContentLoaderV2 @AssistedInject constructor(
stateHolder = stateController,
getStoryContentUseCase = getStoryContentUseCase,
),
WalletDropDownItemsSubscriber(
stateHolder = stateController,
shouldSaveUserWalletsUseCase = shouldSaveUserWalletsUseCase,
clickIntents = clickIntents,
hotWalletFeatureToggles = hotWalletFeatureToggles,
),
if (tangemPayFeatureToggles.isTangemPayEnabled) {
tangemPayMainSubscriberFactory.create(userWallet)

View file

@ -10,13 +10,11 @@ import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.domain.GetSingleWalletWarningsFactory
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.subscribers.*
import com.tangem.features.hotwallet.HotWalletFeatureToggles
@Suppress("LongParameterList")
internal class SingleWalletContentLoader(
@ -33,10 +31,8 @@ internal class SingleWalletContentLoader(
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getOnrampTransactionsUseCase: GetOnrampTransactionsUseCase,
private val onrampRemoveTransactionUseCase: OnrampRemoveTransactionUseCase,
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
) : WalletContentLoader(id = userWallet.walletId) {
override fun create(): List<WalletSubscriber> {
@ -63,12 +59,6 @@ internal class SingleWalletContentLoader(
getSingleWalletWarningsFactory = getSingleWalletWarningsFactory,
walletWarningsAnalyticsSender = walletWarningsAnalyticsSender,
),
WalletDropDownItemsSubscriber(
stateHolder = stateHolder,
shouldSaveUserWalletsUseCase = shouldSaveUserWalletsUseCase,
clickIntents = clickIntents,
hotWalletFeatureToggles = hotWalletFeatureToggles,
),
SingleWalletExpressStatusesSubscriber(
userWallet = userWallet,
stateHolder = stateHolder,

View file

@ -11,12 +11,10 @@ import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.domain.GetSingleWalletWarningsFactory
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import javax.inject.Inject
@ModelScoped
@ -33,10 +31,8 @@ internal class SingleWalletContentLoaderFactory @Inject constructor(
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getOnrampTransactionsUseCase: GetOnrampTransactionsUseCase,
private val onrampRemoveTransactionUseCase: OnrampRemoveTransactionUseCase,
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
) {
fun create(userWallet: UserWallet.Cold, clickIntents: WalletClickIntents, isRefresh: Boolean): WalletContentLoader {
@ -56,8 +52,6 @@ internal class SingleWalletContentLoaderFactory @Inject constructor(
walletWarningsAnalyticsSender = walletWarningsAnalyticsSender,
getOnrampTransactionsUseCase = getOnrampTransactionsUseCase,
onrampRemoveTransactionUseCase = onrampRemoveTransactionUseCase,
shouldSaveUserWalletsUseCase = shouldSaveUserWalletsUseCase,
hotWalletFeatureToggles = hotWalletFeatureToggles,
)
}
}

View file

@ -8,7 +8,6 @@ import com.tangem.domain.onramp.GetOnrampTransactionsUseCase
import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.account.AccountDependencies
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
@ -16,7 +15,6 @@ import com.tangem.feature.wallet.presentation.wallet.domain.GetSingleWalletWarni
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.subscribers.*
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@ -35,13 +33,11 @@ internal class SingleWalletContentLoaderV2 @AssistedInject constructor(
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getOnrampTransactionsUseCase: GetOnrampTransactionsUseCase,
private val onrampRemoveTransactionUseCase: OnrampRemoveTransactionUseCase,
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
private val accountDependencies: AccountDependencies,
private val walletWithFundsChecker: WalletWithFundsChecker,
private val dispatchers: CoroutineDispatcherProvider,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
) : WalletContentLoader(id = userWallet.walletId) {
override fun create(): List<WalletSubscriber> = listOf(
@ -66,12 +62,6 @@ internal class SingleWalletContentLoaderV2 @AssistedInject constructor(
getSingleWalletWarningsFactory = getSingleWalletWarningsFactory,
walletWarningsAnalyticsSender = walletWarningsAnalyticsSender,
),
WalletDropDownItemsSubscriber(
stateHolder = stateHolder,
shouldSaveUserWalletsUseCase = shouldSaveUserWalletsUseCase,
clickIntents = clickIntents,
hotWalletFeatureToggles = hotWalletFeatureToggles,
),
SingleWalletExpressStatusesSubscriberV2(
userWallet = userWallet,
singleAccountStatusListSupplier = accountDependencies.singleAccountStatusListSupplier,

View file

@ -4,7 +4,6 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.promo.GetStoryContentUseCase
import com.tangem.domain.staking.usecase.StakingAvailabilityListUseCase
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyGetShouldShowMainPromoUseCase
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
@ -15,8 +14,10 @@ import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarnin
import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.subscribers.*
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletActionButtonsSubscriber
import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletWarningsSubscriber
import com.tangem.feature.wallet.presentation.wallet.subscribers.SingleWalletWithTokenListSubscriber
import com.tangem.feature.wallet.presentation.wallet.subscribers.WalletSubscriber
@Deprecated("Use SingleWalletWithTokenContentLoaderV2 instead")
@Suppress("LongParameterList")
@ -31,11 +32,9 @@ internal class SingleWalletWithTokenContentLoader(
private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory,
private val tokenListStore: MultiWalletTokenListStore,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
private val getStoryContentUseCase: GetStoryContentUseCase,
private val yieldSupplyApyFlowUseCase: YieldSupplyApyFlowUseCase,
private val stakingAvailabilityListUseCase: StakingAvailabilityListUseCase,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
private val yieldSupplyGetShouldShowMainPromoUseCase: YieldSupplyGetShouldShowMainPromoUseCase,
) : WalletContentLoader(id = userWallet.walletId) {
@ -66,12 +65,6 @@ internal class SingleWalletWithTokenContentLoader(
stateHolder = stateHolder,
getStoryContentUseCase = getStoryContentUseCase,
).let(::add)
WalletDropDownItemsSubscriber(
stateHolder = stateHolder,
shouldSaveUserWalletsUseCase = shouldSaveUserWalletsUseCase,
clickIntents = clickIntents,
hotWalletFeatureToggles = hotWalletFeatureToggles,
).let(::add)
}
}
}

View file

@ -5,7 +5,6 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.promo.GetStoryContentUseCase
import com.tangem.domain.staking.usecase.StakingAvailabilityListUseCase
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyGetShouldShowMainPromoUseCase
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
@ -16,7 +15,6 @@ import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarnin
import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import javax.inject.Inject
// TODO: Refactor
@ -32,11 +30,9 @@ internal class SingleWalletWithTokenContentLoaderFactory @Inject constructor(
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
private val walletWarningsSingleEventSender: WalletWarningsSingleEventSender,
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
private val getStoryContentUseCase: GetStoryContentUseCase,
private val yieldSupplyApyFlowUseCase: YieldSupplyApyFlowUseCase,
private val stakingAvailabilityListUseCase: StakingAvailabilityListUseCase,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
private val yieldSupplyGetShouldShowMainPromoUseCase: YieldSupplyGetShouldShowMainPromoUseCase,
) {
@ -53,10 +49,8 @@ internal class SingleWalletWithTokenContentLoaderFactory @Inject constructor(
walletWarningsAnalyticsSender = walletWarningsAnalyticsSender,
walletWarningsSingleEventSender = walletWarningsSingleEventSender,
getStoryContentUseCase = getStoryContentUseCase,
shouldSaveUserWalletsUseCase = shouldSaveUserWalletsUseCase,
yieldSupplyApyFlowUseCase = yieldSupplyApyFlowUseCase,
stakingAvailabilityListUseCase = stakingAvailabilityListUseCase,
hotWalletFeatureToggles = hotWalletFeatureToggles,
yieldSupplyGetShouldShowMainPromoUseCase = yieldSupplyGetShouldShowMainPromoUseCase,
)
}

View file

@ -1,14 +1,15 @@
package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsSingleEventSender
import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarningsFactory
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.subscribers.*
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.feature.wallet.presentation.wallet.subscribers.CheckWalletWithFundsSubscriber
import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletWarningsSubscriber
import com.tangem.feature.wallet.presentation.wallet.subscribers.SingleWalletWithTokenSubscriber
import com.tangem.feature.wallet.presentation.wallet.subscribers.WalletSubscriber
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@ -23,8 +24,6 @@ internal class SingleWalletWithTokenContentLoaderV2 @AssistedInject constructor(
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
private val walletWarningsSingleEventSender: WalletWarningsSingleEventSender,
private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory,
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
) : WalletContentLoader(id = userWallet.walletId) {
override fun create(): List<WalletSubscriber> = listOf(
@ -37,12 +36,6 @@ internal class SingleWalletWithTokenContentLoaderV2 @AssistedInject constructor(
walletWarningsAnalyticsSender = walletWarningsAnalyticsSender,
walletWarningsSingleEventSender = walletWarningsSingleEventSender,
),
WalletDropDownItemsSubscriber(
stateHolder = stateController,
shouldSaveUserWalletsUseCase = shouldSaveUserWalletsUseCase,
clickIntents = clickIntents,
hotWalletFeatureToggles = hotWalletFeatureToggles,
),
checkWalletWithFundsSubscriberFactory.create(userWallet),
)

View file

@ -1,113 +0,0 @@
package com.tangem.feature.wallet.presentation.wallet.state.model
import androidx.compose.runtime.Immutable
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.feature.wallet.impl.R
@Immutable
internal sealed interface WalletAlertState {
@Immutable
sealed class Basic : WalletAlertState {
abstract val title: TextReference?
abstract val message: TextReference
open val confirmButtonText: TextReference = resourceReference(id = R.string.common_ok)
open val isWarningConfirmButton: Boolean = false
abstract val onConfirmClick: (() -> Unit)?
open val cancelButtonText: TextReference? = null
open val onCancelClick: (() -> Unit)? = null
}
@Immutable
sealed class TextInput : WalletAlertState {
abstract val title: TextReference
abstract val label: TextReference
open val text: String = ""
open val confirmButtonText: TextReference = resourceReference(id = R.string.common_ok)
abstract val onConfirmClick: (String) -> Unit
abstract val errorTextProvider: (String) -> TextReference?
}
data class SimpleOkAlert(val message: TextReference, val onOkClick: () -> Unit) : WalletAlertState
data class DefaultAlert(
override val title: TextReference?,
override val message: TextReference,
override val onConfirmClick: (() -> Unit)?,
) : Basic()
data class RemoveWalletAlert(override val onConfirmClick: (() -> Unit)?) : Basic() {
override val title: TextReference? = null
override val message: TextReference = resourceReference(id = R.string.user_wallet_list_delete_prompt)
override val confirmButtonText: TextReference = resourceReference(id = R.string.common_delete)
override val isWarningConfirmButton: Boolean = true
}
data class VisaLimitsInfo(
val totalLimit: String,
val otherLimit: String,
) : Basic() {
override val title: TextReference? = null
override val message: TextReference = stringReference(
value = "Limits are needed to control costs, improve security, manage risk. " +
"You can spend $totalLimit during the week for card payments in shops and " +
"$otherLimit for other transactions, e. g. subscriptions or debts.",
)
override val onConfirmClick: (() -> Unit)? = null
}
data object WrongCardIsScanned : Basic() {
override val title: TextReference = resourceReference(R.string.common_warning)
override val message: TextReference = resourceReference(R.string.error_wrong_wallet_tapped)
override val onConfirmClick: (() -> Unit)? = null
}
data object RescanWallets : Basic() {
override val title: TextReference = resourceReference(R.string.common_attention)
override val message: TextReference = resourceReference(R.string.key_invalidated_warning_description)
override val onConfirmClick: (() -> Unit)? = null
}
data object VisaBalancesInfo : Basic() {
override val title: TextReference? = null
override val message: TextReference = stringReference(
value = "Available balance is actual funds available, considering pending transactions, " +
"blocked amounts, and debit balance to prevent overdrafts.",
)
override val onConfirmClick: (() -> Unit)? = null
}
data object ProvidersStillLoading : Basic() {
override val title: TextReference = resourceReference(R.string.action_buttons_service_loading_alert_title)
override val message: TextReference = resourceReference(R.string.action_buttons_service_loading_alert_message)
override val onConfirmClick: (() -> Unit)? = null
}
data object UnavailableOperation : Basic() {
override val title: TextReference = resourceReference(R.string.action_buttons_something_wrong_alert_title)
override val message: TextReference = resourceReference(R.string.action_buttons_something_wrong_alert_message)
override val onConfirmClick: (() -> Unit)? = null
}
data object InsufficientTokensCountForSwapping : Basic() {
override val title: TextReference =
resourceReference(id = R.string.action_buttons_swap_no_tokens_added_alert_title)
override val message: TextReference =
resourceReference(id = R.string.action_buttons_swap_no_tokens_added_alert_message)
override val onConfirmClick: (() -> Unit)? = null
}
data class ConfirmExpressStatusHide(
override val onConfirmClick: (() -> Unit),
override val onCancelClick: (() -> Unit),
) : Basic() {
override val title: TextReference = resourceReference(R.string.express_status_hide_dialog_title)
override val message: TextReference = resourceReference(R.string.express_status_hide_dialog_text)
override val confirmButtonText: TextReference = resourceReference(R.string.common_hide)
override val cancelButtonText: TextReference = resourceReference(R.string.common_cancel)
}
}

View file

@ -0,0 +1,96 @@
package com.tangem.feature.wallet.presentation.wallet.state.model
import com.tangem.core.ui.extensions.WrappedList
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.message.DialogMessage
import com.tangem.core.ui.message.EventMessageAction
import com.tangem.domain.models.currency.CryptoCurrency
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(
id = R.string.token_details_unable_hide_alert_title,
formatArgs = WrappedList(listOf(cryptoCurrency.name)),
),
message = resourceReference(
id = R.string.token_details_unable_hide_alert_message,
formatArgs = WrappedList(
listOf(
cryptoCurrency.name,
cryptoCurrency.symbol,
cryptoCurrency.network.name,
),
),
),
)
}
fun hideTokenConfirm(cryptoCurrency: CryptoCurrency, onClick: () -> Unit): DialogMessage {
return DialogMessage(
title = resourceReference(
id = R.string.token_details_hide_alert_title,
formatArgs = WrappedList(listOf(cryptoCurrency.name)),
),
message = resourceReference(R.string.token_details_hide_alert_message),
firstActionBuilder = {
okAction(onClick)
},
)
}
fun providersStillLoading(): DialogMessage {
return DialogMessage(
title = resourceReference(R.string.action_buttons_service_loading_alert_title),
message = resourceReference(R.string.action_buttons_service_loading_alert_message),
)
}
fun unavailableOperation(): DialogMessage {
return DialogMessage(
title = resourceReference(R.string.action_buttons_something_wrong_alert_title),
message = resourceReference(R.string.action_buttons_something_wrong_alert_message),
)
}
fun insufficientTokensCountForSwapping(): DialogMessage {
return DialogMessage(
title = resourceReference(R.string.action_buttons_swap_no_tokens_added_alert_title),
message = resourceReference(R.string.action_buttons_swap_no_tokens_added_alert_message),
)
}
fun confirmExpressStatusHide(onConfirmClick: () -> Unit): DialogMessage {
return DialogMessage(
title = resourceReference(R.string.express_status_hide_dialog_title),
message = resourceReference(R.string.express_status_hide_dialog_text),
firstActionBuilder = {
EventMessageAction(
title = resourceReference(R.string.common_hide),
onClick = onConfirmClick,
)
},
secondActionBuilder = { cancelAction() },
)
}
}

View file

@ -13,12 +13,8 @@ internal sealed class WalletEvent {
data class ShowError(val text: TextReference) : WalletEvent()
data class ShowAlert(val state: WalletAlertState) : WalletEvent()
data object CopyAddress : WalletEvent()
data class RateApp(val onDismissClick: () -> Unit) : WalletEvent()
data class DemonstrateWalletsScrollPreview(val direction: Direction) : WalletEvent() {
enum class Direction {

View file

@ -1,73 +0,0 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.feature.wallet.child.wallet.model.intents.WalletCardClickIntents
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDropDownItems
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
internal class SetWalletCardDropDownItemsTransformer(
private val dropdownEnabled: Boolean,
private val clickIntents: WalletCardClickIntents,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
) : WalletScreenStateTransformer {
override fun transform(prevState: WalletScreenState): WalletScreenState {
return prevState.copy(wallets = prevState.wallets.map(::transformWalletState).toImmutableList())
}
private fun transformWalletState(prevState: WalletState): WalletState {
return when (prevState) {
is WalletState.MultiCurrency.Content -> prevState.copy(
walletCardState = prevState.walletCardState.copySealed(
dropDownItems = constructDropDownItems(prevState.walletCardState.id),
),
)
is WalletState.SingleCurrency.Content -> prevState.copy(
walletCardState = prevState.walletCardState.copySealed(
dropDownItems = constructDropDownItems(prevState.walletCardState.id),
),
)
is WalletState.MultiCurrency.Locked -> prevState.copy(
walletCardState = prevState.walletCardState.copySealed(
dropDownItems = constructDropDownItems(prevState.walletCardState.id),
),
)
is WalletState.SingleCurrency.Locked -> prevState.copy(
walletCardState = prevState.walletCardState.copySealed(
dropDownItems = constructDropDownItems(prevState.walletCardState.id),
),
)
}
}
private fun constructDropDownItems(userWalletId: UserWalletId): ImmutableList<WalletDropDownItems> {
return if (dropdownEnabled) {
buildList {
add(
WalletDropDownItems(
text = resourceReference(id = R.string.common_rename),
icon = R.drawable.ic_edit_24,
onClick = { clickIntents.onRenameBeforeConfirmationClick(userWalletId) },
),
)
if (!hotWalletFeatureToggles.isHotWalletEnabled) {
add(
WalletDropDownItems(
text = resourceReference(id = R.string.common_delete),
icon = R.drawable.ic_trash_24,
onClick = { clickIntents.onDeleteBeforeConfirmationClick(userWalletId) },
),
)
}
}.toImmutableList()
} else {
persistentListOf()
}
}
}

View file

@ -4,12 +4,17 @@ import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent.Companion
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
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 kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.flow.MutableStateFlow
@ -41,13 +46,7 @@ internal class WalletLoadingStateFactory(
private fun createLoadingHotWalletContent(userWallet: UserWallet.Hot): WalletState.MultiCurrency.Content {
return WalletState.MultiCurrency.Content(
pullToRefreshConfig = createPullToRefreshConfig(),
walletCardState = WalletCardState.Loading(
id = userWallet.walletId,
title = userWallet.name,
additionalInfo = WalletAdditionalInfoFactory.resolve(wallet = userWallet),
imageResId = null,
dropDownItems = persistentListOf(),
),
walletCardState = createLoadingWalletCardState(userWallet),
buttons = createMultiWalletActions(userWallet),
warnings = persistentListOf(),
bottomSheetConfig = null,
@ -61,7 +60,7 @@ internal class WalletLoadingStateFactory(
private fun createLoadingMultiCurrencyContent(userWallet: UserWallet.Cold): WalletState.MultiCurrency.Content {
return WalletState.MultiCurrency.Content(
pullToRefreshConfig = createPullToRefreshConfig(),
walletCardState = userWallet.toLoadingWalletCardState(),
walletCardState = createLoadingWalletCardState(userWallet),
buttons = createMultiWalletActions(userWallet),
warnings = persistentListOf(),
bottomSheetConfig = null,
@ -76,7 +75,7 @@ internal class WalletLoadingStateFactory(
val currencySymbol = userWallet.scanResponse.cardTypesResolver.getBlockchain().currency
return WalletState.SingleCurrency.Content(
pullToRefreshConfig = createPullToRefreshConfig(),
walletCardState = userWallet.toLoadingWalletCardState(),
walletCardState = createLoadingWalletCardState(userWallet),
warnings = persistentListOf(),
bottomSheetConfig = null,
buttons = createDimmedButtons(),
@ -98,13 +97,21 @@ internal class WalletLoadingStateFactory(
)
}
private fun UserWallet.Cold.toLoadingWalletCardState(): WalletCardState {
private fun createLoadingWalletCardState(userWallet: UserWallet): WalletCardState {
return WalletCardState.Loading(
id = walletId,
title = name,
additionalInfo = if (isMultiCurrency) WalletAdditionalInfoFactory.resolve(wallet = this) else null,
imageResId = walletImageResolver.resolve(userWallet = this),
dropDownItems = persistentListOf(),
id = userWallet.walletId,
title = userWallet.name,
additionalInfo = if (!userWallet.isMultiCurrency) {
null
} else {
WalletAdditionalInfoFactory.resolve(wallet = userWallet)
},
imageResId = if (userWallet is UserWallet.Cold) {
walletImageResolver.resolve(userWallet)
} else {
null
},
dropDownItems = createDropDownItems(userWalletId = userWallet.walletId),
)
}
@ -150,4 +157,14 @@ internal class WalletLoadingStateFactory(
WalletManageButton.Sell(enabled = true, dimContent = true, onClick = {}),
)
}
private fun createDropDownItems(userWalletId: UserWalletId): ImmutableList<WalletDropDownItems> {
return persistentListOf(
WalletDropDownItems(
text = resourceReference(id = R.string.common_rename),
icon = R.drawable.ic_edit_24,
onClick = { clickIntents.onRenameBeforeConfirmationClick(userWalletId) },
),
)
}
}

View file

@ -1,45 +0,0 @@
package com.tangem.feature.wallet.presentation.wallet.subscribers
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetWalletCardDropDownItemsTransformer
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onEach
internal class WalletDropDownItemsSubscriber(
private val stateHolder: WalletStateController,
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
private val clickIntents: WalletClickIntents,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
) : WalletSubscriber() {
override fun create(coroutineScope: CoroutineScope): Flow<*> {
if (!hotWalletFeatureToggles.isHotWalletEnabled) {
return shouldSaveUserWalletsUseCase.invoke()
.distinctUntilChanged()
.onEach { shouldSaveUserWallets ->
stateHolder.update(
SetWalletCardDropDownItemsTransformer(
dropdownEnabled = shouldSaveUserWallets,
clickIntents = clickIntents,
hotWalletFeatureToggles = hotWalletFeatureToggles,
),
)
}
} else {
return flow<Unit> {
stateHolder.update(
SetWalletCardDropDownItemsTransformer(
dropdownEnabled = true,
clickIntents = clickIntents,
hotWalletFeatureToggles = hotWalletFeatureToggles,
),
)
}
}
}
}

View file

@ -1,98 +0,0 @@
package com.tangem.feature.wallet.presentation.wallet.ui
import androidx.compose.runtime.*
import androidx.compose.ui.text.input.TextFieldValue
import com.tangem.core.ui.components.AdditionalTextInputDialogUM
import com.tangem.core.ui.components.BasicDialog
import com.tangem.core.ui.components.DialogButtonUM
import com.tangem.core.ui.components.TextInputDialog
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAlertState
@Composable
internal fun WalletAlert(state: WalletAlertState, onDismiss: () -> Unit) {
when (state) {
is WalletAlertState.Basic -> BasicAlert(state, onDismiss)
is WalletAlertState.TextInput -> TextInputAlert(state, onDismiss)
is WalletAlertState.SimpleOkAlert -> {
BasicDialog(
message = state.message.resolveReference(),
confirmButton = DialogButtonUM(
onClick = {
state.onOkClick()
onDismiss()
},
),
onDismissDialog = onDismiss,
isDismissable = false,
)
}
}
}
@Composable
private fun BasicAlert(state: WalletAlertState.Basic, onDismiss: () -> Unit) {
val confirmButton: DialogButtonUM
val dismissButton: DialogButtonUM?
val onActionClick = state.onConfirmClick
if (onActionClick != null) {
confirmButton = DialogButtonUM(
title = state.confirmButtonText.resolveReference(),
isWarning = state.isWarningConfirmButton,
onClick = {
onActionClick()
onDismiss()
},
)
dismissButton = DialogButtonUM(
title = stringResourceSafe(id = R.string.common_cancel),
onClick = onDismiss,
)
} else {
confirmButton = DialogButtonUM(
title = state.confirmButtonText.resolveReference(),
isWarning = state.isWarningConfirmButton,
onClick = onDismiss,
)
dismissButton = null
}
BasicDialog(
message = state.message.resolveReference(),
confirmButton = confirmButton,
onDismissDialog = onDismiss,
title = state.title?.resolveReference(),
dismissButton = dismissButton,
)
}
@Composable
private fun TextInputAlert(state: WalletAlertState.TextInput, onDismiss: () -> Unit) {
var value by remember { mutableStateOf(TextFieldValue(text = state.text)) }
TextInputDialog(
fieldValue = value,
confirmButton = DialogButtonUM(
title = state.confirmButtonText.resolveReference(),
isEnabled = value.text.isNotEmpty() &&
value.text != state.text &&
state.errorTextProvider(value.text) == null,
onClick = {
state.onConfirmClick(value.text)
onDismiss()
},
),
onDismissDialog = onDismiss,
onValueChange = { value = it },
title = state.title.resolveReference(),
dismissButton = DialogButtonUM(title = stringResourceSafe(id = R.string.common_cancel), onClick = onDismiss),
textFieldParams = AdditionalTextInputDialogUM(
label = state.label.resolveReference(),
isError = state.errorTextProvider(value.text) != null,
caption = state.errorTextProvider(value.text)?.resolveReference(),
),
)
}

View file

@ -11,9 +11,7 @@ import com.tangem.core.ui.event.StateEvent
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.utils.requestPermission
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAlertState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent
import com.tangem.feature.wallet.presentation.wallet.ui.utils.ReviewManagerRequester
import com.tangem.feature.wallet.presentation.wallet.ui.utils.animateScrollByIndex
import com.tangem.feature.wallet.presentation.wallet.ui.utils.demonstrateScrolling
import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION
@ -24,10 +22,8 @@ internal fun WalletEventEffect(
snackbarHostState: SnackbarHostState,
event: StateEvent<WalletEvent>,
onAutoScrollSet: () -> Unit,
onAlertConfigSet: (WalletAlertState) -> Unit,
) {
val coroutineScope = rememberCoroutineScope()
val context = LocalContext.current
val resources = LocalContext.current.resources
var showPermissionRequest by remember { mutableStateOf<Pair<() -> Unit, () -> Unit>?>(null) }
@ -57,10 +53,6 @@ internal fun WalletEventEffect(
duration = SnackbarDuration.Short,
)
}
is WalletEvent.ShowAlert -> onAlertConfigSet(value.state)
is WalletEvent.RateApp -> {
ReviewManagerRequester.request(context = context, onDismissClick = value.onDismissClick)
}
is WalletEvent.DemonstrateWalletsScrollPreview -> {
walletsListState.demonstrateScrolling(coroutineScope = coroutineScope, direction = value.direction)
}

View file

@ -100,13 +100,6 @@ internal fun WalletScreen(
val snackbarHostState = remember(::SnackbarHostState)
val isAutoScroll = remember { mutableStateOf(value = false) }
var alertConfig by remember { mutableStateOf<WalletAlertState?>(value = null) }
val config = alertConfig
if (config != null) {
WalletAlert(state = config, onDismiss = { alertConfig = null })
}
WalletContent(
state = state,
walletsListState = walletsListState,
@ -114,7 +107,6 @@ internal fun WalletScreen(
isAutoScroll = isAutoScroll,
onAutoScrollReset = { isAutoScroll.value = false },
bottomSheetContent = bottomSheetContent,
alertConfig = alertConfig,
bottomSheetHeaderHeightProvider = bottomSheetHeaderHeightProvider,
onBottomSheetStateChange = onBottomSheetStateChange,
)
@ -124,7 +116,6 @@ internal fun WalletScreen(
snackbarHostState = snackbarHostState,
event = state.event,
onAutoScrollSet = { isAutoScroll.value = true },
onAlertConfigSet = { alertConfig = it },
)
}
@ -135,7 +126,6 @@ private fun WalletContent(
walletsListState: LazyListState,
snackbarHostState: SnackbarHostState,
isAutoScroll: State<Boolean>,
alertConfig: WalletAlertState?,
onAutoScrollReset: () -> Unit,
bottomSheetHeaderHeightProvider: () -> Dp,
onBottomSheetStateChange: (BottomSheetState) -> Unit,
@ -279,7 +269,6 @@ private fun WalletContent(
selectedWallet = selectedWallet,
snackbarHostState = snackbarHostState,
bottomSheetHeaderHeightProvider = bottomSheetHeaderHeightProvider,
alertConfig = alertConfig,
onBottomSheetStateChange = onBottomSheetStateChange,
bottomSheetContent = bottomSheetContent,
content = scaffoldContent,
@ -294,7 +283,6 @@ private inline fun BaseScaffoldWithMarkets(
listState: LazyListState,
selectedWallet: WalletState,
snackbarHostState: SnackbarHostState,
alertConfig: WalletAlertState?,
bottomSheetHeaderHeightProvider: () -> Dp,
noinline onBottomSheetStateChange: (BottomSheetState) -> Unit,
crossinline bottomSheetContent: @Composable () -> Unit,
@ -341,7 +329,6 @@ private inline fun BaseScaffoldWithMarkets(
BottomSheetStateEffects(
bottomSheetState = bottomSheetState,
alertConfig = alertConfig,
onBottomSheetStateChange = onBottomSheetStateChange,
navigationBarVisible = isNavBarVisible,
isSearchFieldFocused = isSearchFieldFocused,
@ -615,7 +602,6 @@ private fun BottomSheetScrim(color: Color, visible: Boolean, onDismissRequest: (
@Composable
private fun BottomSheetStateEffects(
bottomSheetState: TangemSheetState,
alertConfig: WalletAlertState?,
navigationBarVisible: MutableState<Boolean>,
onBottomSheetStateChange: (BottomSheetState) -> Unit,
isSearchFieldFocused: Boolean,
@ -634,7 +620,7 @@ private fun BottomSheetStateEffects(
val isKeyboardVisible by rememberIsKeyboardVisible()
LaunchedEffect(isKeyboardVisible) {
if (isKeyboardVisible && alertConfig == null && isSearchFieldFocused) {
if (isKeyboardVisible && isSearchFieldFocused) {
bottomSheetState.expand()
}
}

View file

@ -1,47 +0,0 @@
package com.tangem.feature.wallet.presentation.wallet.ui.utils
import android.app.Activity
import android.content.Context
import com.google.android.gms.tasks.Task
import com.google.android.play.core.review.ReviewInfo
import com.google.android.play.core.review.ReviewManager
import com.google.android.play.core.review.ReviewManagerFactory
import com.tangem.core.ui.utils.findActivity
import timber.log.Timber
internal object ReviewManagerRequester {
fun request(context: Context, onDismissClick: () -> Unit) {
val reviewManager = ReviewManagerFactory.create(context)
val requestTask = reviewManager.requestReviewFlow()
requestTask
.addOnCompleteListener {
handleOnCompleteRequestTask(
reviewManager = reviewManager,
activity = context.findActivity(),
task = it,
onDismissClick = onDismissClick,
)
}
.addOnFailureListener(Timber::e)
}
private fun handleOnCompleteRequestTask(
reviewManager: ReviewManager,
activity: Activity,
task: Task<ReviewInfo>,
onDismissClick: () -> Unit,
) {
if (task.isSuccessful) {
val reviewFlow = reviewManager.launchReviewFlow(activity, task.result)
reviewFlow
.addOnCompleteListener { resultReviewTask ->
if (!resultReviewTask.isSuccessful) onDismissClick()
}
.addOnFailureListener(Timber::e)
} else {
Timber.e(task.exception)
}
}
}