Updated on 2026-08-14
This commit is contained in:
commit
cc3401a412
943 changed files with 15667 additions and 6451 deletions
|
|
@ -126,6 +126,7 @@ dependencies {
|
|||
implementation(projects.features.tokenRecieve.api)
|
||||
implementation(projects.features.yieldSupply.api)
|
||||
implementation(projects.features.tangempay.details.api)
|
||||
implementation(projects.features.feed.api)
|
||||
|
||||
/** Common modules */
|
||||
implementation(projects.common)
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@
|
|||
<ID>BooleanPropertyNaming:WalletManageButton.kt$WalletManageButton$/** Whether to dim content */ abstract val dimContent: Boolean</ID>
|
||||
<ID>BooleanPropertyNaming:WalletManageButton.kt$WalletManageButton.Swap$val showBadge: Boolean = false</ID>
|
||||
<ID>BooleanPropertyNaming:WalletModel.kt$WalletModel$private var needToRefreshWallet = false</ID>
|
||||
<ID>BooleanPropertyNaming:WalletModel.kt$WalletModel$val initialDataProduced = tangemPayOnboardingRepository.isTangemPayInitialDataProduced()</ID>
|
||||
<ID>BooleanPropertyNaming:WalletNameMigrationUseCase.kt$WalletNameMigrationUseCase$private val useNewListRepository: Boolean</ID>
|
||||
<ID>BooleanPropertyNaming:WalletScreen.kt$val portfolioContent = state is WalletState.MultiCurrency.Content && state.tokensListState is WalletTokensListState.ContentState.PortfolioContent</ID>
|
||||
<ID>BooleanPropertyNaming:WalletScreen.kt$val showMarketsHint by remember { derivedStateOf { // Show hint only when there are items in the list // and when there a no items to scroll listState.layoutInfo.totalItemsCount > 0 && !listState.canScrollBackward && !listState.canScrollForward || listState.canScrollBackward && !listState.canScrollForward } }</ID>
|
||||
|
|
@ -46,7 +45,6 @@
|
|||
<ID>MultilineLambdaItParameter:SetRefreshStateTransformer.kt$SetRefreshStateTransformer${ it.mapNotNull { button -> when (button) { is WalletManageButton.Buy -> button.copy(enabled = isButtonsEnabled) is WalletManageButton.Send -> button.copy(enabled = isButtonsEnabled) is WalletManageButton.Sell -> button.copy(enabled = isButtonsEnabled) is WalletManageButton.Receive -> button is WalletManageButton.Stake -> null is WalletManageButton.Swap -> null } } }</ID>
|
||||
<ID>MultilineLambdaItParameter:SetVisaInfoTransformer.kt$SetVisaInfoTransformer${ if (it is RefreshTokenExpiredException) { return getRefreshTokenExpiredState(prevState) } return prevState.copy( buttons = createVisaButtonsDimmed(), walletCardState = getErrorWalletCardState(prevState.walletCardState), balancesAndLimitBlockState = BalancesAndLimitsBlockState.Error, ) }</ID>
|
||||
<ID>MultilineLambdaItParameter:SingleWalletExpressStatusesSubscriber.kt$SingleWalletExpressStatusesSubscriber${ Timber.e("Unable to get primary currency status: $it") return@onEach }</ID>
|
||||
<ID>MultilineLambdaItParameter:SingleWalletOnrampTransactionConverter.kt$SingleWalletOnrampTransactionConverter${ analyticsEventHandler.send(TokenOnrampAnalyticsEvent.GoToProvider) clickIntents.onGoToProviderClick(it) }</ID>
|
||||
<ID>MultilineLambdaItParameter:TokenListAnalyticsSender.kt$TokenListAnalyticsSender${ val status = it.value if (status is CryptoCurrencyStatus.Loaded) { sendTokenBalancesForSpecificBlockchains(it, status) } }</ID>
|
||||
<ID>MultilineLambdaItParameter:TokenListStateConverter.kt$TokenListStateConverter${ if (isExtend) { clickIntents.onAccountCollapseClick(it) } else { clickIntents.onAccountExpandClick(it) } }</ID>
|
||||
<ID>MultilineLambdaItParameter:TxHistorySubscriber.kt$TxHistorySubscriber${ SetTxHistoryCountErrorTransformer( userWallet = userWallet, error = it, pendingTransactions = status.value.pendingTransactions, clickIntents = clickIntents, ) }</ID>
|
||||
|
|
@ -72,7 +70,6 @@
|
|||
<ID>MultilineLambdaItParameter:WalletCurrencyActionsClickIntents.kt$WalletCurrencyActionsClickIntentsImplementor${ analyticsEventHandler.send(TokenReceiveAnalyticsEvent.ButtonShareAddress(currency.symbol)) shareManager.shareText(text = it) }</ID>
|
||||
<ID>MultilineLambdaItParameter:WalletCurrencyActionsClickIntents.kt$WalletCurrencyActionsClickIntentsImplementor${ onAddressTypeSelected( userWalletId = userWalletId, currency = currency, addressModel = it, ) }</ID>
|
||||
<ID>MultilineLambdaItParameter:WalletLoaderStorage.kt$WalletLoaderStorage${ it.forEach(Job::cancel) loaders.remove(id) }</ID>
|
||||
<ID>MultilineLambdaItParameter:WalletModel.kt$WalletModel${ it .conflate() .distinctUntilChanged() .onEach { selectedWallet -> if (selectedWallet.isMultiCurrency) { selectedWalletAnalyticsSender.send(selectedWallet) } subscribeOnExpressTransactionsUpdates(selectedWallet) observeAndClearNFTCacheIfNeedUseCase(selectedWallet) } .flowOn(dispatchers.main) .launchIn(modelScope) }</ID>
|
||||
<ID>MultilineLambdaItParameter:WalletModel.kt$WalletModel${ walletScreenContentLoader.load( userWallet = it, clickIntents = clickIntents, coroutineScope = modelScope, isRefresh = true, ) }</ID>
|
||||
<ID>MultilineLambdaItParameter:WalletModel.kt$WalletModel${ walletsUpdateActionResolver.resolve( wallets = it, currentState = stateHolder.value, ) }</ID>
|
||||
<ID>MultilineLambdaItParameter:WalletNFTListSubscriber.kt$WalletNFTListSubscriber${ stateHolder.update( SetNFTCollectionsTransformer( userWalletId = userWallet.walletId, nftCollections = it, onItemClick = { clickIntents.onNFTClick(userWallet) }, ), ) }</ID>
|
||||
|
|
@ -85,9 +82,7 @@
|
|||
<ID>MultilineLambdaItParameter:WalletScreen.kt${ nftCollections( modifier = itemModifier, state = it.nftState, ) }</ID>
|
||||
<ID>MultilineLambdaItParameter:WalletWarningsClickIntents.kt$WalletWarningsClickIntentsImplementor${ Timber.e( """ Unable to get user wallet |- ID: $userWalletId |- Exception: $it """.trimIndent(), ) null }</ID>
|
||||
<ID>MultilineLambdaItParameter:WalletWarningsClickIntents.kt$WalletWarningsClickIntentsImplementor${ router.openOnboardingScreen( scanResponse = it.scanResponse, continueBackup = true, ) }</ID>
|
||||
<ID>MultilineLambdaItParameter:WalletWarningsClickIntents.kt$WalletWarningsClickIntentsImplementor${ when (it) { UnlockWalletError.AlreadyUnlocked -> Unit UnlockWalletError.ScannedCardWalletNotMatched -> { uiMessageSender.send( message = DialogMessage( title = resourceReference(R.string.common_warning), message = resourceReference(R.string.error_wrong_wallet_tapped), ), ) } UnlockWalletError.UnableToUnlock -> { Timber.e("Unable to unlock wallet with id: $selectedUserWalletId") uiMessageSender.send( SnackbarMessage(TextReference.Res(R.string.generic_error)), ) } UnlockWalletError.UserCancelled -> Unit UnlockWalletError.UserWalletNotFound -> { // This should never happen in this flow Timber.e("User wallet not found for unlock: $selectedUserWalletId") uiMessageSender.send( SnackbarMessage(TextReference.Res(R.string.generic_error)), ) } } }</ID>
|
||||
<ID>MultilineLambdaItParameter:WalletWithFundsChecker.kt$WalletWithFundsChecker${ val amount = it.value.amount ?: return@any false !amount.isZero() }</ID>
|
||||
<ID>MultilineLambdaItParameter:WalletsUpdateActionResolver.kt$WalletsUpdateActionResolver${ if (it.warnings.any { it is WalletNotification.FinishWalletActivation }) { it.walletCardState.id } else { null } }</ID>
|
||||
<ID>NamedArguments:BasicAccountListSubscriber.kt$BasicAccountListSubscriber$updateContent(convertParams, appCurrency, yieldSupplyApyMap, stakingApyMap)</ID>
|
||||
<ID>NamedArguments:GetMultiWalletWarningsFactory.kt$GetMultiWalletWarningsFactory$addInformationalNotifications(userWallet, cardTypesResolver, flattenCurrencies, clickIntents)</ID>
|
||||
<ID>NamedArguments:GetMultiWalletWarningsFactory.kt$GetMultiWalletWarningsFactory$addWarningNotifications(cardTypesResolver, flattenCurrencies, isNeedToBackup, clickIntents)</ID>
|
||||
|
|
@ -111,23 +106,6 @@
|
|||
<ID>NoNameShadowing:WalletCurrencyActionsClickIntents.kt$WalletCurrencyActionsClickIntentsImplementor${ it is TokensListItemUM.Token }</ID>
|
||||
<ID>NoNameShadowing:WalletNFTItem.kt$modifier</ID>
|
||||
<ID>NoNameShadowing:WalletScreen.kt${ it.organizeTokensButtonConfig?.let { config -> organizeTokensButton( modifier = itemModifier, isEnabled = config.isEnabled, onClick = config.onClick, ) } }</ID>
|
||||
<ID>NoNameShadowing:WalletWarningsClickIntents.kt$WalletWarningsClickIntentsImplementor${ when (it) { UnlockWalletError.AlreadyUnlocked -> Unit UnlockWalletError.ScannedCardWalletNotMatched -> { uiMessageSender.send( message = DialogMessage( title = resourceReference(R.string.common_warning), message = resourceReference(R.string.error_wrong_wallet_tapped), ), ) } UnlockWalletError.UnableToUnlock -> { Timber.e("Unable to unlock wallet with id: $selectedUserWalletId") uiMessageSender.send( SnackbarMessage(TextReference.Res(R.string.generic_error)), ) } UnlockWalletError.UserCancelled -> Unit UnlockWalletError.UserWalletNotFound -> { // This should never happen in this flow Timber.e("User wallet not found for unlock: $selectedUserWalletId") uiMessageSender.send( SnackbarMessage(TextReference.Res(R.string.generic_error)), ) } } }</ID>
|
||||
<ID>NoNameShadowing:WalletsUpdateActionResolver.kt$WalletsUpdateActionResolver${ it is WalletNotification.FinishWalletActivation }</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:AccountDependencies.kt$AccountDependencies$val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:ExpandedAccountsHolder.kt$ExpandedAccountsHolder$private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:GetMultiWalletWarningsFactory.kt$GetMultiWalletWarningsFactory$private val isDemoCardUseCase: IsDemoCardUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:GetMultiWalletWarningsFactory.kt$GetMultiWalletWarningsFactory$private val isNeedToBackupUseCase: IsNeedToBackupUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:GetMultiWalletWarningsFactory.kt$GetMultiWalletWarningsFactory$private val isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:GetSingleWalletWarningsFactory.kt$GetSingleWalletWarningsFactory$private val isDemoCardUseCase: IsDemoCardUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:GetSingleWalletWarningsFactory.kt$GetSingleWalletWarningsFactory$private val isNeedToBackupUseCase: IsNeedToBackupUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:GetSingleWalletWarningsFactory.kt$GetSingleWalletWarningsFactory$private val isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:OrganizeTokensModel.kt$OrganizeTokensModel$private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:ScreenLifecycleProvider.kt$ScreenLifecycleProvider$val isBackgroundState: StateFlow<Boolean> = _isBackgroundState</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:WalletCurrencyActionsClickIntents.kt$WalletCurrencyActionsClickIntentsImplementor$private val isCryptoCurrencyCoinCouldHide: IsCryptoCurrencyCoinCouldHideUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:WalletCurrencyActionsClickIntents.kt$WalletCurrencyActionsClickIntentsImplementor$private val isDemoCardUseCase: IsDemoCardUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:WalletModel.kt$WalletModel$private val isWalletsScrollPreviewEnabled: IsWalletsScrollPreviewEnabled</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:WalletScreen.kt$val isAutoScroll = remember { mutableStateOf(value = false) }</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:WalletScreen.kt$val isNavBarVisible = remember { mutableStateOf(true) }</ID>
|
||||
<ID>NullableToStringCall:DefaultPromoDeeplinkHandler.kt$DefaultPromoDeeplinkHandler$$bitcoinCurrency</ID>
|
||||
<ID>NullableToStringCall:DefaultPromoDeeplinkHandler.kt$DefaultPromoDeeplinkHandler$$bitcoinStatus</ID>
|
||||
<ID>NullableToStringCall:DefaultPromoDeeplinkHandler.kt$DefaultPromoDeeplinkHandler$${cryptoCurrencies?.size}</ID>
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ internal class OrganizeTokensModel @Inject constructor(
|
|||
val onBack = MutableSharedFlow<Unit>()
|
||||
|
||||
init {
|
||||
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.ScreenOpened)
|
||||
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.ScreenOpened())
|
||||
|
||||
getBalanceHidingSettingsUseCase()
|
||||
.onEach {
|
||||
|
|
@ -117,7 +117,7 @@ internal class OrganizeTokensModel @Inject constructor(
|
|||
val list = cachedAccountStatusList ?: return
|
||||
if (list.sortType == TokensSortType.BALANCE) return
|
||||
|
||||
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.ByBalance)
|
||||
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.ByBalance())
|
||||
|
||||
modelScope.launch {
|
||||
toggleTokenListSortingUseCaseV2(list).fold(
|
||||
|
|
@ -132,7 +132,7 @@ internal class OrganizeTokensModel @Inject constructor(
|
|||
val list = cachedTokenList ?: return
|
||||
if (list.sortedBy == TokensSortType.BALANCE) return
|
||||
|
||||
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.ByBalance)
|
||||
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.ByBalance())
|
||||
|
||||
modelScope.launch {
|
||||
toggleTokenListSortingUseCase(list).fold(
|
||||
|
|
@ -150,7 +150,7 @@ internal class OrganizeTokensModel @Inject constructor(
|
|||
if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
val list = cachedAccountStatusList ?: return
|
||||
|
||||
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.Group)
|
||||
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.Group())
|
||||
|
||||
modelScope.launch {
|
||||
toggleTokenListGroupingUseCaseV2(list).fold(
|
||||
|
|
@ -164,7 +164,7 @@ internal class OrganizeTokensModel @Inject constructor(
|
|||
} else {
|
||||
val list = cachedTokenList ?: return
|
||||
|
||||
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.Group)
|
||||
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.Group())
|
||||
|
||||
modelScope.launch {
|
||||
toggleTokenListGroupingUseCase(list).fold(
|
||||
|
|
@ -228,7 +228,7 @@ internal class OrganizeTokensModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onCancelClick() {
|
||||
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.Cancel)
|
||||
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.Cancel())
|
||||
|
||||
modelScope.launch { onBack.emit(Unit) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,20 @@
|
|||
package com.tangem.feature.wallet.child.wallet
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.arkivanov.decompose.extensions.compose.subscribeAsState
|
||||
import com.arkivanov.decompose.router.slot.childSlot
|
||||
import com.arkivanov.decompose.router.slot.dismiss
|
||||
import com.arkivanov.essenty.lifecycle.doOnResume
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
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.components.bottomsheets.state.BottomSheetState
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.core.ui.decompose.ComposableDialogComponent
|
||||
|
|
@ -22,6 +25,8 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogCon
|
|||
import com.tangem.feature.wallet.presentation.wallet.ui.WalletScreen
|
||||
import com.tangem.feature.walletsettings.component.RenameWalletComponent
|
||||
import com.tangem.features.biometry.AskBiometryComponent
|
||||
import com.tangem.features.feed.entry.components.FeedEntryComponent
|
||||
import com.tangem.features.feed.entry.featuretoggle.FeedFeatureToggle
|
||||
import com.tangem.features.markets.entry.MarketsEntryComponent
|
||||
import com.tangem.features.pushnotifications.api.PushNotificationsBottomSheetComponent
|
||||
import com.tangem.features.pushnotifications.api.PushNotificationsParams
|
||||
|
|
@ -36,18 +41,28 @@ import kotlinx.coroutines.launch
|
|||
internal class WalletComponent @AssistedInject constructor(
|
||||
@Assisted appComponentContext: AppComponentContext,
|
||||
@Assisted navigate: (WalletRoute) -> Unit,
|
||||
marketsEntryComponentFactory: MarketsEntryComponent.Factory,
|
||||
feedEntryComponentFactory: FeedEntryComponent.Factory,
|
||||
private val renameWalletComponentFactory: RenameWalletComponent.Factory,
|
||||
private val marketsEntryComponentFactory: MarketsEntryComponent.Factory,
|
||||
private val askBiometryComponentFactory: AskBiometryComponent.Factory,
|
||||
private val pushNotificationsBottomSheetComponent: PushNotificationsBottomSheetComponent.Factory,
|
||||
private val tokenReceiveComponentFactory: TokenReceiveComponent.Factory,
|
||||
private val yieldSupplyDepositedWarningComponent: YieldSupplyDepositedWarningComponent.Factory,
|
||||
private val feedFeatureToggle: FeedFeatureToggle,
|
||||
) : ComposableContentComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: WalletModel = getOrCreateModel()
|
||||
|
||||
private val feedEntryComponent by lazy {
|
||||
feedEntryComponentFactory.create(child("feedEntryComponent"))
|
||||
}
|
||||
private val marketsEntryComponent by lazy {
|
||||
marketsEntryComponentFactory.create(child("marketsEntryComponent"))
|
||||
}
|
||||
|
||||
init {
|
||||
lifecycle.subscribe(model.screenLifecycleProvider)
|
||||
doOnResume { model.onResume() }
|
||||
componentScope.launch { model.innerWalletRouter.navigateToFlow.collect { navigate(it) } }
|
||||
}
|
||||
|
||||
|
|
@ -112,15 +127,23 @@ internal class WalletComponent @AssistedInject constructor(
|
|||
},
|
||||
)
|
||||
|
||||
private val marketsEntryComponent = marketsEntryComponentFactory.create(child("marketsEntryComponent"))
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val bottomSheetState = remember { mutableStateOf(BottomSheetState.COLLAPSED) }
|
||||
var headerSize by remember { mutableStateOf(0.dp) }
|
||||
val dialog by dialog.subscribeAsState()
|
||||
|
||||
WalletScreen(
|
||||
state = model.uiState.collectAsStateWithLifecycle().value,
|
||||
marketsEntryComponent = marketsEntryComponent,
|
||||
bottomSheetContent = {
|
||||
BottomSheetContent(
|
||||
bottomSheetState = bottomSheetState,
|
||||
onHeaderSizeChange = { headerSize = it },
|
||||
modifier = modifier,
|
||||
)
|
||||
},
|
||||
bottomSheetHeaderHeightProvider = { headerSize },
|
||||
onBottomSheetStateChange = { bottomSheetState.value = it },
|
||||
)
|
||||
|
||||
when (val dialog = dialog.child?.instance) {
|
||||
|
|
@ -130,6 +153,27 @@ internal class WalletComponent @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BottomSheetContent(
|
||||
bottomSheetState: State<BottomSheetState>,
|
||||
onHeaderSizeChange: (Dp) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (feedFeatureToggle.isFeedEnabled) {
|
||||
feedEntryComponent.BottomSheetContent(
|
||||
bottomSheetState = bottomSheetState,
|
||||
onHeaderSizeChange = onHeaderSizeChange,
|
||||
modifier = modifier,
|
||||
)
|
||||
} else {
|
||||
marketsEntryComponent.BottomSheetContent(
|
||||
bottomSheetState = bottomSheetState,
|
||||
onHeaderSizeChange = onHeaderSizeChange,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(appComponentContext: AppComponentContext, navigate: (WalletRoute) -> Unit): WalletComponent
|
||||
|
|
|
|||
|
|
@ -7,18 +7,17 @@ import com.arkivanov.decompose.router.slot.dismiss
|
|||
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.analytics.utils.TrackingContextProxy
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||
import com.tangem.domain.account.supplier.SingleAccountListSupplier
|
||||
import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase
|
||||
import com.tangem.domain.apptheme.GetAppThemeModeUseCase
|
||||
import com.tangem.domain.apptheme.model.AppThemeMode
|
||||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.models.wallet.isImported
|
||||
import com.tangem.domain.models.wallet.isLocked
|
||||
import com.tangem.domain.models.wallet.isMultiCurrency
|
||||
import com.tangem.domain.models.wallet.*
|
||||
import com.tangem.domain.nft.ObserveAndClearNFTCacheIfNeedUseCase
|
||||
import com.tangem.domain.notifications.GetIsHuaweiDeviceWithoutGoogleServicesUseCase
|
||||
import com.tangem.domain.notifications.repository.NotificationsRepository
|
||||
|
|
@ -43,6 +42,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.transformers.*
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
|
||||
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
|
||||
|
|
@ -100,6 +100,10 @@ internal class WalletModel @Inject constructor(
|
|||
private val accountsFeatureToggles: AccountsFeatureToggles,
|
||||
private val tangemPayMainScreenCustomerInfoUseCase: TangemPayMainScreenCustomerInfoUseCase,
|
||||
private val getAppThemeModeUseCase: GetAppThemeModeUseCase,
|
||||
private val trackingContextProxy: TrackingContextProxy,
|
||||
private val singleAccountListSupplier: SingleAccountListSupplier,
|
||||
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
|
||||
private val feedFeatureToggle: FeedFeatureToggle,
|
||||
val screenLifecycleProvider: ScreenLifecycleProvider,
|
||||
val innerWalletRouter: InnerWalletRouter,
|
||||
) : Model() {
|
||||
|
|
@ -117,13 +121,9 @@ internal class WalletModel @Inject constructor(
|
|||
private var expressTxStatusTaskScheduler = SingleTaskScheduler<Unit>()
|
||||
|
||||
init {
|
||||
screenLifecycleProvider.isBackgroundState
|
||||
.onEach { isBackground ->
|
||||
if (isBackground.not()) {
|
||||
suggestToEnableBiometrics()
|
||||
}
|
||||
}.launchIn(modelScope)
|
||||
trackScreenOpened()
|
||||
|
||||
updateMarketToggle()
|
||||
suggestToOpenMarkets()
|
||||
|
||||
maybeMigrateNames()
|
||||
|
|
@ -140,6 +140,16 @@ internal class WalletModel @Inject constructor(
|
|||
clickIntents.initialize(innerWalletRouter, modelScope)
|
||||
}
|
||||
|
||||
fun onResume() {
|
||||
suggestToEnableBiometrics()
|
||||
}
|
||||
|
||||
private fun updateMarketToggle() {
|
||||
stateHolder.update {
|
||||
it.copy(isNewMarketEnabled = feedFeatureToggle.isFeedEnabled)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateYieldSupplyApy() {
|
||||
if (yieldSupplyFeatureToggles.isYieldSupplyFeatureEnabled) {
|
||||
modelScope.launch(dispatchers.default) {
|
||||
|
|
@ -168,13 +178,15 @@ internal class WalletModel @Inject constructor(
|
|||
walletScreenContentLoader.cancelAll()
|
||||
}
|
||||
|
||||
private suspend fun suggestToEnableBiometrics() {
|
||||
if (shouldShowAskBiometryBottomSheet()) {
|
||||
delay(timeMillis = 1_800)
|
||||
private fun suggestToEnableBiometrics() {
|
||||
modelScope.launch(dispatchers.main) {
|
||||
if (shouldShowAskBiometryBottomSheet()) {
|
||||
delay(timeMillis = 1_800)
|
||||
|
||||
innerWalletRouter.dialogNavigation.activate(
|
||||
configuration = WalletDialogConfig.AskForBiometry,
|
||||
)
|
||||
innerWalletRouter.dialogNavigation.activate(
|
||||
configuration = WalletDialogConfig.AskForBiometry,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -192,10 +204,45 @@ 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 }
|
||||
|
||||
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(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
analyticsEventsHandler.send(
|
||||
WalletScreenAnalyticsEvent.MainScreen.ScreenOpenedLegacy(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun shouldShowAskBiometryBottomSheet(): Boolean {
|
||||
return if (hotWalletFeatureToggles.isHotWalletEnabled) {
|
||||
userWalletsListRepository.userWalletsSync().any { it is UserWallet.Cold } &&
|
||||
innerWalletRouter.isWalletLastScreen() &&
|
||||
shouldShowAskBiometryUseCase() &&
|
||||
canUseBiometryUseCase()
|
||||
} else {
|
||||
|
|
@ -257,7 +304,9 @@ internal class WalletModel @Inject constructor(
|
|||
notificationsRepository.setShouldAskNotificationPermissionsViaBs(true)
|
||||
return@launch
|
||||
}
|
||||
if (!isBiometricsEnabled) return@launch
|
||||
if (!hotWalletFeatureToggles.isHotWalletEnabled && !isBiometricsEnabled) {
|
||||
return@launch
|
||||
}
|
||||
if (!shouldShow) {
|
||||
return@launch
|
||||
}
|
||||
|
|
@ -278,14 +327,14 @@ internal class WalletModel @Inject constructor(
|
|||
.conflate()
|
||||
.distinctUntilChanged()
|
||||
.onEach { selectedWallet ->
|
||||
trackingContextProxy.setContext(selectedWallet)
|
||||
|
||||
if (selectedWallet.isMultiCurrency) {
|
||||
selectedWalletAnalyticsSender.send(selectedWallet)
|
||||
}
|
||||
|
||||
subscribeOnExpressTransactionsUpdates(selectedWallet)
|
||||
observeAndClearNFTCacheIfNeedUseCase(selectedWallet)
|
||||
|
||||
sendMainScreenOpenedAnalytics(selectedWallet.isImported())
|
||||
}
|
||||
.flowOn(dispatchers.main)
|
||||
.launchIn(modelScope)
|
||||
|
|
@ -419,7 +468,8 @@ internal class WalletModel @Inject constructor(
|
|||
private suspend fun updateWallets(action: WalletsUpdateActionResolver.Action) {
|
||||
when (action) {
|
||||
is WalletsUpdateActionResolver.Action.InitializeWallets -> initializeWallets(action)
|
||||
is WalletsUpdateActionResolver.Action.ReinitializeWallet -> reinitializeWallet(action)
|
||||
is WalletsUpdateActionResolver.Action.ReinitializeNewWallet -> reinitializeNewWallet(action)
|
||||
is WalletsUpdateActionResolver.Action.ReinitializeWallets -> reinitializeWallets(action)
|
||||
is WalletsUpdateActionResolver.Action.AddWallet -> addWallet(action)
|
||||
is WalletsUpdateActionResolver.Action.DeleteWallet -> deleteWallet(action)
|
||||
is WalletsUpdateActionResolver.Action.UnlockWallet -> unlockWallet(action)
|
||||
|
|
@ -502,7 +552,7 @@ internal class WalletModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun reinitializeWallet(action: WalletsUpdateActionResolver.Action.ReinitializeWallet) {
|
||||
private fun reinitializeNewWallet(action: WalletsUpdateActionResolver.Action.ReinitializeNewWallet) {
|
||||
walletScreenContentLoader.cancel(action.prevWalletId)
|
||||
tokenListStore.remove(action.prevWalletId)
|
||||
|
||||
|
|
@ -515,7 +565,7 @@ internal class WalletModel @Inject constructor(
|
|||
fetchWalletContent(userWallet = action.selectedWallet)
|
||||
|
||||
stateHolder.update(
|
||||
ReinitializeWalletTransformer(
|
||||
ReinitializeNewWalletTransformer(
|
||||
prevWalletId = action.prevWalletId,
|
||||
newUserWallet = action.selectedWallet,
|
||||
clickIntents = clickIntents,
|
||||
|
|
@ -524,6 +574,29 @@ internal class WalletModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun reinitializeWallets(action: WalletsUpdateActionResolver.Action.ReinitializeWallets) {
|
||||
action.wallets.forEach { userWallet ->
|
||||
walletScreenContentLoader.cancel(userWallet.walletId)
|
||||
tokenListStore.remove(userWallet.walletId)
|
||||
|
||||
walletScreenContentLoader.load(
|
||||
userWallet = userWallet,
|
||||
clickIntents = clickIntents,
|
||||
coroutineScope = modelScope,
|
||||
)
|
||||
|
||||
fetchWalletContent(userWallet = userWallet)
|
||||
|
||||
stateHolder.update(
|
||||
ReinitializeWalletTransformer(
|
||||
userWallet = userWallet,
|
||||
clickIntents = clickIntents,
|
||||
walletImageResolver = walletImageResolver,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addWallet(action: WalletsUpdateActionResolver.Action.AddWallet) {
|
||||
if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
fetchWalletContent(userWallet = action.selectedWallet)
|
||||
|
|
@ -606,7 +679,7 @@ internal class WalletModel @Inject constructor(
|
|||
}
|
||||
|
||||
private suspend fun unlockWallet(action: WalletsUpdateActionResolver.Action.UnlockWallet) {
|
||||
withContext(dispatchers.io) { delay(timeMillis = 700) }
|
||||
delay(timeMillis = 700)
|
||||
|
||||
stateHolder.update(
|
||||
transformer = UnlockWalletTransformer(
|
||||
|
|
@ -623,7 +696,7 @@ internal class WalletModel @Inject constructor(
|
|||
)
|
||||
|
||||
action.unlockedWallets.onEach { userWallet ->
|
||||
modelScope.launch { fetchWalletContent(userWallet = userWallet) }
|
||||
fetchWalletContent(userWallet = userWallet)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -695,17 +768,6 @@ internal class WalletModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun sendMainScreenOpenedAnalytics(isImported: Boolean) {
|
||||
val result = getAppThemeModeUseCase().firstOrNull()
|
||||
val theme = result?.getOrElse { AppThemeMode.FOLLOW_SYSTEM } ?: AppThemeMode.FOLLOW_SYSTEM
|
||||
analyticsEventsHandler.send(
|
||||
WalletScreenAnalyticsEvent.MainScreen.ScreenOpened(
|
||||
theme = theme.value,
|
||||
isImported = isImported,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
inner class AskBiometryModelCallbacks : AskBiometryComponent.ModelCallbacks {
|
||||
override fun onAllowed() {
|
||||
analyticsEventsHandler.send(MainScreenAnalyticsEvent.EnableBiometrics(AnalyticsParam.OnOffState.On))
|
||||
|
|
|
|||
|
|
@ -64,46 +64,53 @@ internal class WalletsUpdateActionResolver @Inject constructor(
|
|||
selectedWallet: UserWallet,
|
||||
): Action {
|
||||
return when {
|
||||
isHotWalletUpgraded(state, wallets) -> {
|
||||
getHotWalletsUpgradedAction(state, wallets)
|
||||
}
|
||||
isWalletsCountChanged(state, wallets) -> {
|
||||
getChangeWalletsListAction(state, wallets, selectedWallet)
|
||||
}
|
||||
isAnotherWalletSelected(state, selectedWallet) -> {
|
||||
Action.ReinitializeWallet(
|
||||
prevWalletId = state.getPrevSelectedWallet().id,
|
||||
isAnyWalletUnlocked(state, wallets) -> {
|
||||
Action.UnlockWallet(
|
||||
selectedWallet = selectedWallet,
|
||||
unlockedWallets = wallets.filterNot(UserWallet::isLocked),
|
||||
)
|
||||
}
|
||||
isAnyWalletNameChanged(state, wallets) -> {
|
||||
getRenameWalletsAction(state, wallets)
|
||||
}
|
||||
isAnyHotWalletUpgraded(state, wallets) -> {
|
||||
getHotWalletsUpgradedAction(state, wallets, selectedWallet)
|
||||
}
|
||||
isAnyHotWalletBackedUpChange(state, wallets) -> {
|
||||
getHotWalletsBackedUpAction(state, wallets)
|
||||
}
|
||||
else -> getUpdateSelectedWalletAction(state, wallets, selectedWallet)
|
||||
isWalletsCountChanged(state, wallets) -> {
|
||||
getChangeWalletsListAction(state, wallets, selectedWallet)
|
||||
}
|
||||
isAnotherWalletSelected(state, selectedWallet) -> {
|
||||
Action.ReinitializeNewWallet(
|
||||
prevWalletId = state.getPrevSelectedWallet().id,
|
||||
selectedWallet = selectedWallet,
|
||||
)
|
||||
}
|
||||
isSelectedWalletCardsCountChanged(state, selectedWallet) -> {
|
||||
Action.UpdateWalletCardCount(selectedWallet)
|
||||
}
|
||||
else -> Action.Unknown
|
||||
}
|
||||
}
|
||||
|
||||
private fun isAnyHotWalletBackedUpChange(state: WalletScreenState, wallets: List<UserWallet>): Boolean {
|
||||
val incompleteActivationWalletIds = state.incompleteActivationWalletIds()
|
||||
val walletsToUpdate = wallets.filter {
|
||||
it is UserWallet.Hot && it.backedUp == incompleteActivationWalletIds.contains(it.walletId)
|
||||
return wallets.any {
|
||||
it is UserWallet.Hot && it.backedUp && incompleteActivationWalletIds.contains(it.walletId)
|
||||
}
|
||||
return walletsToUpdate.isNotEmpty()
|
||||
}
|
||||
|
||||
private fun isHotWalletUpgraded(state: WalletScreenState, wallets: List<UserWallet>): Boolean {
|
||||
val previousWallet = state
|
||||
.wallets
|
||||
.getOrNull(state.selectedWalletIndex)
|
||||
return when (previousWallet) {
|
||||
is WalletState.MultiCurrency -> {
|
||||
val wallet = wallets.firstOrNull { it.walletId == previousWallet.walletCardState.id }
|
||||
previousWallet.type == WalletState.MultiCurrency.WalletType.Hot && wallet is UserWallet.Cold
|
||||
private fun isAnyHotWalletUpgraded(state: WalletScreenState, wallets: List<UserWallet>): Boolean {
|
||||
return state.wallets.any { walletState ->
|
||||
when (walletState) {
|
||||
is WalletState.MultiCurrency -> {
|
||||
val wallet = wallets.firstOrNull { it.walletId == walletState.walletCardState.id }
|
||||
walletState.type == WalletState.MultiCurrency.WalletType.Hot && wallet is UserWallet.Cold
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -189,9 +196,15 @@ internal class WalletsUpdateActionResolver @Inject constructor(
|
|||
private fun getHotWalletsUpgradedAction(
|
||||
state: WalletScreenState,
|
||||
wallets: List<UserWallet>,
|
||||
): Action.ReloadWallets {
|
||||
val walletsToUpdate = wallets.filter { it.walletId == state.getPrevSelectedWallet().id }
|
||||
return Action.ReloadWallets(walletsToUpdate)
|
||||
selectedWallet: UserWallet,
|
||||
): Action.ReinitializeWallets {
|
||||
val walletsToUpdate = wallets.filter { wallet ->
|
||||
val previousState = state.wallets.firstOrNull { it.walletCardState.id == wallet.walletId }
|
||||
?: return@filter false
|
||||
wallet is UserWallet.Cold && previousState is WalletState.MultiCurrency &&
|
||||
previousState.type == WalletState.MultiCurrency.WalletType.Hot
|
||||
}
|
||||
return Action.ReinitializeWallets(selectedWallet, walletsToUpdate)
|
||||
}
|
||||
|
||||
private fun getRenameWalletsAction(state: WalletScreenState, wallets: List<UserWallet>): Action.RenameWallets {
|
||||
|
|
@ -205,29 +218,14 @@ internal class WalletsUpdateActionResolver @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun getUpdateSelectedWalletAction(
|
||||
state: WalletScreenState,
|
||||
wallets: List<UserWallet>,
|
||||
selectedWallet: UserWallet,
|
||||
): Action {
|
||||
return when {
|
||||
isSelectedWalletUnlocked(state, selectedWallet) -> {
|
||||
Action.UnlockWallet(
|
||||
selectedWallet = selectedWallet,
|
||||
unlockedWallets = wallets.filterNot(UserWallet::isLocked),
|
||||
)
|
||||
}
|
||||
isSelectedWalletCardsCountChanged(state, selectedWallet) -> {
|
||||
Action.UpdateWalletCardCount(selectedWallet)
|
||||
}
|
||||
else -> Action.Unknown
|
||||
private fun isAnyWalletUnlocked(state: WalletScreenState, wallets: List<UserWallet>): Boolean {
|
||||
return state.wallets.any { walletState ->
|
||||
val wallet = wallets.firstOrNull { it.walletId == walletState.walletCardState.id } ?: return@any false
|
||||
!wallet.isLocked &&
|
||||
(walletState is WalletState.MultiCurrency.Locked || walletState is WalletState.SingleCurrency.Locked)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isSelectedWalletUnlocked(state: WalletScreenState, selectedWallet: UserWallet): Boolean {
|
||||
return state.isSelectedWalletLocked() && !selectedWallet.isLocked
|
||||
}
|
||||
|
||||
private fun isSelectedWalletCardsCountChanged(state: WalletScreenState, selectedWallet: UserWallet): Boolean {
|
||||
if (selectedWallet !is UserWallet.Cold) return false
|
||||
val prevSelectedWallet = state.getPrevSelectedWallet()
|
||||
|
|
@ -235,12 +233,6 @@ internal class WalletsUpdateActionResolver @Inject constructor(
|
|||
prevSelectedWallet.cardCount != selectedWallet.getCardsCount()
|
||||
}
|
||||
|
||||
private fun WalletScreenState.isSelectedWalletLocked(): Boolean {
|
||||
val selectedWalletState = wallets.getOrNull(selectedWalletIndex) ?: error("Selected wallet is not found")
|
||||
return selectedWalletState is WalletState.MultiCurrency.Locked ||
|
||||
selectedWalletState is WalletState.SingleCurrency.Locked
|
||||
}
|
||||
|
||||
private fun WalletScreenState.getPrevSelectedWallet(): WalletCardState {
|
||||
return wallets
|
||||
.map(WalletState::walletCardState)
|
||||
|
|
@ -249,9 +241,12 @@ internal class WalletsUpdateActionResolver @Inject constructor(
|
|||
}
|
||||
|
||||
private fun WalletScreenState.incompleteActivationWalletIds(): List<UserWalletId> {
|
||||
return wallets.mapNotNull {
|
||||
if (it.warnings.any { it is WalletNotification.FinishWalletActivation }) {
|
||||
it.walletCardState.id
|
||||
return wallets.mapNotNull { wallet ->
|
||||
if (wallet.warnings.any { it is WalletNotification.FinishWalletActivation } ||
|
||||
wallet.walletCardState is WalletState.MultiCurrency &&
|
||||
wallet.walletCardState.additionalInfo?.isHotBackedUp == false
|
||||
) {
|
||||
wallet.walletCardState.id
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
|
@ -296,7 +291,7 @@ internal class WalletsUpdateActionResolver @Inject constructor(
|
|||
* @property prevWalletId previous selected wallet id
|
||||
* @property selectedWallet selected wallet
|
||||
*/
|
||||
data class ReinitializeWallet(
|
||||
data class ReinitializeNewWallet(
|
||||
val prevWalletId: UserWalletId,
|
||||
val selectedWallet: UserWallet,
|
||||
) : Action() {
|
||||
|
|
@ -306,6 +301,19 @@ internal class WalletsUpdateActionResolver @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reinitialize wallets
|
||||
*/
|
||||
data class ReinitializeWallets(
|
||||
val selectedWallet: UserWallet,
|
||||
val wallets: List<UserWallet>,
|
||||
) : Action() {
|
||||
|
||||
override fun toString(): String {
|
||||
return "ReinitializeWallets(wallets = ${wallets.joinToString { it.walletId.toString() }}"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename wallets
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,19 +1,29 @@
|
|||
package com.tangem.feature.wallet.child.wallet.model.intents
|
||||
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.res.R
|
||||
import com.tangem.core.ui.components.bottomsheets.message.*
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.message.DialogMessage
|
||||
import com.tangem.core.ui.message.EventMessageAction
|
||||
import com.tangem.core.ui.message.ToastMessage
|
||||
import com.tangem.core.ui.message.bottomSheetMessage
|
||||
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
|
||||
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
||||
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.pay.TangemPayDetailsConfig
|
||||
import com.tangem.domain.pay.TangemPayEligibilityManager
|
||||
import com.tangem.domain.pay.repository.OnboardingRepository
|
||||
import com.tangem.domain.pay.usecase.ProduceTangemPayInitialDataUseCase
|
||||
import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
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 com.tangem.features.tangempay.TangemPayFeatureToggles
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
|
@ -22,13 +32,21 @@ internal interface TangemPayIntents {
|
|||
|
||||
suspend fun onPullToRefresh()
|
||||
|
||||
fun onRefreshPayToken(userWalletId: UserWalletId)
|
||||
fun onRefreshPayToken(userWallet: UserWallet)
|
||||
|
||||
fun openDetails(userWalletId: UserWalletId, config: TangemPayDetailsConfig)
|
||||
|
||||
fun onKycProgressClicked(userWalletId: UserWalletId)
|
||||
|
||||
fun onIssuingCardClicked()
|
||||
|
||||
fun onIssuingFailedClicked()
|
||||
|
||||
fun onPaySupportClick()
|
||||
|
||||
fun onOnboardingBannerClick(userWalletId: UserWalletId)
|
||||
|
||||
fun onOnboardingBannerCloseClick(userWalletId: UserWalletId)
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
|
|
@ -41,6 +59,8 @@ internal class TangemPayClickIntentsImplementor @Inject constructor(
|
|||
private val getWalletMetainfoUseCase: GetWalletMetaInfoUseCase,
|
||||
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
|
||||
private val tangemPayMainScreenCustomerInfoUseCase: TangemPayMainScreenCustomerInfoUseCase,
|
||||
private val tangemPayOnboardingRepository: OnboardingRepository,
|
||||
private val tangemPayEligibilityManager: TangemPayEligibilityManager,
|
||||
private val uiMessageSender: UiMessageSender,
|
||||
) : BaseWalletClickIntents(), TangemPayIntents {
|
||||
|
||||
|
|
@ -54,13 +74,69 @@ internal class TangemPayClickIntentsImplementor @Inject constructor(
|
|||
tangemPayMainScreenCustomerInfoUseCase.fetch(userWalletId)
|
||||
}
|
||||
|
||||
override fun onRefreshPayToken(userWalletId: UserWalletId) {
|
||||
override fun onRefreshPayToken(userWallet: UserWallet) {
|
||||
stateHolder.update(TangemPayRefreshShowProgressTransformer(userWallet.walletId))
|
||||
|
||||
modelScope.launch {
|
||||
produceInitialDataTangemPay.invoke(userWalletId)
|
||||
tangemPayMainScreenCustomerInfoUseCase.fetch(userWalletId)
|
||||
produceInitialDataTangemPay.invoke(userWallet.walletId)
|
||||
.onRight { tangemPayMainScreenCustomerInfoUseCase.fetch(userWallet.walletId) }
|
||||
.onLeft {
|
||||
stateHolder.update(
|
||||
transformer = TangemPayRefreshNeededStateTransformer(
|
||||
userWallet = userWallet,
|
||||
userWalletId = userWallet.walletId,
|
||||
onRefreshClick = { onRefreshPayToken(userWallet) },
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun openDetails(userWalletId: UserWalletId, config: TangemPayDetailsConfig) {
|
||||
router.openTangemPayDetails(
|
||||
userWalletId = userWalletId,
|
||||
config = config,
|
||||
)
|
||||
}
|
||||
|
||||
override fun onKycProgressClicked(userWalletId: UserWalletId) {
|
||||
val cancelKycConfirmDialogMessage = DialogMessage(
|
||||
title = resourceReference(R.string.tangempay_kyc_confirm_cancellation_alert_title),
|
||||
message = resourceReference(R.string.tangempay_kyc_confirm_cancellation_description),
|
||||
firstAction = EventMessageAction(
|
||||
title = resourceReference(R.string.common_not_now),
|
||||
onClick = { },
|
||||
),
|
||||
secondAction = EventMessageAction(
|
||||
title = resourceReference(R.string.common_confirm),
|
||||
onClick = { disableTangemPay(userWalletId) },
|
||||
),
|
||||
)
|
||||
val kycInfoBottomSheet = bottomSheetMessage {
|
||||
infoBlock {
|
||||
iconImage(res = com.tangem.core.ui.R.drawable.img_visa_notification)
|
||||
title = resourceReference(R.string.tangempay_kyc_in_progress)
|
||||
body = resourceReference(R.string.tangempay_kyc_in_progress_popup_description)
|
||||
}
|
||||
primaryButton {
|
||||
text = resourceReference(R.string.tangempay_kyc_in_progress_notification_button)
|
||||
onClick = {
|
||||
router.openTangemPayOnboarding(AppRoute.TangemPayOnboarding.Mode.ContinueOnboarding(userWalletId))
|
||||
closeBs()
|
||||
}
|
||||
}
|
||||
secondaryButton {
|
||||
text = resourceReference(R.string.tangempay_cancel_kyc)
|
||||
onClick = {
|
||||
uiMessageSender.send(cancelKycConfirmDialogMessage)
|
||||
closeBs()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uiMessageSender.send(kycInfoBottomSheet)
|
||||
}
|
||||
|
||||
override fun onIssuingCardClicked() {
|
||||
val issuingBottomSheet = bottomSheetMessage {
|
||||
infoBlock {
|
||||
|
|
@ -115,4 +191,30 @@ internal class TangemPayClickIntentsImplementor @Inject constructor(
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOnboardingBannerClick(userWalletId: UserWalletId) {
|
||||
modelScope.launch {
|
||||
val isEligible = tangemPayEligibilityManager.getTangemPayAvailability()
|
||||
if (isEligible) {
|
||||
router.openTangemPayOnboarding(mode = AppRoute.TangemPayOnboarding.Mode.FromBannerOnMain(userWalletId))
|
||||
} else {
|
||||
tangemPayMainScreenCustomerInfoUseCase.fetch(userWalletId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOnboardingBannerCloseClick(userWalletId: UserWalletId) {
|
||||
modelScope.launch {
|
||||
stateHolder.update(transformer = TangemPayHideOnboardingStateTransformer(userWalletId))
|
||||
onboardingRepository.setHideMainOnboardingBanner(userWalletId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun disableTangemPay(userWalletId: UserWalletId) {
|
||||
modelScope.launch {
|
||||
tangemPayOnboardingRepository.disableTangemPay(userWalletId)
|
||||
.onRight { tangemPayMainScreenCustomerInfoUseCase.fetch(userWalletId) }
|
||||
.onLeft { uiMessageSender.send(ToastMessage(resourceReference(R.string.common_something_went_wrong))) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -51,7 +51,7 @@ internal class VisaWalletIntentsImplementor @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onBalancesAndLimitsClick() {
|
||||
analyticsEventHandler.send(MainScreenAnalyticsEvent.LimitsClicked)
|
||||
analyticsEventHandler.send(MainScreenAnalyticsEvent.LimitsClicked())
|
||||
modelScope.launch(dispatchers.main) {
|
||||
val userWalletId = stateController.getSelectedWalletId()
|
||||
val balancesAndLimits = getVisaCurrencyUseCase(userWalletId)
|
||||
|
|
@ -92,7 +92,7 @@ internal class VisaWalletIntentsImplementor @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onExploreClick(exploreUrl: String) {
|
||||
analyticsEventHandler.send(MainScreenAnalyticsEvent.ButtonExplore)
|
||||
analyticsEventHandler.send(MainScreenAnalyticsEvent.ButtonExplore())
|
||||
router.openUrl(exploreUrl)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ internal class WalletCardClickIntentsImplementor @Inject constructor(
|
|||
) : BaseWalletClickIntents(), WalletCardClickIntents {
|
||||
|
||||
override fun onRenameBeforeConfirmationClick(userWalletId: UserWalletId) {
|
||||
analyticsEventHandler.send(MainScreen.EditWalletTapped)
|
||||
analyticsEventHandler.send(MainScreen.EditWalletTapped())
|
||||
|
||||
router.dialogNavigation.activate(
|
||||
configuration = WalletDialogConfig.RenameWallet(
|
||||
|
|
@ -65,7 +65,7 @@ internal class WalletCardClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onDeleteBeforeConfirmationClick(userWalletId: UserWalletId) {
|
||||
analyticsEventHandler.send(MainScreen.DeleteWalletTapped)
|
||||
analyticsEventHandler.send(MainScreen.DeleteWalletTapped())
|
||||
|
||||
walletEventSender.send(
|
||||
event = WalletEvent.ShowAlert(
|
||||
|
|
|
|||
|
|
@ -4,12 +4,13 @@ import arrow.core.getOrElse
|
|||
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
|
||||
import com.tangem.common.ui.tokens.TokenItemStateConverter.ApySource
|
||||
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.domain.models.account.Account
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.staking.YieldBalance
|
||||
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
|
||||
|
|
@ -233,11 +234,13 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
|
||||
override fun onAccountExpandClick(account: Account) {
|
||||
val userWalletId = stateHolder.getSelectedWalletId()
|
||||
analyticsEventHandler.send(MainScreenAnalyticsEvent.AccountShowTokens())
|
||||
accountDependencies.expandedAccountsHolder.expandAccount(userWalletId, account.accountId)
|
||||
}
|
||||
|
||||
override fun onAccountCollapseClick(account: Account) {
|
||||
val userWalletId = stateHolder.getSelectedWalletId()
|
||||
analyticsEventHandler.send(MainScreenAnalyticsEvent.AccountHideTokens())
|
||||
accountDependencies.expandedAccountsHolder.collapseAccount(userWalletId, account.accountId)
|
||||
}
|
||||
|
||||
|
|
@ -288,7 +291,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
token = currencyStatus.currency.symbol,
|
||||
blockchain = currencyStatus.currency.network.name,
|
||||
action = "Staking",
|
||||
state = if (currencyStatus.value.yieldBalance is YieldBalance.Data) {
|
||||
state = if (currencyStatus.value.stakingBalance is StakingBalance.Data) {
|
||||
"Enabled"
|
||||
} else {
|
||||
"Disabled"
|
||||
|
|
@ -392,7 +395,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
is WalletNFTItemUM.Content -> {
|
||||
analyticsEventHandler.send(
|
||||
NFTAnalyticsEvent.NFTListScreenOpened(
|
||||
state = NFTAnalyticsEvent.NFTListScreenOpened.State.Full,
|
||||
state = AnalyticsParam.EmptyFull.Full,
|
||||
allAssetsCount = state.allAssetsCount,
|
||||
collectionsCount = state.collectionsCount,
|
||||
noCollectionAssetsCount = state.noCollectionAssetsCount,
|
||||
|
|
@ -402,7 +405,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
is WalletNFTItemUM.Empty -> {
|
||||
analyticsEventHandler.send(
|
||||
NFTAnalyticsEvent.NFTListScreenOpened(
|
||||
state = NFTAnalyticsEvent.NFTListScreenOpened.State.Empty,
|
||||
state = AnalyticsParam.EmptyFull.Empty,
|
||||
allAssetsCount = 0,
|
||||
collectionsCount = 0,
|
||||
noCollectionAssetsCount = 0,
|
||||
|
|
|
|||
|
|
@ -11,9 +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.components.bottomsheets.message.*
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.message.bottomSheetMessage
|
||||
import com.tangem.domain.card.SetCardWasScannedUseCase
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
|
||||
|
|
@ -33,14 +31,13 @@ import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher
|
|||
import com.tangem.domain.settings.NeverToSuggestRateAppUseCase
|
||||
import com.tangem.domain.settings.RemindToRateAppLaterUseCase
|
||||
import com.tangem.domain.staking.StakingIdFactory
|
||||
import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
|
||||
import com.tangem.domain.staking.multi.MultiStakingBalanceFetcher
|
||||
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.wallets.legacy.UserWalletsListManager.Lockable.UnlockType
|
||||
import com.tangem.domain.wallets.models.UnlockWalletsError
|
||||
import com.tangem.domain.wallets.usecase.*
|
||||
import com.tangem.feature.wallet.child.wallet.model.WalletActivationBannerType
|
||||
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
|
||||
|
|
@ -106,7 +103,7 @@ internal interface WalletWarningsClickIntents {
|
|||
|
||||
fun onDenyPermissions()
|
||||
|
||||
fun onFinishWalletActivationClick(bannerType: WalletActivationBannerType, isBackupExists: Boolean)
|
||||
fun onFinishWalletActivationClick(isBackupExists: Boolean)
|
||||
}
|
||||
|
||||
@Suppress("LargeClass", "LongParameterList", "TooManyFunctions")
|
||||
|
|
@ -131,7 +128,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
private val urlOpener: UrlOpener,
|
||||
private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
|
||||
private val multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
|
||||
private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
|
||||
private val multiStakingBalanceFetcher: MultiStakingBalanceFetcher,
|
||||
private val stakingIdFactory: StakingIdFactory,
|
||||
private val appRouter: AppRouter,
|
||||
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
|
||||
|
|
@ -144,7 +141,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
) : BaseWalletClickIntents(), WalletWarningsClickIntents {
|
||||
|
||||
override fun onAddBackupCardClick() {
|
||||
analyticsEventHandler.send(MainScreen.NoticeBackupYourWalletTapped)
|
||||
analyticsEventHandler.send(MainScreen.NoticeBackupYourWalletTapped())
|
||||
|
||||
prepareAndStartOnboardingProcess()
|
||||
}
|
||||
|
|
@ -172,7 +169,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
|
||||
override fun onGenerateMissedAddressesClick(missedAddressCurrencies: List<CryptoCurrency>) {
|
||||
analyticsEventHandler.send(Basic.CardWasScanned(AnalyticsParam.ScreensSources.Main))
|
||||
analyticsEventHandler.send(MainScreen.NoticeScanYourCardTapped)
|
||||
analyticsEventHandler.send(MainScreen.NoticeScanYourCardTapped())
|
||||
|
||||
modelScope.launch {
|
||||
val userWallet = getSelectedUserWallet() ?: return@launch
|
||||
|
|
@ -190,7 +187,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onOpenUnlockWalletsBottomSheetClick() {
|
||||
analyticsEventHandler.send(MainScreen.WalletUnlockTapped)
|
||||
analyticsEventHandler.send(MainScreen.WalletUnlockTapped())
|
||||
|
||||
if (hotWalletFeatureToggles.isHotWalletEnabled) {
|
||||
modelScope.launch {
|
||||
|
|
@ -202,6 +199,8 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
error.handle(
|
||||
onAlreadyUnlocked = {},
|
||||
onUserCancelled = {},
|
||||
analyticsEventHandler = analyticsEventHandler,
|
||||
isFromUnlockAll = true,
|
||||
showMessage = uiMessageSender::send,
|
||||
)
|
||||
}
|
||||
|
|
@ -221,7 +220,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
|
||||
@Deprecated("Will be removed with hot wallet release")
|
||||
override fun onUnlockWalletClick() {
|
||||
analyticsEventHandler.send(MainScreen.UnlockAllWithBiometrics)
|
||||
analyticsEventHandler.send(MainScreen.UnlockAllWithBiometrics())
|
||||
|
||||
modelScope.launch(dispatchers.main) {
|
||||
unlockWalletsUseCase(type = UnlockType.ALL_WITHOUT_SELECT)
|
||||
|
|
@ -249,7 +248,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
|
||||
@Deprecated("Will be removed with hot wallet release")
|
||||
override fun onScanToUnlockWalletClick() {
|
||||
analyticsEventHandler.send(MainScreen.UnlockWithCardScan)
|
||||
analyticsEventHandler.send(MainScreen.UnlockWithCardScan())
|
||||
openScanCardDialog()
|
||||
}
|
||||
|
||||
|
|
@ -307,22 +306,22 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
override fun onClosePromoClick(promoId: PromoId) {
|
||||
analyticsEventHandler.send(
|
||||
when (promoId) {
|
||||
PromoId.Referral -> MainScreen.ReferralPromoButtonDismiss
|
||||
PromoId.Referral -> MainScreen.ReferralPromoButtonDismiss()
|
||||
PromoId.Sepa -> PromotionBannerClicked(
|
||||
source = AnalyticsParam.ScreensSources.Main,
|
||||
program = Program.Sepa,
|
||||
action = PromotionBannerClicked.BannerAction.Closed,
|
||||
action = PromotionBannerClicked.BannerAction.Closed(),
|
||||
)
|
||||
PromoId.VisaPresale -> PromoAnalyticsEvent.VisaWaitlistPromoDismiss
|
||||
PromoId.VisaPresale -> PromoAnalyticsEvent.VisaWaitlistPromoDismiss()
|
||||
PromoId.BlackFriday -> PromotionBannerClicked(
|
||||
source = AnalyticsParam.ScreensSources.Main,
|
||||
program = Program.BlackFriday,
|
||||
action = PromotionBannerClicked.BannerAction.Closed,
|
||||
action = PromotionBannerClicked.BannerAction.Closed(),
|
||||
)
|
||||
PromoId.OnePlusOne -> PromotionBannerClicked(
|
||||
source = AnalyticsParam.ScreensSources.Main,
|
||||
program = Program.OnePlusOne,
|
||||
action = PromotionBannerClicked.BannerAction.Closed,
|
||||
action = PromotionBannerClicked.BannerAction.Closed(),
|
||||
)
|
||||
},
|
||||
|
||||
|
|
@ -336,7 +335,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
val userWallet = getSelectedUserWallet() ?: return
|
||||
when (promoId) {
|
||||
PromoId.Referral -> {
|
||||
analyticsEventHandler.send(MainScreen.ReferralPromoButtonParticipate)
|
||||
analyticsEventHandler.send(MainScreen.ReferralPromoButtonParticipate())
|
||||
appRouter.push(ReferralProgram(userWalletId = userWallet.walletId))
|
||||
}
|
||||
PromoId.Sepa -> {
|
||||
|
|
@ -344,7 +343,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
PromotionBannerClicked(
|
||||
source = AnalyticsParam.ScreensSources.Main,
|
||||
program = Program.Sepa,
|
||||
action = PromotionBannerClicked.BannerAction.Clicked,
|
||||
action = PromotionBannerClicked.BannerAction.Clicked(),
|
||||
),
|
||||
)
|
||||
cryptoCurrency ?: return
|
||||
|
|
@ -358,7 +357,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
)
|
||||
}
|
||||
PromoId.VisaPresale -> {
|
||||
analyticsEventHandler.send(PromoAnalyticsEvent.VisaWaitlistPromoJoin)
|
||||
analyticsEventHandler.send(PromoAnalyticsEvent.VisaWaitlistPromoJoin())
|
||||
urlOpener.openUrl(VISA_PROMO_LINK)
|
||||
}
|
||||
PromoId.BlackFriday -> {
|
||||
|
|
@ -366,7 +365,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
PromotionBannerClicked(
|
||||
source = AnalyticsParam.ScreensSources.Main,
|
||||
program = Program.BlackFriday,
|
||||
action = PromotionBannerClicked.BannerAction.Clicked,
|
||||
action = PromotionBannerClicked.BannerAction.Clicked(),
|
||||
),
|
||||
)
|
||||
urlOpener.openUrl(BLACK_FRIDAY_PROMO_LINK)
|
||||
|
|
@ -376,7 +375,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
PromotionBannerClicked(
|
||||
source = AnalyticsParam.ScreensSources.Main,
|
||||
program = Program.OnePlusOne,
|
||||
action = PromotionBannerClicked.BannerAction.Clicked,
|
||||
action = PromotionBannerClicked.BannerAction.Clicked(),
|
||||
),
|
||||
)
|
||||
urlOpener.openUrl(ONE_PLUS_ONE_PROMO_LINK)
|
||||
|
|
@ -394,7 +393,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onNoteMigrationButtonClick(url: String) {
|
||||
analyticsEventHandler.send(MainScreen.NotePromoButton)
|
||||
analyticsEventHandler.send(MainScreen.NotePromoButton())
|
||||
modelScope.launch(dispatchers.main) {
|
||||
router.openUrl(url)
|
||||
}
|
||||
|
|
@ -403,7 +402,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
override fun onSeedPhraseNotificationConfirm() {
|
||||
val userWallet = getSelectedUserWallet() ?: return
|
||||
|
||||
analyticsEventHandler.send(MainScreen.NoticeSeedPhraseSupportButtonYes)
|
||||
analyticsEventHandler.send(MainScreen.NoticeSeedPhraseSupportButtonYes())
|
||||
|
||||
walletEventSender.send(
|
||||
event = WalletEvent.ShowAlert(
|
||||
|
|
@ -426,7 +425,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
override fun onSeedPhraseNotificationDecline() {
|
||||
val userWallet = getSelectedUserWallet() ?: return
|
||||
|
||||
analyticsEventHandler.send(MainScreen.NoticeSeedPhraseSupportButtonNo)
|
||||
analyticsEventHandler.send(MainScreen.NoticeSeedPhraseSupportButtonNo())
|
||||
|
||||
walletEventSender.send(
|
||||
event = WalletEvent.ShowAlert(
|
||||
|
|
@ -445,7 +444,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
override fun onSeedPhraseSecondNotificationAccept() {
|
||||
val userWallet = getSelectedUserWallet() ?: return
|
||||
|
||||
analyticsEventHandler.send(MainScreen.NoticeSeedPhraseSupportButtonUsed)
|
||||
analyticsEventHandler.send(MainScreen.NoticeSeedPhraseSupportButtonUsed())
|
||||
|
||||
walletEventSender.send(
|
||||
event = WalletEvent.ShowAlert(
|
||||
|
|
@ -468,51 +467,21 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
override fun onSeedPhraseSecondNotificationReject() {
|
||||
val userWallet = getSelectedUserWallet() ?: return
|
||||
|
||||
analyticsEventHandler.send(MainScreen.NoticeSeedPhraseSupportButtonDeclined)
|
||||
analyticsEventHandler.send(MainScreen.NoticeSeedPhraseSupportButtonDeclined())
|
||||
|
||||
modelScope.launch {
|
||||
seedPhraseNotificationUseCase.rejectSecond(userWalletId = userWallet.walletId)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFinishWalletActivationClick(bannerType: WalletActivationBannerType, isBackupExists: Boolean) {
|
||||
when (bannerType) {
|
||||
WalletActivationBannerType.Attention -> {
|
||||
val userWallet = getSelectedUserWallet() ?: return
|
||||
appRouter.push(WalletActivation(userWallet.walletId, isBackupExists))
|
||||
}
|
||||
WalletActivationBannerType.Warning -> {
|
||||
val message = bottomSheetMessage {
|
||||
infoBlock {
|
||||
icon(R.drawable.img_knight_shield_32) {
|
||||
type = MessageBottomSheetUMV2.Icon.Type.Warning
|
||||
backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.SameAsTint
|
||||
}
|
||||
title = resourceReference(R.string.hw_activation_need_title)
|
||||
body = resourceReference(R.string.hw_activation_need_description)
|
||||
}
|
||||
secondaryButton {
|
||||
text = resourceReference(R.string.common_later)
|
||||
onClick {
|
||||
closeBs()
|
||||
}
|
||||
}
|
||||
primaryButton {
|
||||
text = resourceReference(R.string.hw_activation_need_backup)
|
||||
onClick {
|
||||
val userWallet = getSelectedUserWallet() ?: return@onClick
|
||||
appRouter.push(WalletActivation(userWallet.walletId, isBackupExists))
|
||||
closeBs()
|
||||
}
|
||||
}
|
||||
}
|
||||
uiMessageSender.send(message)
|
||||
}
|
||||
}
|
||||
override fun onFinishWalletActivationClick(isBackupExists: Boolean) {
|
||||
analyticsEventHandler.send(MainScreen.ButtonFinalizeActivation())
|
||||
val userWalletId = stateHolder.getSelectedWalletId()
|
||||
appRouter.push(WalletActivation(userWalletId, isBackupExists))
|
||||
}
|
||||
|
||||
override fun onAllowPermissions() {
|
||||
analyticsEventHandler.send(WalletScreenAnalyticsEvent.PushBannerPromo.ButtonAllowPush)
|
||||
analyticsEventHandler.send(WalletScreenAnalyticsEvent.PushBannerPromo.ButtonAllowPush())
|
||||
walletEventSender.send(
|
||||
event = WalletEvent.RequestPushPermissions(
|
||||
onAllow = {
|
||||
|
|
@ -538,7 +507,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
|
||||
override fun onDenyPermissions() {
|
||||
modelScope.launch {
|
||||
analyticsEventHandler.send(WalletScreenAnalyticsEvent.PushBannerPromo.ButtonLaterPush)
|
||||
analyticsEventHandler.send(WalletScreenAnalyticsEvent.PushBannerPromo.ButtonLaterPush())
|
||||
updateSubscribeOnPushPermissions(false)
|
||||
}
|
||||
}
|
||||
|
|
@ -577,8 +546,11 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = it).getOrNull()
|
||||
}
|
||||
|
||||
multiYieldBalanceFetcher(
|
||||
params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = stakingIds),
|
||||
multiStakingBalanceFetcher(
|
||||
params = MultiStakingBalanceFetcher.Params(
|
||||
userWalletId = userWalletId,
|
||||
stakingIds = stakingIds,
|
||||
),
|
||||
)
|
||||
.onLeft { Timber.e("Unable to fetch yield balances: $it") }
|
||||
},
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ internal class DefaultPromoDeeplinkHandler @AssistedInject constructor(
|
|||
) : PromoDeeplinkHandler {
|
||||
|
||||
init {
|
||||
analyticsEventsHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart)
|
||||
analyticsEventsHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart())
|
||||
val promoCode = queryParams[PROMO_CODE_KEY].orEmpty()
|
||||
if (promoCode.isEmpty()) {
|
||||
showAlert(InvalidPromoCode)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ sealed class PromoActivationAnalytics(
|
|||
params: Map<String, String> = mapOf(),
|
||||
) : AnalyticsEvent(category = "Promotion", event = event, params = params) {
|
||||
|
||||
data object PromoDeepLinkActivationStart : PromoActivationAnalytics(
|
||||
class PromoDeepLinkActivationStart : PromoActivationAnalytics(
|
||||
event = "Bitcoin Promo Deep Link Activation",
|
||||
params = emptyMap(),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package com.tangem.feature.wallet.presentation.common.preview
|
|||
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig.ButtonsState
|
||||
import com.tangem.core.ui.components.token.AccountItemPreviewData
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
|
|
@ -14,6 +13,7 @@ import com.tangem.core.ui.extensions.TextReference
|
|||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.child.wallet.model.WalletActivationBannerType
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.common.WalletPreviewData.topBarConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.*
|
||||
|
|
@ -167,11 +167,12 @@ internal object WalletScreenPreviewData {
|
|||
warnings = persistentListOf(
|
||||
WalletNotification.Warning.SomeNetworksUnreachable,
|
||||
WalletNotification.FinishWalletActivation(
|
||||
iconTint = NotificationConfig.IconTint.Attention,
|
||||
type = WalletActivationBannerType.Attention,
|
||||
buttonsState = ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(R.string.hw_activation_need_finish),
|
||||
onClick = { },
|
||||
),
|
||||
isBackupExists = false,
|
||||
),
|
||||
),
|
||||
bottomSheetConfig = null,
|
||||
|
|
@ -222,6 +223,7 @@ internal object WalletScreenPreviewData {
|
|||
isHidingMode = false,
|
||||
showMarketsOnboarding = false,
|
||||
onDismissMarketsOnboarding = {},
|
||||
isNewMarketEnabled = false,
|
||||
)
|
||||
|
||||
internal val accountScreenState =
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@ sealed class PortfolioOrganizeTokensAnalyticsEvent(
|
|||
params: Map<String, String> = mapOf(),
|
||||
) : AnalyticsEvent("Portfolio / Organize Tokens", event, params) {
|
||||
|
||||
object ScreenOpened : PortfolioOrganizeTokensAnalyticsEvent("Organize Tokens Screen Opened")
|
||||
class ScreenOpened : PortfolioOrganizeTokensAnalyticsEvent("Organize Tokens Screen Opened")
|
||||
|
||||
object ByBalance : PortfolioOrganizeTokensAnalyticsEvent("Button - By Balance")
|
||||
class ByBalance : PortfolioOrganizeTokensAnalyticsEvent("Button - By Balance")
|
||||
|
||||
object Group : PortfolioOrganizeTokensAnalyticsEvent("Button - Group")
|
||||
class Group : PortfolioOrganizeTokensAnalyticsEvent("Button - Group")
|
||||
|
||||
class Apply(
|
||||
grouping: AnalyticsParam.OnOffState,
|
||||
|
|
@ -25,5 +25,5 @@ sealed class PortfolioOrganizeTokensAnalyticsEvent(
|
|||
),
|
||||
)
|
||||
|
||||
object Cancel : PortfolioOrganizeTokensAnalyticsEvent("Button - Cancel")
|
||||
class Cancel : PortfolioOrganizeTokensAnalyticsEvent("Button - Cancel")
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@ import com.tangem.core.ui.format.bigdecimal.fiat
|
|||
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.YieldBalance
|
||||
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
|
||||
|
|
@ -63,12 +63,12 @@ internal class CryptoCurrencyToDraggableItemConverter(
|
|||
}
|
||||
|
||||
private fun getFormattedFiatAmount(currency: CryptoCurrencyStatus, appCurrency: AppCurrency): String {
|
||||
val yieldBalance = currency.value.yieldBalance as? YieldBalance.Data
|
||||
val stakingBalance = currency.value.stakingBalance as? StakingBalance.Data
|
||||
val fiatRate = currency.value.fiatRate ?: BigDecimal.ZERO
|
||||
val fiatYieldBalance = yieldBalance?.getTotalWithRewardsStakingBalance(currency.currency.network.rawId)
|
||||
val fiatStakingBalance = stakingBalance?.getTotalWithRewardsStakingBalance(currency.currency.network.rawId)
|
||||
?.multiply(fiatRate).orZero()
|
||||
|
||||
val fiatAmount = currency.value.fiatAmount ?: return BigDecimalFormatConstants.EMPTY_BALANCE_SIGN
|
||||
return (fiatAmount + fiatYieldBalance).format { fiat(appCurrency.code, appCurrency.symbol) }
|
||||
return (fiatAmount + fiatStakingBalance).format { fiat(appCurrency.code, appCurrency.symbol) }
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ import com.tangem.core.ui.format.bigdecimal.format
|
|||
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.YieldBalance
|
||||
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
|
||||
|
|
@ -56,12 +56,12 @@ internal class CryptoCurrencyToDraggableItemConverterV2(
|
|||
}
|
||||
|
||||
private fun getFormattedFiatAmount(currency: CryptoCurrencyStatus, appCurrency: AppCurrency): String {
|
||||
val yieldBalance = currency.value.yieldBalance as? YieldBalance.Data
|
||||
val stakingBalance = currency.value.stakingBalance as? StakingBalance.Data
|
||||
val fiatRate = currency.value.fiatRate ?: BigDecimal.ZERO
|
||||
val fiatYieldBalance = yieldBalance?.getTotalWithRewardsStakingBalance(currency.currency.network.rawId)
|
||||
val fiatStakingBalance = stakingBalance?.getTotalWithRewardsStakingBalance(currency.currency.network.rawId)
|
||||
?.multiply(fiatRate).orZero()
|
||||
|
||||
val fiatAmount = currency.value.fiatAmount ?: return BigDecimalFormatConstants.EMPTY_BALANCE_SIGN
|
||||
return (fiatAmount + fiatYieldBalance).format { fiat(appCurrency.code, appCurrency.symbol) }
|
||||
return (fiatAmount + fiatStakingBalance).format { fiat(appCurrency.code, appCurrency.symbol) }
|
||||
}
|
||||
}
|
||||
|
|
@ -111,14 +111,8 @@ internal class DefaultWalletRouter @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
override fun openTangemPayOnboarding(userWalletId: UserWalletId) {
|
||||
router.push(
|
||||
AppRoute.TangemPayOnboarding(
|
||||
AppRoute.TangemPayOnboarding.Mode.ContinueOnboarding(
|
||||
userWalletId = userWalletId,
|
||||
),
|
||||
),
|
||||
)
|
||||
override fun openTangemPayOnboarding(mode: AppRoute.TangemPayOnboarding.Mode) {
|
||||
router.push(route = AppRoute.TangemPayOnboarding(mode = mode))
|
||||
}
|
||||
|
||||
override fun openTangemPayDetails(userWalletId: UserWalletId, config: TangemPayDetailsConfig) {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.router
|
|||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.arkivanov.decompose.router.slot.SlotNavigation
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.domain.models.TokenReceiveConfig
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
|
|
@ -63,7 +64,7 @@ internal interface InnerWalletRouter {
|
|||
|
||||
fun openTokenReceiveBottomSheet(tokenReceiveConfig: TokenReceiveConfig)
|
||||
|
||||
fun openTangemPayOnboarding(userWalletId: UserWalletId)
|
||||
fun openTangemPayOnboarding(mode: AppRoute.TangemPayOnboarding.Mode)
|
||||
|
||||
fun openTangemPayDetails(userWalletId: UserWalletId, config: TangemPayDetailsConfig)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.analytics
|
|||
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.AppsFlyerOnlyEvent
|
||||
import com.tangem.core.analytics.models.OneTimeAnalyticsEvent
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
||||
|
|
@ -22,6 +23,11 @@ sealed class WalletScreenAnalyticsEvent {
|
|||
override val oneTimeEventId: String = id + userWalletId.stringValue
|
||||
}
|
||||
|
||||
class AppsFlyerWalletFunded(userWalletId: UserWalletId) : Basic(event = "wallet_funded"),
|
||||
AppsFlyerOnlyEvent, OneTimeAnalyticsEvent {
|
||||
override val oneTimeEventId: String = id + userWalletId.stringValue
|
||||
}
|
||||
|
||||
class CardWasScanned(source: AnalyticsParam.ScreensSources) : Basic(
|
||||
event = "Card Was Scanned",
|
||||
params = mapOf(
|
||||
|
|
@ -37,7 +43,7 @@ sealed class WalletScreenAnalyticsEvent {
|
|||
},
|
||||
)
|
||||
|
||||
class TokenBalance(balance: AnalyticsParam.TokenBalanceState, token: String) : Basic(
|
||||
class TokenBalance(balance: AnalyticsParam.EmptyFull, token: String) : Basic(
|
||||
event = "Token Balance",
|
||||
params = mapOf(
|
||||
AnalyticsParam.STATE to balance.value,
|
||||
|
|
@ -51,15 +57,46 @@ sealed class WalletScreenAnalyticsEvent {
|
|||
params: Map<String, String> = mapOf(),
|
||||
) : AnalyticsEvent(category = "Main Screen", event = event, params = params) {
|
||||
|
||||
class ScreenOpened(
|
||||
class ScreenOpenedLegacy : MainScreen(event = "Screen opened")
|
||||
|
||||
data class ScreenOpened(
|
||||
private val hasMobileWallet: Boolean,
|
||||
private val accountsCount: Int?,
|
||||
val theme: String,
|
||||
val isImported: Boolean,
|
||||
) : MainScreen(
|
||||
event = "Screen opened",
|
||||
params = buildMap {
|
||||
put("Mobile Wallet", if (hasMobileWallet) "Yes" else "No")
|
||||
if (accountsCount != null) put("Accounts Count", accountsCount.toString())
|
||||
put("App Theme", theme)
|
||||
val seedPhrase = if (isImported) {
|
||||
"Seed Phrase"
|
||||
} else {
|
||||
"Seedless"
|
||||
}
|
||||
put("Wallet Type", seedPhrase)
|
||||
},
|
||||
)
|
||||
|
||||
data class NoticeFinishActivation(
|
||||
private val activationState: ActivationState,
|
||||
private val balanceState: AnalyticsParam.EmptyFull,
|
||||
) : MainScreen(
|
||||
event = "Notice - Finish Activation",
|
||||
params = mapOf(
|
||||
"App Theme" to theme,
|
||||
"Wallet Type" to if (isImported) "Seed Phrase" else "Seedless",
|
||||
"Activation State" to activationState.value,
|
||||
"Balance State" to balanceState.value,
|
||||
),
|
||||
) {
|
||||
enum class ActivationState(val value: String) {
|
||||
NotStarted("Not Started"),
|
||||
Unfinished("Unfinished"),
|
||||
}
|
||||
}
|
||||
|
||||
class ButtonFinalizeActivation : MainScreen(
|
||||
event = "Button - Finalize Activation",
|
||||
)
|
||||
|
||||
class WalletSelected(val isImported: Boolean) : MainScreen(
|
||||
|
|
@ -79,9 +116,9 @@ sealed class WalletScreenAnalyticsEvent {
|
|||
params = mapOf("Result" to result.value),
|
||||
)
|
||||
|
||||
data object NoticeBackupYourWalletTapped : MainScreen(event = "Notice - Backup Your Wallet Tapped")
|
||||
data object NoticeScanYourCardTapped : MainScreen(event = "Notice - Scan Your Card Tapped")
|
||||
data object WalletUnlockTapped : MainScreen(event = "Notice - Wallet Unlock Tapped")
|
||||
class NoticeBackupYourWalletTapped : MainScreen(event = "Notice - Backup Your Wallet Tapped")
|
||||
class NoticeScanYourCardTapped : MainScreen(event = "Notice - Scan Your Card Tapped")
|
||||
class WalletUnlockTapped : MainScreen(event = "Notice - Wallet Unlock Tapped")
|
||||
|
||||
class NetworksUnreachable(
|
||||
tokens: List<String>,
|
||||
|
|
@ -90,54 +127,54 @@ sealed class WalletScreenAnalyticsEvent {
|
|||
params = mapOf("Tokens" to tokens.joinToString()),
|
||||
)
|
||||
|
||||
data object MissingAddresses : MainScreen(event = "Notice - Missing Addresses")
|
||||
class MissingAddresses : MainScreen(event = "Notice - Missing Addresses")
|
||||
|
||||
data object CardSignedTransactions : MainScreen(event = "Notice - Card Signed Transactions")
|
||||
class CardSignedTransactions : MainScreen(event = "Notice - Card Signed Transactions")
|
||||
|
||||
data object HowDoYouLikeTangem : MainScreen(event = "Notice - How Do You Like Tangem")
|
||||
class HowDoYouLikeTangem : MainScreen(event = "Notice - How Do You Like Tangem")
|
||||
|
||||
data object ProductSampleCard : MainScreen(event = "Notice - Product Sample Card")
|
||||
class ProductSampleCard : MainScreen(event = "Notice - Product Sample Card")
|
||||
|
||||
data object TestnetCard : MainScreen(event = "Notice - Testnet Card")
|
||||
class TestnetCard : MainScreen(event = "Notice - Testnet Card")
|
||||
|
||||
data object DemoCard : MainScreen(event = "Notice - Demo Card")
|
||||
class DemoCard : MainScreen(event = "Notice - Demo Card")
|
||||
|
||||
data object DevelopmentCard : MainScreen(event = "Notice - Development Card")
|
||||
class DevelopmentCard : MainScreen(event = "Notice - Development Card")
|
||||
|
||||
data object WalletUnlock : MainScreen(event = "Notice - Wallet Unlock")
|
||||
class WalletUnlock : MainScreen(event = "Notice - Wallet Unlock")
|
||||
|
||||
data object BackupYourWallet : MainScreen(event = "Notice - Backup Your Wallet")
|
||||
class BackupYourWallet : MainScreen(event = "Notice - Backup Your Wallet")
|
||||
|
||||
data object BackupError : MainScreen(event = "Notice - Backup Error")
|
||||
class BackupError : MainScreen(event = "Notice - Backup Error")
|
||||
|
||||
data object NotePromo : MainScreen(event = "Notice - Note Promo")
|
||||
class NotePromo : MainScreen(event = "Notice - Note Promo")
|
||||
|
||||
data object NotePromoButton : MainScreen(event = "Note Promo Button")
|
||||
class NotePromoButton : MainScreen(event = "Note Promo Button")
|
||||
|
||||
data object UnlockAllWithBiometrics : MainScreen(event = "Button - Unlock All With Biometrics")
|
||||
class UnlockAllWithBiometrics : MainScreen(event = "Button - Unlock All With Biometrics")
|
||||
|
||||
data object UnlockWithCardScan : MainScreen(event = "Button - Unlock With Card Scan")
|
||||
class UnlockWithCardScan : MainScreen(event = "Button - Unlock With Card Scan")
|
||||
|
||||
data object EditWalletTapped : MainScreen(event = "Button - Edit Wallet Tapped")
|
||||
class EditWalletTapped : MainScreen(event = "Button - Edit Wallet Tapped")
|
||||
|
||||
data object DeleteWalletTapped : MainScreen(event = "Button - Delete Wallet Tapped")
|
||||
class DeleteWalletTapped : MainScreen(event = "Button - Delete Wallet Tapped")
|
||||
|
||||
data object NoticeSeedPhraseSupport : MainScreen(event = "Notice - Seed Phrase Support")
|
||||
class NoticeSeedPhraseSupport : MainScreen(event = "Notice - Seed Phrase Support")
|
||||
|
||||
data object NoticeSeedPhraseSupportSecond : MainScreen(event = "Notice - Seed Phrase Support2")
|
||||
class NoticeSeedPhraseSupportSecond : MainScreen(event = "Notice - Seed Phrase Support2")
|
||||
|
||||
data object NoticeSeedPhraseSupportButtonNo : MainScreen(event = "Button - Support No")
|
||||
class NoticeSeedPhraseSupportButtonNo : MainScreen(event = "Button - Support No")
|
||||
|
||||
data object NoticeSeedPhraseSupportButtonYes : MainScreen(event = "Button - Support Yes")
|
||||
class NoticeSeedPhraseSupportButtonYes : MainScreen(event = "Button - Support Yes")
|
||||
|
||||
data object NoticeSeedPhraseSupportButtonUsed : MainScreen(event = "Button - Support Used")
|
||||
class NoticeSeedPhraseSupportButtonUsed : MainScreen(event = "Button - Support Used")
|
||||
|
||||
data object NoticeSeedPhraseSupportButtonDeclined : MainScreen(event = "Button - Support Declined")
|
||||
class NoticeSeedPhraseSupportButtonDeclined : MainScreen(event = "Button - Support Declined")
|
||||
|
||||
// region Referral Promo
|
||||
data object ReferralPromo : MainScreen(event = "Referral Banner")
|
||||
data object ReferralPromoButtonParticipate : MainScreen(event = "Button - Referral Participate")
|
||||
data object ReferralPromoButtonDismiss : MainScreen(event = "Button - Referral Dismiss")
|
||||
class ReferralPromo : MainScreen(event = "Referral Banner")
|
||||
class ReferralPromoButtonParticipate : MainScreen(event = "Button - Referral Participate")
|
||||
class ReferralPromoButtonDismiss : MainScreen(event = "Button - Referral Dismiss")
|
||||
//endregion
|
||||
}
|
||||
|
||||
|
|
@ -146,8 +183,8 @@ sealed class WalletScreenAnalyticsEvent {
|
|||
params: Map<String, String> = mapOf(),
|
||||
) : AnalyticsEvent(category = "Promo", event = event, params = params) {
|
||||
|
||||
data object PushBanner : PushBannerPromo(event = "Push Banner")
|
||||
data object ButtonAllowPush : PushBannerPromo(event = "Button - Allow Push")
|
||||
data object ButtonLaterPush : PushBannerPromo(event = "Button - Later Push")
|
||||
class PushBanner : PushBannerPromo(event = "Push Banner")
|
||||
class ButtonAllowPush : PushBannerPromo(event = "Button - Allow Push")
|
||||
class ButtonLaterPush : PushBannerPromo(event = "Button - Later Push")
|
||||
}
|
||||
}
|
||||
|
|
@ -29,7 +29,7 @@ internal class SelectedWalletAnalyticsSender @Inject constructor(
|
|||
* that cannot be processed in [WalletWarningsAnalyticsSender].
|
||||
* */
|
||||
private fun getEvent(userWallet: UserWallet): AnalyticsEvent? = when {
|
||||
userWallet.isLocked -> WalletScreenAnalyticsEvent.MainScreen.WalletUnlock
|
||||
userWallet.isLocked -> WalletScreenAnalyticsEvent.MainScreen.WalletUnlock()
|
||||
|
||||
else -> WalletScreenAnalyticsEvent.MainScreen.WalletSelected(userWallet.isImported())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -158,9 +158,9 @@ internal class TokenListAnalyticsSender @Inject constructor(
|
|||
-> {
|
||||
if (balanceWasSentMap[blockchain.currency] != true) {
|
||||
val tokenBalance = if (balanceStatus.amount.isZero()) {
|
||||
AnalyticsParam.TokenBalanceState.Empty
|
||||
AnalyticsParam.EmptyFull.Empty
|
||||
} else {
|
||||
AnalyticsParam.TokenBalanceState.Full
|
||||
AnalyticsParam.EmptyFull.Full
|
||||
}
|
||||
analyticsEventHandler.send(
|
||||
Basic.TokenBalance(
|
||||
|
|
@ -208,6 +208,7 @@ internal class TokenListAnalyticsSender @Inject constructor(
|
|||
}
|
||||
|
||||
analyticsEventHandler.send(Basic.WalletToppedUp(userWallet.walletId, walletType))
|
||||
analyticsEventHandler.send(Basic.AppsFlyerWalletFunded(userWallet.walletId))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.tangem.core.analytics.models.AnalyticsEvent
|
|||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.domain.tokens.model.analytics.PromoAnalyticsEvent.*
|
||||
import com.tangem.feature.wallet.child.wallet.model.WalletActivationBannerType
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
|
||||
|
|
@ -39,16 +40,16 @@ internal class WalletWarningsAnalyticsSender @Inject constructor(
|
|||
@Suppress("CyclomaticComplexMethod")
|
||||
private fun getEvent(warning: WalletNotification): AnalyticsEvent? {
|
||||
return when (warning) {
|
||||
is WalletNotification.Critical.DevCard -> MainScreen.DevelopmentCard
|
||||
is WalletNotification.Critical.FailedCardValidation -> MainScreen.ProductSampleCard
|
||||
is WalletNotification.Warning.MissingBackup -> MainScreen.BackupYourWallet
|
||||
is WalletNotification.Warning.NumberOfSignedHashesIncorrect -> MainScreen.CardSignedTransactions
|
||||
is WalletNotification.Warning.TestNetCard -> MainScreen.TestnetCard
|
||||
is WalletNotification.Informational.DemoCard -> MainScreen.DemoCard
|
||||
is WalletNotification.Informational.MissingAddresses -> MainScreen.MissingAddresses
|
||||
is WalletNotification.RateApp -> MainScreen.HowDoYouLikeTangem
|
||||
is WalletNotification.Critical.BackupError -> MainScreen.BackupError
|
||||
is WalletNotification.NoteMigration -> MainScreen.NotePromo
|
||||
is WalletNotification.Critical.DevCard -> MainScreen.DevelopmentCard()
|
||||
is WalletNotification.Critical.FailedCardValidation -> MainScreen.ProductSampleCard()
|
||||
is WalletNotification.Warning.MissingBackup -> MainScreen.BackupYourWallet()
|
||||
is WalletNotification.Warning.NumberOfSignedHashesIncorrect -> MainScreen.CardSignedTransactions()
|
||||
is WalletNotification.Warning.TestNetCard -> MainScreen.TestnetCard()
|
||||
is WalletNotification.Informational.DemoCard -> MainScreen.DemoCard()
|
||||
is WalletNotification.Informational.MissingAddresses -> MainScreen.MissingAddresses()
|
||||
is WalletNotification.RateApp -> MainScreen.HowDoYouLikeTangem()
|
||||
is WalletNotification.Critical.BackupError -> MainScreen.BackupError()
|
||||
is WalletNotification.NoteMigration -> MainScreen.NotePromo()
|
||||
is WalletNotification.SwapPromo -> NoticePromotionBanner(
|
||||
source = AnalyticsParam.ScreensSources.Main,
|
||||
program = Program.Empty, // Use it on new promo action
|
||||
|
|
@ -65,8 +66,8 @@ internal class WalletWarningsAnalyticsSender @Inject constructor(
|
|||
source = AnalyticsParam.ScreensSources.Main,
|
||||
program = Program.OnePlusOne,
|
||||
)
|
||||
is WalletNotification.ReferralPromo -> MainScreen.ReferralPromo
|
||||
is WalletNotification.VisaPresalePromo -> VisaWaitlistPromo
|
||||
is WalletNotification.ReferralPromo -> MainScreen.ReferralPromo()
|
||||
is WalletNotification.VisaPresalePromo -> VisaWaitlistPromo()
|
||||
is WalletNotification.UnlockWallets -> null // See [SelectedWalletAnalyticsSender]
|
||||
is WalletNotification.Informational.NoAccount,
|
||||
is WalletNotification.Warning.LowSignatures,
|
||||
|
|
@ -74,12 +75,26 @@ internal class WalletWarningsAnalyticsSender @Inject constructor(
|
|||
is WalletNotification.Warning.NetworksUnreachable,
|
||||
is WalletNotification.UsedOutdatedData,
|
||||
is WalletNotification.UnlockVisaAccess,
|
||||
is WalletNotification.FinishWalletActivation,
|
||||
is WalletNotification.Warning.YeildSupplyApprove, // TODO apply correct event
|
||||
-> null
|
||||
is WalletNotification.Critical.SeedPhraseNotification -> MainScreen.NoticeSeedPhraseSupport
|
||||
is WalletNotification.Critical.SeedPhraseSecondNotification -> MainScreen.NoticeSeedPhraseSupportSecond
|
||||
is WalletNotification.PushNotifications -> WalletScreenAnalyticsEvent.PushBannerPromo.PushBanner
|
||||
is WalletNotification.FinishWalletActivation -> {
|
||||
val activationState = if (warning.isBackupExists) {
|
||||
MainScreen.NoticeFinishActivation.ActivationState.Unfinished
|
||||
} else {
|
||||
MainScreen.NoticeFinishActivation.ActivationState.NotStarted
|
||||
}
|
||||
val balanceState = when (warning.type) {
|
||||
WalletActivationBannerType.Attention -> AnalyticsParam.EmptyFull.Empty
|
||||
WalletActivationBannerType.Warning -> AnalyticsParam.EmptyFull.Full
|
||||
}
|
||||
MainScreen.NoticeFinishActivation(
|
||||
activationState = activationState,
|
||||
balanceState = balanceState,
|
||||
)
|
||||
}
|
||||
is WalletNotification.Critical.SeedPhraseNotification -> MainScreen.NoticeSeedPhraseSupport()
|
||||
is WalletNotification.Critical.SeedPhraseSecondNotification -> MainScreen.NoticeSeedPhraseSupportSecond()
|
||||
is WalletNotification.PushNotifications -> WalletScreenAnalyticsEvent.PushBannerPromo.PushBanner()
|
||||
is WalletNotification.Warning.TangemPayRefreshNeeded -> null
|
||||
WalletNotification.Warning.TangemPayUnreachable -> null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,38 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.analytics.utils
|
||||
|
||||
import com.tangem.common.routing.AppRoute.WalletActivation
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetUMV2
|
||||
import com.tangem.core.ui.components.bottomsheets.message.icon
|
||||
import com.tangem.core.ui.components.bottomsheets.message.infoBlock
|
||||
import com.tangem.core.ui.components.bottomsheets.message.onClick
|
||||
import com.tangem.core.ui.components.bottomsheets.message.primaryButton
|
||||
import com.tangem.core.ui.components.bottomsheets.message.secondaryButton
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
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
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
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,
|
||||
private val router: Router,
|
||||
) {
|
||||
private val isActivationBottomSheetShown: ConcurrentHashMap<UserWalletId, Boolean> = ConcurrentHashMap()
|
||||
|
||||
suspend fun send(
|
||||
userWalletId: UserWalletId,
|
||||
|
|
@ -25,10 +45,60 @@ internal class WalletWarningsSingleEventSender @Inject constructor(
|
|||
|
||||
val events = newWarnings.filter { it !in displayedUiState.warnings }
|
||||
|
||||
// We must show activation bs only for the first seen wallet when open the app (if need, see conditions below),
|
||||
// so we keep this wallet id and use for future checks, ignore other wallets during the app session.
|
||||
if (isActivationBottomSheetShown.isEmpty()) {
|
||||
isActivationBottomSheetShown[userWalletId] = false
|
||||
}
|
||||
|
||||
events.forEach { event ->
|
||||
if (event is WalletNotification.Critical.SeedPhraseNotification) {
|
||||
seedPhraseNotificationUseCase.notified(userWalletId = userWalletId)
|
||||
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)
|
||||
if (isActivationBottomSheetShown[userWalletId] == false) {
|
||||
if (event.type == WalletActivationBannerType.Warning) {
|
||||
showFinishActivationBottomSheet(userWalletId)
|
||||
}
|
||||
isActivationBottomSheetShown[userWalletId] = true
|
||||
}
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showFinishActivationBottomSheet(userWalletId: UserWalletId) {
|
||||
val userWallet = getUserWalletUseCase(userWalletId).getOrNull() ?: return
|
||||
if (userWallet !is UserWallet.Hot) return
|
||||
|
||||
val message = bottomSheetMessage {
|
||||
infoBlock {
|
||||
icon(R.drawable.img_knight_shield_32) {
|
||||
type = MessageBottomSheetUMV2.Icon.Type.Warning
|
||||
backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.SameAsTint
|
||||
}
|
||||
title = resourceReference(R.string.hw_activation_need_title)
|
||||
body = resourceReference(R.string.hw_activation_need_description)
|
||||
}
|
||||
secondaryButton {
|
||||
text = resourceReference(R.string.common_later)
|
||||
onClick {
|
||||
closeBs()
|
||||
}
|
||||
}
|
||||
primaryButton {
|
||||
text = resourceReference(R.string.hw_activation_need_backup)
|
||||
onClick {
|
||||
router.push(WalletActivation(userWallet.walletId, userWallet.backedUp))
|
||||
closeBs()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uiMessageSender.send(message)
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,6 @@ import com.tangem.common.ui.notifications.NotificationId
|
|||
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.components.notifications.NotificationConfig.IconTint
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
|
||||
import com.tangem.domain.card.CardTypesResolver
|
||||
|
|
@ -36,6 +35,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotificat
|
|||
import com.tangem.hot.sdk.model.HotWalletId
|
||||
import com.tangem.utils.extensions.addIf
|
||||
import com.tangem.utils.extensions.isPositive
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
|
@ -59,7 +59,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
private val getAccessCodeSkippedUseCase: GetAccessCodeSkippedUseCase,
|
||||
) {
|
||||
|
||||
@Suppress("UNCHECKED_CAST", "MagicNumber")
|
||||
@Suppress("UNCHECKED_CAST", "MagicNumber", "LongMethod")
|
||||
fun create(userWallet: UserWallet, clickIntents: WalletClickIntents): Flow<ImmutableList<WalletNotification>> {
|
||||
val cardTypesResolver = (userWallet as? UserWallet.Cold)?.scanResponse?.cardTypesResolver
|
||||
|
||||
|
|
@ -115,7 +115,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
|
||||
addFinishWalletActivationNotification(
|
||||
userWallet = userWallet,
|
||||
totalFiatBalance = totalFiatBalance,
|
||||
flattenCurrencies = flattenCurrencies,
|
||||
clickIntents = clickIntents,
|
||||
shouldAccessCodeSkipped = shouldAccessCodeSkipped,
|
||||
)
|
||||
|
|
@ -361,8 +361,8 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun TotalFiatBalance?.getFinishWalletActivationType(): WalletActivationBannerType {
|
||||
return if ((this as? TotalFiatBalance.Loaded)?.amount?.isPositive() == true) {
|
||||
private fun List<CryptoCurrencyStatus>?.getFinishWalletActivationType(): WalletActivationBannerType {
|
||||
return if (this?.any { it.value.amount.orZero().isPositive() } == true) {
|
||||
WalletActivationBannerType.Warning
|
||||
} else {
|
||||
WalletActivationBannerType.Attention
|
||||
|
|
@ -371,7 +371,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
|
||||
private fun MutableList<WalletNotification>.addFinishWalletActivationNotification(
|
||||
userWallet: UserWallet,
|
||||
totalFiatBalance: Lce<TokenListError, TotalFiatBalance>,
|
||||
flattenCurrencies: Lce<TokenListError, List<CryptoCurrencyStatus>>,
|
||||
clickIntents: WalletClickIntents,
|
||||
shouldAccessCodeSkipped: Boolean,
|
||||
) {
|
||||
|
|
@ -382,30 +382,26 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
!shouldAccessCodeSkipped
|
||||
val shouldShowFinishActivation = !isBackupExists || isAccessCodeRequired
|
||||
|
||||
val type = totalFiatBalance.fold(
|
||||
ifLoading = { it.getFinishWalletActivationType() },
|
||||
val type = flattenCurrencies.fold(
|
||||
ifLoading = { return },
|
||||
ifContent = { it.getFinishWalletActivationType() },
|
||||
ifError = { WalletActivationBannerType.Attention },
|
||||
)
|
||||
|
||||
val tint = when (type) {
|
||||
WalletActivationBannerType.Attention -> IconTint.Attention
|
||||
WalletActivationBannerType.Warning -> IconTint.Warning
|
||||
}
|
||||
|
||||
addIf(
|
||||
element = WalletNotification.FinishWalletActivation(
|
||||
iconTint = tint,
|
||||
type = type,
|
||||
buttonsState = when (type) {
|
||||
WalletActivationBannerType.Warning -> ButtonsState.PrimaryButtonConfig(
|
||||
text = resourceReference(R.string.hw_activation_need_finish),
|
||||
onClick = { clickIntents.onFinishWalletActivationClick(type, isBackupExists) },
|
||||
onClick = { clickIntents.onFinishWalletActivationClick(isBackupExists) },
|
||||
)
|
||||
else -> ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(R.string.hw_activation_need_finish),
|
||||
onClick = { clickIntents.onFinishWalletActivationClick(type, isBackupExists) },
|
||||
onClick = { clickIntents.onFinishWalletActivationClick(isBackupExists) },
|
||||
)
|
||||
},
|
||||
isBackupExists = isBackupExists,
|
||||
),
|
||||
condition = shouldShowFinishActivation,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ internal object WalletAdditionalInfoFactory {
|
|||
backedUp.not() -> DIVIDER + TextReference.Res(R.string.hw_backup_no_backup)
|
||||
else -> TextReference.Str("")
|
||||
},
|
||||
isHotBackedUp = backedUp,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ internal class WalletStateController @Inject constructor() {
|
|||
isHidingMode = false,
|
||||
showMarketsOnboarding = false,
|
||||
onDismissMarketsOnboarding = {},
|
||||
isNewMarketEnabled = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,11 @@ internal sealed class TangemPayState {
|
|||
|
||||
data object Loading : TangemPayState()
|
||||
|
||||
data class OnboardingBanner(
|
||||
val onClick: () -> Unit,
|
||||
val closeOnClick: () -> Unit,
|
||||
) : TangemPayState()
|
||||
|
||||
data class Progress(
|
||||
val title: TextReference,
|
||||
val description: TextReference,
|
||||
|
|
@ -39,4 +44,6 @@ internal sealed class TangemPayState {
|
|||
) : TangemPayState()
|
||||
|
||||
data class TemporaryUnavailable(val notification: WalletNotification) : TangemPayState()
|
||||
|
||||
data object ExposedDevice : TangemPayState()
|
||||
}
|
||||
|
|
@ -7,4 +7,5 @@ import com.tangem.core.ui.extensions.TextReference
|
|||
data class WalletAdditionalInfo(
|
||||
val hideable: Boolean,
|
||||
val content: TextReference,
|
||||
val isHotBackedUp: Boolean = false,
|
||||
)
|
||||
|
|
@ -10,6 +10,7 @@ import com.tangem.core.ui.extensions.TextReference
|
|||
import com.tangem.core.ui.extensions.pluralReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.feature.wallet.child.wallet.model.WalletActivationBannerType
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import org.joda.time.DateTime
|
||||
|
||||
|
|
@ -150,15 +151,18 @@ sealed class WalletNotification(val config: NotificationConfig) {
|
|||
)
|
||||
|
||||
data class TangemPayRefreshNeeded(
|
||||
@DrawableRes val tangemIcon: Int?,
|
||||
val onRefreshClick: () -> Unit,
|
||||
@DrawableRes private val tangemIcon: Int?,
|
||||
private val onRefreshClick: () -> Unit,
|
||||
private val buttonText: TextReference,
|
||||
private val shouldShowProgress: Boolean,
|
||||
) : Warning(
|
||||
title = resourceReference(id = R.string.tangempay_payment_account_sync_needed),
|
||||
subtitle = resourceReference(id = R.string.tangempay_use_tangem_device_to_restore_payment_account),
|
||||
buttonsState = ButtonsState.PrimaryButtonConfig(
|
||||
text = resourceReference(id = R.string.home_button_scan),
|
||||
text = buttonText,
|
||||
iconResId = tangemIcon,
|
||||
onClick = onRefreshClick,
|
||||
shouldShowProgress = shouldShowProgress,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -287,14 +291,22 @@ sealed class WalletNotification(val config: NotificationConfig) {
|
|||
)
|
||||
|
||||
data class FinishWalletActivation(
|
||||
val iconTint: IconTint,
|
||||
val type: WalletActivationBannerType,
|
||||
val buttonsState: ButtonsState,
|
||||
val isBackupExists: Boolean,
|
||||
) : WalletNotification(
|
||||
config = NotificationConfig(
|
||||
title = resourceReference(R.string.hw_activation_need_title),
|
||||
subtitle = resourceReference(R.string.hw_activation_need_description),
|
||||
subtitle = if (isBackupExists) {
|
||||
resourceReference(R.string.hw_activation_need_warning_description)
|
||||
} else {
|
||||
resourceReference(R.string.hw_activation_need_description)
|
||||
},
|
||||
iconResId = R.drawable.img_knight_shield_32,
|
||||
iconTint = iconTint,
|
||||
iconTint = when (type) {
|
||||
WalletActivationBannerType.Attention -> IconTint.Attention
|
||||
WalletActivationBannerType.Warning -> IconTint.Warning
|
||||
},
|
||||
buttonsState = buttonsState,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -13,5 +13,6 @@ internal data class WalletScreenState(
|
|||
val event: StateEvent<WalletEvent>,
|
||||
val isHidingMode: Boolean,
|
||||
val showMarketsOnboarding: Boolean,
|
||||
val isNewMarketEnabled: Boolean,
|
||||
val onDismissMarketsOnboarding: () -> Unit,
|
||||
)
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
||||
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletLoadingStateFactory
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
/**
|
||||
* Reinitialize and place new wallet at the end transformer
|
||||
*
|
||||
* @property prevWalletId reinitialized wallet id
|
||||
* @property newUserWallet new user wallet
|
||||
* @property clickIntents click intents
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class ReinitializeNewWalletTransformer(
|
||||
private val prevWalletId: UserWalletId,
|
||||
private val newUserWallet: UserWallet,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
private val walletImageResolver: WalletImageResolver,
|
||||
) : WalletScreenStateTransformer {
|
||||
|
||||
private val walletLoadingStateFactory by lazy {
|
||||
WalletLoadingStateFactory(
|
||||
clickIntents = clickIntents,
|
||||
walletImageResolver = walletImageResolver,
|
||||
)
|
||||
}
|
||||
|
||||
override fun transform(prevState: WalletScreenState): WalletScreenState {
|
||||
return prevState.copy(
|
||||
wallets = prevState.wallets
|
||||
.filterNot { it.walletCardState.id == prevWalletId }
|
||||
.plus(
|
||||
element = walletLoadingStateFactory.create(
|
||||
userWallet = newUserWallet,
|
||||
),
|
||||
)
|
||||
.toImmutableList(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +1,23 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
||||
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver
|
||||
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.utils.WalletLoadingStateFactory
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
/**
|
||||
* Reinitialize wallet transformer
|
||||
*
|
||||
* @property prevWalletId reinitialized wallet id
|
||||
* @property newUserWallet new user wallet
|
||||
* @property clickIntents click intents
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
* @property userWallet user wallet to reinitialize
|
||||
* @property clickIntents click intents
|
||||
* @property walletImageResolver wallet image resolver
|
||||
*/
|
||||
internal class ReinitializeWalletTransformer(
|
||||
private val prevWalletId: UserWalletId,
|
||||
private val newUserWallet: UserWallet,
|
||||
private val userWallet: UserWallet,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
private val walletImageResolver: WalletImageResolver,
|
||||
) : WalletScreenStateTransformer {
|
||||
) : WalletStateTransformer(userWalletId = userWallet.walletId) {
|
||||
|
||||
private val walletLoadingStateFactory by lazy {
|
||||
WalletLoadingStateFactory(
|
||||
|
|
@ -31,16 +26,9 @@ internal class ReinitializeWalletTransformer(
|
|||
)
|
||||
}
|
||||
|
||||
override fun transform(prevState: WalletScreenState): WalletScreenState {
|
||||
return prevState.copy(
|
||||
wallets = prevState.wallets
|
||||
.filterNot { it.walletCardState.id == prevWalletId }
|
||||
.plus(
|
||||
element = walletLoadingStateFactory.create(
|
||||
userWallet = newUserWallet,
|
||||
),
|
||||
)
|
||||
.toImmutableList(),
|
||||
override fun transform(prevState: WalletState): WalletState {
|
||||
return walletLoadingStateFactory.create(
|
||||
userWallet = userWallet,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -137,7 +137,7 @@ internal class SetVisaInfoTransformer(
|
|||
fiatAmount = visaCurrency.balances.available.multiply(visaCurrency.fiatRate),
|
||||
fiatRate = visaCurrency.fiatRate,
|
||||
priceChange = visaCurrency.priceChange,
|
||||
yieldBalance = null,
|
||||
stakingBalance = null,
|
||||
yieldSupplyStatus = null,
|
||||
hasCurrentNetworkTransactions = false,
|
||||
pendingTransactions = emptySet(),
|
||||
|
|
@ -154,7 +154,7 @@ internal class SetVisaInfoTransformer(
|
|||
clickIntents.onReceiveClick(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
event = MainScreenAnalyticsEvent.ButtonReceive,
|
||||
event = MainScreenAnalyticsEvent.ButtonReceive(),
|
||||
)
|
||||
},
|
||||
onLongClick = {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
||||
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
|
||||
internal class TangemPayExposedDeviceTransformer(
|
||||
userWalletId: UserWalletId,
|
||||
) : WalletStateTransformer(userWalletId) {
|
||||
override fun transform(prevState: WalletState): WalletState {
|
||||
return if (prevState is WalletState.MultiCurrency.Content) {
|
||||
prevState.copy(tangemPayState = TangemPayState.ExposedDevice)
|
||||
} else {
|
||||
prevState
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
||||
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
|
||||
internal class TangemPayHideOnboardingStateTransformer(
|
||||
userWalletId: UserWalletId,
|
||||
) : WalletStateTransformer(userWalletId) {
|
||||
|
||||
override fun transform(prevState: WalletState): WalletState {
|
||||
return if (prevState is WalletState.MultiCurrency.Content) {
|
||||
prevState.copy(tangemPayState = TangemPayState.Empty)
|
||||
} else {
|
||||
prevState
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
||||
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
|
||||
internal class TangemPayOnboardingBannerStateTransformer(
|
||||
userWalletId: UserWalletId,
|
||||
private val onClick: (UserWalletId) -> Unit,
|
||||
private val closeOnClick: (UserWalletId) -> Unit,
|
||||
) : WalletStateTransformer(userWalletId) {
|
||||
|
||||
override fun transform(prevState: WalletState): WalletState {
|
||||
return if (prevState is WalletState.MultiCurrency.Content) {
|
||||
prevState.copy(
|
||||
tangemPayState = TangemPayState.OnboardingBanner(
|
||||
onClick = { onClick(userWalletId) },
|
||||
closeOnClick = { closeOnClick(userWalletId) },
|
||||
),
|
||||
)
|
||||
} else {
|
||||
prevState
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
||||
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
|
||||
|
|
@ -8,6 +10,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
|||
|
||||
internal class TangemPayRefreshNeededStateTransformer(
|
||||
userWalletId: UserWalletId,
|
||||
private val userWallet: UserWallet,
|
||||
private val onRefreshClick: () -> Unit,
|
||||
) : WalletStateTransformer(userWalletId = userWalletId) {
|
||||
|
||||
|
|
@ -15,7 +18,12 @@ internal class TangemPayRefreshNeededStateTransformer(
|
|||
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)
|
||||
},
|
||||
onRefreshClick = onRefreshClick,
|
||||
shouldShowProgress = false,
|
||||
),
|
||||
)
|
||||
return if (prevState is WalletState.MultiCurrency.Content) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
||||
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
|
||||
internal class TangemPayRefreshShowProgressTransformer(
|
||||
userWalletId: UserWalletId,
|
||||
) : WalletStateTransformer(userWalletId) {
|
||||
|
||||
override fun transform(prevState: WalletState): WalletState {
|
||||
val multiContentState = prevState as? WalletState.MultiCurrency.Content ?: return prevState
|
||||
val refreshNeededState = multiContentState.tangemPayState as? TangemPayState.RefreshNeeded ?: return prevState
|
||||
val refreshNotification =
|
||||
refreshNeededState.notification as? WalletNotification.Warning.TangemPayRefreshNeeded ?: return prevState
|
||||
|
||||
return multiContentState.copy(
|
||||
tangemPayState = refreshNeededState.copy(
|
||||
notification = refreshNotification.copy(shouldShowProgress = true),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
||||
|
||||
import com.tangem.common.ui.R
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
|
|
@ -11,11 +12,10 @@ import com.tangem.domain.pay.model.CustomerInfo.ProductInstance
|
|||
import com.tangem.domain.pay.model.MainScreenCustomerInfo
|
||||
import com.tangem.domain.pay.model.OrderStatus
|
||||
import com.tangem.domain.visa.model.TangemPayCardFrozenState
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.TangemPayIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState.Progress
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.util.TangemPayStateCreator.createCancelledState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.util.TangemPayStateCreator.createIssueProgressState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.util.TangemPayStateCreator.createKycInProgressState
|
||||
import java.util.Currency
|
||||
|
||||
/**
|
||||
|
|
@ -26,12 +26,9 @@ private const val POLYGON_CHAIN_ID = 137
|
|||
|
||||
internal class TangemPayUpdateInfoStateTransformer(
|
||||
userWalletId: UserWalletId,
|
||||
private val value: MainScreenCustomerInfo? = null,
|
||||
private val value: MainScreenCustomerInfo,
|
||||
private val cardFrozenState: TangemPayCardFrozenState,
|
||||
private val onClickKyc: () -> Unit = {},
|
||||
private val onIssuingCard: () -> Unit = {},
|
||||
private val onIssuingFailed: () -> Unit = {},
|
||||
private val openDetails: (config: TangemPayDetailsConfig) -> Unit = {},
|
||||
private val tangemPayClickIntents: TangemPayIntents,
|
||||
) : WalletStateTransformer(userWalletId = userWalletId) {
|
||||
|
||||
override fun transform(prevState: WalletState): WalletState {
|
||||
|
|
@ -44,16 +41,15 @@ internal class TangemPayUpdateInfoStateTransformer(
|
|||
}
|
||||
|
||||
private fun createInitialState(): TangemPayState {
|
||||
val cardInfo = value?.info?.cardInfo
|
||||
val productInstance = value?.info?.productInstance
|
||||
val cardInfo = value.info.cardInfo
|
||||
val productInstance = value.info.productInstance
|
||||
|
||||
// when statement copied to WalletTangemPayAnalyticsEventSender. Be careful when editing.
|
||||
return when {
|
||||
value == null -> TangemPayState.Empty
|
||||
value.orderStatus == OrderStatus.CANCELED -> createCancelledState(onIssuingFailed)
|
||||
!value.info.isKycApproved -> createKycInProgressState(onClickKyc)
|
||||
value.orderStatus == OrderStatus.CANCELED -> createCancelledState()
|
||||
!value.info.isKycApproved -> createKycInProgressState()
|
||||
cardInfo != null && productInstance != null -> getCardInfoState(cardInfo, productInstance)
|
||||
else -> createIssueProgressState(onIssuingCard)
|
||||
else -> createIssueProgressState()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -63,9 +59,11 @@ internal class TangemPayUpdateInfoStateTransformer(
|
|||
balanceText = TextReference.Str(getBalanceText(cardInfo)),
|
||||
balanceSymbol = stringReference("USDC"), // TODO hardcode for now
|
||||
onClick = {
|
||||
openDetails(
|
||||
tangemPayClickIntents.openDetails(
|
||||
userWalletId,
|
||||
TangemPayDetailsConfig(
|
||||
cardId = productInstance.cardId,
|
||||
isPinSet = cardInfo.isPinSet,
|
||||
cardFrozenState = cardFrozenState,
|
||||
customerWalletAddress = cardInfo.customerWalletAddress,
|
||||
cardNumberEnd = cardInfo.lastFourDigits,
|
||||
|
|
@ -81,4 +79,28 @@ internal class TangemPayUpdateInfoStateTransformer(
|
|||
fiat(fiatCurrencyCode = currency.currencyCode, fiatCurrencySymbol = currency.symbol)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createKycInProgressState(): TangemPayState = Progress(
|
||||
title = TextReference.Res(R.string.tangempay_payment_account),
|
||||
description = TextReference.Res(R.string.tangempay_kyc_in_progress),
|
||||
buttonText = TextReference.Res(R.string.tangempay_kyc_in_progress_notification_button),
|
||||
iconRes = R.drawable.ic_promo_kyc_36,
|
||||
onButtonClick = { tangemPayClickIntents.onKycProgressClicked(userWalletId) },
|
||||
)
|
||||
|
||||
private fun createIssueProgressState(): TangemPayState = Progress(
|
||||
title = TextReference.Res(R.string.tangempay_payment_account),
|
||||
description = TextReference.Res(R.string.tangempay_issuing_your_card),
|
||||
buttonText = TextReference.EMPTY,
|
||||
iconRes = R.drawable.ic_tangem_pay_promo_card_36,
|
||||
onButtonClick = tangemPayClickIntents::onIssuingCardClicked,
|
||||
showProgress = true,
|
||||
)
|
||||
|
||||
private fun createCancelledState(): TangemPayState = TangemPayState.FailedIssue(
|
||||
title = TextReference.Res(R.string.tangempay_payment_account),
|
||||
description = TextReference.Res(R.string.tangempay_failed_to_issue_card),
|
||||
iconRes = R.drawable.ic_alert_24,
|
||||
onButtonClick = tangemPayClickIntents::onIssuingFailedClicked,
|
||||
)
|
||||
}
|
||||
|
|
@ -44,12 +44,12 @@ internal class BalancesAndLimitsBottomSheetConverter(
|
|||
}
|
||||
|
||||
private fun balanceInfoOnClick() {
|
||||
analyticsEventHandler.send(MainScreenAnalyticsEvent.NoticeBalancesInfo)
|
||||
analyticsEventHandler.send(MainScreenAnalyticsEvent.NoticeBalancesInfo())
|
||||
eventSender.send(WalletEvent.ShowAlert(WalletAlertState.VisaBalancesInfo))
|
||||
}
|
||||
|
||||
private fun limitInfoOnClick(totalLimit: String, otherLimit: String) {
|
||||
analyticsEventHandler.send(MainScreenAnalyticsEvent.NoticeLimitsInfo)
|
||||
analyticsEventHandler.send(MainScreenAnalyticsEvent.NoticeLimitsInfo())
|
||||
eventSender.send(WalletEvent.ShowAlert(WalletAlertState.VisaLimitsInfo(totalLimit, otherLimit)))
|
||||
}
|
||||
}
|
||||
|
|
@ -42,6 +42,7 @@ internal class MultiWalletCurrencyActionsConverter(
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
private fun mapTokenActionState(
|
||||
actionsState: TokenActionsState.ActionState,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
|
|
@ -105,6 +106,11 @@ internal class MultiWalletCurrencyActionsConverter(
|
|||
icon = R.drawable.ic_analytics_24
|
||||
action = { clickIntents.onAnalyticsClick(cryptoCurrencyStatus) }
|
||||
}
|
||||
is TokenActionsState.ActionState.YieldMode -> {
|
||||
title = resourceReference(R.string.yield_module_start_earning)
|
||||
icon = R.drawable.ic_analytics_up_mini_24
|
||||
action = { /* no-op */ }
|
||||
}
|
||||
}
|
||||
|
||||
return TokenActionButtonConfig(
|
||||
|
|
|
|||
|
|
@ -82,9 +82,9 @@ internal class SingleWalletOnrampTransactionConverter(
|
|||
fallbackResId = R.drawable.ic_currency_24,
|
||||
),
|
||||
iconState = getIconState(value.status),
|
||||
onGoToProviderClick = {
|
||||
analyticsEventHandler.send(TokenOnrampAnalyticsEvent.GoToProvider)
|
||||
clickIntents.onGoToProviderClick(it)
|
||||
onGoToProviderClick = { url ->
|
||||
analyticsEventHandler.send(TokenOnrampAnalyticsEvent.GoToProvider())
|
||||
clickIntents.onGoToProviderClick(url)
|
||||
},
|
||||
onDisposeExpressStatus = clickIntents::onConfirmDisposeExpressStatus,
|
||||
onClick = {
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.util
|
||||
|
||||
import com.tangem.common.ui.R
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState.Progress
|
||||
|
||||
internal object TangemPayStateCreator {
|
||||
|
||||
fun createKycInProgressState(onClickKyc: () -> Unit): TangemPayState = Progress(
|
||||
title = TextReference.Res(R.string.tangempay_payment_account),
|
||||
description = TextReference.Res(R.string.tangempay_kyc_in_progress),
|
||||
buttonText = TextReference.Res(R.string.tangempay_kyc_in_progress_notification_button),
|
||||
iconRes = R.drawable.ic_promo_kyc_36,
|
||||
onButtonClick = onClickKyc,
|
||||
)
|
||||
|
||||
fun createIssueProgressState(onIssuingCardClick: () -> Unit): TangemPayState = Progress(
|
||||
title = TextReference.Res(R.string.tangempay_payment_account),
|
||||
description = TextReference.Res(R.string.tangempay_issuing_your_card),
|
||||
buttonText = TextReference.EMPTY,
|
||||
iconRes = R.drawable.ic_tangem_pay_promo_card_36,
|
||||
onButtonClick = onIssuingCardClick,
|
||||
showProgress = true,
|
||||
)
|
||||
|
||||
fun createCancelledState(onIssueFailedClick: () -> Unit): TangemPayState = TangemPayState.FailedIssue(
|
||||
title = TextReference.Res(R.string.tangempay_payment_account),
|
||||
description = TextReference.Res(R.string.tangempay_failed_to_issue_card),
|
||||
iconRes = R.drawable.ic_alert_24,
|
||||
onButtonClick = onIssueFailedClick,
|
||||
)
|
||||
}
|
||||
|
|
@ -52,7 +52,6 @@ internal abstract class BasicAccountListSubscriber : BasicWalletSubscriber() {
|
|||
stakingAvailabilityMap: Map<CryptoCurrency, StakingAvailability> = emptyMap(),
|
||||
shouldShowMainPromo: Boolean = false,
|
||||
) {
|
||||
val accountFlattenCurrencies = accountList.flattenCurrencies()
|
||||
val mainAccount = accountList.mainAccount
|
||||
|
||||
when {
|
||||
|
|
@ -74,26 +73,14 @@ internal abstract class BasicAccountListSubscriber : BasicWalletSubscriber() {
|
|||
)
|
||||
}
|
||||
isAccountMode -> {
|
||||
val isAllAccountsEmpty = accountFlattenCurrencies.isEmpty()
|
||||
if (isAllAccountsEmpty) {
|
||||
stateController.update(
|
||||
SetTokenListErrorTransformer(
|
||||
selectedWallet = userWallet,
|
||||
error = TokenListError.EmptyTokens,
|
||||
appCurrency = appCurrency,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
val convertParams = TokenConverterParams.Account(accountList, expandedAccounts)
|
||||
|
||||
updateContent(
|
||||
params = convertParams,
|
||||
appCurrency = appCurrency,
|
||||
yieldSupplyApyMap = yieldSupplyApyMap,
|
||||
stakingAvailabilityMap = stakingAvailabilityMap,
|
||||
shouldShowMainPromo = shouldShowMainPromo,
|
||||
)
|
||||
}
|
||||
val convertParams = TokenConverterParams.Account(accountList, expandedAccounts)
|
||||
updateContent(
|
||||
params = convertParams,
|
||||
appCurrency = appCurrency,
|
||||
yieldSupplyApyMap = yieldSupplyApyMap,
|
||||
stakingAvailabilityMap = stakingAvailabilityMap,
|
||||
shouldShowMainPromo = shouldShowMainPromo,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository
|
|||
import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase
|
||||
import com.tangem.domain.visa.model.TangemPayCardFrozenState
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletTangemPayAnalyticsEventSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.*
|
||||
|
|
@ -27,7 +26,6 @@ internal class TangemPayMainSubscriber @AssistedInject constructor(
|
|||
@Assisted private val userWallet: UserWallet,
|
||||
private val stateController: WalletStateController,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
private val innerWalletRouter: InnerWalletRouter,
|
||||
private val cardDetailsRepository: TangemPayCardDetailsRepository,
|
||||
private val tangemPayMainScreenCustomerInfoUseCase: TangemPayMainScreenCustomerInfoUseCase,
|
||||
private val analytics: WalletTangemPayAnalyticsEventSender,
|
||||
|
|
@ -48,7 +46,8 @@ internal class TangemPayMainSubscriber @AssistedInject constructor(
|
|||
stateController.update(
|
||||
transformer = TangemPayRefreshNeededStateTransformer(
|
||||
userWalletId = userWalletId,
|
||||
onRefreshClick = { clickIntents.onRefreshPayToken(userWalletId) },
|
||||
userWallet = userWallet,
|
||||
onRefreshClick = { clickIntents.onRefreshPayToken(userWallet) },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -57,7 +56,10 @@ internal class TangemPayMainSubscriber @AssistedInject constructor(
|
|||
transformer = TangemPayUnavailableStateTransformer(userWalletId),
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
TangemPayCustomerInfoError.ExposedDeviceError -> {
|
||||
stateController.update(TangemPayExposedDeviceTransformer(userWalletId))
|
||||
}
|
||||
TangemPayCustomerInfoError.UnknownError -> {
|
||||
// hide TangemPay block
|
||||
Timber.e("Failed when loading main screen TangemPay info: $tangemPayError")
|
||||
stateController.update(
|
||||
|
|
@ -79,6 +81,13 @@ internal class TangemPayMainSubscriber @AssistedInject constructor(
|
|||
updateTangemPay(data = state.info, userWalletId = userWalletId)
|
||||
analytics.send(customerInfo = state.info)
|
||||
}
|
||||
is MainCustomerInfoContentState.OnboardingBanner -> stateController.update(
|
||||
transformer = TangemPayOnboardingBannerStateTransformer(
|
||||
userWalletId = userWalletId,
|
||||
onClick = clickIntents::onOnboardingBannerClick,
|
||||
closeOnClick = clickIntents::onOnboardingBannerCloseClick,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -91,15 +100,7 @@ internal class TangemPayMainSubscriber @AssistedInject constructor(
|
|||
userWalletId = userWalletId,
|
||||
value = data,
|
||||
cardFrozenState = cardFrozenState,
|
||||
onClickKyc = { innerWalletRouter.openTangemPayOnboarding(userWalletId) },
|
||||
onIssuingCard = clickIntents::onIssuingCardClicked,
|
||||
onIssuingFailed = clickIntents::onIssuingFailedClicked,
|
||||
openDetails = { config ->
|
||||
innerWalletRouter.openTangemPayDetails(
|
||||
userWalletId = userWalletId,
|
||||
config = config,
|
||||
)
|
||||
},
|
||||
tangemPayClickIntents = clickIntents,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ import com.tangem.common.ui.expressStatus.expressTransactionsItems
|
|||
import com.tangem.core.ui.components.atoms.Hand
|
||||
import com.tangem.core.ui.components.atoms.handComposableComponentHeight
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
|
||||
import com.tangem.core.ui.components.containers.pullToRefresh.TangemPullToRefreshContainer
|
||||
import com.tangem.core.ui.components.rememberIsKeyboardVisible
|
||||
import com.tangem.core.ui.components.sheetscaffold.*
|
||||
|
|
@ -87,15 +88,18 @@ import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.TangemPa
|
|||
import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.VisaTxDetailsBottomSheet
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.balancesAndLimitsBlock
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.utils.changeWalletAnimator
|
||||
import com.tangem.features.markets.entry.BottomSheetState
|
||||
import com.tangem.features.markets.entry.MarketsEntryComponent
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@Composable
|
||||
internal fun WalletScreen(state: WalletScreenState, marketsEntryComponent: MarketsEntryComponent) {
|
||||
internal fun WalletScreen(
|
||||
state: WalletScreenState,
|
||||
bottomSheetContent: @Composable (() -> Unit),
|
||||
bottomSheetHeaderHeightProvider: () -> Dp,
|
||||
onBottomSheetStateChange: (BottomSheetState) -> Unit,
|
||||
) {
|
||||
// It means that screen is still initializing
|
||||
if (state.selectedWalletIndex == NOT_INITIALIZED_WALLET_INDEX) return
|
||||
|
||||
|
|
@ -116,8 +120,10 @@ internal fun WalletScreen(state: WalletScreenState, marketsEntryComponent: Marke
|
|||
snackbarHostState = snackbarHostState,
|
||||
isAutoScroll = isAutoScroll,
|
||||
onAutoScrollReset = { isAutoScroll.value = false },
|
||||
marketsEntryComponent = marketsEntryComponent,
|
||||
bottomSheetContent = bottomSheetContent,
|
||||
alertConfig = alertConfig,
|
||||
bottomSheetHeaderHeightProvider = bottomSheetHeaderHeightProvider,
|
||||
onBottomSheetStateChange = onBottomSheetStateChange,
|
||||
)
|
||||
|
||||
WalletEventEffect(
|
||||
|
|
@ -136,9 +142,11 @@ private fun WalletContent(
|
|||
walletsListState: LazyListState,
|
||||
snackbarHostState: SnackbarHostState,
|
||||
isAutoScroll: State<Boolean>,
|
||||
marketsEntryComponent: MarketsEntryComponent,
|
||||
alertConfig: WalletAlertState?,
|
||||
onAutoScrollReset: () -> Unit,
|
||||
bottomSheetHeaderHeightProvider: () -> Dp,
|
||||
onBottomSheetStateChange: (BottomSheetState) -> Unit,
|
||||
bottomSheetContent: @Composable (() -> Unit),
|
||||
) {
|
||||
/*
|
||||
* Don't pass key to remember, because it will brake scroll animation.
|
||||
|
|
@ -286,25 +294,15 @@ private fun WalletContent(
|
|||
)
|
||||
}
|
||||
|
||||
val bottomSheetState = remember { mutableStateOf(BottomSheetState.COLLAPSED) }
|
||||
|
||||
var headerSize by remember { mutableStateOf(0.dp) }
|
||||
|
||||
BaseScaffoldWithMarkets(
|
||||
state = state,
|
||||
listState = listState,
|
||||
selectedWallet = selectedWallet,
|
||||
snackbarHostState = snackbarHostState,
|
||||
bottomSheetHeaderHeightProvider = { headerSize },
|
||||
bottomSheetHeaderHeightProvider = bottomSheetHeaderHeightProvider,
|
||||
alertConfig = alertConfig,
|
||||
onBottomSheetStateChange = { bottomSheetState.value = it },
|
||||
bottomSheetContent = {
|
||||
marketsEntryComponent.BottomSheetContent(
|
||||
bottomSheetState = bottomSheetState,
|
||||
onHeaderSizeChange = { headerSize = it },
|
||||
modifier = Modifier,
|
||||
)
|
||||
},
|
||||
onBottomSheetStateChange = onBottomSheetStateChange,
|
||||
bottomSheetContent = bottomSheetContent,
|
||||
content = scaffoldContent,
|
||||
)
|
||||
}
|
||||
|
|
@ -394,7 +392,11 @@ private inline fun BaseScaffoldWithMarkets(
|
|||
val maxHeight = LocalWindowSize.current.height
|
||||
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val backgroundPrimary = TangemTheme.colors.background.primary
|
||||
val background = if (state.isNewMarketEnabled) {
|
||||
TangemTheme.colors.background.tertiary
|
||||
} else {
|
||||
TangemTheme.colors.background.primary
|
||||
}
|
||||
|
||||
val showMarketsHint by remember {
|
||||
derivedStateOf {
|
||||
|
|
@ -407,7 +409,7 @@ private inline fun BaseScaffoldWithMarkets(
|
|||
}
|
||||
|
||||
CompositionLocalProvider(
|
||||
LocalMainBottomSheetColor provides remember { mutableStateOf(backgroundPrimary) },
|
||||
LocalMainBottomSheetColor provides remember { mutableStateOf(background) },
|
||||
) {
|
||||
val backgroundColor = LocalMainBottomSheetColor.current
|
||||
var isSearchFieldFocused by remember { mutableStateOf(false) }
|
||||
|
|
@ -798,16 +800,11 @@ private fun WalletScreen_Preview(@PreviewParameter(WalletScreenPreviewProvider::
|
|||
TangemThemePreview {
|
||||
WalletScreen(
|
||||
state = data,
|
||||
marketsEntryComponent = object : MarketsEntryComponent {
|
||||
@Composable
|
||||
override fun BottomSheetContent(
|
||||
bottomSheetState: State<BottomSheetState>,
|
||||
onHeaderSizeChange: (Dp) -> Unit,
|
||||
modifier: Modifier,
|
||||
) {
|
||||
Text("Markets Content")
|
||||
}
|
||||
bottomSheetContent = {
|
||||
Text("Markets Content")
|
||||
},
|
||||
bottomSheetHeaderHeightProvider = { 10.dp },
|
||||
onBottomSheetStateChange = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.components.visa
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.block.BlockCard
|
||||
import com.tangem.core.ui.components.inputrow.InputRowImageBase
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
private const val DISABLED_ALPHA = 0.6F
|
||||
|
||||
@Composable
|
||||
internal fun TangemPayExposedDeviceState(modifier: Modifier = Modifier) {
|
||||
BlockCard(
|
||||
modifier = modifier
|
||||
.clip(RoundedCornerShape(size = TangemTheme.dimens.radius14))
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
.alpha(DISABLED_ALPHA),
|
||||
enabled = false,
|
||||
onClick = {},
|
||||
) {
|
||||
InputRowImageBase(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
all = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
subtitle = resourceReference(R.string.tangempay_payment_account),
|
||||
caption = resourceReference(R.string.tangem_pay_rooted_device_subtitle),
|
||||
subtitleColor = TangemTheme.colors.text.primary1,
|
||||
captionColor = TangemTheme.colors.text.tertiary,
|
||||
iconResWebp = R.drawable.img_visa_36,
|
||||
endIconTint = TangemTheme.colors.icon.warning,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -23,7 +23,9 @@ internal fun TangemPayMainScreenBlock(state: TangemPayState, isBalanceHidden: Bo
|
|||
is TangemPayState.RefreshNeeded -> TangemPayRefreshBlock(state, modifier)
|
||||
is TangemPayState.TemporaryUnavailable -> TangemPayUnavailableBlock(state, modifier)
|
||||
is TangemPayState.FailedIssue -> TangemPayFailedIssueState(state, modifier)
|
||||
TangemPayState.Loading -> TangemPayLoadingScreenBlock(modifier)
|
||||
is TangemPayState.OnboardingBanner -> TangemPayOnboardingBanner(state, modifier)
|
||||
is TangemPayState.ExposedDevice -> TangemPayExposedDeviceState(modifier)
|
||||
is TangemPayState.Loading -> TangemPayLoadingScreenBlock(modifier)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -35,6 +37,8 @@ private fun TangemPayMainScreenBlockPreview() {
|
|||
Column(verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) {
|
||||
TangemPayMainScreenBlock(state = TangemPayState.Loading, isBalanceHidden = false)
|
||||
|
||||
TangemPayMainScreenBlock(state = TangemPayState.ExposedDevice, isBalanceHidden = false)
|
||||
|
||||
TangemPayMainScreenBlock(
|
||||
Progress(
|
||||
title = TextReference.Res(R.string.tangempay_kyc_in_progress_notification_title),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,120 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.components.visa
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import androidx.constraintlayout.compose.Dimension
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
|
||||
|
||||
private const val GRADIENT_START_COLOR = 0xFF252934
|
||||
private const val GRADIENT_END_COLOR = 0xFF12141E
|
||||
private const val GRADIENT_OFFSET_X = 0f
|
||||
private const val GRADIENT_OFFSET_Y = 80F
|
||||
private const val GRADIENT_RADIUS = 200F
|
||||
|
||||
@Composable
|
||||
internal fun TangemPayOnboardingBanner(state: TangemPayState.OnboardingBanner, modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(
|
||||
brush = Brush.radialGradient(
|
||||
colors = listOf(Color(GRADIENT_START_COLOR), Color(GRADIENT_END_COLOR)),
|
||||
center = Offset(GRADIENT_OFFSET_X, GRADIENT_OFFSET_Y),
|
||||
radius = GRADIENT_RADIUS,
|
||||
),
|
||||
)
|
||||
.clickable(onClick = state.onClick),
|
||||
) {
|
||||
ConstraintLayout(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
val (image, text, close) = createRefs()
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.constrainAs(text) {
|
||||
top.linkTo(parent.top)
|
||||
start.linkTo(image.end, margin = 12.dp)
|
||||
end.linkTo(parent.end)
|
||||
width = Dimension.fillToConstraints
|
||||
}
|
||||
.padding(top = 16.dp, end = 16.dp, bottom = 16.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.tangempay_onboarding_banner_title),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.constantWhite,
|
||||
)
|
||||
|
||||
SpacerH(6.dp)
|
||||
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.tangempay_onboarding_banner_description),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
|
||||
Image(
|
||||
painter = painterResource(R.drawable.img_tangem_pay_onboarding_banner),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.constrainAs(image) {
|
||||
start.linkTo(parent.start)
|
||||
top.linkTo(text.top)
|
||||
bottom.linkTo(text.bottom)
|
||||
height = Dimension.fillToConstraints
|
||||
}
|
||||
.padding(top = 8.dp, start = 24.dp),
|
||||
)
|
||||
|
||||
Image(
|
||||
painter = painterResource(R.drawable.ic_close_24),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.size(16.dp)
|
||||
.clickable(onClick = state.closeOnClick)
|
||||
.constrainAs(close) {
|
||||
top.linkTo(parent.top, margin = 16.dp)
|
||||
end.linkTo(parent.end, margin = 16.dp)
|
||||
},
|
||||
colorFilter = ColorFilter.tint(TangemTheme.colors.icon.inactive),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun PreviewTangemOnboardingBanner() {
|
||||
TangemThemePreview {
|
||||
TangemPayOnboardingBanner(
|
||||
TangemPayState.OnboardingBanner(
|
||||
onClick = {},
|
||||
closeOnClick = {},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -68,7 +68,9 @@ private fun TangemPayRefreshBlockPreview() {
|
|||
state = TangemPayState.RefreshNeeded(
|
||||
TangemPayRefreshNeeded(
|
||||
tangemIcon = R.drawable.ic_tangem_24,
|
||||
buttonText = resourceReference(id = R.string.tangempay_sync_needed_restore_access),
|
||||
onRefreshClick = {},
|
||||
shouldShowProgress = true,
|
||||
),
|
||||
),
|
||||
modifier = Modifier,
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ class YieldSupplyPromoBannerKeyConverterTest {
|
|||
fiatAmount = BigDecimal.ZERO,
|
||||
fiatRate = BigDecimal.ONE,
|
||||
priceChange = BigDecimal.ZERO,
|
||||
yieldBalance = null,
|
||||
stakingBalance = null,
|
||||
yieldSupplyStatus = if (isYieldActive) {
|
||||
YieldSupplyStatus(
|
||||
isActive = true,
|
||||
|
|
@ -207,7 +207,7 @@ class YieldSupplyPromoBannerKeyConverterTest {
|
|||
fiatAmount = null,
|
||||
fiatRate = null,
|
||||
priceChange = null,
|
||||
yieldBalance = null,
|
||||
stakingBalance = null,
|
||||
yieldSupplyStatus = if (isYieldActive) {
|
||||
YieldSupplyStatus(
|
||||
isActive = true,
|
||||
|
|
|
|||
|
|
@ -115,7 +115,9 @@ class DefaultPromoDeeplinkHandlerTest {
|
|||
Truth.assertThat(sent.title).isEqualTo(resourceReference(R.string.bitcoin_promo_activation_success_title))
|
||||
Truth.assertThat(sent.message).isEqualTo(resourceReference(R.string.bitcoin_promo_activation_success))
|
||||
|
||||
verify(exactly = 1) { analyticsEventHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart) }
|
||||
verify(
|
||||
exactly = 1,
|
||||
) { analyticsEventHandler.send(ofType<PromoActivationAnalytics.PromoDeepLinkActivationStart>()) }
|
||||
verify(exactly = 1) {
|
||||
analyticsEventHandler.send(
|
||||
PromoActivationAnalytics.PromoActivation(PromoCodeActivationResult.Activated),
|
||||
|
|
@ -143,7 +145,9 @@ class DefaultPromoDeeplinkHandlerTest {
|
|||
Truth.assertThat(sent.title).isEqualTo(resourceReference(R.string.bitcoin_promo_invalid_code_title))
|
||||
Truth.assertThat(sent.message).isEqualTo(resourceReference(R.string.bitcoin_promo_invalid_code))
|
||||
|
||||
verify(exactly = 1) { analyticsEventHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart) }
|
||||
verify(
|
||||
exactly = 1,
|
||||
) { analyticsEventHandler.send(ofType<PromoActivationAnalytics.PromoDeepLinkActivationStart>()) }
|
||||
verify(exactly = 1) {
|
||||
analyticsEventHandler.send(
|
||||
PromoActivationAnalytics.PromoActivation(PromoCodeActivationResult.InvalidPromoCode),
|
||||
|
|
@ -172,7 +176,9 @@ class DefaultPromoDeeplinkHandlerTest {
|
|||
Truth.assertThat(sent.title).isEqualTo(resourceReference(R.string.bitcoin_promo_activation_error_title))
|
||||
Truth.assertThat(sent.message).isEqualTo(resourceReference(R.string.bitcoin_promo_activation_error))
|
||||
|
||||
verify(exactly = 1) { analyticsEventHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart) }
|
||||
verify(
|
||||
exactly = 1,
|
||||
) { analyticsEventHandler.send(ofType<PromoActivationAnalytics.PromoDeepLinkActivationStart>()) }
|
||||
verify(exactly = 1) {
|
||||
analyticsEventHandler.send(
|
||||
PromoActivationAnalytics.PromoActivation(PromoCodeActivationResult.Failed),
|
||||
|
|
@ -209,7 +215,9 @@ class DefaultPromoDeeplinkHandlerTest {
|
|||
Truth.assertThat(sent.title).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address_title))
|
||||
Truth.assertThat(sent.message).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address))
|
||||
|
||||
verify(exactly = 1) { analyticsEventHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart) }
|
||||
verify(
|
||||
exactly = 1,
|
||||
) { analyticsEventHandler.send(ofType<PromoActivationAnalytics.PromoDeepLinkActivationStart>()) }
|
||||
verify(exactly = 1) {
|
||||
analyticsEventHandler.send(
|
||||
PromoActivationAnalytics.PromoActivation(PromoCodeActivationResult.NoBitcoinAddress),
|
||||
|
|
@ -248,7 +256,9 @@ class DefaultPromoDeeplinkHandlerTest {
|
|||
Truth.assertThat(sent.title).isEqualTo(resourceReference(R.string.bitcoin_promo_activation_success_title))
|
||||
Truth.assertThat(sent.message).isEqualTo(resourceReference(R.string.bitcoin_promo_activation_success))
|
||||
|
||||
verify(exactly = 1) { analyticsEventHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart) }
|
||||
verify(
|
||||
exactly = 1,
|
||||
) { analyticsEventHandler.send(ofType<PromoActivationAnalytics.PromoDeepLinkActivationStart>()) }
|
||||
verify(exactly = 1) {
|
||||
analyticsEventHandler.send(
|
||||
PromoActivationAnalytics.PromoActivation(PromoCodeActivationResult.Activated),
|
||||
|
|
@ -264,7 +274,9 @@ class DefaultPromoDeeplinkHandlerTest {
|
|||
expectedMessage = resourceReference(R.string.bitcoin_promo_invalid_code),
|
||||
)
|
||||
|
||||
verify(exactly = 1) { analyticsEventHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart) }
|
||||
verify(
|
||||
exactly = 1,
|
||||
) { analyticsEventHandler.send(ofType<PromoActivationAnalytics.PromoDeepLinkActivationStart>()) }
|
||||
verify(exactly = 1) {
|
||||
analyticsEventHandler.send(
|
||||
PromoActivationAnalytics.PromoActivation(PromoCodeActivationResult.InvalidPromoCode),
|
||||
|
|
@ -280,7 +292,9 @@ class DefaultPromoDeeplinkHandlerTest {
|
|||
expectedMessage = resourceReference(R.string.bitcoin_promo_activation_error),
|
||||
)
|
||||
|
||||
verify(exactly = 1) { analyticsEventHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart) }
|
||||
verify(
|
||||
exactly = 1,
|
||||
) { analyticsEventHandler.send(ofType<PromoActivationAnalytics.PromoDeepLinkActivationStart>()) }
|
||||
verify(exactly = 1) {
|
||||
analyticsEventHandler.send(
|
||||
PromoActivationAnalytics.PromoActivation(PromoCodeActivationResult.Failed),
|
||||
|
|
@ -296,7 +310,9 @@ class DefaultPromoDeeplinkHandlerTest {
|
|||
expectedMessage = resourceReference(R.string.bitcoin_promo_no_address),
|
||||
)
|
||||
|
||||
verify(exactly = 1) { analyticsEventHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart) }
|
||||
verify(
|
||||
exactly = 1,
|
||||
) { analyticsEventHandler.send(ofType<PromoActivationAnalytics.PromoDeepLinkActivationStart>()) }
|
||||
verify(exactly = 1) {
|
||||
analyticsEventHandler.send(
|
||||
PromoActivationAnalytics.PromoActivation(PromoCodeActivationResult.NoBitcoinAddress),
|
||||
|
|
@ -312,7 +328,9 @@ class DefaultPromoDeeplinkHandlerTest {
|
|||
expectedMessage = resourceReference(R.string.bitcoin_promo_already_activated),
|
||||
)
|
||||
|
||||
verify(exactly = 1) { analyticsEventHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart) }
|
||||
verify(
|
||||
exactly = 1,
|
||||
) { analyticsEventHandler.send(ofType<PromoActivationAnalytics.PromoDeepLinkActivationStart>()) }
|
||||
verify(exactly = 1) {
|
||||
analyticsEventHandler.send(
|
||||
PromoActivationAnalytics.PromoActivation(PromoCodeActivationResult.PromoCodeAlreadyUsed),
|
||||
|
|
@ -425,7 +443,9 @@ class DefaultPromoDeeplinkHandlerTest {
|
|||
Truth.assertThat(sent.title).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address_title))
|
||||
Truth.assertThat(sent.message).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address))
|
||||
|
||||
verify(exactly = 1) { analyticsEventHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart) }
|
||||
verify(
|
||||
exactly = 1,
|
||||
) { analyticsEventHandler.send(ofType<PromoActivationAnalytics.PromoDeepLinkActivationStart>()) }
|
||||
verify(exactly = 1) {
|
||||
analyticsEventHandler.send(
|
||||
PromoActivationAnalytics.PromoActivation(PromoCodeActivationResult.NoBitcoinAddress),
|
||||
|
|
@ -539,7 +559,9 @@ class DefaultPromoDeeplinkHandlerTest {
|
|||
coVerify(exactly = 1) { activateBitcoinPromocodeUseCase.invoke("bc1qcard", promoCode) }
|
||||
coVerify(exactly = 0) { activateBitcoinPromocodeUseCase.invoke("bc1qcustom", promoCode) }
|
||||
|
||||
verify(exactly = 1) { analyticsEventHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart) }
|
||||
verify(
|
||||
exactly = 1,
|
||||
) { analyticsEventHandler.send(ofType<PromoActivationAnalytics.PromoDeepLinkActivationStart>()) }
|
||||
verify(exactly = 1) {
|
||||
analyticsEventHandler.send(
|
||||
PromoActivationAnalytics.PromoActivation(PromoCodeActivationResult.Activated),
|
||||
|
|
@ -594,7 +616,9 @@ class DefaultPromoDeeplinkHandlerTest {
|
|||
|
||||
coVerify(exactly = 0) { activateBitcoinPromocodeUseCase.invoke(any(), any()) }
|
||||
|
||||
verify(exactly = 1) { analyticsEventHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart) }
|
||||
verify(
|
||||
exactly = 1,
|
||||
) { analyticsEventHandler.send(ofType<PromoActivationAnalytics.PromoDeepLinkActivationStart>()) }
|
||||
verify(exactly = 1) {
|
||||
analyticsEventHandler.send(
|
||||
PromoActivationAnalytics.PromoActivation(PromoCodeActivationResult.NoBitcoinAddress),
|
||||
|
|
@ -649,7 +673,9 @@ class DefaultPromoDeeplinkHandlerTest {
|
|||
|
||||
coVerify(exactly = 0) { activateBitcoinPromocodeUseCase.invoke(any(), any()) }
|
||||
|
||||
verify(exactly = 1) { analyticsEventHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart) }
|
||||
verify(
|
||||
exactly = 1,
|
||||
) { analyticsEventHandler.send(ofType<PromoActivationAnalytics.PromoDeepLinkActivationStart>()) }
|
||||
verify(exactly = 1) {
|
||||
analyticsEventHandler.send(
|
||||
PromoActivationAnalytics.PromoActivation(PromoCodeActivationResult.NoBitcoinAddress),
|
||||
|
|
@ -700,7 +726,9 @@ class DefaultPromoDeeplinkHandlerTest {
|
|||
|
||||
coVerify(exactly = 0) { activateBitcoinPromocodeUseCase.invoke(any(), any()) }
|
||||
|
||||
verify(exactly = 1) { analyticsEventHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart) }
|
||||
verify(
|
||||
exactly = 1,
|
||||
) { analyticsEventHandler.send(ofType<PromoActivationAnalytics.PromoDeepLinkActivationStart>()) }
|
||||
verify(exactly = 1) {
|
||||
analyticsEventHandler.send(
|
||||
PromoActivationAnalytics.PromoActivation(PromoCodeActivationResult.NoBitcoinAddress),
|
||||
|
|
@ -751,7 +779,9 @@ class DefaultPromoDeeplinkHandlerTest {
|
|||
|
||||
coVerify(exactly = 0) { activateBitcoinPromocodeUseCase.invoke(any(), any()) }
|
||||
|
||||
verify(exactly = 1) { analyticsEventHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart) }
|
||||
verify(
|
||||
exactly = 1,
|
||||
) { analyticsEventHandler.send(ofType<PromoActivationAnalytics.PromoDeepLinkActivationStart>()) }
|
||||
verify(exactly = 1) {
|
||||
analyticsEventHandler.send(
|
||||
PromoActivationAnalytics.PromoActivation(PromoCodeActivationResult.NoBitcoinAddress),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue