Updated on 2026-08-14

This commit is contained in:
Tangem 2024-02-08 18:55:24 +03:00
parent b6585c69a9
commit 850bc92749
11 changed files with 336 additions and 29 deletions

View file

@ -125,6 +125,7 @@ sealed class AnalyticsParam {
const val SOURCE = "Source"
const val BALANCE = "Balance"
const val BATCH = "Batch"
const val TYPE = "Type"
const val FEE_TYPE = "Fee Type"
const val PERMISSION_TYPE = "Permission Type"
const val PRODUCT_TYPE = "Product Type"

View file

@ -45,6 +45,8 @@ dependencies {
implementation(projects.core.ui)
implementation(projects.core.utils)
implementation(projects.core.navigation)
implementation(projects.core.analytics)
implementation(projects.core.analytics.models)
/** Common */
implementation(projects.common)

View file

@ -8,6 +8,7 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import arrow.core.getOrElse
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.ui.components.SystemBarsEffect
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.screen.ComposeFragment
@ -41,6 +42,9 @@ internal class SendFragment : ComposeFragment() {
@Inject
lateinit var listenToQrScanningUseCase: ListenToQrScanningUseCase
@Inject
lateinit var analyticsEventsHandler: AnalyticsEventHandler
private val viewModel by viewModels<SendViewModel>()
private val innerSendRouter: InnerSendRouter
get() = requireNotNull(router as? InnerSendRouter) {
@ -57,6 +61,7 @@ internal class SendFragment : ComposeFragment() {
StateRouter(
fragmentManager = WeakReference(parentFragmentManager),
isEditingDisabled = isEditingDisabled,
analyticsEventsHandler = analyticsEventsHandler,
),
)
listenToQrCode()

View file

@ -0,0 +1,136 @@
package com.tangem.features.send.impl.presentation.analytics
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsParam.Key.SOURCE
import com.tangem.core.analytics.models.AnalyticsParam.Key.TYPE
import com.tangem.core.analytics.models.AnalyticsParam.Key.VALIDATION
/**
* Send screen analytics
*/
internal sealed class SendAnalyticEvents(
event: String,
params: Map<String, String> = mapOf(),
) : AnalyticsEvent(category = "Token / Send", event = event, params = params) {
/** Send screen opened */
object SendOpened : SendAnalyticEvents(event = "Send Screen Opened")
/** Next button clicked */
data class NextButtonClicked(val source: SendScreenSource) : SendAnalyticEvents(
event = "Button - Next",
params = mapOf(SOURCE to source.name),
)
/** Back button clicked */
data class BackButtonClicked(val source: SendScreenSource) : SendAnalyticEvents(
event = "Button - Back",
params = mapOf(SOURCE to source.name),
)
// region Address
/** Recipient address screen opened */
object AddressScreenOpened : SendAnalyticEvents(event = "Address Screen Opened")
/** Address to send entered */
data class AddressEntered(val source: EnterAddressSource, val isValid: Boolean) : SendAnalyticEvents(
event = "Address Entered",
params = mapOf(
SOURCE to source.name,
VALIDATION to if (isValid) "Success" else "Fail",
),
)
/** Paste from clipboard button clicked */
data class PasteButtonClicked(val type: PasteType) : SendAnalyticEvents(
event = "Button - Paste",
params = mapOf(
TYPE to type.name,
),
)
/** Qr Code button clicked */
object QrCodeButtonClicked : SendAnalyticEvents(event = "Button - QR Code")
// endregion
// region Amount
/** Amount screen opened */
object AmountScreenOpened : SendAnalyticEvents(event = "Amount Screen Opened")
/** Selected currency */
data class SelectedCurrency(val type: SelectedCurrencyType) : SendAnalyticEvents(
event = "Selected Currency",
params = mapOf(TYPE to type.value),
)
/** Currency selector button clicked */
object SwapCurrencyButtonClicked : SendAnalyticEvents(event = "Button - Swap Currency")
// endregion
// region Fee
/** Fee screen opened */
object FeeScreenOpened : SendAnalyticEvents(event = "Fee Screen Opened")
/** Selected fee (send after next screen opened) */
data class SelectedFee(val fee: String) : SendAnalyticEvents(
event = "Fee Selected",
params = mapOf("Commission" to fee),
)
/** Custom fee selected */
object CustomFeeButtonClicked : SendAnalyticEvents(event = "Custom Fee Clicked")
/** Custom fee edited */
object GasPriceInserter : SendAnalyticEvents(event = "Gas Price Inserted")
/** Subtract from amount selector switched (send after next screen opened) */
object SubtractFromAmount : SendAnalyticEvents(event = "Subtract from Amount")
// endregion
// region Confirmation
/** Confirmation screen opened */
object ConfirmationScreenOpened : SendAnalyticEvents(event = "Confirm Screen Opened")
/** Send transaction button clicked */
object SendButtonClicked : SendAnalyticEvents(event = "Button - Send")
/** Screen reopened from confirmation screen */
data class ScreenReopened(val source: SendScreenSource) : SendAnalyticEvents(
event = "Screen Reopened",
params = mapOf(SOURCE to source.name),
)
// endregion
// region Transaction Result
/** Transaction send screen opened */
object TransactionScreenOpened : SendAnalyticEvents(event = "Transaction Sent Screen Opened")
/** Share button clicked */
object ShareButtonClicked : SendAnalyticEvents(event = "Button - Share")
/** Expore button clicked */
object ExploreButtonClicked : SendAnalyticEvents(event = "Button - Explore")
// endregion
}
internal enum class SendScreenSource {
Address,
Amount,
Fee,
}
internal enum class EnterAddressSource {
QRCode,
PasteButton,
RecentAddress,
}
internal enum class PasteType {
Address,
Memo,
}
internal enum class SelectedCurrencyType(val value: String) {
Token("Token"),
AppCurrency("App Currency"),
}

View file

@ -0,0 +1,44 @@
package com.tangem.features.send.impl.presentation.analytics.utils
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.features.send.impl.presentation.analytics.SelectedCurrencyType
import com.tangem.features.send.impl.presentation.analytics.SendAnalyticEvents
import com.tangem.features.send.impl.presentation.state.SendUiState
import com.tangem.features.send.impl.presentation.state.SendUiStateType
import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState
import com.tangem.features.send.impl.presentation.state.fee.FeeType
internal class SendOnNextScreenAnalyticSender(
private val analyticsEventHandler: AnalyticsEventHandler,
) {
fun send(prevScreen: SendUiStateType, state: SendUiState) {
when (prevScreen) {
SendUiStateType.Fee -> {
val feeState = state.feeState ?: return
val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content
feeSelectorState?.selectedFee?.let { selectedFee ->
val isCustomFeeEdited = feeState.fee?.amount?.value != feeSelectorState.fees.normal.amount.value
if (selectedFee == FeeType.Custom && isCustomFeeEdited) {
analyticsEventHandler.send(SendAnalyticEvents.GasPriceInserter)
}
analyticsEventHandler.send(SendAnalyticEvents.SelectedFee(selectedFee.name))
}
if (feeState.isSubtract) {
analyticsEventHandler.send(SendAnalyticEvents.SubtractFromAmount)
}
}
SendUiStateType.Amount -> {
val isFiatSelected = state.amountState?.amountTextField?.isFiatValue ?: return
val selectedCurrency = if (isFiatSelected) {
SelectedCurrencyType.Token
} else {
SelectedCurrencyType.AppCurrency
}
analyticsEventHandler.send(
SendAnalyticEvents.SelectedCurrency(selectedCurrency),
)
}
else -> Unit
}
}
}

View file

@ -0,0 +1,26 @@
package com.tangem.features.send.impl.presentation.analytics.utils
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.features.send.impl.presentation.analytics.EnterAddressSource
import com.tangem.features.send.impl.presentation.analytics.PasteType
import com.tangem.features.send.impl.presentation.analytics.SendAnalyticEvents
internal class SendRecipientAnalyticsSender(
private val analyticsEventHandler: AnalyticsEventHandler,
) {
fun sendAddressAnalytics(type: EnterAddressSource?, isValidAddress: Boolean) {
type?.let {
if (type == EnterAddressSource.PasteButton) {
analyticsEventHandler.send(SendAnalyticEvents.PasteButtonClicked(PasteType.Address))
}
analyticsEventHandler.send(SendAnalyticEvents.AddressEntered(it, isValidAddress))
}
}
fun sendMemoAnalytics(isPasted: Boolean) {
if (isPasted) {
analyticsEventHandler.send(SendAnalyticEvents.PasteButtonClicked(PasteType.Memo))
}
}
}

View file

@ -1,6 +1,9 @@
package com.tangem.features.send.impl.presentation.state
import androidx.fragment.app.FragmentManager
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.features.send.impl.presentation.analytics.SendAnalyticEvents
import com.tangem.features.send.impl.presentation.analytics.SendScreenSource
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
@ -8,6 +11,7 @@ import java.lang.ref.WeakReference
internal class StateRouter(
private val fragmentManager: WeakReference<FragmentManager>,
private val analyticsEventsHandler: AnalyticsEventHandler,
private val isEditingDisabled: Boolean,
) {
private var mutableCurrentState: MutableStateFlow<SendUiStateType> = MutableStateFlow(
@ -28,26 +32,51 @@ internal class StateRouter(
when {
isSuccess -> popBackStack()
isEditingDisabled -> when (currentState.value) {
SendUiStateType.Send -> showFee()
SendUiStateType.Send -> {
analyticsEventsHandler.send(SendAnalyticEvents.BackButtonClicked(SendScreenSource.Fee))
showFee()
}
else -> popBackStack()
}
else -> when (currentState.value) {
SendUiStateType.Amount -> showRecipient()
SendUiStateType.Fee -> showAmount()
SendUiStateType.Send -> showFee()
SendUiStateType.Amount -> {
analyticsEventsHandler.send(SendAnalyticEvents.BackButtonClicked(SendScreenSource.Address))
showRecipient()
}
SendUiStateType.Fee -> {
analyticsEventsHandler.send(SendAnalyticEvents.BackButtonClicked(SendScreenSource.Amount))
showAmount()
}
SendUiStateType.Send -> {
analyticsEventsHandler.send(SendAnalyticEvents.BackButtonClicked(SendScreenSource.Fee))
showFee()
}
else -> popBackStack()
}
}
}
fun onNextClick() {
fun onNextClick(): SendUiStateType {
val prevState = currentState.value
when (currentState.value) {
SendUiStateType.Recipient -> showAmount()
SendUiStateType.Amount -> showFee()
SendUiStateType.Fee -> showSend()
SendUiStateType.Send -> onBackClick()
SendUiStateType.Recipient -> {
analyticsEventsHandler.send(SendAnalyticEvents.NextButtonClicked(SendScreenSource.Amount))
showAmount()
}
SendUiStateType.Amount -> {
analyticsEventsHandler.send(SendAnalyticEvents.NextButtonClicked(SendScreenSource.Fee))
showFee()
}
SendUiStateType.Fee -> {
analyticsEventsHandler.send(SendAnalyticEvents.NextButtonClicked(SendScreenSource.Fee))
showSend()
}
SendUiStateType.Send -> {
onBackClick()
}
else -> popBackStack()
}
return prevState
}
fun onPrevClick() {
@ -55,26 +84,36 @@ internal class StateRouter(
popBackStack()
} else {
when (currentState.value) {
SendUiStateType.Amount -> showRecipient()
SendUiStateType.Fee -> showAmount()
SendUiStateType.Amount -> {
analyticsEventsHandler.send(SendAnalyticEvents.BackButtonClicked(SendScreenSource.Amount))
showRecipient()
}
SendUiStateType.Fee -> {
analyticsEventsHandler.send(SendAnalyticEvents.BackButtonClicked(SendScreenSource.Fee))
showAmount()
}
else -> popBackStack()
}
}
}
fun showAmount() {
analyticsEventsHandler.send(SendAnalyticEvents.AmountScreenOpened)
mutableCurrentState.update { SendUiStateType.Amount }
}
fun showRecipient() {
analyticsEventsHandler.send(SendAnalyticEvents.AddressScreenOpened)
mutableCurrentState.update { SendUiStateType.Recipient }
}
fun showFee() {
analyticsEventsHandler.send(SendAnalyticEvents.FeeScreenOpened)
mutableCurrentState.update { SendUiStateType.Fee }
}
private fun showSend() {
analyticsEventsHandler.send(SendAnalyticEvents.ConfirmationScreenOpened)
mutableCurrentState.update { SendUiStateType.Send }
}
}

View file

@ -110,6 +110,7 @@ private fun SendPrimaryNavigationButton(uiState: SendUiState, modifier: Modifier
textRes = textId,
txUrl = txUrl,
onExploreClick = { uiState.clickIntents.onExploreClick(txUrl) },
onShareClick = uiState.clickIntents::onShareClick,
onDoneClick = buttonClick,
modifier = Modifier,
)
@ -130,6 +131,7 @@ private fun PrimaryButtonsDone(
@StringRes textRes: Int,
txUrl: String,
onExploreClick: () -> Unit,
onShareClick: () -> Unit,
onDoneClick: () -> Unit,
modifier: Modifier = Modifier,
) {
@ -152,6 +154,7 @@ private fun PrimaryButtonsDone(
onClick = {
hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
context.shareText(txUrl)
onShareClick()
},
modifier = Modifier.weight(1f),
)

View file

@ -24,6 +24,7 @@ import com.tangem.core.ui.components.inputrow.InputRowRecipient
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.send.impl.R
import com.tangem.features.send.impl.presentation.analytics.EnterAddressSource
import com.tangem.features.send.impl.presentation.domain.SendRecipientListContent
import com.tangem.features.send.impl.presentation.state.SendStates
import com.tangem.features.send.impl.presentation.ui.common.FooterContainer
@ -58,7 +59,7 @@ internal fun SendRecipientContent(
title = address.label,
placeholder = address.placeholder,
onValueChange = address.onValueChange,
onPasteClick = clickIntents::onRecipientAddressValueChange,
onPasteClick = { clickIntents.onRecipientAddressValueChange(it, EnterAddressSource.PasteButton) },
isError = isError,
isLoading = isValidating,
error = address.error,
@ -80,7 +81,7 @@ internal fun SendRecipientContent(
placeholder = placeholder,
footer = stringResource(R.string.send_recipient_memo_footer),
onValueChange = memoField.onValueChange,
onPasteClick = clickIntents::onRecipientMemoValueChange,
onPasteClick = { clickIntents.onRecipientMemoValueChange(it, isPasted = true) },
modifier = Modifier.padding(top = TangemTheme.dimens.spacing20),
isError = memoField.isError,
error = memoField.error,
@ -165,7 +166,9 @@ private fun LazyListScope.recipientListItem(
},
)
.background(TangemTheme.colors.background.action),
onClick = { clickIntents.onRecipientAddressValueChange(title) },
onClick = {
clickIntents.onRecipientAddressValueChange(title, EnterAddressSource.RecentAddress)
},
)
}
}
@ -203,7 +206,7 @@ private fun RecipientWalletListItem(
ListItemWithIcon(
title = wallet.title.resolveReference(),
subtitle = wallet.subtitle.resolveReference(),
onClick = { clickIntents.onRecipientAddressValueChange(title) },
onClick = { clickIntents.onRecipientAddressValueChange(title, EnterAddressSource.RecentAddress) },
)
}
if (!item.isWalletsOnly) {

View file

@ -2,10 +2,11 @@ package com.tangem.features.send.impl.presentation.viewmodel
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.features.send.impl.presentation.analytics.EnterAddressSource
import com.tangem.features.send.impl.presentation.state.fee.FeeType
@Suppress("TooManyFunctions")
interface SendClickIntents {
internal interface SendClickIntents {
fun popBackStack()
@ -30,9 +31,9 @@ interface SendClickIntents {
// endregion
// region Recipient
fun onRecipientAddressValueChange(value: String)
fun onRecipientAddressValueChange(value: String, type: EnterAddressSource? = null)
fun onRecipientMemoValueChange(value: String)
fun onRecipientMemoValueChange(value: String, isPasted: Boolean = false)
// endregion
// region Fee
@ -56,6 +57,8 @@ interface SendClickIntents {
fun onExploreClick(txUrl: String)
fun onShareClick()
fun onAmountReduceClick(reducedAmount: String)
fun onAmountReduceIgnoreClick()

View file

@ -9,6 +9,7 @@ import arrow.core.Either
import arrow.core.getOrElse
import com.tangem.blockchain.common.TransactionData
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
@ -34,6 +35,11 @@ import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.usecase.*
import com.tangem.features.send.api.navigation.SendRouter
import com.tangem.features.send.impl.navigation.InnerSendRouter
import com.tangem.features.send.impl.presentation.analytics.EnterAddressSource
import com.tangem.features.send.impl.presentation.analytics.SendAnalyticEvents
import com.tangem.features.send.impl.presentation.analytics.SendScreenSource
import com.tangem.features.send.impl.presentation.analytics.utils.SendOnNextScreenAnalyticSender
import com.tangem.features.send.impl.presentation.analytics.utils.SendRecipientAnalyticsSender
import com.tangem.features.send.impl.presentation.domain.AvailableWallet
import com.tangem.features.send.impl.presentation.state.*
import com.tangem.features.send.impl.presentation.state.amount.AmountStateFactory
@ -75,6 +81,7 @@ internal class SendViewModel @Inject constructor(
private val reduxStateHolder: ReduxStateHolder,
private val isAmountSubtractAvailableUseCase: IsAmountSubtractAvailableUseCase,
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
currencyChecksRepository: CurrencyChecksRepository,
isFeeApproximateUseCase: IsFeeApproximateUseCase,
getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
@ -147,6 +154,12 @@ internal class SendViewModel @Inject constructor(
clickIntents = this,
)
private val sendOnNextScreenAnalyticSender by lazy(LazyThreadSafetyMode.NONE) {
SendOnNextScreenAnalyticSender(analyticsEventHandler)
}
private val sendRecipientAnalyticsSender by lazy(LazyThreadSafetyMode.NONE) {
SendRecipientAnalyticsSender(analyticsEventHandler)
}
// todo convert to StateFlow
var uiState: SendUiState by mutableStateOf(stateFactory.getInitialState())
private set
@ -170,6 +183,7 @@ internal class SendViewModel @Inject constructor(
subscribeOnCurrencyStatusUpdates(owner)
onStateActive()
subscribeOnBalanceHidden(owner)
analyticsEventHandler.send(SendAnalyticEvents.SendOpened)
}
fun setRouter(router: InnerSendRouter, stateRouter: StateRouter) {
@ -273,11 +287,11 @@ internal class SendViewModel @Inject constructor(
if (transactionId != null && amount != null && destinationAddress != null) {
uiState = stateFactory.getReadyState(amount, destinationAddress)
showFee()
stateRouter.showFee()
} else {
getWalletsAndRecent()
uiState = stateFactory.getReadyState()
showRecipient()
stateRouter.showRecipient()
}
updateNotifications()
}
@ -392,10 +406,17 @@ internal class SendViewModel @Inject constructor(
// region screen state navigation
override fun popBackStack() = stateRouter.popBackStack()
override fun onBackClick() = stateRouter.onBackClick(uiState.sendState.isSuccess)
override fun onNextClick() = stateRouter.onNextClick()
override fun onNextClick() {
val prevScreen = stateRouter.onNextClick()
sendOnNextScreenAnalyticSender.send(prevScreen, uiState)
}
override fun onPrevClick() = stateRouter.onPrevClick()
override fun onQrCodeScanClick() = innerRouter.openQrCodeScanner(cryptoCurrency.network.name)
override fun onQrCodeScanClick() {
analyticsEventHandler.send(SendAnalyticEvents.QrCodeButtonClicked)
innerRouter.openQrCodeScanner(cryptoCurrency.network.name)
}
override fun onFailedTxEmailClick(errorMessage: String) {
reduxStateHolder.dispatch(LegacyAction.SendEmailTransactionFailed(errorMessage))
@ -407,6 +428,7 @@ internal class SendViewModel @Inject constructor(
// region amount state clicks
override fun onCurrencyChangeClick(isFiat: Boolean) {
analyticsEventHandler.send(SendAnalyticEvents.SwapCurrencyButtonClicked)
uiState = amountStateFactory.getOnCurrencyChangedState(isFiat)
}
@ -424,7 +446,7 @@ internal class SendViewModel @Inject constructor(
viewModelScope.launch(dispatchers.main) {
parseSharedAddressUseCase(address, cryptoCurrency.network).fold(
ifRight = { parsedCode ->
onRecipientAddressValueChange(parsedCode.address)
onRecipientAddressValueChange(parsedCode.address, EnterAddressSource.QRCode)
parsedCode.amount?.let { onAmountValueChange(it.toPlainString()) }
parsedCode.memo?.let { onRecipientMemoValueChange(it) }
},
@ -435,24 +457,26 @@ internal class SendViewModel @Inject constructor(
}.saveIn(qrScannerJobHolder)
}
override fun onRecipientAddressValueChange(value: String) {
override fun onRecipientAddressValueChange(value: String, type: EnterAddressSource?) {
viewModelScope.launch(dispatchers.main) {
if (!checkIfXrpAddressValue(value)) {
uiState = stateFactory.onRecipientAddressValueChange(value)
uiState = stateFactory.getOnRecipientAddressValidationStarted()
val isValidAddress = validateAddress(value)
uiState = stateFactory.getOnRecipientAddressValidState(value, isValidAddress)
sendRecipientAnalyticsSender.sendAddressAnalytics(type, isValidAddress)
}
}.saveIn(addressValidationJobHolder)
}
override fun onRecipientMemoValueChange(value: String) {
override fun onRecipientMemoValueChange(value: String, isPasted: Boolean) {
viewModelScope.launch(dispatchers.main) {
if (!checkIfXrpAddressValue(value)) {
uiState = stateFactory.getOnRecipientMemoValueChange(value)
uiState = stateFactory.getOnRecipientAddressValidationStarted()
val isValidAddress = validateAddress(uiState.recipientState?.addressTextField?.value.orEmpty())
uiState = stateFactory.getOnRecipientMemoValidState(value, isValidAddress)
sendRecipientAnalyticsSender.sendMemoAnalytics(isPasted)
}
}.saveIn(addressValidationJobHolder)
}
@ -482,6 +506,9 @@ internal class SendViewModel @Inject constructor(
override fun onFeeSelectorClick(feeType: FeeType) {
uiState = feeStateFactory.onFeeSelectedState(feeType)
updateFeeNotifications()
if (feeType == FeeType.Custom) {
analyticsEventHandler.send(SendAnalyticEvents.CustomFeeButtonClicked)
}
}
override fun onCustomFeeValueChange(index: Int, value: String) {
@ -542,15 +569,32 @@ internal class SendViewModel @Inject constructor(
onCheckFeeUpdate()
}
sendIdleTimer = System.currentTimeMillis()
analyticsEventHandler.send(SendAnalyticEvents.SendButtonClicked)
}
override fun showAmount() = stateRouter.showAmount()
override fun showAmount() {
stateRouter.showAmount()
analyticsEventHandler.send(SendAnalyticEvents.ScreenReopened(SendScreenSource.Amount))
}
override fun showRecipient() = stateRouter.showRecipient()
override fun showRecipient() {
stateRouter.showRecipient()
analyticsEventHandler.send(SendAnalyticEvents.ScreenReopened(SendScreenSource.Address))
}
override fun showFee() = stateRouter.showFee()
override fun showFee() {
stateRouter.showFee()
analyticsEventHandler.send(SendAnalyticEvents.ScreenReopened(SendScreenSource.Fee))
}
override fun onExploreClick(txUrl: String) = innerRouter.openUrl(txUrl)
override fun onExploreClick(txUrl: String) {
analyticsEventHandler.send(SendAnalyticEvents.ExploreButtonClicked)
innerRouter.openUrl(txUrl)
}
override fun onShareClick() {
analyticsEventHandler.send(SendAnalyticEvents.ShareButtonClicked)
}
override fun onAmountReduceClick(reducedAmount: String) {
uiState = amountStateFactory.getOnAmountValueChange(reducedAmount)
@ -616,6 +660,7 @@ internal class SendViewModel @Inject constructor(
uiState = stateFactory.getSendingStateUpdate(isSending = false)
uiState = stateFactory.getTransactionSendState(txData)
scheduleBalanceUpdate()
analyticsEventHandler.send(SendAnalyticEvents.TransactionScreenOpened)
},
)
}