Updated on 2026-08-14
This commit is contained in:
commit
3cbc2dadfb
822 changed files with 12838 additions and 5366 deletions
|
|
@ -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,7 @@ internal class WalletModel @Inject constructor(
|
|||
private var expressTxStatusTaskScheduler = SingleTaskScheduler<Unit>()
|
||||
|
||||
init {
|
||||
screenLifecycleProvider.isBackgroundState
|
||||
.onEach { isBackground ->
|
||||
if (isBackground.not()) {
|
||||
suggestToEnableBiometrics()
|
||||
}
|
||||
}.launchIn(modelScope)
|
||||
|
||||
updateMarketToggle()
|
||||
suggestToOpenMarkets()
|
||||
|
||||
maybeMigrateNames()
|
||||
|
|
@ -140,6 +138,18 @@ internal class WalletModel @Inject constructor(
|
|||
clickIntents.initialize(innerWalletRouter, modelScope)
|
||||
}
|
||||
|
||||
fun onResume() {
|
||||
modelScope.launch(dispatchers.main) {
|
||||
suggestToEnableBiometrics()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateMarketToggle() {
|
||||
stateHolder.update {
|
||||
it.copy(isNewMarketEnabled = feedFeatureToggle.isFeedEnabled)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateYieldSupplyApy() {
|
||||
if (yieldSupplyFeatureToggles.isYieldSupplyFeatureEnabled) {
|
||||
modelScope.launch(dispatchers.default) {
|
||||
|
|
@ -195,7 +205,6 @@ internal class WalletModel @Inject constructor(
|
|||
private suspend fun shouldShowAskBiometryBottomSheet(): Boolean {
|
||||
return if (hotWalletFeatureToggles.isHotWalletEnabled) {
|
||||
userWalletsListRepository.userWalletsSync().any { it is UserWallet.Cold } &&
|
||||
innerWalletRouter.isWalletLastScreen() &&
|
||||
shouldShowAskBiometryUseCase() &&
|
||||
canUseBiometryUseCase()
|
||||
} else {
|
||||
|
|
@ -278,14 +287,39 @@ internal class WalletModel @Inject constructor(
|
|||
.conflate()
|
||||
.distinctUntilChanged()
|
||||
.onEach { selectedWallet ->
|
||||
trackingContextProxy.setContext(selectedWallet)
|
||||
|
||||
if (hotWalletFeatureToggles.isHotWalletEnabled) {
|
||||
modelScope.launch {
|
||||
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(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedWallet.isMultiCurrency) {
|
||||
selectedWalletAnalyticsSender.send(selectedWallet)
|
||||
}
|
||||
|
||||
subscribeOnExpressTransactionsUpdates(selectedWallet)
|
||||
observeAndClearNFTCacheIfNeedUseCase(selectedWallet)
|
||||
|
||||
sendMainScreenOpenedAnalytics(selectedWallet.isImported())
|
||||
}
|
||||
.flowOn(dispatchers.main)
|
||||
.launchIn(modelScope)
|
||||
|
|
@ -420,6 +454,7 @@ internal class WalletModel @Inject constructor(
|
|||
when (action) {
|
||||
is WalletsUpdateActionResolver.Action.InitializeWallets -> initializeWallets(action)
|
||||
is WalletsUpdateActionResolver.Action.ReinitializeWallet -> reinitializeWallet(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)
|
||||
|
|
@ -524,6 +559,32 @@ 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,
|
||||
)
|
||||
|
||||
modelScope.launch(dispatchers.main) {
|
||||
fetchWalletContent(userWallet = userWallet)
|
||||
}
|
||||
|
||||
stateHolder.update(
|
||||
ReinitializeWalletTransformer(
|
||||
prevWalletId = userWallet.walletId,
|
||||
newUserWallet = userWallet,
|
||||
clickIntents = clickIntents,
|
||||
walletImageResolver = walletImageResolver,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addWallet(action: WalletsUpdateActionResolver.Action.AddWallet) {
|
||||
if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
fetchWalletContent(userWallet = action.selectedWallet)
|
||||
|
|
@ -695,17 +756,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,8 +64,11 @@ internal class WalletsUpdateActionResolver @Inject constructor(
|
|||
selectedWallet: UserWallet,
|
||||
): Action {
|
||||
return when {
|
||||
isHotWalletUpgraded(state, wallets) -> {
|
||||
getHotWalletsUpgradedAction(state, wallets)
|
||||
isAnyHotWalletUpgraded(state, wallets) -> {
|
||||
getHotWalletsUpgradedAction(state, wallets, selectedWallet)
|
||||
}
|
||||
isAnyHotWalletBackedUpChange(state, wallets) -> {
|
||||
getHotWalletsBackedUpAction(state, wallets)
|
||||
}
|
||||
isWalletsCountChanged(state, wallets) -> {
|
||||
getChangeWalletsListAction(state, wallets, selectedWallet)
|
||||
|
|
@ -79,31 +82,35 @@ internal class WalletsUpdateActionResolver @Inject constructor(
|
|||
isAnyWalletNameChanged(state, wallets) -> {
|
||||
getRenameWalletsAction(state, wallets)
|
||||
}
|
||||
isAnyHotWalletBackedUpChange(state, wallets) -> {
|
||||
getHotWalletsBackedUpAction(state, wallets)
|
||||
isAnyWalletUnlocked(state, wallets) -> {
|
||||
Action.UnlockWallet(
|
||||
selectedWallet = selectedWallet,
|
||||
unlockedWallets = wallets.filterNot(UserWallet::isLocked),
|
||||
)
|
||||
}
|
||||
else -> getUpdateSelectedWalletAction(state, wallets, 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
|
||||
}
|
||||
|
|
@ -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,5 +1,6 @@
|
|||
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
|
||||
|
|
@ -14,6 +15,7 @@ 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.features.tangempay.TangemPayFeatureToggles
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
|
@ -29,6 +31,10 @@ internal interface TangemPayIntents {
|
|||
fun onIssuingFailedClicked()
|
||||
|
||||
fun onPaySupportClick()
|
||||
|
||||
fun onOnboardingBannerClick(userWalletId: UserWalletId)
|
||||
|
||||
fun onOnboardingBannerCloseClick(userWalletId: UserWalletId)
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
|
|
@ -115,4 +121,15 @@ internal class TangemPayClickIntentsImplementor @Inject constructor(
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOnboardingBannerClick(userWalletId: UserWalletId) {
|
||||
router.openTangemPayOnboarding(mode = AppRoute.TangemPayOnboarding.Mode.FromBannerOnMain(userWalletId))
|
||||
}
|
||||
|
||||
override fun onOnboardingBannerCloseClick(userWalletId: UserWalletId) {
|
||||
modelScope.launch {
|
||||
stateHolder.update(transformer = TangemPayHideOnboardingStateTransformer(userWalletId))
|
||||
onboardingRepository.setHideMainOnboardingBanner(userWalletId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,11 +4,12 @@ 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.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
|
||||
|
|
@ -205,11 +206,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)
|
||||
}
|
||||
|
||||
|
|
@ -260,7 +263,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"
|
||||
|
|
@ -364,7 +367,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,
|
||||
|
|
@ -374,7 +377,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,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
error.handle(
|
||||
onAlreadyUnlocked = {},
|
||||
onUserCancelled = {},
|
||||
analyticsEventHandler = analyticsEventHandler,
|
||||
showMessage = uiMessageSender::send,
|
||||
)
|
||||
}
|
||||
|
|
@ -221,7 +219,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 +247,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 +305,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 +334,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 +342,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 +356,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 +364,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 +374,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 +392,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 +401,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 +424,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 +443,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 +466,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 +506,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 +545,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)
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,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 +51,44 @@ sealed class WalletScreenAnalyticsEvent {
|
|||
params: Map<String, String> = mapOf(),
|
||||
) : AnalyticsEvent(category = "Main Screen", event = event, params = params) {
|
||||
|
||||
class ScreenOpened(
|
||||
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 +108,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 +119,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 +175,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())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -156,9 +156,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(
|
||||
|
|
|
|||
|
|
@ -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,8 +1,24 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.analytics.utils
|
||||
|
||||
import arrow.atomic.AtomicBoolean
|
||||
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
|
||||
|
|
@ -12,8 +28,13 @@ import javax.inject.Inject
|
|||
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: AtomicBoolean = AtomicBoolean(false)
|
||||
|
||||
suspend fun send(
|
||||
userWalletId: UserWalletId,
|
||||
displayedUiState: WalletState?,
|
||||
|
|
@ -26,9 +47,49 @@ internal class WalletWarningsSingleEventSender @Inject constructor(
|
|||
val events = newWarnings.filter { it !in displayedUiState.warnings }
|
||||
|
||||
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 -> {
|
||||
if (event.type == WalletActivationBannerType.Warning && !isActivationBottomSheetShown.get()) {
|
||||
showFinishActivationBottomSheet(userWalletId)
|
||||
}
|
||||
isActivationBottomSheetShown.set(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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
@ -287,14 +288,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,
|
||||
)
|
||||
|
|
@ -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,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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ internal abstract class BasicAccountListSubscriber : BasicWalletSubscriber() {
|
|||
stakingApyMap: Map<String, List<Yield.Validator>> = emptyMap(),
|
||||
shouldShowMainPromo: Boolean = false,
|
||||
) {
|
||||
val accountFlattenCurrencies = accountList.flattenCurrencies()
|
||||
val mainAccount = accountList.mainAccount
|
||||
|
||||
when {
|
||||
|
|
@ -73,26 +72,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,
|
||||
stakingApyMap = stakingApyMap,
|
||||
shouldShowMainPromo = shouldShowMainPromo,
|
||||
)
|
||||
}
|
||||
val convertParams = TokenConverterParams.Account(accountList, expandedAccounts)
|
||||
updateContent(
|
||||
params = convertParams,
|
||||
appCurrency = appCurrency,
|
||||
yieldSupplyApyMap = yieldSupplyApyMap,
|
||||
stakingApyMap = stakingApyMap,
|
||||
shouldShowMainPromo = shouldShowMainPromo,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.pay.model.MainCustomerInfoContentState
|
||||
|
|
@ -79,6 +80,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,7 +99,11 @@ internal class TangemPayMainSubscriber @AssistedInject constructor(
|
|||
userWalletId = userWalletId,
|
||||
value = data,
|
||||
cardFrozenState = cardFrozenState,
|
||||
onClickKyc = { innerWalletRouter.openTangemPayOnboarding(userWalletId) },
|
||||
onClickKyc = {
|
||||
innerWalletRouter.openTangemPayOnboarding(
|
||||
mode = AppRoute.TangemPayOnboarding.Mode.ContinueOnboarding(userWalletId),
|
||||
)
|
||||
},
|
||||
onIssuingCard = clickIntents::onIssuingCardClicked,
|
||||
onIssuingFailed = clickIntents::onIssuingFailedClicked,
|
||||
openDetails = { config ->
|
||||
|
|
|
|||
|
|
@ -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 = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ 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)
|
||||
is TangemPayState.OnboardingBanner -> TangemPayOnboardingBanner(state, modifier)
|
||||
TangemPayState.Loading -> TangemPayLoadingScreenBlock(modifier)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,129 @@
|
|||
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.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.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 com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.components.SpacerH4
|
||||
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),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(IntrinsicSize.Min)
|
||||
.padding(horizontal = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.img_tangem_pay_onboarding_banner),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 8.dp)
|
||||
.width(55.dp)
|
||||
.height(95.dp)
|
||||
.offset(y = 10.dp),
|
||||
)
|
||||
|
||||
SpacerH4()
|
||||
|
||||
Column(
|
||||
modifier = Modifier.weight(1f),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
) {
|
||||
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,
|
||||
)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier.fillMaxHeight(),
|
||||
contentAlignment = Alignment.TopEnd,
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.clickable(onClick = state.closeOnClick)
|
||||
.padding(4.dp)
|
||||
.padding(top = 12.dp)
|
||||
.size(12.dp),
|
||||
painter = painterResource(id = R.drawable.ic_close_24),
|
||||
colorFilter = ColorFilter.tint(TangemTheme.colors.icon.inactive),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun PreviewTangemOnboardingBanner() {
|
||||
TangemThemePreview {
|
||||
TangemPayOnboardingBanner(
|
||||
TangemPayState.OnboardingBanner(
|
||||
onClick = {},
|
||||
closeOnClick = {},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -151,7 +151,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