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 cf64bb4d14..f8528755c7 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 @@ -1,5 +1,7 @@ package com.tangem.core.analytics.models +const val IS_NOT_HTTP_ERROR = "Is not http error" + sealed class AnalyticsParam { sealed class CardBalanceState(val value: String) { diff --git a/data/markets/src/main/java/com/tangem/data/markets/analytics/MarketsDataAnalyticsEvent.kt b/data/markets/src/main/java/com/tangem/data/markets/analytics/MarketsDataAnalyticsEvent.kt index dc94cd9ceb..56fef028a1 100644 --- a/data/markets/src/main/java/com/tangem/data/markets/analytics/MarketsDataAnalyticsEvent.kt +++ b/data/markets/src/main/java/com/tangem/data/markets/analytics/MarketsDataAnalyticsEvent.kt @@ -2,6 +2,7 @@ package com.tangem.data.markets.analytics import com.tangem.core.analytics.models.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsParam +import com.tangem.core.analytics.models.IS_NOT_HTTP_ERROR sealed interface MarketsDataAnalyticsEvent { @@ -82,8 +83,4 @@ sealed interface MarketsDataAnalyticsEvent { Custom("Custom"), Unknown("Unknown"), } - - private companion object { - const val IS_NOT_HTTP_ERROR = "Is not http error" - } } \ No newline at end of file diff --git a/data/news/src/main/java/com/tangem/data/news/repository/DefaultNewsRepository.kt b/data/news/src/main/java/com/tangem/data/news/repository/DefaultNewsRepository.kt index 9a60bf6729..ec3ad96779 100644 --- a/data/news/src/main/java/com/tangem/data/news/repository/DefaultNewsRepository.kt +++ b/data/news/src/main/java/com/tangem/data/news/repository/DefaultNewsRepository.kt @@ -1,5 +1,9 @@ package com.tangem.data.news.repository +import arrow.core.Either +import arrow.core.flatten +import arrow.core.left +import arrow.core.right import com.tangem.datasource.api.common.response.ApiResponse import com.tangem.datasource.api.common.response.ApiResponseError import com.tangem.datasource.api.common.response.fold @@ -90,7 +94,7 @@ internal class DefaultNewsRepository( .combine(newsLikedStore.getAll()) { articles, likedFlags -> articles.map { article -> article.copy( - isLiked = isNewsLiked(article.id), + isLiked = likedFlags[article.id] == true, ) } } @@ -99,9 +103,10 @@ internal class DefaultNewsRepository( } } - override suspend fun fetchDetailedArticles(newsIds: Collection, language: String?) { - fetchDetailedArticlesInternal(newsIds = newsIds, language = language) - } + override suspend fun fetchDetailedArticles( + newsIds: Collection, + language: String?, + ): Either, Unit> = fetchDetailedArticlesInternal(newsIds = newsIds, language = language) override suspend fun fetchTrendingNews(limit: Int, language: String?) { fetchAndStoreTrendingNews(limit = limit, language = language) @@ -145,10 +150,9 @@ internal class DefaultNewsRepository( return newsLikedStore.getSync()[articleId] == true } - override suspend fun toggleNewsLiked(articleId: Int): Boolean = withContext(dispatchers.io) { + override suspend fun toggleNewsLiked(articleId: Int) = withContext(dispatchers.io) { val isNewsLikedValue = !isNewsLiked(articleId) newsLikedStore.updateLiked(listOf(articleId), isNewsLikedValue) - isNewsLikedValue } private fun updateViewedStatusForNewsBatch( @@ -162,9 +166,12 @@ internal class DefaultNewsRepository( ) } - private suspend fun fetchDetailedArticlesInternal(newsIds: Collection, language: String?) = + private suspend fun fetchDetailedArticlesInternal( + newsIds: Collection, + language: String?, + ): Either, Unit> = Either.catch { withContext(dispatchers.io) { - if (newsIds.isEmpty()) return@withContext + if (newsIds.isEmpty()) return@withContext Unit.right() val uniqueIds = newsIds.distinct() val idsToFetch = buildList { @@ -174,26 +181,42 @@ internal class DefaultNewsRepository( } } - if (idsToFetch.isEmpty()) return@withContext + if (idsToFetch.isEmpty()) return@withContext Unit.right() val fetchedArticles = supervisorScope { idsToFetch.map { newsId -> async { - newsApi.getNewsDetails(newsId = newsId, language = language) - .getOrThrow() - .toDomainDetailedArticle( - isLiked = isNewsLiked(newsId), - ) + Either.catch { + newsApi.getNewsDetails(newsId = newsId, language = language) + .getOrThrow() + .toDomainDetailedArticle( + isLiked = isNewsLiked(newsId), + ) + }.mapLeft { + newsId to it + } } }.awaitAll() } - if (fetchedArticles.isNotEmpty()) { - newsDetailsStore.store( - articles = fetchedArticles.associateBy(DetailedArticle::id), - ) - } + fetchedArticles + .filterIsInstance>() + .map { it.value } + .let { articles -> + if (articles.isNotEmpty()) { + newsDetailsStore.store( + articles = articles.associateBy(DetailedArticle::id), + ) + } + } + + val errors = fetchedArticles + .filterIsInstance>>() + .associate { it.value.first to it.value.second } + + if (errors.isEmpty()) Unit.right() else errors.left() } + }.mapLeft { t -> mapOf(GLOBAL_ERROR_ID to t) }.flatten() private suspend fun fetchAndStoreTrendingNews(limit: Int, language: String?) { return withContext(dispatchers.io) { @@ -378,5 +401,6 @@ internal class DefaultNewsRepository( private const val INITIAL_BATCH_KEY = 0 private const val FIRST_PAGE = 1 private const val TRENDING_NEWS_KEY = "trending_news" + private const val GLOBAL_ERROR_ID = -1 } } \ No newline at end of file diff --git a/domain/news/src/main/java/com/tangem/domain/news/repository/NewsRepository.kt b/domain/news/src/main/java/com/tangem/domain/news/repository/NewsRepository.kt index cf906001f2..44266d3935 100644 --- a/domain/news/src/main/java/com/tangem/domain/news/repository/NewsRepository.kt +++ b/domain/news/src/main/java/com/tangem/domain/news/repository/NewsRepository.kt @@ -1,5 +1,6 @@ package com.tangem.domain.news.repository +import arrow.core.Either import com.tangem.domain.models.news.ArticleCategory import com.tangem.domain.models.news.DetailedArticle import com.tangem.domain.models.news.ShortArticle @@ -37,7 +38,7 @@ interface NewsRepository { /** * Fetches and caches detailed articles for provided ids in parallel. */ - suspend fun fetchDetailedArticles(newsIds: Collection, language: String?) + suspend fun fetchDetailedArticles(newsIds: Collection, language: String?): Either, Unit> /** * Fetch list of trending news by limit and with correct locale and store it in runtime data store. @@ -64,7 +65,6 @@ interface NewsRepository { /** * Toggle news liked state - * @return an actual liked state of the article */ - suspend fun toggleNewsLiked(articleId: Int): Boolean + suspend fun toggleNewsLiked(articleId: Int) } \ No newline at end of file diff --git a/domain/news/src/main/java/com/tangem/domain/news/usecase/ObserveNewsDetailsUseCase.kt b/domain/news/src/main/java/com/tangem/domain/news/usecase/ObserveNewsDetailsUseCase.kt index 373456dce2..06d9678f90 100644 --- a/domain/news/src/main/java/com/tangem/domain/news/usecase/ObserveNewsDetailsUseCase.kt +++ b/domain/news/src/main/java/com/tangem/domain/news/usecase/ObserveNewsDetailsUseCase.kt @@ -24,7 +24,6 @@ class ObserveNewsDetailsUseCase( /** * Prefetches the given article ids (can be called with current + next ids for pager preloading). */ - suspend fun prefetch(newsIds: Collection, language: String?): Either = Either.catch { + suspend fun prefetch(newsIds: Collection, language: String?): Either, Unit> = repository.fetchDetailedArticles(newsIds, language) - } } \ No newline at end of file diff --git a/domain/news/src/main/java/com/tangem/domain/news/usecase/ToggleArticleLikedUseCase.kt b/domain/news/src/main/java/com/tangem/domain/news/usecase/ToggleArticleLikedUseCase.kt index 90cac29915..dfccde2c5b 100644 --- a/domain/news/src/main/java/com/tangem/domain/news/usecase/ToggleArticleLikedUseCase.kt +++ b/domain/news/src/main/java/com/tangem/domain/news/usecase/ToggleArticleLikedUseCase.kt @@ -1,5 +1,6 @@ package com.tangem.domain.news.usecase +import arrow.core.Either import com.tangem.domain.news.repository.NewsRepository class ToggleArticleLikedUseCase(private val repository: NewsRepository) { @@ -7,7 +8,7 @@ class ToggleArticleLikedUseCase(private val repository: NewsRepository) { /** * Toggle an article liked state. */ - suspend fun toggleLiked(articleId: Int): Boolean { - return repository.toggleNewsLiked(articleId) + suspend fun toggleLiked(articleId: Int) = Either.catch { + repository.toggleNewsLiked(articleId) } } \ No newline at end of file diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/analytics/MarketTokenAnalyticsEvent.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/analytics/MarketTokenAnalyticsEvent.kt new file mode 100644 index 0000000000..255e31fb5b --- /dev/null +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/analytics/MarketTokenAnalyticsEvent.kt @@ -0,0 +1,47 @@ +package com.tangem.features.feed.components.market.details.analytics + +import com.tangem.core.analytics.models.AnalyticsEvent +import com.tangem.core.analytics.models.AnalyticsParam +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.IS_NOT_HTTP_ERROR +import com.tangem.core.analytics.models.OneTimePerSessionEvent + +internal sealed class MarketTokenAnalyticsEvent( + event: String, + params: Map = emptyMap(), +) : AnalyticsEvent(category = "CoinPage", event = event, params = params) { + + data class TokenNewsViewed( + private val token: String, + ) : MarketTokenAnalyticsEvent( + event = "Token News Viewed", + params = mapOf( + AnalyticsParam.TOKEN_PARAM to token, + ), + ), OneTimePerSessionEvent { + override val oneTimeEventId: String = event + token + } + + data class TokenNewsCarouselScrolled( + private val token: String, + ) : MarketTokenAnalyticsEvent( + event = "Token News Carousel Scrolled", + params = mapOf( + AnalyticsParam.TOKEN_PARAM to token, + ), + ), OneTimePerSessionEvent { + override val oneTimeEventId: String = event + token + } + + data class TokenNewsLoadError( + private val code: Int?, + private val message: String, + ) : MarketTokenAnalyticsEvent( + event = "Token News Load Error", + params = mapOf( + ERROR_CODE to (code ?: IS_NOT_HTTP_ERROR).toString(), + ERROR_MESSAGE to message, + ), + ) +} \ No newline at end of file diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/feed/analytics/FeedAnalyticsEvent.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/feed/analytics/FeedAnalyticsEvent.kt index cd869f6455..7c9792e701 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/feed/analytics/FeedAnalyticsEvent.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/feed/analytics/FeedAnalyticsEvent.kt @@ -5,6 +5,7 @@ import com.tangem.core.analytics.models.AnalyticsParam 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.IS_NOT_HTTP_ERROR import com.tangem.core.analytics.models.OneTimePerSessionEvent internal sealed class FeedAnalyticsEvent( @@ -89,8 +90,4 @@ internal sealed class FeedAnalyticsEvent( ) class TokenSearchedClicked : FeedAnalyticsEvent(event = "Token Searched Clicked") - - private companion object { - const val IS_NOT_HTTP_ERROR = "Is not http error" - } } \ No newline at end of file diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/details/MarketsTokenDetailsModel.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/details/MarketsTokenDetailsModel.kt index 2750d9d6c3..f1c5bb2319 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/details/MarketsTokenDetailsModel.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/details/MarketsTokenDetailsModel.kt @@ -22,6 +22,7 @@ import com.tangem.core.ui.format.bigdecimal.fiat import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.format.bigdecimal.percent import com.tangem.core.ui.format.bigdecimal.price +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.card.common.extensions.hotWalletExcludedBlockchains @@ -36,6 +37,7 @@ import com.tangem.domain.settings.usercountry.models.UserCountry import com.tangem.domain.settings.usercountry.models.needApplyFCARestrictions import com.tangem.domain.wallets.usecase.GetWalletsUseCase import com.tangem.features.feed.components.market.details.DefaultMarketsTokenDetailsComponent +import com.tangem.features.feed.components.market.details.analytics.MarketTokenAnalyticsEvent import com.tangem.features.feed.impl.R import com.tangem.features.feed.model.market.details.analytics.MarketDetailsAnalyticsEvent import com.tangem.features.feed.model.market.details.converter.DescriptionConverter @@ -242,6 +244,8 @@ internal class MarketsTokenDetailsModel @Inject constructor( relatedNews = MarketsTokenDetailsUM.RelatedNews( articles = persistentListOf(), onArticledClicked = {}, + onFirstVisible = {}, + onScroll = {}, ), ), ) @@ -322,10 +326,36 @@ internal class MarketsTokenDetailsModel @Inject constructor( /* preselectedIds */ relatedNews.map { it.id }, ) }, + onFirstVisible = { + analyticsEventHandler.send( + MarketTokenAnalyticsEvent.TokenNewsViewed( + token = params.token.symbol, + ), + ) + }, + onScroll = { + analyticsEventHandler.send( + MarketTokenAnalyticsEvent.TokenNewsCarouselScrolled( + token = params.token.symbol, + ), + ) + }, ), ) } } + }.onLeft { throwable -> + val (code, message) = if (throwable is ApiResponseError.HttpException) { + throwable.code.numericCode to throwable.message.orEmpty() + } else { + null to "" + } + analyticsEventHandler.send( + MarketTokenAnalyticsEvent.TokenNewsLoadError( + code = code, + message = message, + ), + ) } } } diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/news/details/NewsDetailsModel.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/news/details/NewsDetailsModel.kt index 5dba83b983..91f9fa30d2 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/news/details/NewsDetailsModel.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/news/details/NewsDetailsModel.kt @@ -3,11 +3,13 @@ package com.tangem.features.feed.model.news.details import androidx.compose.runtime.Stable import arrow.core.getOrElse 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 import com.tangem.core.navigation.share.ShareManager import com.tangem.core.navigation.url.UrlOpener +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.markets.GetTokenMarketInfoUseCase @@ -33,6 +35,7 @@ import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch +import timber.log.Timber import java.util.Locale import javax.inject.Inject @@ -129,7 +132,9 @@ internal class NewsDetailsModel @Inject constructor( private fun onLikeClick(articleId: Int) { modelScope.launch { - toggleArticleLikedUseCase.toggleLiked(articleId) + toggleArticleLikedUseCase + .toggleLiked(articleId) + .onLeft { Timber.e(it) } analyticsEventHandler.send(NewsDetailsAnalyticsEvent.NewsLikeClicked(articleId)) } } @@ -202,7 +207,35 @@ internal class NewsDetailsModel @Inject constructor( observeNewsDetailsUseCase.prefetch( newsIds = params.preselectedArticlesId, language = currentLanguage, - ).mapLeft { + ).onLeft { errors -> + errors.onEach { (newsId, throwable) -> + when { + // an article is opened from deeplink and is not found + params.screenSource == AnalyticsParam.ScreensSources.NewsLink.value && + newsId == params.articleId && + throwable is ApiResponseError.HttpException && + throwable.code == ApiResponseError.HttpException.Code.NOT_FOUND + -> { + analyticsEventHandler.send( + NewsDetailsAnalyticsEvent.NewsLinkMismatch( + newsId = params.articleId, + ), + ) + } + // global request executing error + newsId < 0 -> { + Timber.e(throwable) + } + else -> { + analyticsEventHandler.send( + NewsDetailsAnalyticsEvent.NewsArticleLoadError( + newsId = newsId, + ), + ) + } + } + } + }.mapLeft { stateFactory.createErrorState() } } diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/news/details/analytics/NewsDetailsAnalyticsEvent.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/news/details/analytics/NewsDetailsAnalyticsEvent.kt index 73b92d193e..128235303e 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/news/details/analytics/NewsDetailsAnalyticsEvent.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/news/details/analytics/NewsDetailsAnalyticsEvent.kt @@ -41,4 +41,22 @@ internal sealed class NewsDetailsAnalyticsEvent( ), OneTimePerSessionEvent { override val oneTimeEventId: String = event + newsId } + + data class NewsArticleLoadError( + private val newsId: Int, + ) : NewsDetailsAnalyticsEvent( + event = "News Article Load Error", + params = mapOf( + "News Id" to newsId.toString(), + ), + ) + + data class NewsLinkMismatch( + private val newsId: Int, + ) : NewsDetailsAnalyticsEvent( + event = "News Link Mismatch", + params = mapOf( + "News Id" to newsId.toString(), + ), + ) } \ No newline at end of file diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/news/list/analytics/NewsListAnalyticsEvent.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/news/list/analytics/NewsListAnalyticsEvent.kt index efb0ae3ea6..407b1d06b4 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/news/list/analytics/NewsListAnalyticsEvent.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/news/list/analytics/NewsListAnalyticsEvent.kt @@ -3,6 +3,7 @@ package com.tangem.features.feed.model.news.list.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.IS_NOT_HTTP_ERROR internal sealed class NewsListAnalyticsEvent( event: String, @@ -28,8 +29,4 @@ internal sealed class NewsListAnalyticsEvent( "Selected Categories" to categoryId.toString(), ), ) - - private companion object { - const val IS_NOT_HTTP_ERROR = "Is not http error" - } } \ No newline at end of file diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/TokenMarketDetailsBody.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/TokenMarketDetailsBody.kt index 764fcff180..434ba73d53 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/TokenMarketDetailsBody.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/TokenMarketDetailsBody.kt @@ -3,7 +3,7 @@ package com.tangem.features.feed.ui.market.detailed.components import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyRow -import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -11,9 +11,9 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.onFirstVisible import androidx.compose.ui.unit.dp import com.tangem.common.ui.news.ArticleCard -import com.tangem.common.ui.news.ArticleConfigUM import com.tangem.core.ui.components.UnableToLoadData import com.tangem.core.ui.components.block.TangemBlockCardColors import com.tangem.core.ui.components.items.DescriptionItem @@ -24,6 +24,8 @@ import com.tangem.features.feed.impl.R import com.tangem.features.feed.ui.market.detailed.state.MarketsTokenDetailsUM import com.tangem.features.feed.ui.market.detailed.state.MarketsTokenDetailsUM.RelatedNews +private const val FOURTH_ITEM_INDEX = 3 + @Suppress("CanBeNonNullable") // TODO will be removed after [REDACTED_JIRA] internal fun LazyListScope.tokenMarketDetailsBody( state: MarketsTokenDetailsUM.Body, @@ -215,7 +217,11 @@ private fun LazyListScope.relatedNews(relatedNews: RelatedNews) { Column( modifier = Modifier .fillMaxWidth() - .padding(bottom = 32.dp, top = 20.dp), + .padding(bottom = 32.dp, top = 20.dp) + .onFirstVisible( + minFractionVisible = 0.5f, + callback = relatedNews.onFirstVisible, + ), verticalArrangement = Arrangement.spacedBy(12.dp), ) { Text( @@ -231,14 +237,23 @@ private fun LazyListScope.relatedNews(relatedNews: RelatedNews) { horizontalArrangement = Arrangement.spacedBy(12.dp), state = listState, ) { - items( + itemsIndexed( items = relatedNews.articles, - key = ArticleConfigUM::id, - ) { article -> + key = { index, article -> article.id }, + ) { index, article -> + val articleModifier = if (index == FOURTH_ITEM_INDEX) { + Modifier.onFirstVisible( + minFractionVisible = 0.5f, + callback = relatedNews.onScroll, + ) + } else { + Modifier + } + ArticleCard( articleConfigUM = article, onArticleClick = { relatedNews.onArticledClicked(article.id) }, - modifier = Modifier + modifier = articleModifier .height(164.dp) .width(216.dp), colors = TangemBlockCardColors.copy(containerColor = TangemTheme.colors.background.action), diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/preview/MarketsTokenDetailsPreview.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/preview/MarketsTokenDetailsPreview.kt index 964ccd4dad..2c578fbe68 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/preview/MarketsTokenDetailsPreview.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/preview/MarketsTokenDetailsPreview.kt @@ -46,6 +46,8 @@ internal object MarketsTokenDetailsPreview { relatedNews = MarketsTokenDetailsUM.RelatedNews( articles = persistentListOf(), onArticledClicked = {}, + onFirstVisible = {}, + onScroll = {}, ), ) @@ -136,6 +138,8 @@ internal object MarketsTokenDetailsPreview { relatedNews = MarketsTokenDetailsUM.RelatedNews( articles = persistentListOf(), onArticledClicked = {}, + onFirstVisible = {}, + onScroll = {}, ), ) } \ No newline at end of file diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/state/MarketsTokenDetailsUM.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/state/MarketsTokenDetailsUM.kt index a1155099a5..e150d95894 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/state/MarketsTokenDetailsUM.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/state/MarketsTokenDetailsUM.kt @@ -76,5 +76,7 @@ internal data class MarketsTokenDetailsUM( data class RelatedNews( val articles: ImmutableList, val onArticledClicked: (id: Int) -> Unit, + val onFirstVisible: () -> Unit, + val onScroll: () -> Unit, ) } \ No newline at end of file