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.common.wallets.UserWalletsListRepository
|
||||||
import com.tangem.domain.markets.*
|
import com.tangem.domain.markets.*
|
||||||
import com.tangem.domain.markets.repositories.MarketsTokenRepository
|
import com.tangem.domain.markets.repositories.MarketsTokenRepository
|
||||||
import com.tangem.domain.promo.PromoRepository
|
|
||||||
import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier
|
import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
|
|
@ -83,16 +82,4 @@ object MarketsDomainModule {
|
||||||
fun provideGetTokenExchangesUseCase(marketsTokenRepository: MarketsTokenRepository): GetTokenExchangesUseCase {
|
fun provideGetTokenExchangesUseCase(marketsTokenRepository: MarketsTokenRepository): GetTokenExchangesUseCase {
|
||||||
return GetTokenExchangesUseCase(marketsTokenRepository = marketsTokenRepository)
|
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 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 WALLET_FIRST_USAGE_DATE_KEY by lazy { longPreferencesKey(name = "walletFirstUsageDate") }
|
||||||
|
|
||||||
val IS_WALLET_NAMES_MIGRATION_DONE_KEY by lazy { booleanPreferencesKey(name = "isWalletNamesMigrationDone") }
|
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.TangemTechMarketsApi
|
||||||
import com.tangem.datasource.api.markets.models.response.TokenMarketExchangesResponse
|
import com.tangem.datasource.api.markets.models.response.TokenMarketExchangesResponse
|
||||||
import com.tangem.datasource.local.datastore.RuntimeStateStore
|
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.UserWalletsListRepository
|
||||||
import com.tangem.domain.common.wallets.getSyncStrict
|
import com.tangem.domain.common.wallets.getSyncStrict
|
||||||
import com.tangem.domain.markets.*
|
import com.tangem.domain.markets.*
|
||||||
|
|
@ -31,7 +30,6 @@ import com.tangem.pagination.*
|
||||||
import com.tangem.pagination.fetcher.LimitOffsetBatchFetcher
|
import com.tangem.pagination.fetcher.LimitOffsetBatchFetcher
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import timber.log.Timber
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
import java.util.concurrent.atomic.AtomicLong
|
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 {
|
inline fun <T> catchListErrorAndSendEvent(block: () -> T): T {
|
||||||
return catchErrorAndSendEvent(block, ::createListErrorEvent)
|
return catchErrorAndSendEvent(block, ::createListErrorEvent)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,6 @@ internal fun TokenMarketListConfig.Order.toRequestParam(): String = when (this)
|
||||||
TokenMarketListConfig.Order.Buyers -> "buyers"
|
TokenMarketListConfig.Order.Buyers -> "buyers"
|
||||||
TokenMarketListConfig.Order.TopGainers -> "gainers"
|
TokenMarketListConfig.Order.TopGainers -> "gainers"
|
||||||
TokenMarketListConfig.Order.TopLosers -> "losers"
|
TokenMarketListConfig.Order.TopLosers -> "losers"
|
||||||
TokenMarketListConfig.Order.Staking -> "staking"
|
|
||||||
TokenMarketListConfig.Order.YieldSupply -> "yield"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun PriceChangeInterval.toRequestParam(): String = when (this) {
|
internal fun PriceChangeInterval.toRequestParam(): String = when (this) {
|
||||||
|
|
|
||||||
|
|
@ -96,20 +96,6 @@ internal class DefaultPromoRepository(
|
||||||
appPreferencesStore.store(PreferencesKeys.getShouldShowPromoKey(promoId = promoId.name), false)
|
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 {
|
override suspend fun isMoonpayPromoActive(): Boolean {
|
||||||
val banner = runCatching(dispatchers.io) {
|
val banner = runCatching(dispatchers.io) {
|
||||||
val response = promoBannerStore.getSyncOrNull(MOONPAY_NAME) ?: run {
|
val response = promoBannerStore.getSyncOrNull(MOONPAY_NAME) ?: run {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ data class TokenMarketListConfig(
|
||||||
) {
|
) {
|
||||||
|
|
||||||
enum class Order {
|
enum class Order {
|
||||||
ByRating, Trending, Buyers, TopGainers, TopLosers, Staking, YieldSupply,
|
ByRating, Trending, Buyers, TopGainers, TopLosers
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class Interval {
|
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
|
package com.tangem.domain.markets.repositories
|
||||||
|
|
||||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
|
||||||
import com.tangem.domain.markets.*
|
import com.tangem.domain.markets.*
|
||||||
import com.tangem.domain.models.account.DerivationIndex
|
import com.tangem.domain.models.account.DerivationIndex
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
|
|
@ -54,6 +53,4 @@ interface MarketsTokenRepository {
|
||||||
* @param tokenId token id
|
* @param tokenId token id
|
||||||
*/
|
*/
|
||||||
suspend fun getTokenExchanges(tokenId: CryptoCurrency.RawID): List<TokenMarketExchange>
|
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)
|
suspend fun setNeverToShowTokenPromo(promoId: PromoId)
|
||||||
|
|
||||||
fun isMarketsYieldSupplyNotificationHideClicked(): Flow<Boolean>
|
|
||||||
|
|
||||||
suspend fun setMarketsYieldSupplyNotificationHideClicked()
|
|
||||||
|
|
||||||
suspend fun isMoonpayPromoActive(): Boolean
|
suspend fun isMoonpayPromoActive(): Boolean
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -353,8 +353,6 @@ internal class FeedComponentModel @Inject constructor(
|
||||||
SortByTypeUM.ExperiencedBuyers,
|
SortByTypeUM.ExperiencedBuyers,
|
||||||
SortByTypeUM.TopGainers,
|
SortByTypeUM.TopGainers,
|
||||||
SortByTypeUM.TopLosers,
|
SortByTypeUM.TopLosers,
|
||||||
SortByTypeUM.Staking,
|
|
||||||
SortByTypeUM.YieldSupply,
|
|
||||||
-> {
|
-> {
|
||||||
analyticsEventHandler.send(
|
analyticsEventHandler.send(
|
||||||
FeedAnalyticsEvent.TokenListOpened(
|
FeedAnalyticsEvent.TokenListOpened(
|
||||||
|
|
@ -439,8 +437,6 @@ internal class FeedComponentModel @Inject constructor(
|
||||||
SortByTypeUM.ExperiencedBuyers -> TokenMarketListConfig.Order.Buyers
|
SortByTypeUM.ExperiencedBuyers -> TokenMarketListConfig.Order.Buyers
|
||||||
SortByTypeUM.TopGainers -> TokenMarketListConfig.Order.TopGainers
|
SortByTypeUM.TopGainers -> TokenMarketListConfig.Order.TopGainers
|
||||||
SortByTypeUM.TopLosers -> TokenMarketListConfig.Order.TopLosers
|
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.Buyers -> SortByTypeUM.ExperiencedBuyers
|
||||||
TokenMarketListConfig.Order.TopGainers -> SortByTypeUM.TopGainers
|
TokenMarketListConfig.Order.TopGainers -> SortByTypeUM.TopGainers
|
||||||
TokenMarketListConfig.Order.TopLosers -> SortByTypeUM.TopLosers
|
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
|
package com.tangem.features.feed.model.market.list
|
||||||
|
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import arrow.core.Either
|
|
||||||
import arrow.core.getOrElse
|
import arrow.core.getOrElse
|
||||||
import com.tangem.common.ui.markets.models.MarketsListItemUM
|
import com.tangem.common.ui.markets.models.MarketsListItemUM
|
||||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
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.GetSelectedAppCurrencyUseCase
|
||||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||||
import com.tangem.domain.markets.GetMarketsTokenListFlowUseCase
|
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.markets.toSerializableParam
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
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.components.market.list.DefaultMarketsTokenListComponent
|
||||||
import com.tangem.features.feed.model.market.list.analytics.MarketsListAnalyticsEvent
|
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.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.state.SortByTypeUM
|
||||||
import com.tangem.features.feed.model.market.list.statemanager.MarketsListBatchFlowManager
|
import com.tangem.features.feed.model.market.list.statemanager.MarketsListBatchFlowManager
|
||||||
import com.tangem.features.feed.model.market.list.statemanager.MarketsListUMStateManager
|
import com.tangem.features.feed.model.market.list.statemanager.MarketsListUMStateManager
|
||||||
|
|
@ -49,11 +39,8 @@ internal class MarketsListModel @Inject constructor(
|
||||||
override val dispatchers: CoroutineDispatcherProvider,
|
override val dispatchers: CoroutineDispatcherProvider,
|
||||||
getMarketsTokenListFlowUseCase: GetMarketsTokenListFlowUseCase,
|
getMarketsTokenListFlowUseCase: GetMarketsTokenListFlowUseCase,
|
||||||
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||||
shouldShowYieldModeMarketPromoUseCase: ShouldShowYieldModeMarketPromoUseCase,
|
|
||||||
paramsContainer: ParamsContainer,
|
paramsContainer: ParamsContainer,
|
||||||
private val promoRepository: PromoRepository,
|
|
||||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||||
private val getUserCountryUseCase: GetUserCountryUseCase,
|
|
||||||
) : Model() {
|
) : Model() {
|
||||||
|
|
||||||
private val updateQuotesJob = JobHolder()
|
private val updateQuotesJob = JobHolder()
|
||||||
|
|
@ -126,61 +113,30 @@ internal class MarketsListModel @Inject constructor(
|
||||||
flow = searchMarketsListManager.uiItems,
|
flow = searchMarketsListManager.uiItems,
|
||||||
flow2 = searchMarketsListManager.isInInitialLoadingErrorState,
|
flow2 = searchMarketsListManager.isInInitialLoadingErrorState,
|
||||||
flow3 = searchMarketsListManager.isSearchNotFoundState,
|
flow3 = searchMarketsListManager.isSearchNotFoundState,
|
||||||
flow4 = shouldShowYieldModeMarketPromoUseCase(
|
) { uiItems, isInInitialLoadingErrorState, isSearchNotFoundState ->
|
||||||
appCurrency = currentAppCurrency.value,
|
|
||||||
interval = marketsListUMStateManager.selectedInterval.toBatchRequestInterval(),
|
|
||||||
).conflate(),
|
|
||||||
flow5 = getUserCountryUseCase.invoke(),
|
|
||||||
) { uiItems, isInInitialLoadingErrorState, isSearchNotFoundState, isYieldModePromo, userCountry ->
|
|
||||||
MarketsItemsData(
|
MarketsItemsData(
|
||||||
items = uiItems,
|
items = uiItems,
|
||||||
isInErrorState = isInInitialLoadingErrorState,
|
isInErrorState = isInInitialLoadingErrorState,
|
||||||
isSearchNotFound = isSearchNotFoundState,
|
isSearchNotFound = isSearchNotFoundState,
|
||||||
shouldShowYieldModePromo = isYieldModePromo,
|
|
||||||
userCountry = userCountry,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
combine(
|
combine(
|
||||||
flow = mainMarketsListManager.uiItems,
|
flow = mainMarketsListManager.uiItems,
|
||||||
flow2 = mainMarketsListManager.isInInitialLoadingErrorState,
|
flow2 = mainMarketsListManager.isInInitialLoadingErrorState,
|
||||||
flow3 = shouldShowYieldModeMarketPromoUseCase(
|
) { uiItems, isInInitialLoadingErrorState ->
|
||||||
appCurrency = currentAppCurrency.value,
|
|
||||||
interval = marketsListUMStateManager.selectedInterval.toBatchRequestInterval(),
|
|
||||||
).conflate(),
|
|
||||||
flow4 = getUserCountryUseCase.invoke(),
|
|
||||||
) { uiItems, isInInitialLoadingErrorState, shouldShowYieldModePromo, userCountry ->
|
|
||||||
MarketsItemsData(
|
MarketsItemsData(
|
||||||
items = uiItems,
|
items = uiItems,
|
||||||
isInErrorState = isInInitialLoadingErrorState,
|
isInErrorState = isInInitialLoadingErrorState,
|
||||||
isSearchNotFound = false,
|
isSearchNotFound = false,
|
||||||
shouldShowYieldModePromo = shouldShowYieldModePromo,
|
|
||||||
userCountry = userCountry,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.collect { marketsItemsData ->
|
}.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(
|
marketsListUMStateManager.onUiItemsChanged(
|
||||||
uiItems = marketsItemsData.items,
|
uiItems = marketsItemsData.items,
|
||||||
isInErrorState = marketsItemsData.isInErrorState,
|
isInErrorState = marketsItemsData.isInErrorState,
|
||||||
isSearchNotFound = marketsItemsData.isSearchNotFound,
|
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()
|
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() {
|
private fun initAnalytics() {
|
||||||
searchMarketsListManager
|
searchMarketsListManager
|
||||||
.isSearchNotFoundState
|
.isSearchNotFoundState
|
||||||
|
|
@ -331,18 +279,9 @@ internal class MarketsListModel @Inject constructor(
|
||||||
}.saveIn(updateQuotesJob)
|
}.saveIn(updateQuotesJob)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onYieldModeNotificationCloseClick() {
|
|
||||||
analyticsEventHandler.send(MarketsListAnalyticsEvent.YieldModePromoClosed())
|
|
||||||
modelScope.launch {
|
|
||||||
promoRepository.setMarketsYieldSupplyNotificationHideClicked()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class MarketsItemsData(
|
private class MarketsItemsData(
|
||||||
val items: ImmutableList<MarketsListItemUM>,
|
val items: ImmutableList<MarketsListItemUM>,
|
||||||
val isInErrorState: Boolean,
|
val isInErrorState: Boolean,
|
||||||
val isSearchNotFound: Boolean,
|
val isSearchNotFound: Boolean,
|
||||||
val shouldShowYieldModePromo: Boolean,
|
|
||||||
val userCountry: Either<UserCountryError, UserCountry>,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -23,8 +23,6 @@ internal sealed class MarketsListAnalyticsEvent(
|
||||||
SortByTypeUM.ExperiencedBuyers -> "Buyers"
|
SortByTypeUM.ExperiencedBuyers -> "Buyers"
|
||||||
SortByTypeUM.TopGainers -> "Gainers"
|
SortByTypeUM.TopGainers -> "Gainers"
|
||||||
SortByTypeUM.TopLosers -> "Losers"
|
SortByTypeUM.TopLosers -> "Losers"
|
||||||
SortByTypeUM.Staking -> "Staking"
|
|
||||||
SortByTypeUM.YieldSupply -> "Yield Supply"
|
|
||||||
},
|
},
|
||||||
"Period" to when (interval) {
|
"Period" to when (interval) {
|
||||||
MarketsListUM.TrendInterval.H24 -> "24h"
|
MarketsListUM.TrendInterval.H24 -> "24h"
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ internal data class MarketsListUM(
|
||||||
val selectedInterval: TrendInterval,
|
val selectedInterval: TrendInterval,
|
||||||
val onIntervalClick: (TrendInterval) -> Unit,
|
val onIntervalClick: (TrendInterval) -> Unit,
|
||||||
val onSortByButtonClick: () -> Unit,
|
val onSortByButtonClick: () -> Unit,
|
||||||
val marketsNotificationUM: MarketsNotificationUM?,
|
|
||||||
val onSearchClicked: () -> Unit,
|
val onSearchClicked: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val isInSearchMode
|
val isInSearchMode
|
||||||
|
|
@ -43,8 +42,6 @@ internal enum class SortByTypeUM(val text: TextReference) {
|
||||||
ExperiencedBuyers(resourceReference(R.string.markets_sort_by_experienced_buyers_title)),
|
ExperiencedBuyers(resourceReference(R.string.markets_sort_by_experienced_buyers_title)),
|
||||||
TopGainers(resourceReference(R.string.markets_sort_by_top_gainers_title)),
|
TopGainers(resourceReference(R.string.markets_sort_by_top_gainers_title)),
|
||||||
TopLosers(resourceReference(R.string.markets_sort_by_top_losers_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
|
@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.ImmutableList
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
private const val LOG_EVENTS = true
|
private const val LOG_EVENTS = true
|
||||||
|
|
||||||
|
|
@ -305,8 +307,6 @@ internal class MarketsListBatchFlowManager(
|
||||||
SortByTypeUM.ExperiencedBuyers -> TokenMarketListConfig.Order.Buyers
|
SortByTypeUM.ExperiencedBuyers -> TokenMarketListConfig.Order.Buyers
|
||||||
SortByTypeUM.TopGainers -> TokenMarketListConfig.Order.TopGainers
|
SortByTypeUM.TopGainers -> TokenMarketListConfig.Order.TopGainers
|
||||||
SortByTypeUM.TopLosers -> TokenMarketListConfig.Order.TopLosers
|
SortByTypeUM.TopLosers -> TokenMarketListConfig.Order.TopLosers
|
||||||
SortByTypeUM.Staking -> TokenMarketListConfig.Order.Staking
|
|
||||||
SortByTypeUM.YieldSupply -> TokenMarketListConfig.Order.YieldSupply
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,6 @@ internal class MarketsListUMStateManager(
|
||||||
isInErrorState: Boolean,
|
isInErrorState: Boolean,
|
||||||
isSearchNotFound: Boolean,
|
isSearchNotFound: Boolean,
|
||||||
uiItems: ImmutableList<MarketsListItemUM>,
|
uiItems: ImmutableList<MarketsListItemUM>,
|
||||||
marketsNotificationUM: MarketsNotificationUM?,
|
|
||||||
) {
|
) {
|
||||||
state.update { marketsListUM ->
|
state.update { marketsListUM ->
|
||||||
when {
|
when {
|
||||||
|
|
@ -115,19 +114,13 @@ internal class MarketsListUMStateManager(
|
||||||
marketsListUM.copy(list = ListUM.Loading)
|
marketsListUM.copy(list = ListUM.Loading)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
marketsListUM.updateItems(
|
marketsListUM.updateItems(newItems = uiItems)
|
||||||
newItems = uiItems,
|
|
||||||
marketsNotificationUM = marketsNotificationUM,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun MarketsListUM.updateItems(
|
private fun MarketsListUM.updateItems(newItems: ImmutableList<MarketsListItemUM>): MarketsListUM {
|
||||||
newItems: ImmutableList<MarketsListItemUM>,
|
|
||||||
marketsNotificationUM: MarketsNotificationUM?,
|
|
||||||
): MarketsListUM {
|
|
||||||
val currentState = this
|
val currentState = this
|
||||||
|
|
||||||
if (isInSearchMode.not() || currentState.showUnder100kButtonAlreadyPressed()) {
|
if (isInSearchMode.not() || currentState.showUnder100kButtonAlreadyPressed()) {
|
||||||
|
|
@ -139,7 +132,6 @@ internal class MarketsListUMStateManager(
|
||||||
shouldShowUnder100kTokensNotificationWasHidden = currentState
|
shouldShowUnder100kTokensNotificationWasHidden = currentState
|
||||||
.showUnder100kButtonAlreadyPressed(),
|
.showUnder100kButtonAlreadyPressed(),
|
||||||
),
|
),
|
||||||
marketsNotificationUM = marketsNotificationUM,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -243,7 +235,6 @@ internal class MarketsListUMStateManager(
|
||||||
onOptionClicked = ::onBottomSheetOptionClicked,
|
onOptionClicked = ::onBottomSheetOptionClicked,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
marketsNotificationUM = null,
|
|
||||||
onSearchClicked = {
|
onSearchClicked = {
|
||||||
analyticsEventHandler.send(FeedAnalyticsEvent.TokenSearchedClicked())
|
analyticsEventHandler.send(FeedAnalyticsEvent.TokenSearchedClicked())
|
||||||
changeSearchBarIsActive(true)
|
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.charts.state.MarketChartRawData
|
||||||
import com.tangem.common.ui.markets.models.MarketsListItemUM
|
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.R
|
||||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||||
import com.tangem.core.ui.components.label.entity.LabelLeadingContentUM
|
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.model.market.list.state.SortByTypeUM
|
||||||
import com.tangem.features.feed.ui.earn.state.EarnListItemUM
|
import com.tangem.features.feed.ui.earn.state.EarnListItemUM
|
||||||
import com.tangem.features.feed.ui.earn.state.EarnListUM
|
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 com.tangem.features.feed.ui.feed.state.*
|
||||||
import kotlinx.collections.immutable.*
|
import kotlinx.collections.immutable.*
|
||||||
|
|
||||||
|
|
@ -77,8 +77,6 @@ internal object FeedListPreviewDataProvider {
|
||||||
items = items,
|
items = items,
|
||||||
isSelected = false,
|
isSelected = false,
|
||||||
),
|
),
|
||||||
SortByTypeUM.Staking to MarketChartUM.Loading,
|
|
||||||
SortByTypeUM.TopLosers to MarketChartUM.Loading,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val ratingChart = if (includeErrorState) {
|
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.fields.entity.SearchBarUM
|
||||||
import com.tangem.core.ui.components.keyboardAsState
|
import com.tangem.core.ui.components.keyboardAsState
|
||||||
import com.tangem.core.ui.event.consumedEvent
|
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.LocalMainBottomSheetColor
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
import com.tangem.core.ui.res.TangemThemePreview
|
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.model.market.list.state.*
|
||||||
import com.tangem.features.feed.ui.market.list.components.MarketsListLazyColumn
|
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.MarketsListSortByBottomSheet
|
||||||
import com.tangem.features.feed.ui.market.list.components.YieldSupplyInMarketsPromoNotification
|
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
|
||||||
private const val SHOW_MORE_KEY = "privacyPolicy"
|
|
||||||
private const val DELAY_FOR_FOCUS_REQUEST = 500L
|
private const val DELAY_FOR_FOCUS_REQUEST = 500L
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -152,40 +152,6 @@ private fun ColumnScope.Content(state: MarketsListUM, modifier: Modifier = Modif
|
||||||
onSortByClick = state.onSortByButtonClick,
|
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
|
val strokeWidth = TangemTheme.dimens.size0_5
|
||||||
|
|
@ -376,10 +342,6 @@ private fun Preview() {
|
||||||
onDismissRequest = {},
|
onDismissRequest = {},
|
||||||
content = SortByBottomSheetContentUM(selectedOption = SortByTypeUM.Rating) {},
|
content = SortByBottomSheetContentUM(selectedOption = SortByTypeUM.Rating) {},
|
||||||
),
|
),
|
||||||
marketsNotificationUM = MarketsNotificationUM.YieldSupplyPromo(
|
|
||||||
onClick = {},
|
|
||||||
onCloseClick = {},
|
|
||||||
),
|
|
||||||
onSearchClicked = {},
|
onSearchClicked = {},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,6 @@ internal sealed class MarketsListAnalyticsEvent(
|
||||||
SortByTypeUM.ExperiencedBuyers -> "Buyers"
|
SortByTypeUM.ExperiencedBuyers -> "Buyers"
|
||||||
SortByTypeUM.TopGainers -> "Gainers"
|
SortByTypeUM.TopGainers -> "Gainers"
|
||||||
SortByTypeUM.TopLosers -> "Losers"
|
SortByTypeUM.TopLosers -> "Losers"
|
||||||
SortByTypeUM.Staking -> "Staking"
|
|
||||||
SortByTypeUM.YieldSupply -> "Yield Supply"
|
|
||||||
},
|
},
|
||||||
"Period" to when (interval) {
|
"Period" to when (interval) {
|
||||||
MarketsListUM.TrendInterval.H24 -> "24h"
|
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.GetSelectedAppCurrencyUseCase
|
||||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||||
import com.tangem.domain.markets.GetMarketsTokenListFlowUseCase
|
import com.tangem.domain.markets.GetMarketsTokenListFlowUseCase
|
||||||
import com.tangem.domain.markets.ShouldShowYieldModeMarketPromoUseCase
|
|
||||||
import com.tangem.domain.markets.TokenMarket
|
import com.tangem.domain.markets.TokenMarket
|
||||||
import com.tangem.domain.markets.TokenMarketListConfig
|
import com.tangem.domain.markets.TokenMarketListConfig
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
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.GetUserCountryUseCase
|
||||||
import com.tangem.domain.settings.usercountry.models.UserCountry
|
import com.tangem.domain.settings.usercountry.models.UserCountry
|
||||||
import com.tangem.domain.settings.usercountry.models.UserCountryError
|
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.analytics.MarketsListAnalyticsEvent
|
||||||
import com.tangem.features.markets.tokenlist.impl.model.statemanager.MarketsListBatchFlowManager
|
import com.tangem.features.markets.tokenlist.impl.model.statemanager.MarketsListBatchFlowManager
|
||||||
import com.tangem.features.markets.tokenlist.impl.model.statemanager.MarketsListUMStateManager
|
import com.tangem.features.markets.tokenlist.impl.model.statemanager.MarketsListUMStateManager
|
||||||
|
|
@ -46,8 +43,6 @@ internal class MarketsListModel @Inject constructor(
|
||||||
override val dispatchers: CoroutineDispatcherProvider,
|
override val dispatchers: CoroutineDispatcherProvider,
|
||||||
getMarketsTokenListFlowUseCase: GetMarketsTokenListFlowUseCase,
|
getMarketsTokenListFlowUseCase: GetMarketsTokenListFlowUseCase,
|
||||||
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||||
shouldShowYieldModeMarketPromoUseCase: ShouldShowYieldModeMarketPromoUseCase,
|
|
||||||
private val promoRepository: PromoRepository,
|
|
||||||
private val getUserCountryUseCase: GetUserCountryUseCase,
|
private val getUserCountryUseCase: GetUserCountryUseCase,
|
||||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||||
) : Model() {
|
) : Model() {
|
||||||
|
|
@ -118,17 +113,12 @@ internal class MarketsListModel @Inject constructor(
|
||||||
flow = searchMarketsListManager.uiItems,
|
flow = searchMarketsListManager.uiItems,
|
||||||
flow2 = searchMarketsListManager.isInInitialLoadingErrorState,
|
flow2 = searchMarketsListManager.isInInitialLoadingErrorState,
|
||||||
flow3 = searchMarketsListManager.isSearchNotFoundState,
|
flow3 = searchMarketsListManager.isSearchNotFoundState,
|
||||||
flow4 = shouldShowYieldModeMarketPromoUseCase(
|
flow4 = getUserCountryUseCase.invoke(),
|
||||||
appCurrency = currentAppCurrency.value,
|
) { uiItems, isInInitialLoadingErrorState, isSearchNotFoundState, userCountry ->
|
||||||
interval = marketsListUMStateManager.selectedInterval.toBatchRequestInterval(),
|
|
||||||
).conflate(),
|
|
||||||
flow5 = getUserCountryUseCase.invoke(),
|
|
||||||
) { uiItems, isInInitialLoadingErrorState, isSearchNotFoundState, isYieldModePromo, userCountry ->
|
|
||||||
MarketsItemsData(
|
MarketsItemsData(
|
||||||
items = uiItems,
|
items = uiItems,
|
||||||
isInErrorState = isInInitialLoadingErrorState,
|
isInErrorState = isInInitialLoadingErrorState,
|
||||||
isSearchNotFound = isSearchNotFoundState,
|
isSearchNotFound = isSearchNotFoundState,
|
||||||
shouldShowYieldModePromo = isYieldModePromo,
|
|
||||||
userCountry = userCountry,
|
userCountry = userCountry,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -136,43 +126,21 @@ internal class MarketsListModel @Inject constructor(
|
||||||
combine(
|
combine(
|
||||||
flow = mainMarketsListManager.uiItems,
|
flow = mainMarketsListManager.uiItems,
|
||||||
flow2 = mainMarketsListManager.isInInitialLoadingErrorState,
|
flow2 = mainMarketsListManager.isInInitialLoadingErrorState,
|
||||||
flow3 = shouldShowYieldModeMarketPromoUseCase(
|
flow3 = getUserCountryUseCase.invoke(),
|
||||||
appCurrency = currentAppCurrency.value,
|
) { uiItems, isInInitialLoadingErrorState, userCountry ->
|
||||||
interval = marketsListUMStateManager.selectedInterval.toBatchRequestInterval(),
|
|
||||||
).conflate(),
|
|
||||||
flow4 = getUserCountryUseCase.invoke(),
|
|
||||||
) { uiItems, isInInitialLoadingErrorState, shouldShowYieldModePromo, userCountry ->
|
|
||||||
MarketsItemsData(
|
MarketsItemsData(
|
||||||
items = uiItems,
|
items = uiItems,
|
||||||
isInErrorState = isInInitialLoadingErrorState,
|
isInErrorState = isInInitialLoadingErrorState,
|
||||||
isSearchNotFound = false,
|
isSearchNotFound = false,
|
||||||
shouldShowYieldModePromo = shouldShowYieldModePromo,
|
|
||||||
userCountry = userCountry,
|
userCountry = userCountry,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.collect { marketsItemsData ->
|
}.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(
|
marketsListUMStateManager.onUiItemsChanged(
|
||||||
uiItems = marketsItemsData.items,
|
uiItems = marketsItemsData.items,
|
||||||
isInErrorState = marketsItemsData.isInErrorState,
|
isInErrorState = marketsItemsData.isInErrorState,
|
||||||
isSearchNotFound = marketsItemsData.isSearchNotFound,
|
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)
|
}.saveIn(updateQuotesJob)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onYieldModeNotificationCloseClick() {
|
|
||||||
analyticsEventHandler.send(MarketsListAnalyticsEvent.YieldModePromoClosed())
|
|
||||||
modelScope.launch {
|
|
||||||
promoRepository.setMarketsYieldSupplyNotificationHideClicked()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class MarketsItemsData(
|
private class MarketsItemsData(
|
||||||
val items: ImmutableList<MarketsListItemUM>,
|
val items: ImmutableList<MarketsListItemUM>,
|
||||||
val isInErrorState: Boolean,
|
val isInErrorState: Boolean,
|
||||||
val isSearchNotFound: Boolean,
|
val isSearchNotFound: Boolean,
|
||||||
val shouldShowYieldModePromo: Boolean,
|
|
||||||
val userCountry: Either<UserCountryError, UserCountry>,
|
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.ExperiencedBuyers -> TokenMarketListConfig.Order.Buyers
|
||||||
SortByTypeUM.TopGainers -> TokenMarketListConfig.Order.TopGainers
|
SortByTypeUM.TopGainers -> TokenMarketListConfig.Order.TopGainers
|
||||||
SortByTypeUM.TopLosers -> TokenMarketListConfig.Order.TopLosers
|
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.core.ui.extensions.resourceReference
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import com.tangem.features.markets.impl.R
|
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.features.markets.tokenlist.impl.ui.state.*
|
||||||
import com.tangem.utils.Provider
|
import com.tangem.utils.Provider
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
@ -94,7 +93,6 @@ internal class MarketsListUMStateManager(
|
||||||
isInErrorState: Boolean,
|
isInErrorState: Boolean,
|
||||||
isSearchNotFound: Boolean,
|
isSearchNotFound: Boolean,
|
||||||
uiItems: ImmutableList<MarketsListItemUM>,
|
uiItems: ImmutableList<MarketsListItemUM>,
|
||||||
marketsNotificationUM: MarketsNotificationUM?,
|
|
||||||
) {
|
) {
|
||||||
state.update { currentState ->
|
state.update { currentState ->
|
||||||
when {
|
when {
|
||||||
|
|
@ -110,19 +108,13 @@ internal class MarketsListUMStateManager(
|
||||||
currentState.copy(list = ListUM.Loading)
|
currentState.copy(list = ListUM.Loading)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
currentState.updateItems(
|
currentState.updateItems(newItems = uiItems)
|
||||||
newItems = uiItems,
|
|
||||||
marketsNotificationUM = marketsNotificationUM,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun MarketsListUM.updateItems(
|
private fun MarketsListUM.updateItems(newItems: ImmutableList<MarketsListItemUM>): MarketsListUM {
|
||||||
newItems: ImmutableList<MarketsListItemUM>,
|
|
||||||
marketsNotificationUM: MarketsNotificationUM?,
|
|
||||||
): MarketsListUM {
|
|
||||||
val currentState = this
|
val currentState = this
|
||||||
|
|
||||||
if (isInSearchMode.not() || currentState.showUnder100kButtonAlreadyPressed()) {
|
if (isInSearchMode.not() || currentState.showUnder100kButtonAlreadyPressed()) {
|
||||||
|
|
@ -133,7 +125,6 @@ internal class MarketsListUMStateManager(
|
||||||
.copy(
|
.copy(
|
||||||
wasUnder100kTokensNotificationHidden = currentState.showUnder100kButtonAlreadyPressed(),
|
wasUnder100kTokensNotificationHidden = currentState.showUnder100kButtonAlreadyPressed(),
|
||||||
),
|
),
|
||||||
marketsNotificationUM = marketsNotificationUM,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -255,6 +246,5 @@ internal class MarketsListUMStateManager(
|
||||||
onOptionClicked = ::onBottomSheetOptionClicked,
|
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.fields.entity.SearchBarUM
|
||||||
import com.tangem.core.ui.components.keyboardAsState
|
import com.tangem.core.ui.components.keyboardAsState
|
||||||
import com.tangem.core.ui.event.consumedEvent
|
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.LocalMainBottomSheetColor
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
import com.tangem.core.ui.res.TangemThemePreview
|
import com.tangem.core.ui.res.TangemThemePreview
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import com.tangem.features.markets.impl.R
|
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.MarketsListLazyColumn
|
||||||
import com.tangem.features.markets.tokenlist.impl.ui.components.MarketsListSortByBottomSheet
|
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.preview.MarketChartListItemPreviewDataProvider
|
||||||
import com.tangem.features.markets.tokenlist.impl.ui.state.ListUM
|
import com.tangem.features.markets.tokenlist.impl.ui.state.ListUM
|
||||||
import com.tangem.features.markets.tokenlist.impl.ui.state.MarketsListUM
|
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.persistentListOf
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
|
|
||||||
private const val SHOW_MORE_KEY = "privacyPolicy"
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun MarketsList(
|
internal fun MarketsList(
|
||||||
state: MarketsListUM,
|
state: MarketsListUM,
|
||||||
|
|
@ -175,40 +173,6 @@ private fun ColumnScope.Content(state: MarketsListUM, modifier: Modifier = Modif
|
||||||
onSortByClick = state.onSortByButtonClick,
|
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
|
val strokeWidth = TangemTheme.dimens.size0_5
|
||||||
|
|
@ -416,10 +380,6 @@ private fun Preview() {
|
||||||
onDismissRequest = {},
|
onDismissRequest = {},
|
||||||
content = SortByBottomSheetContentUM(selectedOption = SortByTypeUM.Rating) {},
|
content = SortByBottomSheetContentUM(selectedOption = SortByTypeUM.Rating) {},
|
||||||
),
|
),
|
||||||
marketsNotificationUM = MarketsNotificationUM.YieldSupplyPromo(
|
|
||||||
onClick = {},
|
|
||||||
onCloseClick = {},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
onHeaderSizeChange = {},
|
onHeaderSizeChange = {},
|
||||||
bottomSheetState = BottomSheetState.EXPANDED,
|
bottomSheetState = BottomSheetState.EXPANDED,
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import com.tangem.core.ui.extensions.TextReference
|
||||||
import com.tangem.core.ui.extensions.resourceReference
|
import com.tangem.core.ui.extensions.resourceReference
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import com.tangem.features.markets.impl.R
|
import com.tangem.features.markets.impl.R
|
||||||
import com.tangem.features.markets.tokenlist.impl.model.MarketsNotificationUM
|
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
internal data class MarketsListUM(
|
internal data class MarketsListUM(
|
||||||
|
|
@ -19,7 +18,6 @@ internal data class MarketsListUM(
|
||||||
val selectedInterval: TrendInterval,
|
val selectedInterval: TrendInterval,
|
||||||
val onIntervalClick: (TrendInterval) -> Unit,
|
val onIntervalClick: (TrendInterval) -> Unit,
|
||||||
val onSortByButtonClick: () -> Unit,
|
val onSortByButtonClick: () -> Unit,
|
||||||
val marketsNotificationUM: MarketsNotificationUM?,
|
|
||||||
) {
|
) {
|
||||||
val isInSearchMode
|
val isInSearchMode
|
||||||
get() = searchBar.isActive
|
get() = searchBar.isActive
|
||||||
|
|
@ -37,8 +35,6 @@ enum class SortByTypeUM(val text: TextReference) {
|
||||||
ExperiencedBuyers(resourceReference(R.string.markets_sort_by_experienced_buyers_title)),
|
ExperiencedBuyers(resourceReference(R.string.markets_sort_by_experienced_buyers_title)),
|
||||||
TopGainers(resourceReference(R.string.markets_sort_by_top_gainers_title)),
|
TopGainers(resourceReference(R.string.markets_sort_by_top_gainers_title)),
|
||||||
TopLosers(resourceReference(R.string.markets_sort_by_top_losers_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
|
@Immutable
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue