Updated on 2026-08-14
This commit is contained in:
parent
72cf995bd6
commit
9925da2864
15 changed files with 410 additions and 18 deletions
|
|
@ -81,6 +81,12 @@ sealed class AnalyticsParam {
|
|||
override val feeType: FeeType,
|
||||
) : TxSentFrom("Swap"), TxData
|
||||
|
||||
data class Staking(
|
||||
override val blockchain: String,
|
||||
override val token: String,
|
||||
override val feeType: FeeType,
|
||||
) : TxSentFrom("Staking"), TxData
|
||||
|
||||
data class Approve(
|
||||
override val blockchain: String,
|
||||
override val token: String,
|
||||
|
|
|
|||
|
|
@ -25,6 +25,13 @@ sealed class YieldBalance {
|
|||
.filter { it.type == BalanceType.REWARDS }
|
||||
.sumOf { it.amount }
|
||||
}
|
||||
|
||||
fun getValidatorsCount(): Int {
|
||||
return balance.items
|
||||
.filterNot { it.validatorAddress.isNullOrBlank() }
|
||||
.distinctBy { it.validatorAddress }
|
||||
.size
|
||||
}
|
||||
}
|
||||
|
||||
data object Empty : YieldBalance()
|
||||
|
|
|
|||
|
|
@ -77,4 +77,9 @@ sealed class TokenScreenAnalyticsEvent(
|
|||
event = "Button - Token Trustline",
|
||||
params = mapOf("Token" to tokenSymbol, "Blockchain" to blockchain),
|
||||
)
|
||||
|
||||
data class StakingClicked(val token: String) : TokenScreenAnalyticsEvent(
|
||||
event = "Staking Clicked",
|
||||
params = mapOf("Token" to token),
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
package com.tangem.features.staking.impl.analytics
|
||||
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionType
|
||||
|
||||
internal sealed class StakingAnalyticsEvents(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
) : AnalyticsEvent(
|
||||
category = "Staking",
|
||||
event = event,
|
||||
params = params,
|
||||
) {
|
||||
|
||||
data class StakingInfoScreenOpened(
|
||||
val validatorsCount: Int,
|
||||
val token: String,
|
||||
) : StakingAnalyticsEvents(
|
||||
event = "Staking Info Screen Opened",
|
||||
params = mapOf(
|
||||
"Validators Count" to validatorsCount.toString(),
|
||||
AnalyticsParam.TOKEN_PARAM to token,
|
||||
),
|
||||
)
|
||||
|
||||
data class WhatIsStaking(val token: String) : StakingAnalyticsEvents(
|
||||
event = "Link - What Is Staking",
|
||||
params = mapOf(
|
||||
AnalyticsParam.TOKEN_PARAM to token,
|
||||
),
|
||||
)
|
||||
|
||||
data class AmountScreenOpened(val token: String) : StakingAnalyticsEvents(
|
||||
event = "Amount Screen Opened",
|
||||
params = mapOf(
|
||||
AnalyticsParam.TOKEN_PARAM to token,
|
||||
),
|
||||
)
|
||||
|
||||
data class ConfirmationScreenOpened(
|
||||
val token: String,
|
||||
val validator: String,
|
||||
val action: StakingActionType,
|
||||
) : StakingAnalyticsEvents(
|
||||
event = "Confirmation Screen Opened",
|
||||
params = mapOf(
|
||||
AnalyticsParam.TOKEN_PARAM to token,
|
||||
"Validator" to validator,
|
||||
"Action" to action.name,
|
||||
),
|
||||
)
|
||||
|
||||
data class StakeInProgressScreenOpened(
|
||||
val validator: String,
|
||||
val token: String,
|
||||
) : StakingAnalyticsEvents(
|
||||
event = "Stake In Progress Screen Opened",
|
||||
params = mapOf(
|
||||
AnalyticsParam.TOKEN_PARAM to token,
|
||||
"Validator" to validator,
|
||||
),
|
||||
)
|
||||
|
||||
data class RewardScreenOpened(val token: String) : StakingAnalyticsEvents(
|
||||
event = "Reward Screen Opened",
|
||||
params = mapOf(
|
||||
AnalyticsParam.TOKEN_PARAM to token,
|
||||
),
|
||||
)
|
||||
|
||||
data class AmountSelectCurrency(
|
||||
val token: String,
|
||||
val isAppCurrency: Boolean,
|
||||
) : StakingAnalyticsEvents(
|
||||
event = "Selected Currency",
|
||||
params = mapOf(
|
||||
AnalyticsParam.TOKEN_PARAM to token,
|
||||
AnalyticsParam.TYPE to if (isAppCurrency) "App Currency" else "Token",
|
||||
),
|
||||
)
|
||||
|
||||
data class ButtonMax(val token: String) : StakingAnalyticsEvents(
|
||||
event = "Button - Max",
|
||||
params = mapOf(
|
||||
AnalyticsParam.TOKEN_PARAM to token,
|
||||
),
|
||||
)
|
||||
|
||||
data class ButtonCancel(
|
||||
val source: StakeScreenSource,
|
||||
val token: String,
|
||||
) : StakingAnalyticsEvents(
|
||||
event = "Button - Cancel",
|
||||
params = mapOf(
|
||||
AnalyticsParam.TOKEN_PARAM to token,
|
||||
AnalyticsParam.SOURCE to source.name,
|
||||
),
|
||||
)
|
||||
|
||||
data class ButtonValidator(
|
||||
val source: StakeScreenSource,
|
||||
val token: String,
|
||||
) : StakingAnalyticsEvents(
|
||||
event = "Button - Validator",
|
||||
params = mapOf(
|
||||
AnalyticsParam.TOKEN_PARAM to token,
|
||||
AnalyticsParam.SOURCE to source.name,
|
||||
),
|
||||
)
|
||||
|
||||
data class ButtonRewards(
|
||||
val token: String,
|
||||
) : StakingAnalyticsEvents(
|
||||
event = "Button - Rewards",
|
||||
params = mapOf(
|
||||
AnalyticsParam.TOKEN_PARAM to token,
|
||||
),
|
||||
)
|
||||
|
||||
data class ButtonAction(
|
||||
val action: String,
|
||||
val token: String,
|
||||
val validator: String,
|
||||
) : StakingAnalyticsEvents(
|
||||
event = "Button - $action",
|
||||
params = mapOf(
|
||||
AnalyticsParam.TOKEN_PARAM to token,
|
||||
"Validator" to validator,
|
||||
),
|
||||
)
|
||||
|
||||
data object ButtonShare : StakingAnalyticsEvents(event = "Button - Share")
|
||||
|
||||
data object ButtonExplore : StakingAnalyticsEvents(event = "Button - Explore")
|
||||
|
||||
data class StakingError(
|
||||
val token: String,
|
||||
) : StakingAnalyticsEvents(
|
||||
event = "Errors",
|
||||
params = mapOf(AnalyticsParam.TOKEN_PARAM to token),
|
||||
)
|
||||
|
||||
data class TransactionError(
|
||||
val token: String,
|
||||
) : StakingAnalyticsEvents(
|
||||
event = "Error - Transaction Rejected",
|
||||
params = mapOf(AnalyticsParam.TOKEN_PARAM to token),
|
||||
)
|
||||
}
|
||||
|
||||
enum class StakeScreenSource {
|
||||
Info,
|
||||
Amount,
|
||||
Confirmation,
|
||||
Validators,
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
package com.tangem.features.staking.impl.analytics.utils
|
||||
|
||||
import com.tangem.common.ui.bottomsheet.permission.state.ApproveType
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionType
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.features.staking.impl.analytics.StakeScreenSource
|
||||
import com.tangem.features.staking.impl.analytics.StakingAnalyticsEvents
|
||||
import com.tangem.features.staking.impl.presentation.state.*
|
||||
|
||||
internal class StakingAnalyticSender(
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) {
|
||||
|
||||
fun initialInfoScreen(value: StakingUiState) {
|
||||
val initialInfoState = value.initialInfoState as? StakingStates.InitialInfoState.Data
|
||||
val validatorState = initialInfoState?.yieldBalance as? InnerYieldBalanceState.Data
|
||||
val validatorCount = validatorState?.balance
|
||||
?.filterNot { it.validator?.address.isNullOrBlank() }
|
||||
?.distinctBy { it.validator?.address }
|
||||
?.size ?: 0
|
||||
|
||||
analyticsEventHandler.send(
|
||||
StakingAnalyticsEvents.StakingInfoScreenOpened(
|
||||
validatorsCount = validatorCount,
|
||||
token = value.cryptoCurrencySymbol,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun confirmationScreen(value: StakingUiState) {
|
||||
val confirmationState = value.confirmationState as? StakingStates.ConfirmationState.Data
|
||||
val validatorState = confirmationState?.validatorState as? ValidatorState.Content
|
||||
val validatorName = validatorState?.chosenValidator?.name ?: return
|
||||
|
||||
if (confirmationState.innerState == InnerConfirmationStakingState.COMPLETED) return
|
||||
|
||||
analyticsEventHandler.send(
|
||||
StakingAnalyticsEvents.ConfirmationScreenOpened(
|
||||
token = value.cryptoCurrencySymbol,
|
||||
validator = validatorName,
|
||||
action = when (value.actionType) {
|
||||
StakingActionCommonType.ENTER -> StakingActionType.STAKE
|
||||
StakingActionCommonType.EXIT -> StakingActionType.UNSTAKE
|
||||
StakingActionCommonType.PENDING_REWARDS,
|
||||
StakingActionCommonType.PENDING_OTHER,
|
||||
-> confirmationState.pendingAction?.type ?: StakingActionType.UNKNOWN
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun screenCancel(value: StakingUiState) {
|
||||
analyticsEventHandler.send(
|
||||
StakingAnalyticsEvents.ButtonCancel(
|
||||
source = when (value.currentStep) {
|
||||
StakingStep.InitialInfo -> StakeScreenSource.Info
|
||||
StakingStep.Amount -> StakeScreenSource.Amount
|
||||
StakingStep.Confirmation -> StakeScreenSource.Confirmation
|
||||
StakingStep.Validators,
|
||||
StakingStep.RewardsValidators,
|
||||
-> StakeScreenSource.Validators
|
||||
},
|
||||
token = value.cryptoCurrencyName,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun sendTransactionApprovalAnalytics(tokenCryptoCurrency: CryptoCurrency) {
|
||||
analyticsEventHandler.send(
|
||||
Basic.TransactionSent(
|
||||
sentFrom = AnalyticsParam.TxSentFrom.Approve(
|
||||
blockchain = tokenCryptoCurrency.network.name,
|
||||
token = tokenCryptoCurrency.symbol,
|
||||
feeType = AnalyticsParam.FeeType.Normal,
|
||||
permissionType = ApproveType.LIMITED.name,
|
||||
),
|
||||
memoType = Basic.TransactionSent.MemoType.Null,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun sendTransactionStakingAnalytics(cryptoCurrency: CryptoCurrency, validator: String) {
|
||||
analyticsEventHandler.send(
|
||||
Basic.TransactionSent(
|
||||
sentFrom = AnalyticsParam.TxSentFrom.Staking(
|
||||
blockchain = cryptoCurrency.network.name,
|
||||
token = cryptoCurrency.symbol,
|
||||
feeType = AnalyticsParam.FeeType.Normal,
|
||||
),
|
||||
memoType = Basic.TransactionSent.MemoType.Null,
|
||||
),
|
||||
)
|
||||
analyticsEventHandler.send(
|
||||
StakingAnalyticsEvents.StakeInProgressScreenOpened(
|
||||
validator = validator,
|
||||
token = cryptoCurrency.symbol,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -54,6 +54,7 @@ internal class StakingFragment : ComposeFragment() {
|
|||
StakingStateRouter(
|
||||
appRouter = appRouter,
|
||||
stateController = stateController,
|
||||
analyticsEventsHandler = analyticsEventsHandler,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ internal class StakingStateController @Inject constructor(
|
|||
clickIntents = StakingClickIntentsStub,
|
||||
walletName = "",
|
||||
cryptoCurrencyName = "",
|
||||
cryptoCurrencySymbol = "",
|
||||
currentStep = StakingStep.InitialInfo,
|
||||
initialInfoState = StakingStates.InitialInfoState.Empty(),
|
||||
amountState = AmountState.Empty(),
|
||||
|
|
|
|||
|
|
@ -1,14 +1,21 @@
|
|||
package com.tangem.features.staking.impl.presentation.state
|
||||
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
||||
import com.tangem.features.staking.impl.analytics.StakingAnalyticsEvents
|
||||
import com.tangem.features.staking.impl.analytics.utils.StakingAnalyticSender
|
||||
|
||||
internal class StakingStateRouter(
|
||||
private val appRouter: AppRouter,
|
||||
private val stateController: StakingStateController,
|
||||
private val analyticsEventsHandler: AnalyticsEventHandler,
|
||||
) {
|
||||
|
||||
private val analyticSender = StakingAnalyticSender(analyticsEventsHandler)
|
||||
|
||||
fun onBackClick() {
|
||||
analyticSender.screenCancel(stateController.value)
|
||||
appRouter.pop()
|
||||
stateController.clear()
|
||||
}
|
||||
|
|
@ -48,14 +55,21 @@ internal class StakingStateRouter(
|
|||
}
|
||||
|
||||
private fun showInitial() {
|
||||
analyticSender.initialInfoScreen(stateController.value)
|
||||
stateController.update { it.copy(currentStep = StakingStep.InitialInfo) }
|
||||
}
|
||||
|
||||
fun showRewardsValidators() {
|
||||
private fun showRewardsValidators() {
|
||||
analyticsEventsHandler.send(
|
||||
StakingAnalyticsEvents.RewardScreenOpened(stateController.value.cryptoCurrencySymbol),
|
||||
)
|
||||
stateController.update { it.copy(currentStep = StakingStep.RewardsValidators) }
|
||||
}
|
||||
|
||||
fun showAmount() {
|
||||
private fun showAmount() {
|
||||
analyticsEventsHandler.send(
|
||||
StakingAnalyticsEvents.AmountScreenOpened(stateController.value.cryptoCurrencySymbol),
|
||||
)
|
||||
stateController.update { it.copy(currentStep = StakingStep.Amount) }
|
||||
}
|
||||
|
||||
|
|
@ -63,7 +77,8 @@ internal class StakingStateRouter(
|
|||
stateController.update { it.copy(currentStep = StakingStep.Validators) }
|
||||
}
|
||||
|
||||
fun showConfirmation() {
|
||||
private fun showConfirmation() {
|
||||
analyticSender.confirmationScreen(stateController.value)
|
||||
stateController.update { it.copy(currentStep = StakingStep.Confirmation) }
|
||||
}
|
||||
}
|
||||
|
|
@ -27,6 +27,7 @@ internal data class StakingUiState(
|
|||
val clickIntents: StakingClickIntents,
|
||||
val walletName: String,
|
||||
val cryptoCurrencyName: String,
|
||||
val cryptoCurrencySymbol: String,
|
||||
val currentStep: StakingStep,
|
||||
val initialInfoState: StakingStates.InitialInfoState,
|
||||
val amountState: AmountState,
|
||||
|
|
|
|||
|
|
@ -69,4 +69,6 @@ internal object StakingClickIntentsStub : StakingClickIntents {
|
|||
override fun onNotificationCancel(notification: Class<out NotificationUM>) {}
|
||||
|
||||
override fun openTokenDetails(cryptoCurrency: CryptoCurrency) {}
|
||||
|
||||
override fun onActiveStakeAnalytic() {}
|
||||
}
|
||||
|
|
@ -56,16 +56,19 @@ internal class SetInitialDataStateTransformer(
|
|||
}
|
||||
|
||||
private val yieldBalancesConverter by lazy(LazyThreadSafetyMode.NONE) {
|
||||
YieldBalancesConverter(cryptoCurrencyStatusProvider, appCurrencyProvider, yield)
|
||||
YieldBalancesConverter(
|
||||
cryptoCurrencyStatusProvider,
|
||||
appCurrencyProvider,
|
||||
yield,
|
||||
)
|
||||
}
|
||||
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
val cryptoCurrencyName = cryptoCurrencyStatusProvider().currency.name
|
||||
|
||||
val cryptoCurrency = cryptoCurrencyStatusProvider().currency
|
||||
return prevState.copy(
|
||||
title = TextReference.EMPTY,
|
||||
walletName = userWalletProvider.invoke().name,
|
||||
cryptoCurrencyName = cryptoCurrencyName,
|
||||
cryptoCurrencyName = cryptoCurrency.name,
|
||||
cryptoCurrencySymbol = cryptoCurrency.symbol,
|
||||
clickIntents = clickIntents,
|
||||
currentStep = StakingStep.InitialInfo,
|
||||
initialInfoState = createInitialInfoState(),
|
||||
|
|
|
|||
|
|
@ -173,8 +173,9 @@ private fun LazyListScope.activeStakingBlock(
|
|||
) { balance ->
|
||||
ActiveStakingBlock(
|
||||
balance = balance,
|
||||
onClick = clickIntents::onActiveStake,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
onClick = clickIntents::onActiveStake,
|
||||
onAnalytic = clickIntents::onActiveStakeAnalytic,
|
||||
modifier = Modifier
|
||||
.animateItemPlacement()
|
||||
.then(
|
||||
|
|
@ -268,8 +269,9 @@ private fun StakingRewardBlock(
|
|||
@Composable
|
||||
private fun ActiveStakingBlock(
|
||||
balance: BalanceState,
|
||||
onClick: (BalanceState) -> Unit,
|
||||
isBalanceHidden: Boolean,
|
||||
onClick: (BalanceState) -> Unit,
|
||||
onAnalytic: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val (icon, iconTint) = balance.type.getIcon()
|
||||
|
|
@ -287,7 +289,10 @@ private fun ActiveStakingBlock(
|
|||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = rememberRipple(),
|
||||
enabled = balance.isClickable,
|
||||
onClick = { onClick(balance) },
|
||||
onClick = {
|
||||
onAnalytic()
|
||||
onClick(balance)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ internal interface StakingClickIntents : AmountScreenClickIntents {
|
|||
|
||||
fun onActiveStake(activeStake: BalanceState)
|
||||
|
||||
fun onActiveStakeAnalytic()
|
||||
|
||||
fun showApprovalBottomSheet()
|
||||
|
||||
fun onApprovalClick()
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import com.tangem.common.routing.bundle.unbundle
|
|||
import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.ui.haptic.TangemHapticEffect
|
||||
import com.tangem.core.ui.haptic.VibratorHapticManager
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
|
|
@ -29,7 +30,9 @@ import com.tangem.domain.staking.*
|
|||
import com.tangem.domain.staking.model.StakingApproval
|
||||
import com.tangem.domain.staking.model.stakekit.PendingAction
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionType
|
||||
import com.tangem.domain.staking.model.stakekit.transaction.ActionParams
|
||||
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction
|
||||
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransactionType
|
||||
|
|
@ -45,6 +48,9 @@ import com.tangem.domain.utils.convertToSdkAmount
|
|||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.features.staking.impl.analytics.StakeScreenSource
|
||||
import com.tangem.features.staking.impl.analytics.StakingAnalyticsEvents
|
||||
import com.tangem.features.staking.impl.analytics.utils.StakingAnalyticSender
|
||||
import com.tangem.features.staking.impl.navigation.InnerStakingRouter
|
||||
import com.tangem.features.staking.impl.presentation.state.*
|
||||
import com.tangem.features.staking.impl.presentation.state.bottomsheet.InfoType
|
||||
|
|
@ -106,6 +112,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
private val getCurrencyCheckUseCase: GetCurrencyCheckUseCase,
|
||||
private val isAmountSubtractAvailableUseCase: IsAmountSubtractAvailableUseCase,
|
||||
private val isAnyTokenStakedUseCase: IsAnyTokenStakedUseCase,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
@DelayedWork private val coroutineScope: CoroutineScope,
|
||||
savedStateHandle: SavedStateHandle,
|
||||
) : ViewModel(), DefaultLifecycleObserver, StakingClickIntents {
|
||||
|
|
@ -135,6 +142,8 @@ internal class StakingViewModel @Inject constructor(
|
|||
private var userWallet: UserWallet by Delegates.notNull()
|
||||
private var appCurrency: AppCurrency by Delegates.notNull()
|
||||
|
||||
private var isInitialInfoAnalyticSent: Boolean = false
|
||||
|
||||
private val stakingEventFactory: StakingEventFactory
|
||||
get() = StakingEventFactory(
|
||||
stateController = stateController,
|
||||
|
|
@ -142,6 +151,10 @@ internal class StakingViewModel @Inject constructor(
|
|||
onFailedTxEmailClick = ::onFailedTxEmailClick,
|
||||
)
|
||||
|
||||
private val stakingAnalyticSender = StakingAnalyticSender(
|
||||
analyticsEventHandler = analyticsEventHandler,
|
||||
)
|
||||
|
||||
private var stakingApproval: StakingApproval = StakingApproval.Empty
|
||||
private var isAmountSubtractAvailable: Boolean = false
|
||||
private val allowanceTaskScheduler = SingleTaskScheduler<BigDecimal>()
|
||||
|
|
@ -270,6 +283,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
),
|
||||
).getOrElse {
|
||||
Timber.e(it.toString())
|
||||
analyticsEventHandler.send(StakingAnalyticsEvents.StakingError(value.cryptoCurrencyName))
|
||||
stakingEventFactory.createStakingErrorAlert(it)
|
||||
stateController.update(SetConfirmationStateResetAssentTransformer)
|
||||
return@launch
|
||||
|
|
@ -284,6 +298,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
transactionId = transaction.id,
|
||||
).getOrElse {
|
||||
Timber.e(it.toString())
|
||||
analyticsEventHandler.send(StakingAnalyticsEvents.StakingError(value.cryptoCurrencyName))
|
||||
stakingEventFactory.createStakingErrorAlert(it)
|
||||
stateController.update(SetConfirmationStateResetAssentTransformer)
|
||||
return@launch
|
||||
|
|
@ -301,6 +316,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
fullTransactionsData = fullTransactionsData,
|
||||
fee = fee,
|
||||
pendingAction = confirmationState.pendingAction,
|
||||
validator = validatorState.chosenValidator.name,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -326,6 +342,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
type = pendingAction?.type,
|
||||
),
|
||||
).getOrElse {
|
||||
analyticsEventHandler.send(StakingAnalyticsEvents.StakingError(value.cryptoCurrencyName))
|
||||
stateController.update(AddStakingErrorTransformer())
|
||||
updateNotifications(GetFeeError.UnknownError)
|
||||
return
|
||||
|
|
@ -365,6 +382,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
updateNotifications()
|
||||
},
|
||||
ifLeft = {
|
||||
analyticsEventHandler.send(StakingAnalyticsEvents.TransactionError(value.cryptoCurrencyName))
|
||||
stateController.update(AddStakingErrorTransformer())
|
||||
updateNotifications(it)
|
||||
},
|
||||
|
|
@ -389,6 +407,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onInitialInfoBannerClick() {
|
||||
analyticsEventHandler.send(StakingAnalyticsEvents.WhatIsStaking(cryptoCurrencyStatus.currency.symbol))
|
||||
innerRouter.openUrl(WHAT_IS_STAKING_ARTICLE_URL)
|
||||
}
|
||||
|
||||
|
|
@ -409,26 +428,47 @@ internal class StakingViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onMaxValueClick() {
|
||||
analyticsEventHandler.send(StakingAnalyticsEvents.ButtonMax(cryptoCurrencyStatus.currency.symbol))
|
||||
stateController.update(AmountMaxValueStateTransformer(cryptoCurrencyStatus, yield))
|
||||
}
|
||||
|
||||
override fun onCurrencyChangeClick(isFiat: Boolean) {
|
||||
analyticsEventHandler.send(
|
||||
StakingAnalyticsEvents.AmountSelectCurrency(cryptoCurrencyStatus.currency.symbol, isFiat),
|
||||
)
|
||||
stateController.update(AmountCurrencyChangeStateTransformer(cryptoCurrencyStatus, isFiat))
|
||||
}
|
||||
|
||||
override fun openValidators() = stakingStateRouter.showValidators()
|
||||
override fun openValidators() {
|
||||
analyticsEventHandler.send(
|
||||
StakingAnalyticsEvents.ButtonValidator(
|
||||
source = StakeScreenSource.Confirmation,
|
||||
token = cryptoCurrencyStatus.currency.symbol,
|
||||
),
|
||||
)
|
||||
stakingStateRouter.showValidators()
|
||||
}
|
||||
|
||||
override fun onValidatorSelect(validator: Yield.Validator) {
|
||||
stateController.update(ValidatorSelectChangeTransformer(validator))
|
||||
}
|
||||
|
||||
override fun openRewardsValidators() {
|
||||
analyticsEventHandler.send(
|
||||
StakingAnalyticsEvents.ButtonRewards(value.cryptoCurrencyName),
|
||||
)
|
||||
val rewardsValidators =
|
||||
stateController.value.rewardsValidatorsState as? StakingStates.RewardsValidatorsState.Data
|
||||
val rewards = rewardsValidators?.rewards
|
||||
if (rewards != null && rewards.isSingleItem()) {
|
||||
onActiveStake(rewards.first())
|
||||
} else {
|
||||
analyticsEventHandler.send(
|
||||
StakingAnalyticsEvents.ButtonValidator(
|
||||
source = StakeScreenSource.Info,
|
||||
token = cryptoCurrencyStatus.currency.symbol,
|
||||
),
|
||||
)
|
||||
onNextClick(actionTypeToOverwrite = StakingActionCommonType.PENDING_REWARDS)
|
||||
}
|
||||
}
|
||||
|
|
@ -463,6 +503,15 @@ internal class StakingViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onActiveStakeAnalytic() {
|
||||
analyticsEventHandler.send(
|
||||
StakingAnalyticsEvents.ButtonValidator(
|
||||
source = StakeScreenSource.Info,
|
||||
token = cryptoCurrencyStatus.currency.symbol,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
override fun showApprovalBottomSheet() {
|
||||
stateController.update(
|
||||
ShowApprovalBottomSheetTransformer(
|
||||
|
|
@ -503,6 +552,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
).fold(
|
||||
ifLeft = { error ->
|
||||
Timber.e(error.toString())
|
||||
analyticsEventHandler.send(StakingAnalyticsEvents.TransactionError(value.cryptoCurrencyName))
|
||||
stateController.update(
|
||||
SetConfirmationStateAssentApprovalTransformer(
|
||||
appCurrencyProvider = Provider { appCurrency },
|
||||
|
|
@ -524,6 +574,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
).fold(
|
||||
ifLeft = { error ->
|
||||
Timber.e(error.toString())
|
||||
analyticsEventHandler.send(StakingAnalyticsEvents.TransactionError(value.cryptoCurrencyName))
|
||||
stateController.update(
|
||||
SetConfirmationStateAssentApprovalTransformer(
|
||||
appCurrencyProvider = Provider { appCurrency },
|
||||
|
|
@ -535,6 +586,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
stateController.update(SetConfirmationStateResetAssentTransformer)
|
||||
},
|
||||
ifRight = {
|
||||
stakingAnalyticSender.sendTransactionApprovalAnalytics(tokenCryptoCurrency)
|
||||
stateController.update(SetApprovalInProgressTransformer)
|
||||
stateController.update(DismissBottomSheetStateTransformer())
|
||||
awaitForAllowance(confirmationState.pendingAction)
|
||||
|
|
@ -652,10 +704,10 @@ internal class StakingViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onExploreClick() {
|
||||
analyticsEventHandler.send(StakingAnalyticsEvents.ButtonExplore)
|
||||
val confirmationDataState = uiState.value.confirmationState as? StakingStates.ConfirmationState.Data
|
||||
val transactionDoneState = confirmationDataState?.transactionDoneState as? TransactionDoneState.Content
|
||||
val txUrl = transactionDoneState?.txUrl
|
||||
|
||||
if (txUrl != null) {
|
||||
innerRouter.openUrl(txUrl)
|
||||
}
|
||||
|
|
@ -666,12 +718,11 @@ internal class StakingViewModel @Inject constructor(
|
|||
val transactionDoneState = confirmationDataState?.transactionDoneState as? TransactionDoneState.Content
|
||||
val txUrl = transactionDoneState?.txUrl
|
||||
|
||||
analyticsEventHandler.send(StakingAnalyticsEvents.ButtonShare)
|
||||
if (txUrl != null) {
|
||||
vibratorHapticManager.performOneTime(TangemHapticEffect.OneTime.Click)
|
||||
stakingEventFactory.createShareDialog(txUrl = txUrl)
|
||||
}
|
||||
|
||||
// TODO staking [REDACTED_TASK_KEY]
|
||||
}
|
||||
|
||||
override fun onFailedTxEmailClick(errorMessage: String) {
|
||||
|
|
@ -742,6 +793,17 @@ internal class StakingViewModel @Inject constructor(
|
|||
.onEach { maybeStatus ->
|
||||
maybeStatus.fold(
|
||||
ifRight = { status ->
|
||||
if (!isInitialInfoAnalyticSent) {
|
||||
isInitialInfoAnalyticSent = true
|
||||
val balances = status.value.yieldBalance as? YieldBalance.Data
|
||||
analyticsEventHandler.send(
|
||||
StakingAnalyticsEvents.StakingInfoScreenOpened(
|
||||
validatorsCount = balances?.getValidatorsCount() ?: 0,
|
||||
token = status.currency.symbol,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
feeCryptoCurrencyStatus =
|
||||
getFeePaidCryptoCurrencyStatusSyncUseCase(userWalletId, status).getOrNull()
|
||||
cryptoCurrencyStatus = status
|
||||
|
|
@ -800,14 +862,33 @@ internal class StakingViewModel @Inject constructor(
|
|||
fullTransactionsData: List<FullTransactionData>,
|
||||
fee: Fee,
|
||||
pendingAction: PendingAction?,
|
||||
validator: String,
|
||||
) {
|
||||
val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||
val action = when (value.actionType) {
|
||||
StakingActionCommonType.ENTER -> StakingActionType.STAKE
|
||||
StakingActionCommonType.EXIT -> StakingActionType.UNSTAKE
|
||||
StakingActionCommonType.PENDING_REWARDS,
|
||||
StakingActionCommonType.PENDING_OTHER,
|
||||
-> pendingAction?.type
|
||||
}
|
||||
action?.let {
|
||||
analyticsEventHandler.send(
|
||||
StakingAnalyticsEvents.ButtonAction(
|
||||
action = it.name,
|
||||
token = value.cryptoCurrencyName,
|
||||
validator = validator,
|
||||
),
|
||||
)
|
||||
}
|
||||
sendMultipleTransactionUseCase(
|
||||
txsData = fullTransactionsData.map { it.tangemTransaction },
|
||||
userWallet = userWallet,
|
||||
network = cryptoCurrencyStatus.currency.network,
|
||||
network = cryptoCurrency.network,
|
||||
).fold(
|
||||
ifLeft = { error ->
|
||||
Timber.e(error.toString())
|
||||
analyticsEventHandler.send(StakingAnalyticsEvents.StakingError(value.cryptoCurrencyName))
|
||||
stateController.update(
|
||||
SetConfirmationStateAssentTransformer(
|
||||
appCurrencyProvider = Provider { appCurrency },
|
||||
|
|
@ -820,6 +901,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
stateController.update(SetConfirmationStateResetAssentTransformer)
|
||||
},
|
||||
ifRight = { transactionHashes ->
|
||||
stakingAnalyticSender.sendTransactionStakingAnalytics(cryptoCurrency, validator)
|
||||
transactionsInProgress.clear()
|
||||
submitHash(
|
||||
transactionIds = fullTransactionsData.map { it.stakeKitTransaction.id },
|
||||
|
|
@ -828,9 +910,8 @@ internal class StakingViewModel @Inject constructor(
|
|||
scheduleUpdates()
|
||||
val txUrl = getExplorerTransactionUrlUseCase(
|
||||
txHash = transactionHashes.last(),
|
||||
networkId = cryptoCurrencyStatus.currency.network.id,
|
||||
networkId = cryptoCurrency.network.id,
|
||||
).getOrElse { "" }
|
||||
|
||||
stateController.update(
|
||||
SetConfirmationStateCompletedTransformer(
|
||||
appCurrencyProvider = Provider { appCurrency },
|
||||
|
|
@ -852,6 +933,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
transactionHash = transactionHash,
|
||||
)
|
||||
.onLeft {
|
||||
analyticsEventHandler.send(StakingAnalyticsEvents.StakingError(value.cryptoCurrencyName))
|
||||
saveUnsubmittedHashUseCase.invoke(
|
||||
transactionId = transactionId,
|
||||
transactionHash = transactionHash,
|
||||
|
|
|
|||
|
|
@ -466,6 +466,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onStakeBannerClick() {
|
||||
analyticsEventsHandler.send(TokenScreenAnalyticsEvent.StakingClicked(cryptoCurrency.symbol))
|
||||
openStaking()
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue