From 5e12c2dc29636443e0060517d65f590e75b57c13 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 20 Apr 2026 11:40:26 +0200 Subject: [PATCH] Updated on 2026-08-14 --- .../core/analytics/models/AnalyticsParam.kt | 1 + .../components/DefaultFeedEntryComponent.kt | 4 +- .../feed/components/FeedEntryChildFactory.kt | 5 +- .../components/earn/DefaultEarnComponent.kt | 2 +- .../list/DefaultMarketsTokenListComponent.kt | 2 +- .../search/DefaultSearchComponent.kt | 1 + .../features/feed/model/earn/EarnModel.kt | 3 +- .../feed/model/feed/FeedComponentModel.kt | 2 +- .../feed/model/feed/FeedModelClickIntents.kt | 2 +- .../model/market/list/MarketsListModel.kt | 3 +- .../MarketsListBatchFlowManager.kt | 15 ++++ .../features/feed/model/search/SearchModel.kt | 61 +++++++++++++- .../search/analytics/SearchAnalyticsEvent.kt | 84 +++++++++++++++++++ .../search/analytics/SearchAnalyticsHelper.kt | 64 ++++++++++++++ 14 files changed, 238 insertions(+), 11 deletions(-) create mode 100644 features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/search/analytics/SearchAnalyticsEvent.kt create mode 100644 features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/search/analytics/SearchAnalyticsHelper.kt diff --git a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt index a670a8226a..d6b77eb6ba 100644 --- a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt +++ b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt @@ -99,6 +99,7 @@ sealed class AnalyticsParam { data object NewsPage : ScreensSources("News Page") data object Portfolio : ScreensSources("Portfolio") data object Staking : ScreensSources("Staking") + data object Earn : ScreensSources("Earn") } sealed class TxSentFrom(val value: String) { diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/DefaultFeedEntryComponent.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/DefaultFeedEntryComponent.kt index 74271e53a0..a0b3a03088 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/DefaultFeedEntryComponent.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/DefaultFeedEntryComponent.kt @@ -132,8 +132,8 @@ internal class DefaultFeedEntryComponent @AssistedInject constructor( innerRouter.push(FeedEntryChildFactory.Child.Earn) } - override fun openSearch() { - innerRouter.push(FeedEntryChildFactory.Child.Search) + override fun openSearch(source: String) { + innerRouter.push(FeedEntryChildFactory.Child.Search(source)) } } diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/FeedEntryChildFactory.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/FeedEntryChildFactory.kt index ef34cba3d1..e8f7af83ef 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/FeedEntryChildFactory.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/FeedEntryChildFactory.kt @@ -65,7 +65,7 @@ internal class FeedEntryChildFactory @Inject constructor( @Serializable @Immutable - data object Search : Child + data class Search(val source: String) : Child } @Suppress("LongMethod") @@ -144,7 +144,7 @@ internal class FeedEntryChildFactory @Inject constructor( addToPortfolioComponentFactory = addToPortfolioPreselectedDataComponent, ) } - Child.Search -> DefaultSearchComponent( + is Child.Search -> DefaultSearchComponent( appComponentContext = appComponentContext, params = DefaultSearchComponent.Params( onBackClick = onBackClicked, @@ -155,6 +155,7 @@ internal class FeedEntryChildFactory @Inject constructor( source = AnalyticsParam.ScreensSources.Market.value, ) }, + sourceParams = child.source, ), ) } diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/earn/DefaultEarnComponent.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/earn/DefaultEarnComponent.kt index 86bb48edfb..8ddecd2dc6 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/earn/DefaultEarnComponent.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/earn/DefaultEarnComponent.kt @@ -148,6 +148,6 @@ internal class DefaultEarnComponent( data class Params( val onBackClick: () -> Unit, - val onSearchClicked: () -> Unit, + val onSearchClicked: (source: String) -> Unit, ) } \ No newline at end of file diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/list/DefaultMarketsTokenListComponent.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/list/DefaultMarketsTokenListComponent.kt index 29e359843b..79cbf99fa3 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/list/DefaultMarketsTokenListComponent.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/list/DefaultMarketsTokenListComponent.kt @@ -134,7 +134,7 @@ internal class DefaultMarketsTokenListComponent( data class ClickIntents( val onBackClicked: () -> Unit, - val onSearchClicked: () -> Unit, + val onSearchClicked: (source: String) -> Unit, val onTokenClick: ((TokenMarketParams, AppCurrency) -> Unit), ) diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/search/DefaultSearchComponent.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/search/DefaultSearchComponent.kt index 45392661d4..0ad95b2ae6 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/search/DefaultSearchComponent.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/search/DefaultSearchComponent.kt @@ -120,5 +120,6 @@ internal class DefaultSearchComponent( data class Params( val onBackClick: () -> Unit, val onMarketTokenClick: ((TokenMarketParams, AppCurrency) -> Unit), + val sourceParams: String, ) } \ No newline at end of file diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/earn/EarnModel.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/earn/EarnModel.kt index b69171f8f3..9ecb1ab5f4 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/earn/EarnModel.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/earn/EarnModel.kt @@ -8,6 +8,7 @@ import com.arkivanov.decompose.router.slot.dismiss import com.tangem.common.routing.AppRoute import com.tangem.common.routing.AppRouter import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer @@ -325,7 +326,7 @@ internal class EarnModel @Inject constructor( onNetworkFilterClick = ::onNetworkFilterClick, onTypeFilterClick = ::onTypeFilterClick, onScroll = ::onMostlyUsedScrolled, - onSearchBarClicked = params.onSearchClicked, + onSearchBarClicked = { params.onSearchClicked(AnalyticsParam.ScreensSources.Earn.value) }, ), ) } diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/feed/FeedComponentModel.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/feed/FeedComponentModel.kt index c3052275b3..b1d45c9693 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/feed/FeedComponentModel.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/feed/FeedComponentModel.kt @@ -240,7 +240,7 @@ internal class FeedComponentModel @Inject constructor( onBarClick = { analyticsEventHandler.send(FeedAnalyticsEvent.TokenSearchedClicked()) if (designFeatureToggles.isRedesignEnabled) { - params.feedClickIntents.openSearch() + params.feedClickIntents.openSearch(AnalyticsParam.ScreensSources.Markets.value) } else { params.feedClickIntents.onMarketOpenClick(null) } diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/feed/FeedModelClickIntents.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/feed/FeedModelClickIntents.kt index 96ff7cc2f1..7c81362bf3 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/feed/FeedModelClickIntents.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/feed/FeedModelClickIntents.kt @@ -30,5 +30,5 @@ internal interface FeedModelClickIntents { fun onOpenEarnPage() - fun openSearch() + fun openSearch(source: String) } \ No newline at end of file diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/list/MarketsListModel.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/list/MarketsListModel.kt index 79c0d6a2db..dfd35cd4a7 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/list/MarketsListModel.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/list/MarketsListModel.kt @@ -4,6 +4,7 @@ import androidx.compose.runtime.Stable import arrow.core.getOrElse import com.tangem.common.ui.markets.models.MarketsListItemUM import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer @@ -70,7 +71,7 @@ internal class MarketsListModel @Inject constructor( preselectedInterval = Provider { modelParams.params.preselectedInterval }, onBackClick = modelParams.clickIntents.onBackClicked, analyticsEventHandler = analyticsEventHandler, - onSearchBarClick = modelParams.clickIntents.onSearchClicked, + onSearchBarClick = { modelParams.clickIntents.onSearchClicked(AnalyticsParam.ScreensSources.Market.value) }, ) } diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/list/statemanager/MarketsListBatchFlowManager.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/list/statemanager/MarketsListBatchFlowManager.kt index 781dceeb6d..c7b455abfe 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/list/statemanager/MarketsListBatchFlowManager.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/list/statemanager/MarketsListBatchFlowManager.kt @@ -141,6 +141,21 @@ internal class MarketsListBatchFlowManager( initialValue = false, ) + val initialLoadingError: Flow = batchFlow.state + .map { it.status } + .distinctUntilChanged() + .filterIsInstance() + .map { it.throwable } + + val totalCount: StateFlow = batchFlow.state + .map { it.totalCount } + .distinctUntilChanged() + .stateIn( + scope = modelScope, + started = SharingStarted.Eagerly, + initialValue = null, + ) + val isSearchNotFoundState = batchFlow.state .map { batchListState -> currentSearchText().isNullOrEmpty().not() && diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/search/SearchModel.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/search/SearchModel.kt index 839d5877f7..8dc79c7ec2 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/search/SearchModel.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/search/SearchModel.kt @@ -13,6 +13,7 @@ import com.tangem.common.ui.markets.models.MarketsListItemUM import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.datasource.api.common.response.ApiResponseError import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase @@ -28,6 +29,7 @@ import com.tangem.features.feed.components.search.SearchBottomSheetRoute import com.tangem.features.feed.model.market.list.state.MarketsListUM import com.tangem.features.feed.model.market.list.state.SortByTypeUM import com.tangem.features.feed.model.market.list.statemanager.MarketsListBatchFlowManager +import com.tangem.features.feed.model.search.analytics.SearchAnalyticsHelper import com.tangem.features.feed.model.search.converter.* import com.tangem.features.feed.model.search.state.SearchStateController import com.tangem.features.feed.model.search.state.transformers.* @@ -47,6 +49,7 @@ import javax.inject.Inject private const val UPDATE_QUOTES_TIMER_MILLIS = 60000L private const val MARKET_SEARCH_DEBOUNCE_MS = 500L +private const val RESULTS_SHOWN_DEBOUNCE_MS = 1000L @Suppress("LongParameterList", "LargeClass") @ModelScoped @@ -63,6 +66,7 @@ internal class SearchModel @Inject constructor( private val getTokenPriceChartUseCase: GetTokenPriceChartUseCase, private val appRouter: AppRouter, private val stateController: SearchStateController, + private val searchAnalyticsHelper: SearchAnalyticsHelper, ) : Model() { private val params = paramsContainer.require() @@ -71,7 +75,6 @@ internal class SearchModel @Inject constructor( private val searchResultsJob = JobHolder() private val marketSearchDebounceJob = JobHolder() private var shouldShowAllTokensIncludingUnder100k = false - private val currentAppCurrency = getSelectedAppCurrencyUseCase().map { maybeAppCurrency -> maybeAppCurrency.getOrElse { AppCurrency.Default } }.stateIn( @@ -122,7 +125,10 @@ internal class SearchModel @Inject constructor( subscribeToMarketUiItems() subscribeToQuotesPolling() subscribeToAppCurrencyChanges() + subscribeToMarketLoadingErrors() + subscribeToResultsShown() loadHistory() + searchAnalyticsHelper.sendSearchScreenOpened(params.sourceParams) } fun loadMore() { @@ -138,11 +144,14 @@ internal class SearchModel @Inject constructor( fun clearSearchHistory() { modelScope.launch(dispatchers.default) { clearSearchHistoryUseCase() + searchAnalyticsHelper.sendClearButtonClicked() } } fun onTextHintClick(text: String) { stateController.update(UpdateSearchBarQueryTransformer(text)) + searchAnalyticsHelper.sendHintClicked(text) + searchAnalyticsHelper.sendSearchStarted() } fun onResultMarketTokenClick(item: MarketsListItemUM) { @@ -155,6 +164,7 @@ internal class SearchModel @Inject constructor( ) saveRecentSearchTokenUseCase(marketsListItemToRecentSearchTokenConverter.convert(input)) saveSearchQueryUseCase(stateController.value.searchBar.query) + searchAnalyticsHelper.sendMarketItemClicked(item.currencySymbol) withContext(dispatchers.mainImmediate) { searchMarketsListManager.getTokenById(item.id)?.let { found -> params.onMarketTokenClick(found.toSerializableParam(), appCurrency) @@ -176,6 +186,7 @@ internal class SearchModel @Inject constructor( ), imageUrl = item.iconUrl, ) + searchAnalyticsHelper.sendRecentItemClicked(item.currencySymbol) params.onMarketTokenClick(tokenMarketParams, currentAppCurrency.value) } @@ -195,6 +206,7 @@ internal class SearchModel @Inject constructor( private fun onQueryChange(query: String) { stateController.update(UpdateSearchBarQueryTransformer(query)) + searchAnalyticsHelper.sendSearchStarted() } private fun onClearClick() { @@ -202,6 +214,7 @@ internal class SearchModel @Inject constructor( } private fun onSingleUserAssetClick(entry: UserAssetSearchEntry) { + searchAnalyticsHelper.sendPortfolioItemClicked(entry.currencyStatus.currency.symbol) appRouter.push( AppRoute.CurrencyDetails( userWalletId = entry.userWalletId, @@ -211,6 +224,7 @@ internal class SearchModel @Inject constructor( } private fun onGroupedUserAssetClick(grouped: UserAssetSearchItem.Grouped) { + searchAnalyticsHelper.sendGroupClicked(grouped.tokenSymbol) bottomSheetNavigation.activate( SearchBottomSheetRoute.TokenSelector( entries = grouped.entries, @@ -291,6 +305,50 @@ internal class SearchModel @Inject constructor( }.launchIn(modelScope) } + private fun subscribeToMarketLoadingErrors() { + searchMarketsListManager.initialLoadingError + .onEach { throwable -> + val (code, message) = when (throwable) { + is ApiResponseError.HttpException -> + throwable.code.numericCode to throwable.message.orEmpty() + else -> null to throwable.message.orEmpty() + } + searchAnalyticsHelper.sendErrorMarketsData(code, message) + } + .launchIn(modelScope) + } + + @OptIn(ExperimentalCoroutinesApi::class, FlowPreview::class) + private fun subscribeToResultsShown() { + stateController.uiState + .map { it.searchBar.query.trim() } + .distinctUntilChanged() + .flatMapLatest { query -> + if (query.isEmpty()) { + emptyFlow() + } else { + combine( + searchMarketsListManager.totalCount.filterNotNull(), + stateController.uiState.map { state -> + (state.content as? SearchContentUM.Results)?.userAssets?.size ?: 0 + }.distinctUntilChanged(), + ) { marketCount, userAssetsCount -> + marketCount to userAssetsCount + } + .debounce(RESULTS_SHOWN_DEBOUNCE_MS) + .take(1) + } + } + .onEach { (marketCount, userAssetsCount) -> + searchAnalyticsHelper.sendResultShown( + totalResultsCount = marketCount + userAssetsCount, + marketsResultsCount = marketCount, + userTokensResultsCount = userAssetsCount, + ) + } + .launchIn(modelScope) + } + private fun subscribeToMarketUiItems() { combine( flow = stateController.uiState.map { it.searchBar.query }.distinctUntilChanged(), @@ -312,6 +370,7 @@ internal class SearchModel @Inject constructor( ) } .filterNotNull() + .distinctUntilChanged() .onEach { snapshot -> stateController.update(ApplySearchMarketBatchTransformer(snapshot)) } diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/search/analytics/SearchAnalyticsEvent.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/search/analytics/SearchAnalyticsEvent.kt new file mode 100644 index 0000000000..9abc00632c --- /dev/null +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/search/analytics/SearchAnalyticsEvent.kt @@ -0,0 +1,84 @@ +package com.tangem.features.feed.model.search.analytics + +import com.tangem.core.analytics.models.AnalyticsEvent +import com.tangem.core.analytics.models.AnalyticsParam.Key.ERROR_CODE +import com.tangem.core.analytics.models.AnalyticsParam.Key.ERROR_MESSAGE +import com.tangem.core.analytics.models.AnalyticsParam.Key.SOURCE +import com.tangem.core.analytics.models.AnalyticsParam.Key.TOKEN_PARAM +import com.tangem.core.analytics.models.IS_NOT_HTTP_ERROR + +internal sealed class SearchAnalyticsEvent( + event: String, + params: Map = emptyMap(), +) : AnalyticsEvent(category = "Search", event = event, params = params) { + + data class SearchScreenOpened( + private val screensSource: String, + ) : SearchAnalyticsEvent( + event = "Search Screen Opened", + params = mapOf(SOURCE to screensSource), + ) + + class SearchStarted : SearchAnalyticsEvent(event = "Search Started") + + data class ResultsShown( + private val totalResultsCount: Int, + private val marketsResultsCount: Int, + private val userTokensResultsCount: Int, + ) : SearchAnalyticsEvent( + event = "Results Shown", + params = mapOf( + "Total Results" to totalResultsCount.toString(), + "User Tokens Count" to userTokensResultsCount.toString(), + "Market Tokens Count" to marketsResultsCount.toString(), + ), + ) + + data class ErrorMarketsData( + private val code: Int?, + private val message: String, + ) : SearchAnalyticsEvent( + event = "Error - Markets Data", + params = mapOf( + ERROR_CODE to (code ?: IS_NOT_HTTP_ERROR).toString(), + ERROR_MESSAGE to message, + ), + ) + + data class PortfolioItemClicked( + private val tokenSymbol: String, + ) : SearchAnalyticsEvent( + event = "Portfolio Item Clicked", + params = mapOf(TOKEN_PARAM to tokenSymbol), + ) + + data class HintClicked( + private val hint: String, + ) : SearchAnalyticsEvent( + event = "Hint Clicked", + params = mapOf("Text" to hint), + ) + + data class RecentItemClicked( + private val tokenSymbol: String, + ) : SearchAnalyticsEvent( + event = "Recent Item Clicked", + params = mapOf(TOKEN_PARAM to tokenSymbol), + ) + + data class MarketItemClicked( + private val tokenSymbol: String, + ) : SearchAnalyticsEvent( + event = "Market Item Clicked", + params = mapOf(TOKEN_PARAM to tokenSymbol), + ) + + data class GroupClicked( + private val tokenSymbol: String, + ) : SearchAnalyticsEvent( + event = "Group Clicked", + params = mapOf(TOKEN_PARAM to tokenSymbol), + ) + + class ButtonClearHistoryClick : SearchAnalyticsEvent(event = "Button - Clear History") +} \ No newline at end of file diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/search/analytics/SearchAnalyticsHelper.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/search/analytics/SearchAnalyticsHelper.kt new file mode 100644 index 0000000000..5a1f8d71a1 --- /dev/null +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/search/analytics/SearchAnalyticsHelper.kt @@ -0,0 +1,64 @@ +package com.tangem.features.feed.model.search.analytics + +import com.tangem.core.analytics.api.AnalyticsEventHandler +import javax.inject.Inject + +class SearchAnalyticsHelper @Inject constructor( + private val analyticsEventHandler: AnalyticsEventHandler, +) { + + private var isSearchStartedWasSent = false + + fun sendSearchScreenOpened(source: String) { + analyticsEventHandler.send(SearchAnalyticsEvent.SearchScreenOpened(source)) + } + + fun sendMarketItemClicked(tokenSymbol: String) { + analyticsEventHandler.send(SearchAnalyticsEvent.MarketItemClicked(tokenSymbol)) + } + + fun sendClearButtonClicked() { + analyticsEventHandler.send(SearchAnalyticsEvent.ButtonClearHistoryClick()) + } + + fun sendHintClicked(text: String) { + analyticsEventHandler.send(SearchAnalyticsEvent.HintClicked(text)) + } + + fun sendRecentItemClicked(tokenSymbol: String) { + analyticsEventHandler.send(SearchAnalyticsEvent.RecentItemClicked(tokenSymbol)) + } + + fun sendSearchStarted() { + if (isSearchStartedWasSent) return + analyticsEventHandler.send(SearchAnalyticsEvent.SearchStarted()) + isSearchStartedWasSent = true + } + + fun sendPortfolioItemClicked(tokenSymbol: String) { + analyticsEventHandler.send(SearchAnalyticsEvent.PortfolioItemClicked(tokenSymbol)) + } + + fun sendGroupClicked(tokenSymbol: String) { + analyticsEventHandler.send(SearchAnalyticsEvent.GroupClicked(tokenSymbol)) + } + + fun sendResultShown(totalResultsCount: Int, marketsResultsCount: Int, userTokensResultsCount: Int) { + analyticsEventHandler.send( + SearchAnalyticsEvent.ResultsShown( + totalResultsCount = totalResultsCount, + marketsResultsCount = marketsResultsCount, + userTokensResultsCount = userTokensResultsCount, + ), + ) + } + + fun sendErrorMarketsData(code: Int?, message: String) { + analyticsEventHandler.send( + SearchAnalyticsEvent.ErrorMarketsData( + code = code, + message = message, + ), + ) + } +} \ No newline at end of file