Updated on 2026-08-14
This commit is contained in:
parent
64693f66ca
commit
43f27a2f33
26 changed files with 21 additions and 370 deletions
|
|
@ -4,7 +4,6 @@ import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
|||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.markets.*
|
||||
import com.tangem.domain.markets.repositories.MarketsTokenRepository
|
||||
import com.tangem.domain.promo.PromoRepository
|
||||
import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -83,16 +82,4 @@ object MarketsDomainModule {
|
|||
fun provideGetTokenExchangesUseCase(marketsTokenRepository: MarketsTokenRepository): GetTokenExchangesUseCase {
|
||||
return GetTokenExchangesUseCase(marketsTokenRepository = marketsTokenRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideShouldShowYieldModeMarketPromoUseCase(
|
||||
promoRepository: PromoRepository,
|
||||
marketsTokenRepository: MarketsTokenRepository,
|
||||
): ShouldShowYieldModeMarketPromoUseCase {
|
||||
return ShouldShowYieldModeMarketPromoUseCase(
|
||||
promoRepository = promoRepository,
|
||||
marketsTokenRepository = marketsTokenRepository,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -79,10 +79,6 @@ object PreferencesKeys {
|
|||
|
||||
val SHOULD_SHOW_MARKETS_TOOLTIP_KEY by lazy { booleanPreferencesKey(name = "shouldShowMarketsTooltip") }
|
||||
|
||||
val MARKETS_YIELD_SUPPLY_NOTIFICATION_HIDE_CLICKED_KEY by lazy {
|
||||
booleanPreferencesKey(name = "marketsYieldSupplyNotificationHideClicked")
|
||||
}
|
||||
|
||||
val WALLET_FIRST_USAGE_DATE_KEY by lazy { longPreferencesKey(name = "walletFirstUsageDate") }
|
||||
|
||||
val IS_WALLET_NAMES_MIGRATION_DONE_KEY by lazy { booleanPreferencesKey(name = "isWalletNamesMigrationDone") }
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ import com.tangem.datasource.api.common.response.getOrThrow
|
|||
import com.tangem.datasource.api.markets.TangemTechMarketsApi
|
||||
import com.tangem.datasource.api.markets.models.response.TokenMarketExchangesResponse
|
||||
import com.tangem.datasource.local.datastore.RuntimeStateStore
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.common.wallets.getSyncStrict
|
||||
import com.tangem.domain.markets.*
|
||||
|
|
@ -31,7 +30,6 @@ import com.tangem.pagination.*
|
|||
import com.tangem.pagination.fetcher.LimitOffsetBatchFetcher
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
|
||||
|
|
@ -294,26 +292,6 @@ internal class DefaultMarketsTokenRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun showYieldModePromo(
|
||||
appCurrency: AppCurrency,
|
||||
interval: TokenMarketListConfig.Interval,
|
||||
): Boolean = try {
|
||||
val hasYieldSupplyTokens = marketsApi.getCoinsList(
|
||||
currency = appCurrency.code,
|
||||
interval = interval.toRequestParam(),
|
||||
order = TokenMarketListConfig.Order.YieldSupply.toRequestParam(),
|
||||
offset = 0,
|
||||
limit = 40,
|
||||
timestamp = null,
|
||||
search = null,
|
||||
).getOrThrow().tokens.isNotEmpty()
|
||||
|
||||
hasYieldSupplyTokens
|
||||
} catch (error: Exception) {
|
||||
Timber.e(error)
|
||||
false
|
||||
}
|
||||
|
||||
inline fun <T> catchListErrorAndSendEvent(block: () -> T): T {
|
||||
return catchErrorAndSendEvent(block, ::createListErrorEvent)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,8 +15,6 @@ internal fun TokenMarketListConfig.Order.toRequestParam(): String = when (this)
|
|||
TokenMarketListConfig.Order.Buyers -> "buyers"
|
||||
TokenMarketListConfig.Order.TopGainers -> "gainers"
|
||||
TokenMarketListConfig.Order.TopLosers -> "losers"
|
||||
TokenMarketListConfig.Order.Staking -> "staking"
|
||||
TokenMarketListConfig.Order.YieldSupply -> "yield"
|
||||
}
|
||||
|
||||
internal fun PriceChangeInterval.toRequestParam(): String = when (this) {
|
||||
|
|
|
|||
|
|
@ -96,20 +96,6 @@ internal class DefaultPromoRepository(
|
|||
appPreferencesStore.store(PreferencesKeys.getShouldShowPromoKey(promoId = promoId.name), false)
|
||||
}
|
||||
|
||||
override fun isMarketsYieldSupplyNotificationHideClicked(): Flow<Boolean> {
|
||||
return appPreferencesStore.get(
|
||||
key = PreferencesKeys.MARKETS_YIELD_SUPPLY_NOTIFICATION_HIDE_CLICKED_KEY,
|
||||
default = false,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun setMarketsYieldSupplyNotificationHideClicked() {
|
||||
appPreferencesStore.store(
|
||||
key = PreferencesKeys.MARKETS_YIELD_SUPPLY_NOTIFICATION_HIDE_CLICKED_KEY,
|
||||
value = true,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun isMoonpayPromoActive(): Boolean {
|
||||
val banner = runCatching(dispatchers.io) {
|
||||
val response = promoBannerStore.getSyncOrNull(MOONPAY_NAME) ?: run {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ data class TokenMarketListConfig(
|
|||
) {
|
||||
|
||||
enum class Order {
|
||||
ByRating, Trending, Buyers, TopGainers, TopLosers, Staking, YieldSupply,
|
||||
ByRating, Trending, Buyers, TopGainers, TopLosers
|
||||
}
|
||||
|
||||
enum class Interval {
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
package com.tangem.domain.markets
|
||||
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.markets.repositories.MarketsTokenRepository
|
||||
import com.tangem.domain.promo.PromoRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
class ShouldShowYieldModeMarketPromoUseCase(
|
||||
private val promoRepository: PromoRepository,
|
||||
private val marketsTokenRepository: MarketsTokenRepository,
|
||||
) {
|
||||
|
||||
operator fun invoke(appCurrency: AppCurrency, interval: TokenMarketListConfig.Interval): Flow<Boolean> {
|
||||
val hideClickedFlow = promoRepository.isMarketsYieldSupplyNotificationHideClicked()
|
||||
|
||||
return hideClickedFlow.map { hideClicked ->
|
||||
!hideClicked && marketsTokenRepository.showYieldModePromo(
|
||||
appCurrency = appCurrency,
|
||||
interval = interval,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.domain.markets.repositories
|
||||
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.markets.*
|
||||
import com.tangem.domain.models.account.DerivationIndex
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
|
|
@ -54,6 +53,4 @@ interface MarketsTokenRepository {
|
|||
* @param tokenId token id
|
||||
*/
|
||||
suspend fun getTokenExchanges(tokenId: CryptoCurrency.RawID): List<TokenMarketExchange>
|
||||
|
||||
suspend fun showYieldModePromo(appCurrency: AppCurrency, interval: TokenMarketListConfig.Interval): Boolean
|
||||
}
|
||||
|
|
@ -16,10 +16,6 @@ interface PromoRepository {
|
|||
|
||||
suspend fun setNeverToShowTokenPromo(promoId: PromoId)
|
||||
|
||||
fun isMarketsYieldSupplyNotificationHideClicked(): Flow<Boolean>
|
||||
|
||||
suspend fun setMarketsYieldSupplyNotificationHideClicked()
|
||||
|
||||
suspend fun isMoonpayPromoActive(): Boolean
|
||||
// endregion
|
||||
|
||||
|
|
|
|||
|
|
@ -353,8 +353,6 @@ internal class FeedComponentModel @Inject constructor(
|
|||
SortByTypeUM.ExperiencedBuyers,
|
||||
SortByTypeUM.TopGainers,
|
||||
SortByTypeUM.TopLosers,
|
||||
SortByTypeUM.Staking,
|
||||
SortByTypeUM.YieldSupply,
|
||||
-> {
|
||||
analyticsEventHandler.send(
|
||||
FeedAnalyticsEvent.TokenListOpened(
|
||||
|
|
@ -439,8 +437,6 @@ internal class FeedComponentModel @Inject constructor(
|
|||
SortByTypeUM.ExperiencedBuyers -> TokenMarketListConfig.Order.Buyers
|
||||
SortByTypeUM.TopGainers -> TokenMarketListConfig.Order.TopGainers
|
||||
SortByTypeUM.TopLosers -> TokenMarketListConfig.Order.TopLosers
|
||||
SortByTypeUM.Staking -> TokenMarketListConfig.Order.Staking
|
||||
SortByTypeUM.YieldSupply -> TokenMarketListConfig.Order.YieldSupply
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -341,8 +341,6 @@ internal class FeedMarketsBatchFlowManager(
|
|||
TokenMarketListConfig.Order.Buyers -> SortByTypeUM.ExperiencedBuyers
|
||||
TokenMarketListConfig.Order.TopGainers -> SortByTypeUM.TopGainers
|
||||
TokenMarketListConfig.Order.TopLosers -> SortByTypeUM.TopLosers
|
||||
TokenMarketListConfig.Order.Staking -> SortByTypeUM.Staking
|
||||
TokenMarketListConfig.Order.YieldSupply -> SortByTypeUM.YieldSupply
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.features.feed.model.market.list
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import arrow.core.Either
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.common.ui.markets.models.MarketsListItemUM
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
|
|
@ -12,20 +11,11 @@ import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
|
|||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.markets.GetMarketsTokenListFlowUseCase
|
||||
import com.tangem.domain.markets.ShouldShowYieldModeMarketPromoUseCase
|
||||
import com.tangem.domain.markets.TokenMarketListConfig
|
||||
import com.tangem.domain.markets.toSerializableParam
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.promo.PromoRepository
|
||||
import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
|
||||
import com.tangem.domain.settings.usercountry.models.UserCountry
|
||||
import com.tangem.domain.settings.usercountry.models.UserCountryError
|
||||
import com.tangem.domain.settings.usercountry.models.needApplyFCARestrictions
|
||||
import com.tangem.features.feed.components.market.list.DefaultMarketsTokenListComponent
|
||||
import com.tangem.features.feed.model.market.list.analytics.MarketsListAnalyticsEvent
|
||||
import com.tangem.features.feed.model.market.list.state.ListUM
|
||||
import com.tangem.features.feed.model.market.list.state.MarketsListUM
|
||||
import com.tangem.features.feed.model.market.list.state.MarketsNotificationUM
|
||||
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.market.list.statemanager.MarketsListUMStateManager
|
||||
|
|
@ -49,11 +39,8 @@ internal class MarketsListModel @Inject constructor(
|
|||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
getMarketsTokenListFlowUseCase: GetMarketsTokenListFlowUseCase,
|
||||
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
shouldShowYieldModeMarketPromoUseCase: ShouldShowYieldModeMarketPromoUseCase,
|
||||
paramsContainer: ParamsContainer,
|
||||
private val promoRepository: PromoRepository,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val getUserCountryUseCase: GetUserCountryUseCase,
|
||||
) : Model() {
|
||||
|
||||
private val updateQuotesJob = JobHolder()
|
||||
|
|
@ -126,61 +113,30 @@ internal class MarketsListModel @Inject constructor(
|
|||
flow = searchMarketsListManager.uiItems,
|
||||
flow2 = searchMarketsListManager.isInInitialLoadingErrorState,
|
||||
flow3 = searchMarketsListManager.isSearchNotFoundState,
|
||||
flow4 = shouldShowYieldModeMarketPromoUseCase(
|
||||
appCurrency = currentAppCurrency.value,
|
||||
interval = marketsListUMStateManager.selectedInterval.toBatchRequestInterval(),
|
||||
).conflate(),
|
||||
flow5 = getUserCountryUseCase.invoke(),
|
||||
) { uiItems, isInInitialLoadingErrorState, isSearchNotFoundState, isYieldModePromo, userCountry ->
|
||||
) { uiItems, isInInitialLoadingErrorState, isSearchNotFoundState ->
|
||||
MarketsItemsData(
|
||||
items = uiItems,
|
||||
isInErrorState = isInInitialLoadingErrorState,
|
||||
isSearchNotFound = isSearchNotFoundState,
|
||||
shouldShowYieldModePromo = isYieldModePromo,
|
||||
userCountry = userCountry,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
combine(
|
||||
flow = mainMarketsListManager.uiItems,
|
||||
flow2 = mainMarketsListManager.isInInitialLoadingErrorState,
|
||||
flow3 = shouldShowYieldModeMarketPromoUseCase(
|
||||
appCurrency = currentAppCurrency.value,
|
||||
interval = marketsListUMStateManager.selectedInterval.toBatchRequestInterval(),
|
||||
).conflate(),
|
||||
flow4 = getUserCountryUseCase.invoke(),
|
||||
) { uiItems, isInInitialLoadingErrorState, shouldShowYieldModePromo, userCountry ->
|
||||
) { uiItems, isInInitialLoadingErrorState ->
|
||||
MarketsItemsData(
|
||||
items = uiItems,
|
||||
isInErrorState = isInInitialLoadingErrorState,
|
||||
isSearchNotFound = false,
|
||||
shouldShowYieldModePromo = shouldShowYieldModePromo,
|
||||
userCountry = userCountry,
|
||||
)
|
||||
}
|
||||
}
|
||||
}.collect { marketsItemsData ->
|
||||
val isApplyFCARestrictions = marketsItemsData.userCountry.getOrNull().needApplyFCARestrictions()
|
||||
val shouldShowYieldModePromo = marketsItemsData.shouldShowYieldModePromo && !isApplyFCARestrictions
|
||||
if (marketsListUMStateManager.state.value.marketsNotificationUM == null && shouldShowYieldModePromo) {
|
||||
analyticsEventHandler.send(MarketsListAnalyticsEvent.YieldModePromoShown())
|
||||
}
|
||||
|
||||
marketsListUMStateManager.onUiItemsChanged(
|
||||
uiItems = marketsItemsData.items,
|
||||
isInErrorState = marketsItemsData.isInErrorState,
|
||||
isSearchNotFound = marketsItemsData.isSearchNotFound,
|
||||
marketsNotificationUM = if (shouldShowYieldModePromo) {
|
||||
MarketsNotificationUM.YieldSupplyPromo(
|
||||
onClick = {
|
||||
analyticsEventHandler.send(MarketsListAnalyticsEvent.YieldModeMoreInfoClicked())
|
||||
marketsListUMStateManager.selectedSortByType = SortByTypeUM.YieldSupply
|
||||
},
|
||||
onCloseClick = { onYieldModeNotificationCloseClick() },
|
||||
)
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -282,14 +238,6 @@ internal class MarketsListModel @Inject constructor(
|
|||
mainMarketsListManager.reload()
|
||||
}
|
||||
|
||||
private fun MarketsListUM.TrendInterval.toBatchRequestInterval(): TokenMarketListConfig.Interval {
|
||||
return when (this) {
|
||||
MarketsListUM.TrendInterval.H24 -> TokenMarketListConfig.Interval.H24
|
||||
MarketsListUM.TrendInterval.D7 -> TokenMarketListConfig.Interval.WEEK
|
||||
MarketsListUM.TrendInterval.M1 -> TokenMarketListConfig.Interval.MONTH
|
||||
}
|
||||
}
|
||||
|
||||
private fun initAnalytics() {
|
||||
searchMarketsListManager
|
||||
.isSearchNotFoundState
|
||||
|
|
@ -331,18 +279,9 @@ internal class MarketsListModel @Inject constructor(
|
|||
}.saveIn(updateQuotesJob)
|
||||
}
|
||||
|
||||
private fun onYieldModeNotificationCloseClick() {
|
||||
analyticsEventHandler.send(MarketsListAnalyticsEvent.YieldModePromoClosed())
|
||||
modelScope.launch {
|
||||
promoRepository.setMarketsYieldSupplyNotificationHideClicked()
|
||||
}
|
||||
}
|
||||
|
||||
private class MarketsItemsData(
|
||||
val items: ImmutableList<MarketsListItemUM>,
|
||||
val isInErrorState: Boolean,
|
||||
val isSearchNotFound: Boolean,
|
||||
val shouldShowYieldModePromo: Boolean,
|
||||
val userCountry: Either<UserCountryError, UserCountry>,
|
||||
)
|
||||
}
|
||||
|
|
@ -23,8 +23,6 @@ internal sealed class MarketsListAnalyticsEvent(
|
|||
SortByTypeUM.ExperiencedBuyers -> "Buyers"
|
||||
SortByTypeUM.TopGainers -> "Gainers"
|
||||
SortByTypeUM.TopLosers -> "Losers"
|
||||
SortByTypeUM.Staking -> "Staking"
|
||||
SortByTypeUM.YieldSupply -> "Yield Supply"
|
||||
},
|
||||
"Period" to when (interval) {
|
||||
MarketsListUM.TrendInterval.H24 -> "24h"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ internal data class MarketsListUM(
|
|||
val selectedInterval: TrendInterval,
|
||||
val onIntervalClick: (TrendInterval) -> Unit,
|
||||
val onSortByButtonClick: () -> Unit,
|
||||
val marketsNotificationUM: MarketsNotificationUM?,
|
||||
val onSearchClicked: () -> Unit,
|
||||
) {
|
||||
val isInSearchMode
|
||||
|
|
@ -43,8 +42,6 @@ internal enum class SortByTypeUM(val text: TextReference) {
|
|||
ExperiencedBuyers(resourceReference(R.string.markets_sort_by_experienced_buyers_title)),
|
||||
TopGainers(resourceReference(R.string.markets_sort_by_top_gainers_title)),
|
||||
TopLosers(resourceReference(R.string.markets_sort_by_top_losers_title)),
|
||||
Staking(resourceReference(R.string.common_staking)),
|
||||
YieldSupply(resourceReference(R.string.markets_sort_by_yield_mode_title)),
|
||||
}
|
||||
|
||||
@Immutable
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
package com.tangem.features.feed.model.market.list.state
|
||||
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.features.feed.impl.R
|
||||
|
||||
internal sealed class MarketsNotificationUM(val config: NotificationConfig) {
|
||||
|
||||
data class YieldSupplyPromo(
|
||||
val onClick: () -> Unit,
|
||||
val onCloseClick: () -> Unit,
|
||||
) : MarketsNotificationUM(
|
||||
config = NotificationConfig(
|
||||
iconResId = R.drawable.img_yield_supply_in_market_notification,
|
||||
title = resourceReference(R.string.markets_yield_supply_banner_title),
|
||||
subtitle = TextReference.EMPTY,
|
||||
onClick = onClick,
|
||||
onCloseClick = onCloseClick,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -24,8 +24,10 @@ import com.tangem.utils.coroutines.saveIn
|
|||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
private const val LOG_EVENTS = true
|
||||
|
||||
|
|
@ -305,8 +307,6 @@ internal class MarketsListBatchFlowManager(
|
|||
SortByTypeUM.ExperiencedBuyers -> TokenMarketListConfig.Order.Buyers
|
||||
SortByTypeUM.TopGainers -> TokenMarketListConfig.Order.TopGainers
|
||||
SortByTypeUM.TopLosers -> TokenMarketListConfig.Order.TopLosers
|
||||
SortByTypeUM.Staking -> TokenMarketListConfig.Order.Staking
|
||||
SortByTypeUM.YieldSupply -> TokenMarketListConfig.Order.YieldSupply
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -99,7 +99,6 @@ internal class MarketsListUMStateManager(
|
|||
isInErrorState: Boolean,
|
||||
isSearchNotFound: Boolean,
|
||||
uiItems: ImmutableList<MarketsListItemUM>,
|
||||
marketsNotificationUM: MarketsNotificationUM?,
|
||||
) {
|
||||
state.update { marketsListUM ->
|
||||
when {
|
||||
|
|
@ -115,19 +114,13 @@ internal class MarketsListUMStateManager(
|
|||
marketsListUM.copy(list = ListUM.Loading)
|
||||
}
|
||||
else -> {
|
||||
marketsListUM.updateItems(
|
||||
newItems = uiItems,
|
||||
marketsNotificationUM = marketsNotificationUM,
|
||||
)
|
||||
marketsListUM.updateItems(newItems = uiItems)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun MarketsListUM.updateItems(
|
||||
newItems: ImmutableList<MarketsListItemUM>,
|
||||
marketsNotificationUM: MarketsNotificationUM?,
|
||||
): MarketsListUM {
|
||||
private fun MarketsListUM.updateItems(newItems: ImmutableList<MarketsListItemUM>): MarketsListUM {
|
||||
val currentState = this
|
||||
|
||||
if (isInSearchMode.not() || currentState.showUnder100kButtonAlreadyPressed()) {
|
||||
|
|
@ -139,7 +132,6 @@ internal class MarketsListUMStateManager(
|
|||
shouldShowUnder100kTokensNotificationWasHidden = currentState
|
||||
.showUnder100kButtonAlreadyPressed(),
|
||||
),
|
||||
marketsNotificationUM = marketsNotificationUM,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -243,7 +235,6 @@ internal class MarketsListUMStateManager(
|
|||
onOptionClicked = ::onBottomSheetOptionClicked,
|
||||
),
|
||||
),
|
||||
marketsNotificationUM = null,
|
||||
onSearchClicked = {
|
||||
analyticsEventHandler.send(FeedAnalyticsEvent.TokenSearchedClicked())
|
||||
changeSearchBarIsActive(true)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.tangem.features.feed.ui.feed.preview
|
|||
|
||||
import com.tangem.common.ui.charts.state.MarketChartRawData
|
||||
import com.tangem.common.ui.markets.models.MarketsListItemUM
|
||||
import com.tangem.features.feed.ui.feed.components.articles.ArticleConfigUM
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.components.label.entity.LabelLeadingContentUM
|
||||
|
|
@ -15,6 +14,7 @@ import com.tangem.domain.models.currency.CryptoCurrency
|
|||
import com.tangem.features.feed.model.market.list.state.SortByTypeUM
|
||||
import com.tangem.features.feed.ui.earn.state.EarnListItemUM
|
||||
import com.tangem.features.feed.ui.earn.state.EarnListUM
|
||||
import com.tangem.features.feed.ui.feed.components.articles.ArticleConfigUM
|
||||
import com.tangem.features.feed.ui.feed.state.*
|
||||
import kotlinx.collections.immutable.*
|
||||
|
||||
|
|
@ -77,8 +77,6 @@ internal object FeedListPreviewDataProvider {
|
|||
items = items,
|
||||
isSelected = false,
|
||||
),
|
||||
SortByTypeUM.Staking to MarketChartUM.Loading,
|
||||
SortByTypeUM.TopLosers to MarketChartUM.Loading,
|
||||
)
|
||||
|
||||
val ratingChart = if (includeErrorState) {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,9 @@ import com.tangem.core.ui.components.fields.TangemSearchBarDefaults
|
|||
import com.tangem.core.ui.components.fields.entity.SearchBarUM
|
||||
import com.tangem.core.ui.components.keyboardAsState
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.LocalMainBottomSheetColor
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
|
@ -42,12 +44,10 @@ import com.tangem.features.feed.impl.R
|
|||
import com.tangem.features.feed.model.market.list.state.*
|
||||
import com.tangem.features.feed.ui.market.list.components.MarketsListLazyColumn
|
||||
import com.tangem.features.feed.ui.market.list.components.MarketsListSortByBottomSheet
|
||||
import com.tangem.features.feed.ui.market.list.components.YieldSupplyInMarketsPromoNotification
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
private const val SHOW_MORE_KEY = "privacyPolicy"
|
||||
private const val DELAY_FOR_FOCUS_REQUEST = 500L
|
||||
|
||||
@Composable
|
||||
|
|
@ -152,40 +152,6 @@ private fun ColumnScope.Content(state: MarketsListUM, modifier: Modifier = Modif
|
|||
onSortByClick = state.onSortByButtonClick,
|
||||
)
|
||||
}
|
||||
|
||||
AnimatedVisibility(
|
||||
state.list !is ListUM.LoadingError && state.isInSearchMode.not() &&
|
||||
state.selectedSortBy != SortByTypeUM.YieldSupply,
|
||||
) {
|
||||
val wrappedNotification = remember(state.list) { state.marketsNotificationUM }
|
||||
val showMore = stringResourceSafe(R.string.common_show_more)
|
||||
|
||||
when (wrappedNotification) {
|
||||
is MarketsNotificationUM.YieldSupplyPromo -> {
|
||||
val description = stringResourceSafe(
|
||||
R.string.markets_yield_supply_banner_description,
|
||||
showMore,
|
||||
)
|
||||
|
||||
val clickableDescription = annotatedReference {
|
||||
append(description.substringBefore(showMore))
|
||||
|
||||
pushStringAnnotation(SHOW_MORE_KEY, "")
|
||||
appendColored(showMore, TangemTheme.colors.text.accent)
|
||||
pop()
|
||||
}
|
||||
|
||||
YieldSupplyInMarketsPromoNotification(
|
||||
config = wrappedNotification.config.copy(
|
||||
subtitle = clickableDescription,
|
||||
),
|
||||
modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12),
|
||||
)
|
||||
}
|
||||
else -> { /* no-op */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val strokeWidth = TangemTheme.dimens.size0_5
|
||||
|
|
@ -376,10 +342,6 @@ private fun Preview() {
|
|||
onDismissRequest = {},
|
||||
content = SortByBottomSheetContentUM(selectedOption = SortByTypeUM.Rating) {},
|
||||
),
|
||||
marketsNotificationUM = MarketsNotificationUM.YieldSupplyPromo(
|
||||
onClick = {},
|
||||
onCloseClick = {},
|
||||
),
|
||||
onSearchClicked = {},
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -23,8 +23,6 @@ internal sealed class MarketsListAnalyticsEvent(
|
|||
SortByTypeUM.ExperiencedBuyers -> "Buyers"
|
||||
SortByTypeUM.TopGainers -> "Gainers"
|
||||
SortByTypeUM.TopLosers -> "Losers"
|
||||
SortByTypeUM.Staking -> "Staking"
|
||||
SortByTypeUM.YieldSupply -> "Yield Supply"
|
||||
},
|
||||
"Period" to when (interval) {
|
||||
MarketsListUM.TrendInterval.H24 -> "24h"
|
||||
|
|
|
|||
|
|
@ -10,15 +10,12 @@ import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
|
|||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.markets.GetMarketsTokenListFlowUseCase
|
||||
import com.tangem.domain.markets.ShouldShowYieldModeMarketPromoUseCase
|
||||
import com.tangem.domain.markets.TokenMarket
|
||||
import com.tangem.domain.markets.TokenMarketListConfig
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.promo.PromoRepository
|
||||
import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
|
||||
import com.tangem.domain.settings.usercountry.models.UserCountry
|
||||
import com.tangem.domain.settings.usercountry.models.UserCountryError
|
||||
import com.tangem.domain.settings.usercountry.models.needApplyFCARestrictions
|
||||
import com.tangem.features.markets.tokenlist.impl.analytics.MarketsListAnalyticsEvent
|
||||
import com.tangem.features.markets.tokenlist.impl.model.statemanager.MarketsListBatchFlowManager
|
||||
import com.tangem.features.markets.tokenlist.impl.model.statemanager.MarketsListUMStateManager
|
||||
|
|
@ -46,8 +43,6 @@ internal class MarketsListModel @Inject constructor(
|
|||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
getMarketsTokenListFlowUseCase: GetMarketsTokenListFlowUseCase,
|
||||
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
shouldShowYieldModeMarketPromoUseCase: ShouldShowYieldModeMarketPromoUseCase,
|
||||
private val promoRepository: PromoRepository,
|
||||
private val getUserCountryUseCase: GetUserCountryUseCase,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) : Model() {
|
||||
|
|
@ -118,17 +113,12 @@ internal class MarketsListModel @Inject constructor(
|
|||
flow = searchMarketsListManager.uiItems,
|
||||
flow2 = searchMarketsListManager.isInInitialLoadingErrorState,
|
||||
flow3 = searchMarketsListManager.isSearchNotFoundState,
|
||||
flow4 = shouldShowYieldModeMarketPromoUseCase(
|
||||
appCurrency = currentAppCurrency.value,
|
||||
interval = marketsListUMStateManager.selectedInterval.toBatchRequestInterval(),
|
||||
).conflate(),
|
||||
flow5 = getUserCountryUseCase.invoke(),
|
||||
) { uiItems, isInInitialLoadingErrorState, isSearchNotFoundState, isYieldModePromo, userCountry ->
|
||||
flow4 = getUserCountryUseCase.invoke(),
|
||||
) { uiItems, isInInitialLoadingErrorState, isSearchNotFoundState, userCountry ->
|
||||
MarketsItemsData(
|
||||
items = uiItems,
|
||||
isInErrorState = isInInitialLoadingErrorState,
|
||||
isSearchNotFound = isSearchNotFoundState,
|
||||
shouldShowYieldModePromo = isYieldModePromo,
|
||||
userCountry = userCountry,
|
||||
)
|
||||
}
|
||||
|
|
@ -136,43 +126,21 @@ internal class MarketsListModel @Inject constructor(
|
|||
combine(
|
||||
flow = mainMarketsListManager.uiItems,
|
||||
flow2 = mainMarketsListManager.isInInitialLoadingErrorState,
|
||||
flow3 = shouldShowYieldModeMarketPromoUseCase(
|
||||
appCurrency = currentAppCurrency.value,
|
||||
interval = marketsListUMStateManager.selectedInterval.toBatchRequestInterval(),
|
||||
).conflate(),
|
||||
flow4 = getUserCountryUseCase.invoke(),
|
||||
) { uiItems, isInInitialLoadingErrorState, shouldShowYieldModePromo, userCountry ->
|
||||
flow3 = getUserCountryUseCase.invoke(),
|
||||
) { uiItems, isInInitialLoadingErrorState, userCountry ->
|
||||
MarketsItemsData(
|
||||
items = uiItems,
|
||||
isInErrorState = isInInitialLoadingErrorState,
|
||||
isSearchNotFound = false,
|
||||
shouldShowYieldModePromo = shouldShowYieldModePromo,
|
||||
userCountry = userCountry,
|
||||
)
|
||||
}
|
||||
}
|
||||
}.collect { marketsItemsData ->
|
||||
val isApplyFCARestrictions = marketsItemsData.userCountry.getOrNull().needApplyFCARestrictions()
|
||||
val shouldShowYieldModePromo = marketsItemsData.shouldShowYieldModePromo && !isApplyFCARestrictions
|
||||
if (marketsListUMStateManager.state.value.marketsNotificationUM == null && shouldShowYieldModePromo) {
|
||||
analyticsEventHandler.send(MarketsListAnalyticsEvent.YieldModePromoShown())
|
||||
}
|
||||
|
||||
marketsListUMStateManager.onUiItemsChanged(
|
||||
uiItems = marketsItemsData.items,
|
||||
isInErrorState = marketsItemsData.isInErrorState,
|
||||
isSearchNotFound = marketsItemsData.isSearchNotFound,
|
||||
marketsNotificationUM = if (shouldShowYieldModePromo) {
|
||||
MarketsNotificationUM.YieldSupplyPromo(
|
||||
onClick = {
|
||||
analyticsEventHandler.send(MarketsListAnalyticsEvent.YieldModeMoreInfoClicked())
|
||||
marketsListUMStateManager.selectedSortByType = SortByTypeUM.YieldSupply
|
||||
},
|
||||
onCloseClick = { onYieldModeNotificationCloseClick() },
|
||||
)
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -327,18 +295,10 @@ internal class MarketsListModel @Inject constructor(
|
|||
}.saveIn(updateQuotesJob)
|
||||
}
|
||||
|
||||
private fun onYieldModeNotificationCloseClick() {
|
||||
analyticsEventHandler.send(MarketsListAnalyticsEvent.YieldModePromoClosed())
|
||||
modelScope.launch {
|
||||
promoRepository.setMarketsYieldSupplyNotificationHideClicked()
|
||||
}
|
||||
}
|
||||
|
||||
private class MarketsItemsData(
|
||||
val items: ImmutableList<MarketsListItemUM>,
|
||||
val isInErrorState: Boolean,
|
||||
val isSearchNotFound: Boolean,
|
||||
val shouldShowYieldModePromo: Boolean,
|
||||
val userCountry: Either<UserCountryError, UserCountry>,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
package com.tangem.features.markets.tokenlist.impl.model
|
||||
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.features.markets.impl.R
|
||||
|
||||
internal sealed class MarketsNotificationUM(val config: NotificationConfig) {
|
||||
|
||||
data class YieldSupplyPromo(
|
||||
val onClick: () -> Unit,
|
||||
val onCloseClick: () -> Unit,
|
||||
) : MarketsNotificationUM(
|
||||
config = NotificationConfig(
|
||||
iconResId = R.drawable.img_yield_supply_in_market_notification,
|
||||
title = resourceReference(R.string.markets_yield_supply_banner_title),
|
||||
subtitle = TextReference.EMPTY,
|
||||
onClick = onClick,
|
||||
onCloseClick = onCloseClick,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -347,8 +347,6 @@ internal class MarketsListBatchFlowManager(
|
|||
SortByTypeUM.ExperiencedBuyers -> TokenMarketListConfig.Order.Buyers
|
||||
SortByTypeUM.TopGainers -> TokenMarketListConfig.Order.TopGainers
|
||||
SortByTypeUM.TopLosers -> TokenMarketListConfig.Order.TopLosers
|
||||
SortByTypeUM.Staking -> TokenMarketListConfig.Order.Staking
|
||||
SortByTypeUM.YieldSupply -> TokenMarketListConfig.Order.YieldSupply
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import com.tangem.core.ui.event.triggeredEvent
|
|||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.features.markets.impl.R
|
||||
import com.tangem.features.markets.tokenlist.impl.model.MarketsNotificationUM
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.state.*
|
||||
import com.tangem.utils.Provider
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
|
@ -94,7 +93,6 @@ internal class MarketsListUMStateManager(
|
|||
isInErrorState: Boolean,
|
||||
isSearchNotFound: Boolean,
|
||||
uiItems: ImmutableList<MarketsListItemUM>,
|
||||
marketsNotificationUM: MarketsNotificationUM?,
|
||||
) {
|
||||
state.update { currentState ->
|
||||
when {
|
||||
|
|
@ -110,19 +108,13 @@ internal class MarketsListUMStateManager(
|
|||
currentState.copy(list = ListUM.Loading)
|
||||
}
|
||||
else -> {
|
||||
currentState.updateItems(
|
||||
newItems = uiItems,
|
||||
marketsNotificationUM = marketsNotificationUM,
|
||||
)
|
||||
currentState.updateItems(newItems = uiItems)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun MarketsListUM.updateItems(
|
||||
newItems: ImmutableList<MarketsListItemUM>,
|
||||
marketsNotificationUM: MarketsNotificationUM?,
|
||||
): MarketsListUM {
|
||||
private fun MarketsListUM.updateItems(newItems: ImmutableList<MarketsListItemUM>): MarketsListUM {
|
||||
val currentState = this
|
||||
|
||||
if (isInSearchMode.not() || currentState.showUnder100kButtonAlreadyPressed()) {
|
||||
|
|
@ -133,7 +125,6 @@ internal class MarketsListUMStateManager(
|
|||
.copy(
|
||||
wasUnder100kTokensNotificationHidden = currentState.showUnder100kButtonAlreadyPressed(),
|
||||
),
|
||||
marketsNotificationUM = marketsNotificationUM,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -255,6 +246,5 @@ internal class MarketsListUMStateManager(
|
|||
onOptionClicked = ::onBottomSheetOptionClicked,
|
||||
),
|
||||
),
|
||||
marketsNotificationUM = null,
|
||||
)
|
||||
}
|
||||
|
|
@ -38,16 +38,16 @@ import com.tangem.core.ui.components.fields.SearchBar
|
|||
import com.tangem.core.ui.components.fields.entity.SearchBarUM
|
||||
import com.tangem.core.ui.components.keyboardAsState
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.LocalMainBottomSheetColor
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.features.markets.impl.R
|
||||
import com.tangem.features.markets.tokenlist.impl.model.MarketsNotificationUM
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.components.MarketsListLazyColumn
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.components.MarketsListSortByBottomSheet
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.components.YieldSupplyInMarketsPromoNotification
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.preview.MarketChartListItemPreviewDataProvider
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.state.ListUM
|
||||
import com.tangem.features.markets.tokenlist.impl.ui.state.MarketsListUM
|
||||
|
|
@ -56,8 +56,6 @@ import com.tangem.features.markets.tokenlist.impl.ui.state.SortByTypeUM
|
|||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
private const val SHOW_MORE_KEY = "privacyPolicy"
|
||||
|
||||
@Composable
|
||||
internal fun MarketsList(
|
||||
state: MarketsListUM,
|
||||
|
|
@ -175,40 +173,6 @@ private fun ColumnScope.Content(state: MarketsListUM, modifier: Modifier = Modif
|
|||
onSortByClick = state.onSortByButtonClick,
|
||||
)
|
||||
}
|
||||
|
||||
val marketsNotificationUM = state.marketsNotificationUM
|
||||
AnimatedVisibility(
|
||||
state.list !is ListUM.LoadingError &&
|
||||
state.isInSearchMode.not() && state.selectedSortBy != SortByTypeUM.YieldSupply,
|
||||
) {
|
||||
val showMore = stringResourceSafe(R.string.common_show_more)
|
||||
|
||||
when (marketsNotificationUM) {
|
||||
is MarketsNotificationUM.YieldSupplyPromo -> {
|
||||
val description = stringResourceSafe(
|
||||
R.string.markets_yield_supply_banner_description,
|
||||
showMore,
|
||||
)
|
||||
|
||||
val clickableDescription = annotatedReference {
|
||||
append(description.substringBefore(showMore))
|
||||
|
||||
pushStringAnnotation(SHOW_MORE_KEY, "")
|
||||
appendColored(showMore, TangemTheme.colors.text.accent)
|
||||
pop()
|
||||
}
|
||||
|
||||
YieldSupplyInMarketsPromoNotification(
|
||||
config = marketsNotificationUM.config.copy(
|
||||
subtitle = clickableDescription,
|
||||
),
|
||||
modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12),
|
||||
)
|
||||
}
|
||||
else -> { /* no-op */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val strokeWidth = TangemTheme.dimens.size0_5
|
||||
|
|
@ -416,10 +380,6 @@ private fun Preview() {
|
|||
onDismissRequest = {},
|
||||
content = SortByBottomSheetContentUM(selectedOption = SortByTypeUM.Rating) {},
|
||||
),
|
||||
marketsNotificationUM = MarketsNotificationUM.YieldSupplyPromo(
|
||||
onClick = {},
|
||||
onCloseClick = {},
|
||||
),
|
||||
),
|
||||
onHeaderSizeChange = {},
|
||||
bottomSheetState = BottomSheetState.EXPANDED,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import com.tangem.core.ui.extensions.TextReference
|
|||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.features.markets.impl.R
|
||||
import com.tangem.features.markets.tokenlist.impl.model.MarketsNotificationUM
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
internal data class MarketsListUM(
|
||||
|
|
@ -19,7 +18,6 @@ internal data class MarketsListUM(
|
|||
val selectedInterval: TrendInterval,
|
||||
val onIntervalClick: (TrendInterval) -> Unit,
|
||||
val onSortByButtonClick: () -> Unit,
|
||||
val marketsNotificationUM: MarketsNotificationUM?,
|
||||
) {
|
||||
val isInSearchMode
|
||||
get() = searchBar.isActive
|
||||
|
|
@ -37,8 +35,6 @@ enum class SortByTypeUM(val text: TextReference) {
|
|||
ExperiencedBuyers(resourceReference(R.string.markets_sort_by_experienced_buyers_title)),
|
||||
TopGainers(resourceReference(R.string.markets_sort_by_top_gainers_title)),
|
||||
TopLosers(resourceReference(R.string.markets_sort_by_top_losers_title)),
|
||||
Staking(resourceReference(R.string.common_staking)),
|
||||
YieldSupply(resourceReference(R.string.yield_module_earn_sheet_title)),
|
||||
}
|
||||
|
||||
@Immutable
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue