Updated on 2026-08-14
This commit is contained in:
commit
47dc1c38df
762 changed files with 23960 additions and 10924 deletions
|
|
@ -7,6 +7,8 @@ import com.tangem.core.decompose.context.AppComponentContext
|
|||
import com.tangem.core.decompose.navigation.Route
|
||||
import com.tangem.core.ui.decompose.ComposableModularBottomSheetContentComponent
|
||||
import com.tangem.features.feed.components.earn.DefaultEarnComponent
|
||||
import com.tangem.features.promobanners.api.NewPromoBannersFeatureToggles
|
||||
import com.tangem.features.promobanners.api.PromoBannersBlockComponent
|
||||
import com.tangem.features.feed.components.feed.DefaultFeedComponent
|
||||
import com.tangem.features.feed.components.market.details.DefaultMarketsTokenDetailsComponent
|
||||
import com.tangem.features.feed.components.market.details.portfolio.add.AddToPortfolioPreselectedDataComponent
|
||||
|
|
@ -21,6 +23,8 @@ internal class FeedEntryChildFactory @Inject constructor(
|
|||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val portfolioComponentFactory: MarketsPortfolioComponent.Factory,
|
||||
private val addToPortfolioPreselectedDataComponent: AddToPortfolioPreselectedDataComponent.Factory,
|
||||
private val promoBannersBlockComponentFactory: PromoBannersBlockComponent.Factory,
|
||||
private val newPromoBannersFeatureToggles: NewPromoBannersFeatureToggles,
|
||||
) {
|
||||
|
||||
@Serializable
|
||||
|
|
@ -100,6 +104,8 @@ internal class FeedEntryChildFactory @Inject constructor(
|
|||
appComponentContext = appComponentContext,
|
||||
params = DefaultFeedComponent.FeedParams(feedClickIntents = feedEntryClickIntents),
|
||||
addToPortfolioComponentFactory = addToPortfolioPreselectedDataComponent,
|
||||
promoBannersBlockComponentFactory = promoBannersBlockComponentFactory,
|
||||
newPromoBannersFeatureToggles = newPromoBannersFeatureToggles,
|
||||
)
|
||||
}
|
||||
is Child.Earn -> {
|
||||
|
|
|
|||
|
|
@ -11,12 +11,15 @@ import com.arkivanov.decompose.extensions.compose.subscribeAsState
|
|||
import com.arkivanov.decompose.router.slot.childSlot
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.context.child
|
||||
import com.tangem.core.decompose.context.childByContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.core.ui.decompose.ComposableModularBottomSheetContentComponent
|
||||
import com.tangem.core.ui.decompose.EmptyComposableBottomSheetComponent
|
||||
import com.tangem.features.promobanners.api.NewPromoBannersFeatureToggles
|
||||
import com.tangem.features.promobanners.api.PromoBannersBlockComponent
|
||||
import com.tangem.features.feed.components.market.details.portfolio.add.AddToPortfolioPreselectedDataComponent
|
||||
import com.tangem.features.feed.components.market.details.portfolio.add.AddToPortfolioPreselectedDataComponent.Params
|
||||
import com.tangem.features.feed.model.feed.FeedComponentModel
|
||||
|
|
@ -28,10 +31,22 @@ internal class DefaultFeedComponent(
|
|||
appComponentContext: AppComponentContext,
|
||||
private val params: FeedParams,
|
||||
private val addToPortfolioComponentFactory: AddToPortfolioPreselectedDataComponent.Factory,
|
||||
private val promoBannersBlockComponentFactory: PromoBannersBlockComponent.Factory,
|
||||
private val newPromoBannersFeatureToggles: NewPromoBannersFeatureToggles,
|
||||
) : ComposableModularBottomSheetContentComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val feedComponentModel = getOrCreateModel<FeedComponentModel, FeedParams>(params = params)
|
||||
|
||||
private val promoBannersBlockComponent: PromoBannersBlockComponent? by lazy {
|
||||
if (!newPromoBannersFeatureToggles.isNewPromoBannersEnabled) return@lazy null
|
||||
promoBannersBlockComponentFactory.create(
|
||||
context = child("promoBannersBlockComponent"),
|
||||
params = PromoBannersBlockComponent.Params(
|
||||
placeholder = PromoBannersBlockComponent.Placeholder.FEED,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private val bottomSheetSlot = childSlot(
|
||||
source = feedComponentModel.bottomSheetNavigation,
|
||||
serializer = null,
|
||||
|
|
@ -62,6 +77,7 @@ internal class DefaultFeedComponent(
|
|||
FeedList(
|
||||
modifier = modifier,
|
||||
state = state,
|
||||
promoBannersBlockComponent = promoBannersBlockComponent,
|
||||
)
|
||||
bottomSheet.child?.instance?.BottomSheet()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ internal class PortfolioTokenUMConverter(
|
|||
if (action.unavailabilityReason == ScenarioUnavailabilityReason.None) {
|
||||
when (action) {
|
||||
is TokenActionsState.ActionState.Buy -> QuickActionUM.Buy
|
||||
is TokenActionsState.ActionState.Swap -> QuickActionUM.Exchange(action.showBadge)
|
||||
is TokenActionsState.ActionState.Swap -> QuickActionUM.Exchange(action.shouldShowBadge)
|
||||
is TokenActionsState.ActionState.Receive -> QuickActionUM.Receive
|
||||
is TokenActionsState.ActionState.Stake -> QuickActionUM.Stake
|
||||
is TokenActionsState.ActionState.YieldMode -> QuickActionUM.YieldMode(action.apy)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,16 @@
|
|||
package com.tangem.features.feed.components.news.details
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
|
|
@ -12,7 +19,13 @@ import com.tangem.core.ui.components.appbar.TangemTopAppBar
|
|||
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
|
||||
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
|
||||
import com.tangem.core.ui.decompose.ComposableModularBottomSheetContentComponent
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBar
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBarType
|
||||
import com.tangem.core.ui.extensions.clickableSingle
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.LocalMainBottomSheetColor
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.markets.TokenMarketParams
|
||||
import com.tangem.domain.news.model.NewsListConfig
|
||||
|
|
@ -32,21 +45,65 @@ internal class DefaultNewsDetailsComponent(
|
|||
override fun Title(bottomSheetState: State<BottomSheetState>) {
|
||||
val background = LocalMainBottomSheetColor.current.value
|
||||
val state by newsDetailsModel.state.collectAsStateWithLifecycle()
|
||||
TangemTopAppBar(
|
||||
containerColor = background,
|
||||
title = null,
|
||||
startButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = R.drawable.ic_back_24,
|
||||
onClicked = state.onBackClick,
|
||||
isEnabled = bottomSheetState.value == BottomSheetState.EXPANDED,
|
||||
),
|
||||
endButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = R.drawable.ic_share_24,
|
||||
onClicked = state.onShareClick,
|
||||
isEnabled = bottomSheetState.value == BottomSheetState.EXPANDED &&
|
||||
state.articlesStateUM is ArticlesStateUM.Content,
|
||||
),
|
||||
)
|
||||
if (LocalRedesignEnabled.current) {
|
||||
TangemTopBar(
|
||||
title = resourceReference(R.string.common_news),
|
||||
type = TangemTopBarType.BottomSheet,
|
||||
startContent = {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.ic_arrow_back_28),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors2.graphic.neutral.primary,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens2.x11)
|
||||
.background(
|
||||
color = TangemTheme.colors2.button.backgroundSecondary,
|
||||
shape = CircleShape,
|
||||
)
|
||||
.clickableSingle(
|
||||
onClick = state.onBackClick,
|
||||
enabled = bottomSheetState.value == BottomSheetState.EXPANDED,
|
||||
)
|
||||
.padding(TangemTheme.dimens2.x2),
|
||||
)
|
||||
},
|
||||
endContent = {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.ic_share_new_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors2.graphic.neutral.primary,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens2.x11)
|
||||
.background(
|
||||
color = TangemTheme.colors2.button.backgroundSecondary,
|
||||
shape = CircleShape,
|
||||
)
|
||||
.clickableSingle(
|
||||
onClick = state.onShareClick,
|
||||
enabled = bottomSheetState.value == BottomSheetState.EXPANDED &&
|
||||
state.articlesStateUM is ArticlesStateUM.Content,
|
||||
)
|
||||
.padding(TangemTheme.dimens2.x2_5),
|
||||
)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
TangemTopAppBar(
|
||||
containerColor = background,
|
||||
title = null,
|
||||
startButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = R.drawable.ic_back_24,
|
||||
onClicked = state.onBackClick,
|
||||
isEnabled = bottomSheetState.value == BottomSheetState.EXPANDED,
|
||||
),
|
||||
endButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = R.drawable.ic_share_24,
|
||||
onClicked = state.onShareClick,
|
||||
isEnabled = bottomSheetState.value == BottomSheetState.EXPANDED &&
|
||||
state.articlesStateUM is ArticlesStateUM.Content,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -1,9 +1,16 @@
|
|||
package com.tangem.features.feed.components.news.list
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
|
|
@ -12,8 +19,14 @@ import com.tangem.core.ui.components.appbar.TangemTopAppBar
|
|||
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
|
||||
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
|
||||
import com.tangem.core.ui.decompose.ComposableModularBottomSheetContentComponent
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBar
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBarType
|
||||
import com.tangem.core.ui.extensions.clickableSingle
|
||||
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.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.domain.news.model.NewsListConfig
|
||||
import com.tangem.features.feed.model.news.list.NewsListModel
|
||||
import com.tangem.features.feed.ui.news.list.NewsListContent
|
||||
|
|
@ -30,15 +43,40 @@ internal class DefaultNewsListComponent(
|
|||
override fun Title(bottomSheetState: State<BottomSheetState>) {
|
||||
val background = LocalMainBottomSheetColor.current.value
|
||||
val state by newsListModel.state.collectAsStateWithLifecycle()
|
||||
TangemTopAppBar(
|
||||
containerColor = background,
|
||||
title = stringResourceSafe(R.string.common_news),
|
||||
startButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = R.drawable.ic_back_24,
|
||||
onClicked = state.onBackClick,
|
||||
isEnabled = bottomSheetState.value == BottomSheetState.EXPANDED,
|
||||
),
|
||||
)
|
||||
if (LocalRedesignEnabled.current) {
|
||||
TangemTopBar(
|
||||
title = resourceReference(R.string.common_news),
|
||||
type = TangemTopBarType.BottomSheet,
|
||||
startContent = {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.ic_arrow_back_28),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors2.graphic.neutral.primary,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens2.x11)
|
||||
.background(
|
||||
color = TangemTheme.colors2.button.backgroundSecondary,
|
||||
shape = CircleShape,
|
||||
)
|
||||
.clickableSingle(
|
||||
onClick = state.onBackClick,
|
||||
enabled = bottomSheetState.value == BottomSheetState.EXPANDED,
|
||||
)
|
||||
.padding(TangemTheme.dimens2.x2),
|
||||
)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
TangemTopAppBar(
|
||||
containerColor = background,
|
||||
title = stringResourceSafe(R.string.common_news),
|
||||
startButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = R.drawable.ic_back_24,
|
||||
onClicked = state.onBackClick,
|
||||
isEnabled = bottomSheetState.value == BottomSheetState.EXPANDED,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
package com.tangem.features.feed.deeplink
|
||||
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.features.feed.entry.deeplink.MarketsDeepLinkHandler
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
internal class DefaultMarketsDeepLinkHandler @AssistedInject constructor(
|
||||
appRouter: AppRouter,
|
||||
) : MarketsDeepLinkHandler {
|
||||
|
||||
init {
|
||||
appRouter.push(AppRoute.Markets)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : MarketsDeepLinkHandler.Factory {
|
||||
override fun create(): DefaultMarketsDeepLinkHandler
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
package com.tangem.features.feed.deeplink
|
||||
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.common.routing.deeplink.DeeplinkConst.TOKEN_ID_KEY
|
||||
import com.tangem.data.common.currency.getTokenIconUrlFromDefaultHost
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.markets.GetTokenMarketInfoUseCase
|
||||
import com.tangem.domain.markets.TokenMarketParams
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.features.feed.entry.deeplink.MarketsTokenDetailDeepLinkHandler
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
internal class DefaultMarketsTokenDetailDeepLinkHandler @AssistedInject constructor(
|
||||
@Assisted private val scope: CoroutineScope,
|
||||
@Assisted private val queryParams: Map<String, String>,
|
||||
private val appRouter: AppRouter,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val getTokenMarketInfoUseCase: GetTokenMarketInfoUseCase,
|
||||
) : MarketsTokenDetailDeepLinkHandler {
|
||||
|
||||
init {
|
||||
handleDeepLink()
|
||||
}
|
||||
|
||||
private fun handleDeepLink() {
|
||||
val tokenId = queryParams[TOKEN_ID_KEY]
|
||||
|
||||
val rawTokenId = CryptoCurrency.RawID(tokenId.orEmpty())
|
||||
|
||||
scope.launch {
|
||||
val appCurrency = getSelectedAppCurrencyUseCase.invokeSync().getOrElse {
|
||||
AppCurrency.Default
|
||||
}
|
||||
val tokenInfo = getTokenMarketInfoUseCase(
|
||||
appCurrency = appCurrency,
|
||||
tokenId = rawTokenId,
|
||||
tokenSymbol = "", // used for analytics
|
||||
).getOrElse {
|
||||
Timber.e("Failed to get market token info")
|
||||
return@launch
|
||||
}
|
||||
|
||||
appRouter.push(
|
||||
AppRoute.MarketsTokenDetails(
|
||||
token = TokenMarketParams(
|
||||
id = rawTokenId,
|
||||
name = tokenInfo.name,
|
||||
symbol = tokenInfo.symbol,
|
||||
tokenQuotes = TokenMarketParams.Quotes(
|
||||
currentPrice = tokenInfo.quotes.currentPrice,
|
||||
h24Percent = tokenInfo.quotes.h24ChangePercent,
|
||||
weekPercent = tokenInfo.quotes.weekChangePercent,
|
||||
monthPercent = tokenInfo.quotes.monthChangePercent,
|
||||
),
|
||||
imageUrl = getTokenIconUrlFromDefaultHost(rawTokenId),
|
||||
),
|
||||
appCurrency = appCurrency,
|
||||
shouldShowPortfolio = true,
|
||||
analyticsParams = null,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : MarketsTokenDetailDeepLinkHandler.Factory {
|
||||
override fun create(
|
||||
coroutineScope: CoroutineScope,
|
||||
queryParams: Map<String, String>,
|
||||
): DefaultMarketsTokenDetailDeepLinkHandler
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.features.feed.deeplink.di
|
||||
|
||||
import com.tangem.features.feed.deeplink.DefaultMarketsDeepLinkHandler
|
||||
import com.tangem.features.feed.deeplink.DefaultMarketsTokenDetailDeepLinkHandler
|
||||
import com.tangem.features.feed.entry.deeplink.MarketsDeepLinkHandler
|
||||
import com.tangem.features.feed.entry.deeplink.MarketsTokenDetailDeepLinkHandler
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal interface MarketsDeepLinkModule {
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindMarketsDeepLinkHandlerFactory(impl: DefaultMarketsDeepLinkHandler.Factory): MarketsDeepLinkHandler.Factory
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindMarketsTokenDetailDeepLinkHandlerFactory(
|
||||
impl: DefaultMarketsTokenDetailDeepLinkHandler.Factory,
|
||||
): MarketsTokenDetailDeepLinkHandler.Factory
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.features.feed.featuretoggle
|
||||
|
||||
import com.tangem.core.configtoggle.FeatureToggles
|
||||
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
|
||||
import com.tangem.features.feed.entry.featuretoggle.FeedFeatureToggle
|
||||
|
||||
|
|
@ -8,5 +9,5 @@ internal class DefaultFeedFeatureToggle(
|
|||
) : FeedFeatureToggle {
|
||||
|
||||
override val isEarnBlockEnabled: Boolean
|
||||
get() = featureTogglesManager.isFeatureEnabled("EARN_BLOCK_ENABLED")
|
||||
get() = featureTogglesManager.isFeatureEnabled(FeatureToggles.EARN_BLOCK_ENABLED)
|
||||
}
|
||||
|
|
@ -8,7 +8,6 @@ import com.tangem.domain.models.currency.CryptoCurrency
|
|||
import com.tangem.domain.models.news.ShortArticle
|
||||
import com.tangem.features.feed.ui.feed.components.articles.ArticleConfigUM
|
||||
import com.tangem.features.feed.ui.utils.mapFormattedDate
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.ImmutableSet
|
||||
|
|
@ -16,7 +15,7 @@ import kotlinx.collections.immutable.toPersistentList
|
|||
import kotlinx.collections.immutable.toPersistentSet
|
||||
|
||||
internal class ShortArticleToArticleConfigUMConverter(
|
||||
private val isTrending: Provider<Boolean>?,
|
||||
private val isTrending: Boolean?,
|
||||
) : Converter<List<ShortArticle>, ImmutableList<ArticleConfigUM>> {
|
||||
|
||||
override fun convert(value: List<ShortArticle>): ImmutableList<ArticleConfigUM> {
|
||||
|
|
@ -25,7 +24,7 @@ internal class ShortArticleToArticleConfigUMConverter(
|
|||
id = shortArticle.id,
|
||||
title = shortArticle.title,
|
||||
score = shortArticle.score,
|
||||
isTrending = isTrending?.invoke() ?: shortArticle.isTrending,
|
||||
isTrending = isTrending ?: shortArticle.isTrending,
|
||||
tags = buildArticleTags(shortArticle),
|
||||
createdAt = mapFormattedDate(shortArticle.createdAt),
|
||||
isViewed = shortArticle.viewed,
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import com.tangem.features.feed.model.feed.analytics.FeedAnalyticsEvent
|
|||
import com.tangem.features.feed.ui.feed.state.FeedListUM
|
||||
import com.tangem.features.feed.ui.feed.state.NewsUM
|
||||
import com.tangem.features.feed.ui.feed.state.NewsUMState
|
||||
import com.tangem.utils.Provider
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import org.joda.time.DateTime
|
||||
|
|
@ -92,6 +91,6 @@ internal class UpdateTrendingNewsStateTransformer(
|
|||
}
|
||||
|
||||
private fun getShortArticleConfigConverter(isTrending: Boolean): ShortArticleToArticleConfigUMConverter {
|
||||
return ShortArticleToArticleConfigUMConverter(isTrending = Provider { isTrending })
|
||||
return ShortArticleToArticleConfigUMConverter(isTrending = isTrending)
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ import com.tangem.core.decompose.model.Model
|
|||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.navigation.share.ShareManager
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.core.ui.DesignFeatureToggles
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
|
|
@ -47,6 +48,7 @@ import com.tangem.features.feed.model.converter.ShortArticleToArticleConfigUMCon
|
|||
import com.tangem.features.feed.model.market.details.analytics.MarketDetailsAnalyticsEvent
|
||||
import com.tangem.features.feed.model.market.details.converter.DescriptionConverter
|
||||
import com.tangem.features.feed.model.market.details.converter.ExchangeItemStateConverter
|
||||
import com.tangem.features.feed.model.market.details.converter.ExchangeItemStateConverterV2
|
||||
import com.tangem.features.feed.model.market.details.converter.TokenMarketInfoConverter
|
||||
import com.tangem.features.feed.model.market.details.formatter.*
|
||||
import com.tangem.features.feed.model.market.details.state.QuotesStateUpdater
|
||||
|
|
@ -77,6 +79,7 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
|||
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
getUserCountryUseCase: GetUserCountryUseCase,
|
||||
paramsContainer: ParamsContainer,
|
||||
private val designFeatureToggles: DesignFeatureToggles,
|
||||
private val getTokenPriceChartUseCase: GetTokenPriceChartUseCase,
|
||||
private val getTokenMarketInfoUseCase: GetTokenMarketInfoUseCase,
|
||||
private val getTokenFullQuotesUseCase: GetTokenFullQuotesUseCase,
|
||||
|
|
@ -147,11 +150,12 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
|||
needApplyFCARestrictions = Provider {
|
||||
userCountry.needApplyFCARestrictions()
|
||||
},
|
||||
isRedesignEnabled = designFeatureToggles.isRedesignEnabled,
|
||||
// ==================
|
||||
)
|
||||
|
||||
private val shortArticleToArticleConfigUMConverter by lazy {
|
||||
ShortArticleToArticleConfigUMConverter(isTrending = Provider { false })
|
||||
ShortArticleToArticleConfigUMConverter(isTrending = false)
|
||||
}
|
||||
|
||||
private val descriptionConverter = DescriptionConverter(
|
||||
|
|
@ -701,9 +705,15 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
|||
ExchangesBottomSheetContent.Error(onRetryClick = { onListedOnClick(exchangesCount) })
|
||||
},
|
||||
ifRight = { list ->
|
||||
ExchangesBottomSheetContent.Content(
|
||||
exchangeItems = ExchangeItemStateConverter.convertList(list).toImmutableList(),
|
||||
)
|
||||
if (designFeatureToggles.isRedesignEnabled) {
|
||||
ExchangesBottomSheetContent.ContentV2(
|
||||
exchangeItemsV2 = ExchangeItemStateConverterV2.convertList(list).toImmutableList(),
|
||||
)
|
||||
} else {
|
||||
ExchangesBottomSheetContent.ContentV1(
|
||||
exchangeItems = ExchangeItemStateConverter.convertList(list).toImmutableList(),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
package com.tangem.features.feed.model.market.details.converter
|
||||
|
||||
import com.tangem.core.ui.components.audits.AuditLabelUM
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.format.bigdecimal.compact
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.domain.markets.TokenMarketExchange
|
||||
import com.tangem.domain.markets.TokenMarketExchange.TrustScore
|
||||
import com.tangem.features.feed.impl.R
|
||||
import com.tangem.features.feed.ui.market.detailed.state.ExchangeItemUM
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal object ExchangeItemStateConverterV2 : Converter<TokenMarketExchange, ExchangeItemUM> {
|
||||
|
||||
override fun convert(value: TokenMarketExchange): ExchangeItemUM.Content {
|
||||
return ExchangeItemUM.Content(
|
||||
id = value.id,
|
||||
title = stringReference(value.name),
|
||||
subTitle = stringReference(value = if (value.isCentralized) "CEX" else "DEX"),
|
||||
icon = TangemIconUM.Currency(
|
||||
CurrencyIconState.CoinIcon(
|
||||
url = value.imageUrl,
|
||||
fallbackResId = R.drawable.ic_alert_24,
|
||||
isGrayscale = false,
|
||||
shouldShowCustomBadge = false,
|
||||
),
|
||||
),
|
||||
volumeInUsd = stringReference(
|
||||
value.volumeInUsd.format {
|
||||
fiat(
|
||||
fiatCurrencyCode = "USD",
|
||||
fiatCurrencySymbol = "$",
|
||||
).compact()
|
||||
},
|
||||
),
|
||||
auditLabel = value.trustScore.toAuditLabelUM(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun TrustScore.toAuditLabelUM(): AuditLabelUM {
|
||||
return when (this) {
|
||||
TrustScore.Risky -> AuditLabelUM(
|
||||
text = resourceReference(id = R.string.markets_token_details_exchange_trust_score_risky),
|
||||
type = AuditLabelUM.Type.Prohibition,
|
||||
)
|
||||
TrustScore.Caution -> AuditLabelUM(
|
||||
text = resourceReference(id = R.string.markets_token_details_exchange_trust_score_caution),
|
||||
type = AuditLabelUM.Type.Warning,
|
||||
)
|
||||
TrustScore.Trusted -> AuditLabelUM(
|
||||
text = resourceReference(id = R.string.markets_token_details_exchange_trust_score_trusted),
|
||||
type = AuditLabelUM.Type.Permit,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,8 @@ package com.tangem.features.feed.model.market.details.converter
|
|||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.format.bigdecimal.compact
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
|
|
@ -9,20 +11,23 @@ import com.tangem.core.ui.format.bigdecimal.format
|
|||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.markets.TokenMarketInfo
|
||||
import com.tangem.features.feed.impl.R
|
||||
import com.tangem.features.feed.ui.market.detailed.state.InfoBottomSheetContent
|
||||
import com.tangem.features.feed.ui.market.detailed.state.InfoPointUM
|
||||
import com.tangem.features.feed.ui.market.detailed.state.MetricsUM
|
||||
import com.tangem.features.feed.ui.market.detailed.state.*
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.StringsSigns
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
|
||||
@Suppress("LargeClass")
|
||||
@Stable
|
||||
internal class MetricsConverter(
|
||||
private val appCurrency: Provider<AppCurrency>,
|
||||
private val tokenSymbol: String,
|
||||
private val onInfoClick: (InfoBottomSheetContent) -> Unit,
|
||||
private val isRedesignEnabled: Boolean,
|
||||
) : Converter<TokenMarketInfo.Metrics, MetricsUM> {
|
||||
|
||||
@Suppress("LongMethod")
|
||||
|
|
@ -115,10 +120,120 @@ internal class MetricsConverter(
|
|||
},
|
||||
),
|
||||
),
|
||||
metricsV2 = if (isRedesignEnabled) {
|
||||
convertToMetricsV2UM(value)
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod", "NestedScopeFunctions")
|
||||
private fun convertToMetricsV2UM(value: TokenMarketInfo.Metrics): MetricsV2UM {
|
||||
val infoPoints = with(value) {
|
||||
val liquidity = getLiquidity(value.volume24h, value.marketCap)
|
||||
persistentListOf(
|
||||
InfoPointUMV2.MarketCap(
|
||||
capitalizationValue = stringReference(marketCap.formatAmount()),
|
||||
onInfoClick = {
|
||||
onInfoClick(
|
||||
InfoBottomSheetContent(
|
||||
title = resourceReference(
|
||||
R.string.markets_token_details_market_capitalization_full,
|
||||
),
|
||||
body = resourceReference(
|
||||
R.string.markets_token_details_market_capitalization_description,
|
||||
),
|
||||
),
|
||||
)
|
||||
},
|
||||
),
|
||||
InfoPointUMV2.TradingVolume(
|
||||
tradingValue = stringReference(volume24h.formatAmount()),
|
||||
liquidity = liquidity,
|
||||
trendingVolumeLiquidityType = getTrendingVolumeLiquidityType(liquidity),
|
||||
onInfoClick = {
|
||||
onInfoClick(
|
||||
InfoBottomSheetContent(
|
||||
title = resourceReference(R.string.markets_token_details_trading_volume_full),
|
||||
body = resourceReference(
|
||||
R.string.markets_token_details_trading_volume_24h_description,
|
||||
),
|
||||
),
|
||||
)
|
||||
},
|
||||
),
|
||||
InfoPointUMV2.MarketPosition(
|
||||
position = marketRating?.toString() ?: StringsSigns.DASH_SIGN,
|
||||
rangeValue = getMarketRatingRangeValue(marketRating),
|
||||
marketRatingType = getMarketRatingType(marketRating),
|
||||
onInfoClick = {
|
||||
onInfoClick(
|
||||
InfoBottomSheetContent(
|
||||
title = resourceReference(R.string.markets_token_details_market_rating_full),
|
||||
body = resourceReference(R.string.markets_token_details_market_rating_description),
|
||||
),
|
||||
)
|
||||
},
|
||||
),
|
||||
InfoPointUMV2.FullyDilutedValuation(
|
||||
value = resourceReference(
|
||||
R.string.markets_token_details_valuation_value_in_total,
|
||||
wrappedList(fullyDilutedValuation.formatAmount()),
|
||||
),
|
||||
onInfoClick = {
|
||||
onInfoClick(
|
||||
InfoBottomSheetContent(
|
||||
title = resourceReference(
|
||||
R.string.markets_token_details_fully_diluted_valuation_full,
|
||||
),
|
||||
body = resourceReference(
|
||||
R.string.markets_token_details_fully_diluted_valuation_description,
|
||||
),
|
||||
),
|
||||
)
|
||||
},
|
||||
),
|
||||
InfoPointUMV2.CirculatingSupply(
|
||||
currentValue = stringReference(circulatingSupply.formatAmount(crypto = true)),
|
||||
maxValue = maxSupply?.let { supply ->
|
||||
if (supply > BigDecimal.ZERO) {
|
||||
stringReference(supply.formatMaxSupply())
|
||||
} else {
|
||||
null
|
||||
}
|
||||
},
|
||||
fillValue = getCirculatingSupplyFillValue(circulatingSupply, maxSupply),
|
||||
onInfoClick = {
|
||||
onInfoClick(
|
||||
InfoBottomSheetContent(
|
||||
title = resourceReference(R.string.markets_token_details_max_supply_full),
|
||||
body = resourceReference(R.string.markets_token_details_total_supply_description),
|
||||
),
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
return buildMetricsV2UM(infoPoints)
|
||||
}
|
||||
|
||||
private fun buildMetricsV2UM(infoPoints: ImmutableList<InfoPointUMV2>): MetricsV2UM {
|
||||
val rows = infoPoints.chunked(size = 2)
|
||||
.map { chunk ->
|
||||
MetricsV2UM.Row(
|
||||
first = chunk.first(),
|
||||
second = chunk.getOrNull(1),
|
||||
)
|
||||
}
|
||||
|
||||
return MetricsV2UM(
|
||||
rows = rows.toImmutableList(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun BigDecimal?.formatMaxSupply(): String {
|
||||
when (this) {
|
||||
null -> return StringsSigns.DASH_SIGN
|
||||
|
|
@ -149,4 +264,92 @@ internal class MetricsConverter(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private fun getLiquidity(volume24h: BigDecimal?, marketCap: BigDecimal?): Float {
|
||||
if (volume24h == null || marketCap == null || marketCap == BigDecimal.ZERO) return 0f
|
||||
|
||||
val ratio = volume24h
|
||||
.divide(marketCap, 6, RoundingMode.HALF_UP)
|
||||
.toFloat()
|
||||
|
||||
return ratio.coerceIn(0f, 1f)
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private fun getTrendingVolumeLiquidityType(liquidity: Float): TrendingVolumeLiquidityType {
|
||||
return when {
|
||||
liquidity >= 0.5f -> TrendingVolumeLiquidityType.HIGH
|
||||
liquidity in 0.2f..<0.5f -> TrendingVolumeLiquidityType.MEDIUM
|
||||
else -> TrendingVolumeLiquidityType.LOW
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private fun getMarketRatingType(marketRating: Int?): MarketRatingType {
|
||||
return when (marketRating) {
|
||||
1 -> MarketRatingType.GOLD
|
||||
2 -> MarketRatingType.SILVER
|
||||
3 -> MarketRatingType.BRONZE
|
||||
else -> MarketRatingType.OTHER
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private fun getMarketRatingRangeValue(marketRating: Int?): Float {
|
||||
if (marketRating == null) return 0f
|
||||
|
||||
return when {
|
||||
marketRating <= 20 -> {
|
||||
val segmentStart = 1f
|
||||
val segmentEnd = 0.75f
|
||||
val rangeMin = 1
|
||||
val rangeMax = 20
|
||||
val progress = segmentStart +
|
||||
(marketRating - rangeMin).toFloat() / (rangeMax - rangeMin) * (segmentEnd - segmentStart)
|
||||
progress.coerceIn(0f, 1f)
|
||||
}
|
||||
marketRating <= 100 -> {
|
||||
val segmentStart = 0.76f
|
||||
val segmentEnd = 0.5f
|
||||
val rangeMin = 21
|
||||
val rangeMax = 100
|
||||
val progress = segmentStart +
|
||||
(marketRating - rangeMin).toFloat() / (rangeMax - rangeMin) * (segmentEnd - segmentStart)
|
||||
progress.coerceIn(0f, 1f)
|
||||
}
|
||||
marketRating <= 1000 -> {
|
||||
val segmentStart = 0.51f
|
||||
val segmentEnd = 0.25f
|
||||
val rangeMin = 101
|
||||
val rangeMax = 1000
|
||||
val progress = segmentStart +
|
||||
(marketRating - rangeMin).toFloat() / (rangeMax - rangeMin) * (segmentEnd - segmentStart)
|
||||
progress.coerceIn(0f, 1f)
|
||||
}
|
||||
marketRating <= 10000 -> {
|
||||
val segmentStart = 0.26f
|
||||
val segmentEnd = 0.01f
|
||||
val rangeMin = 1001
|
||||
val rangeMax = 10000
|
||||
val progress = segmentStart +
|
||||
(marketRating - rangeMin).toFloat() / (rangeMax - rangeMin) * (segmentEnd - segmentStart)
|
||||
progress.coerceIn(0f, 1f)
|
||||
}
|
||||
else -> {
|
||||
0f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private fun getCirculatingSupplyFillValue(circulatingSupply: BigDecimal?, maxSupply: BigDecimal?): Float? {
|
||||
if (circulatingSupply == null || maxSupply == null || maxSupply == BigDecimal.ZERO) return null
|
||||
|
||||
val ratio = circulatingSupply
|
||||
.divide(maxSupply, 6, RoundingMode.HALF_UP)
|
||||
.toFloat()
|
||||
|
||||
return ratio.coerceIn(0f, 1f)
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ import com.tangem.utils.converter.Converter
|
|||
@Stable
|
||||
@Suppress("LongParameterList")
|
||||
internal class TokenMarketInfoConverter(
|
||||
private val isRedesignEnabled: Boolean,
|
||||
private val appCurrency: Provider<AppCurrency>,
|
||||
private val needApplyFCARestrictions: Provider<Boolean>,
|
||||
private val onInfoClick: (TangemBottomSheetConfigContent) -> Unit,
|
||||
|
|
@ -47,6 +48,7 @@ internal class TokenMarketInfoConverter(
|
|||
tokenSymbol = value.symbol,
|
||||
appCurrency = appCurrency,
|
||||
onInfoClick = onInfoClick,
|
||||
isRedesignEnabled = isRedesignEnabled,
|
||||
)
|
||||
|
||||
val exchangesAmount = value.exchangesAmount
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
package com.tangem.features.feed.ui.components
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
@Composable
|
||||
internal fun ContainerWithDivider(
|
||||
modifier: Modifier = Modifier,
|
||||
showDivider: Boolean = false,
|
||||
paddingValues: PaddingValues = PaddingValues(start = TangemTheme.dimens2.x3),
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
Box(modifier = modifier) {
|
||||
content()
|
||||
if (showDivider) {
|
||||
HorizontalDivider(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.padding(paddingValues),
|
||||
color = TangemTheme.colors2.graphic.neutral.quaternary,
|
||||
thickness = 1.dp,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
package com.tangem.features.feed.ui.components
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.extensions.conditional
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
|
||||
@Composable
|
||||
internal fun MetricsCard(
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: (() -> Unit)? = null,
|
||||
cardColor: Color = TangemTheme.colors2.surface.level3,
|
||||
title: @Composable () -> Unit,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(
|
||||
color = cardColor,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens2.x5),
|
||||
)
|
||||
.conditional(
|
||||
condition = onClick != null,
|
||||
modifier = {
|
||||
if (onClick != null) {
|
||||
clickable(onClick = onClick)
|
||||
} else {
|
||||
this
|
||||
}
|
||||
},
|
||||
)
|
||||
.padding(TangemTheme.dimens2.x4),
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
title()
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun MetricsCardPreview() {
|
||||
TangemThemePreviewRedesign {
|
||||
MetricsCard(
|
||||
modifier = Modifier.heightIn(120.dp),
|
||||
title = {
|
||||
Text(
|
||||
text = "$ 22.4 M",
|
||||
style = TangemTheme.typography2.headingBold22,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
)
|
||||
},
|
||||
content = {
|
||||
Text(
|
||||
text = "Market cap",
|
||||
style = TangemTheme.typography2.captionSemibold12,
|
||||
color = TangemTheme.colors2.text.neutral.tertiary,
|
||||
)
|
||||
},
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
package com.tangem.features.feed.ui.components
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.SpacerWMax
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
|
||||
@Composable
|
||||
internal fun TokenMarketInformationBlock(
|
||||
modifier: Modifier = Modifier,
|
||||
shape: Shape = RoundedCornerShape(TangemTheme.dimens2.x6),
|
||||
contentPadding: Dp = TangemTheme.dimens2.x4,
|
||||
title: (@Composable () -> Unit),
|
||||
content: (@Composable BoxScope.() -> Unit)? = null,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.clip(shape)
|
||||
.background(color = TangemTheme.colors2.surface.level3)
|
||||
.padding(contentPadding),
|
||||
horizontalAlignment = Alignment.Start,
|
||||
) {
|
||||
title()
|
||||
|
||||
if (content != null) {
|
||||
Box(modifier = Modifier.fillMaxWidth()) {
|
||||
content(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
private fun TokenMarketInformationBlockPreview() {
|
||||
TangemThemePreviewRedesign {
|
||||
TokenMarketInformationBlock(
|
||||
title = {
|
||||
Row(
|
||||
modifier = Modifier.padding(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = "Listed on",
|
||||
style = TangemTheme.typography2.headingSemibold20,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
)
|
||||
|
||||
SpacerWMax()
|
||||
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_chevron_24),
|
||||
tint = TangemTheme.colors2.markers.iconGray,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
},
|
||||
content = {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(TangemTheme.colors2.surface.level2)
|
||||
.padding(12.dp),
|
||||
) {
|
||||
Text(
|
||||
text = "Smth",
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ import androidx.compose.animation.AnimatedContent
|
|||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.*
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
|
|
@ -13,33 +13,21 @@ import androidx.compose.runtime.mutableStateOf
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.onFirstVisible
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.SmallButtonShimmer
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.components.SpacerWMax
|
||||
import com.tangem.core.ui.components.UnableToLoadData
|
||||
import com.tangem.core.ui.components.buttons.SecondarySmallButton
|
||||
import com.tangem.core.ui.components.buttons.SmallButtonConfig
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition
|
||||
import com.tangem.core.ui.components.*
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.components.list.InfiniteListHandler
|
||||
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.res.LocalMainBottomSheetColor
|
||||
import com.tangem.core.ui.res.TangemColorPalette
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.feed.ui.earn.components.EarnItemPlaceholder
|
||||
import com.tangem.features.feed.ui.earn.components.EarnListItem
|
||||
import com.tangem.features.feed.ui.earn.components.MostlyUsedCard
|
||||
import com.tangem.features.feed.ui.earn.components.MostlyUsedPlaceholder
|
||||
import com.tangem.core.ui.extensions.conditional
|
||||
import com.tangem.core.ui.extensions.conditionalCompose
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.*
|
||||
import com.tangem.features.feed.ui.earn.components.*
|
||||
import com.tangem.features.feed.ui.earn.state.*
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
|
|
@ -51,6 +39,7 @@ internal fun EarnContent(state: EarnUM, modifier: Modifier = Modifier) {
|
|||
val density = LocalDensity.current
|
||||
val bottomBarHeight = with(density) { WindowInsets.systemBars.getBottom(this).toDp() }
|
||||
val listState = rememberLazyListState()
|
||||
val isRedesignEnabled = LocalRedesignEnabled.current
|
||||
|
||||
if (state.bestOpportunities is EarnBestOpportunitiesUM.Content) {
|
||||
PaginationHandler(
|
||||
|
|
@ -69,7 +58,9 @@ internal fun EarnContent(state: EarnUM, modifier: Modifier = Modifier) {
|
|||
item(key = "mostly_used_header") {
|
||||
SectionHeader(
|
||||
title = stringResourceSafe(R.string.earn_mostly_used),
|
||||
modifier = Modifier.padding(top = 16.dp),
|
||||
modifier = Modifier.padding(
|
||||
top = if (isRedesignEnabled) 10.dp else 16.dp,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -83,7 +74,9 @@ internal fun EarnContent(state: EarnUM, modifier: Modifier = Modifier) {
|
|||
item(key = "best_opportunities_header") {
|
||||
SectionHeader(
|
||||
title = stringResourceSafe(R.string.earn_best_opportunities),
|
||||
modifier = Modifier.padding(top = 20.dp),
|
||||
modifier = Modifier.padding(
|
||||
top = if (isRedesignEnabled) 32.dp else 20.dp,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -97,14 +90,17 @@ internal fun EarnContent(state: EarnUM, modifier: Modifier = Modifier) {
|
|||
)
|
||||
}
|
||||
|
||||
bestOpportunitiesItems(
|
||||
state = state.bestOpportunities,
|
||||
)
|
||||
if (isRedesignEnabled) {
|
||||
bestOpportunitiesItemsV2(state = state.bestOpportunities)
|
||||
} else {
|
||||
bestOpportunitiesItemsV1(state = state.bestOpportunities)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MostlyUsedContent(state: EarnListUM, onScroll: () -> Unit) {
|
||||
val isRedesignEnabled = LocalRedesignEnabled.current
|
||||
AnimatedContent(
|
||||
targetState = state,
|
||||
contentKey = { it::class.java },
|
||||
|
|
@ -115,10 +111,7 @@ private fun MostlyUsedContent(state: EarnListUM, onScroll: () -> Unit) {
|
|||
}
|
||||
is EarnListUM.Content -> {
|
||||
LazyRow(
|
||||
contentPadding = PaddingValues(
|
||||
horizontal = 16.dp,
|
||||
vertical = 12.dp,
|
||||
),
|
||||
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 12.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
itemsIndexed(
|
||||
|
|
@ -127,12 +120,7 @@ private fun MostlyUsedContent(state: EarnListUM, onScroll: () -> Unit) {
|
|||
) { index, item ->
|
||||
val cardModifier = Modifier.conditional(
|
||||
condition = index == FOURTH_ITEM_INDEX,
|
||||
modifier = {
|
||||
onFirstVisible(
|
||||
minFractionVisible = 0.5f,
|
||||
callback = onScroll,
|
||||
)
|
||||
},
|
||||
modifier = { onFirstVisible(minFractionVisible = 0.5f, callback = onScroll) },
|
||||
)
|
||||
MostlyUsedCard(
|
||||
modifier = cardModifier,
|
||||
|
|
@ -147,15 +135,24 @@ private fun MostlyUsedContent(state: EarnListUM, onScroll: () -> Unit) {
|
|||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp)
|
||||
.background(
|
||||
color = TangemTheme.colors.background.action,
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
.conditionalCompose(
|
||||
condition = isRedesignEnabled,
|
||||
modifier = {
|
||||
background(
|
||||
color = TangemTheme.colors2.surface.level3,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens2.x4),
|
||||
)
|
||||
},
|
||||
otherModifier = {
|
||||
background(
|
||||
color = TangemTheme.colors.background.action,
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
)
|
||||
},
|
||||
)
|
||||
.padding(vertical = 32.dp, horizontal = 12.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
UnableToLoadData(onRetryClick = animatedState.onRetryClicked)
|
||||
}
|
||||
) { UnableToLoadData(onRetryClick = animatedState.onRetryClicked) }
|
||||
}
|
||||
EarnListUM.Empty -> Unit // no need to handle
|
||||
}
|
||||
|
|
@ -179,7 +176,7 @@ private fun BestOpportunitiesFilters(
|
|||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.bestOpportunitiesItems(state: EarnBestOpportunitiesUM) {
|
||||
private fun LazyListScope.bestOpportunitiesItemsV1(state: EarnBestOpportunitiesUM) {
|
||||
when (state) {
|
||||
is EarnBestOpportunitiesUM.Loading -> {
|
||||
val lastIndex = PLACEHOLDER_ITEMS_COUNT - 1
|
||||
|
|
@ -187,7 +184,7 @@ private fun LazyListScope.bestOpportunitiesItems(state: EarnBestOpportunitiesUM)
|
|||
count = PLACEHOLDER_ITEMS_COUNT,
|
||||
key = { "placeholder_$it" },
|
||||
) { index ->
|
||||
EarnItemPlaceholder(
|
||||
EarnItemPlaceholderV1(
|
||||
modifier = Modifier
|
||||
.roundedShapeItemDecoration(
|
||||
currentIndex = index,
|
||||
|
|
@ -249,129 +246,126 @@ private fun LazyListScope.bestOpportunitiesItems(state: EarnBestOpportunitiesUM)
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FilterButtons(
|
||||
earnFilterUM: EarnFilterUM,
|
||||
onNetworkFilterClick: () -> Unit,
|
||||
onTypeFilterClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier.padding(horizontal = 16.dp),
|
||||
) {
|
||||
SecondarySmallButton(
|
||||
config = SmallButtonConfig(
|
||||
text = when (earnFilterUM.selectedNetworkFilter) {
|
||||
is EarnFilterNetworkUM.AllNetworks -> TextReference.Res(R.string.earn_filter_all_networks)
|
||||
is EarnFilterNetworkUM.MyNetworks -> TextReference.Res(R.string.earn_filter_my_networks)
|
||||
is EarnFilterNetworkUM.Network -> TextReference.Str(earnFilterUM.selectedNetworkFilter.text)
|
||||
},
|
||||
onClick = onNetworkFilterClick,
|
||||
icon = TangemButtonIconPosition.End(iconResId = R.drawable.ic_chevron_24),
|
||||
isEnabled = earnFilterUM.isNetworkFilterEnabled,
|
||||
),
|
||||
)
|
||||
|
||||
SpacerWMax()
|
||||
|
||||
SecondarySmallButton(
|
||||
config = SmallButtonConfig(
|
||||
text = earnFilterUM.selectedTypeFilter.text,
|
||||
onClick = onTypeFilterClick,
|
||||
icon = TangemButtonIconPosition.End(iconResId = R.drawable.ic_chevron_24),
|
||||
isEnabled = earnFilterUM.isTypeFilterEnabled,
|
||||
),
|
||||
)
|
||||
private fun LazyListScope.bestOpportunitiesItemsV2(state: EarnBestOpportunitiesUM) {
|
||||
when (state) {
|
||||
is EarnBestOpportunitiesUM.Loading -> {
|
||||
val lastIndex = PLACEHOLDER_ITEMS_COUNT - 1
|
||||
items(
|
||||
count = PLACEHOLDER_ITEMS_COUNT,
|
||||
key = { "placeholder_$it" },
|
||||
) { index ->
|
||||
EarnItemPlaceholderV2(
|
||||
modifier = Modifier
|
||||
.roundedShapeItemDecoration(
|
||||
currentIndex = index,
|
||||
lastIndex = lastIndex,
|
||||
backgroundColor = TangemTheme.colors2.surface.level3,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
is EarnBestOpportunitiesUM.Empty -> {
|
||||
item(key = "best_opportunities_empty") {
|
||||
SpacerH(12.dp)
|
||||
BestOpportunitiesEmpty() // TODO in [REDACTED_TASK_KEY]
|
||||
}
|
||||
}
|
||||
is EarnBestOpportunitiesUM.EmptyFiltered -> {
|
||||
item(key = "best_opportunities_empty_filtered") {
|
||||
SpacerH(12.dp)
|
||||
BestOpportunitiesEmptyFiltered(onClearFilterClick = state.onClearFilterClick) // TODO in [REDACTED_TASK_KEY]
|
||||
}
|
||||
}
|
||||
is EarnBestOpportunitiesUM.Content -> {
|
||||
if (state.items.isNotEmpty()) {
|
||||
val lastIndex = state.items.lastIndex
|
||||
itemsIndexed(
|
||||
items = state.items,
|
||||
key = { _, item -> "${item.tokenName}-${item.network}" },
|
||||
) { index, item ->
|
||||
EarnListItem(
|
||||
item = item,
|
||||
modifier = Modifier
|
||||
.roundedShapeItemDecoration(
|
||||
currentIndex = index,
|
||||
lastIndex = lastIndex,
|
||||
backgroundColor = TangemTheme.colors2.surface.level3,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
is EarnBestOpportunitiesUM.Error -> {
|
||||
item(key = "best_opportunities_error") {
|
||||
SpacerH(12.dp)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.background(
|
||||
color = TangemTheme.colors2.surface.level3,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens2.x5),
|
||||
)
|
||||
.padding(vertical = 142.dp, horizontal = 114.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
UnableToLoadData(onRetryClick = state.onRetryClicked)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FilterButtonsShimmer(modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
modifier = modifier.padding(horizontal = 16.dp),
|
||||
) {
|
||||
SmallButtonShimmer(
|
||||
modifier = Modifier.width(110.dp),
|
||||
)
|
||||
|
||||
SpacerWMax()
|
||||
|
||||
SmallButtonShimmer(
|
||||
modifier = Modifier.width(90.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BestOpportunitiesEmpty(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.background(
|
||||
color = TangemTheme.colors.background.action,
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
Row(modifier = modifier.padding(horizontal = 16.dp, vertical = 4.dp)) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(130.dp)
|
||||
.height(36.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
.padding(vertical = 32.dp, horizontal = 12.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
painter = painterResource(R.drawable.ic_empty_64),
|
||||
contentDescription = null,
|
||||
tint = Color.Unspecified,
|
||||
)
|
||||
SpacerH(24.dp)
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 32.dp),
|
||||
text = stringResourceSafe(R.string.earn_empty),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BestOpportunitiesEmptyFiltered(onClearFilterClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.background(
|
||||
color = TangemTheme.colors.background.action,
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
SpacerWMax()
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(106.dp)
|
||||
.height(36.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
.padding(vertical = 32.dp, horizontal = 12.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.earn_no_results),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
SpacerH(12.dp)
|
||||
SecondarySmallButton(
|
||||
config = SmallButtonConfig(
|
||||
text = resourceReference(R.string.earn_clear_filter),
|
||||
onClick = onClearFilterClick,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
SmallButtonShimmer(
|
||||
modifier = Modifier.width(110.dp),
|
||||
)
|
||||
SpacerWMax()
|
||||
SmallButtonShimmer(
|
||||
modifier = Modifier.width(90.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SectionHeader(title: String, modifier: Modifier = Modifier) {
|
||||
Text(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 20.dp, end = 16.dp),
|
||||
text = title,
|
||||
style = TangemTheme.typography.h3,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
if (LocalRedesignEnabled.current) {
|
||||
Text(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 24.dp),
|
||||
text = title,
|
||||
style = TangemTheme.typography2.headingSemibold20,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 20.dp, end = 16.dp),
|
||||
text = title,
|
||||
style = TangemTheme.typography.h3,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -392,7 +386,7 @@ private fun PaginationHandler(listState: LazyListState, state: EarnBestOpportuni
|
|||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun EarnContentPreview() {
|
||||
private fun EarnContentPreviewV1() {
|
||||
TangemThemePreview {
|
||||
val background = TangemTheme.colors.background.tertiary
|
||||
CompositionLocalProvider(
|
||||
|
|
@ -434,7 +428,49 @@ private fun EarnContentPreview() {
|
|||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun EarnContentLoadingPreview() {
|
||||
private fun EarnContentPreviewV2() {
|
||||
TangemThemePreviewRedesign {
|
||||
val background = TangemTheme.colors2.surface.level3
|
||||
CompositionLocalProvider(
|
||||
LocalMainBottomSheetColor provides remember { mutableStateOf(background) },
|
||||
) {
|
||||
EarnContent(
|
||||
state = previewEarnUM(
|
||||
mostlyUsed = EarnListUM.Content(
|
||||
items = persistentListOf(
|
||||
previewEarnListItemUM(),
|
||||
previewEarnListItemUM(
|
||||
tokenName = "Cosmos",
|
||||
symbol = "ATOM",
|
||||
network = "Cosmos",
|
||||
),
|
||||
),
|
||||
),
|
||||
bestOpportunities = EarnBestOpportunitiesUM.Content(
|
||||
items = persistentListOf(
|
||||
previewEarnListItemUM(
|
||||
tokenName = "Cosmos Hub",
|
||||
symbol = "ATOM",
|
||||
network = "Cosmos network",
|
||||
),
|
||||
previewEarnListItemUM(
|
||||
tokenName = "Tether",
|
||||
symbol = "USDT",
|
||||
network = "Ethereum Network",
|
||||
),
|
||||
),
|
||||
onLoadMore = {},
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun EarnContentLoadingPreviewV1() {
|
||||
TangemThemePreview {
|
||||
val background = TangemTheme.colors.background.tertiary
|
||||
CompositionLocalProvider(
|
||||
|
|
@ -453,7 +489,26 @@ private fun EarnContentLoadingPreview() {
|
|||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun EarnContentErrorPreview() {
|
||||
private fun EarnContentLoadingPreviewV2() {
|
||||
TangemThemePreviewRedesign {
|
||||
val background = TangemTheme.colors2.surface.level3
|
||||
CompositionLocalProvider(
|
||||
LocalMainBottomSheetColor provides remember { mutableStateOf(background) },
|
||||
) {
|
||||
EarnContent(
|
||||
state = previewEarnUM(
|
||||
mostlyUsed = EarnListUM.Error(onRetryClicked = {}),
|
||||
bestOpportunities = EarnBestOpportunitiesUM.Loading,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun EarnContentErrorPreviewV1() {
|
||||
TangemThemePreview {
|
||||
val background = TangemTheme.colors.background.tertiary
|
||||
CompositionLocalProvider(
|
||||
|
|
@ -481,7 +536,35 @@ private fun EarnContentErrorPreview() {
|
|||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun EarnContentEmptyPreview() {
|
||||
private fun EarnContentErrorPreviewV2() {
|
||||
TangemThemePreviewRedesign {
|
||||
val background = TangemTheme.colors2.surface.level3
|
||||
CompositionLocalProvider(
|
||||
LocalMainBottomSheetColor provides remember { mutableStateOf(background) },
|
||||
) {
|
||||
EarnContent(
|
||||
state = previewEarnUM(
|
||||
mostlyUsed = EarnListUM.Content(
|
||||
items = persistentListOf(
|
||||
previewEarnListItemUM(),
|
||||
previewEarnListItemUM(
|
||||
tokenName = "Cosmos",
|
||||
symbol = "ATOM",
|
||||
network = "Cosmos",
|
||||
),
|
||||
),
|
||||
),
|
||||
bestOpportunities = EarnBestOpportunitiesUM.Error(onRetryClicked = {}),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun EarnContentEmptyPreviewV1() {
|
||||
TangemThemePreview {
|
||||
val background = TangemTheme.colors.background.tertiary
|
||||
CompositionLocalProvider(
|
||||
|
|
@ -506,6 +589,34 @@ private fun EarnContentEmptyPreview() {
|
|||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun EarnContentEmptyPreviewV2() {
|
||||
TangemThemePreviewRedesign {
|
||||
val background = TangemTheme.colors2.surface.level3
|
||||
CompositionLocalProvider(
|
||||
LocalMainBottomSheetColor provides remember { mutableStateOf(background) },
|
||||
) {
|
||||
EarnContent(
|
||||
state = previewEarnUM(
|
||||
mostlyUsed = EarnListUM.Content(
|
||||
items = persistentListOf(
|
||||
previewEarnListItemUM(),
|
||||
previewEarnListItemUM(
|
||||
tokenName = "Cosmos",
|
||||
symbol = "ATOM",
|
||||
network = "Cosmos",
|
||||
),
|
||||
),
|
||||
),
|
||||
bestOpportunities = EarnBestOpportunitiesUM.Empty,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun previewEarnListItemUM(
|
||||
tokenName: String = "USDC",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
package com.tangem.features.feed.ui.earn.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
@Composable
|
||||
internal fun BestOpportunitiesEmpty(modifier: Modifier = Modifier) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
BestOpportunitiesEmptyV2(modifier)
|
||||
} else {
|
||||
BestOpportunitiesEmptyV1(modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BestOpportunitiesEmptyV1(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.background(
|
||||
color = TangemTheme.colors.background.action,
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
)
|
||||
.padding(vertical = 32.dp, horizontal = 12.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
painter = painterResource(R.drawable.ic_empty_64),
|
||||
contentDescription = null,
|
||||
tint = Color.Unspecified,
|
||||
)
|
||||
SpacerH(TangemTheme.dimens2.x6)
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens2.x8),
|
||||
text = stringResourceSafe(R.string.earn_empty),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BestOpportunitiesEmptyV2(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = TangemTheme.dimens2.x4)
|
||||
.background(
|
||||
color = TangemTheme.colors2.surface.level3,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens2.x5),
|
||||
)
|
||||
.padding(vertical = TangemTheme.dimens2.x8, horizontal = TangemTheme.dimens2.x3),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
painter = painterResource(R.drawable.ic_empty_64),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors2.graphic.neutral.quaternary,
|
||||
)
|
||||
SpacerH(TangemTheme.dimens2.x5)
|
||||
Text(
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens2.x8),
|
||||
text = stringResourceSafe(R.string.earn_empty),
|
||||
style = TangemTheme.typography2.bodyRegular14,
|
||||
color = TangemTheme.colors2.text.neutral.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
package com.tangem.features.feed.ui.earn.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.components.buttons.SecondarySmallButton
|
||||
import com.tangem.core.ui.components.buttons.SmallButtonConfig
|
||||
import com.tangem.core.ui.ds.button.*
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
@Composable
|
||||
internal fun BestOpportunitiesEmptyFiltered(onClearFilterClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
BestOpportunitiesEmptyFilteredV2(onClearFilterClick, modifier)
|
||||
} else {
|
||||
BestOpportunitiesEmptyFilteredV1(onClearFilterClick, modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BestOpportunitiesEmptyFilteredV2(onClearFilterClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = TangemTheme.dimens2.x4)
|
||||
.background(
|
||||
color = TangemTheme.colors2.surface.level3,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens2.x5),
|
||||
)
|
||||
.padding(vertical = TangemTheme.dimens2.x8, horizontal = TangemTheme.dimens2.x3),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.earn_no_results),
|
||||
style = TangemTheme.typography2.bodyRegular14,
|
||||
color = TangemTheme.colors2.text.neutral.secondary,
|
||||
)
|
||||
SpacerH(TangemTheme.dimens2.x2)
|
||||
TangemButton(
|
||||
buttonUM = TangemButtonUM(
|
||||
text = resourceReference(R.string.earn_clear_filter),
|
||||
onClick = onClearFilterClick,
|
||||
type = TangemButtonType.Secondary,
|
||||
size = TangemButtonSize.X8,
|
||||
shape = TangemButtonShape.Rounded,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BestOpportunitiesEmptyFilteredV1(onClearFilterClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.background(
|
||||
color = TangemTheme.colors.background.action,
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
)
|
||||
.padding(vertical = 32.dp, horizontal = 12.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.earn_no_results),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
SpacerH(12.dp)
|
||||
SecondarySmallButton(
|
||||
config = SmallButtonConfig(
|
||||
text = resourceReference(R.string.earn_clear_filter),
|
||||
onClick = onClearFilterClick,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package com.tangem.features.feed.ui.earn.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
@Composable
|
||||
internal fun CardFilterBlock(modifier: Modifier = Modifier, content: @Composable ColumnScope.() -> Unit) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.background(
|
||||
color = TangemTheme.colors2.surface.level2,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens2.x5),
|
||||
)
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = TangemTheme.colors2.border.neutral.primary,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens2.x5),
|
||||
),
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
package com.tangem.features.feed.ui.earn.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetType
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBar
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBarType
|
||||
import com.tangem.core.ui.extensions.clickableSingle
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.feed.impl.R
|
||||
|
||||
@Composable
|
||||
internal inline fun <reified T : TangemBottomSheetConfigContent> EarnFilterBottomSheet(
|
||||
config: TangemBottomSheetConfig,
|
||||
crossinline content: @Composable (T) -> Unit,
|
||||
) {
|
||||
TangemBottomSheet<T>(
|
||||
config = config,
|
||||
type = TangemBottomSheetType.Modal,
|
||||
containerColor = TangemTheme.colors2.surface.level3,
|
||||
title = {
|
||||
TangemTopBar(
|
||||
title = resourceReference(R.string.earn_filter_by),
|
||||
type = TangemTopBarType.BottomSheet,
|
||||
endContent = {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.ic_close_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors2.graphic.neutral.primary,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens2.x11)
|
||||
.background(
|
||||
color = TangemTheme.colors2.button.backgroundSecondary,
|
||||
shape = CircleShape,
|
||||
)
|
||||
.clickableSingle(onClick = config.onDismissRequest)
|
||||
.padding(TangemTheme.dimens2.x2_5),
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,9 +1,7 @@
|
|||
package com.tangem.features.feed.ui.earn.components
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
|
|
@ -15,11 +13,13 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.layout.layoutId
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastForEachIndexed
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
|
||||
|
|
@ -28,28 +28,53 @@ import com.tangem.core.ui.components.inputrow.inner.DividerContainer
|
|||
import com.tangem.core.ui.components.rows.RowContentContainer
|
||||
import com.tangem.core.ui.components.rows.RowText
|
||||
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
|
||||
import com.tangem.core.ui.ds.checkbox.TangemCheckbox
|
||||
import com.tangem.core.ui.ds.row.TangemRowContainer
|
||||
import com.tangem.core.ui.ds.row.TangemRowLayoutId
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
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.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
import com.tangem.features.feed.impl.R
|
||||
import com.tangem.features.feed.ui.earn.state.EarnFilterByNetworkBottomSheetContentUM
|
||||
import com.tangem.features.feed.ui.earn.state.EarnFilterNetworkUM
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
@Composable
|
||||
internal fun EarnFilterByNetworkBottomSheet(config: TangemBottomSheetConfig) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
EarnFilterByNetworkBottomSheetV2(config = config)
|
||||
} else {
|
||||
EarnFilterByNetworkBottomSheetV1(config = config)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun EarnFilterByNetworkBottomSheetV1(config: TangemBottomSheetConfig) {
|
||||
TangemBottomSheet<EarnFilterByNetworkBottomSheetContentUM>(
|
||||
config = config,
|
||||
titleText = resourceReference(R.string.earn_filter_by),
|
||||
containerColor = TangemTheme.colors.background.tertiary,
|
||||
content = { Content(it) },
|
||||
content = { ContentV1(it) },
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Content(content: EarnFilterByNetworkBottomSheetContentUM) {
|
||||
private fun EarnFilterByNetworkBottomSheetV2(config: TangemBottomSheetConfig) {
|
||||
EarnFilterBottomSheet<EarnFilterByNetworkBottomSheetContentUM>(
|
||||
config = config,
|
||||
content = { ContentV2(it) },
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ContentV1(content: EarnFilterByNetworkBottomSheetContentUM) {
|
||||
val allMyNetworks = remember(content) {
|
||||
content.networks.filterIsInstance<EarnFilterNetworkUM.AllNetworks>() +
|
||||
content.networks.filterIsInstance<EarnFilterNetworkUM.MyNetworks>()
|
||||
|
|
@ -79,6 +104,139 @@ private fun Content(content: EarnFilterByNetworkBottomSheetContentUM) {
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ContentV2(content: EarnFilterByNetworkBottomSheetContentUM) {
|
||||
val allMyNetworks = remember(content) {
|
||||
(content.networks.filterIsInstance<EarnFilterNetworkUM.AllNetworks>() +
|
||||
content.networks.filterIsInstance<EarnFilterNetworkUM.MyNetworks>()).toImmutableList()
|
||||
}
|
||||
val specificNetworks = remember(content) {
|
||||
content
|
||||
.networks
|
||||
.filterIsInstance<EarnFilterNetworkUM.Network>()
|
||||
.toImmutableList()
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens2.x4)
|
||||
.padding(bottom = TangemTheme.dimens2.x4)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x3),
|
||||
) {
|
||||
NetworksTypesBlock(
|
||||
allMyNetworks = allMyNetworks,
|
||||
onOptionClick = content.onOptionClick,
|
||||
)
|
||||
|
||||
SpecificNetworksBlock(
|
||||
specificNetworks = specificNetworks,
|
||||
onOptionClick = content.onOptionClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun NetworksTypesBlock(
|
||||
allMyNetworks: ImmutableList<EarnFilterNetworkUM>,
|
||||
onOptionClick: (EarnFilterNetworkUM) -> Unit,
|
||||
) {
|
||||
CardFilterBlock {
|
||||
allMyNetworks.fastForEachIndexed { index, item ->
|
||||
TangemRowContainer(
|
||||
modifier = Modifier
|
||||
.roundedShapeItemDecoration(
|
||||
currentIndex = index,
|
||||
lastIndex = allMyNetworks.lastIndex,
|
||||
addDefaultPadding = false,
|
||||
)
|
||||
.clickable { onOptionClick(item) },
|
||||
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 12.dp),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.layoutId(layoutId = TangemRowLayoutId.START_TOP),
|
||||
text = when (item) {
|
||||
is EarnFilterNetworkUM.AllNetworks -> TextReference.Res(R.string.earn_filter_all_networks)
|
||||
is EarnFilterNetworkUM.MyNetworks -> TextReference.Res(R.string.earn_filter_my_networks)
|
||||
is EarnFilterNetworkUM.Network -> TextReference.Str(item.text)
|
||||
}.resolveReference(),
|
||||
style = TangemTheme.typography2.bodySemibold16,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
|
||||
TangemCheckbox(
|
||||
modifier = Modifier
|
||||
.padding(start = 8.dp)
|
||||
.layoutId(layoutId = TangemRowLayoutId.TAIL),
|
||||
isChecked = item.isSelected,
|
||||
onCheckedChange = { onOptionClick(item) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SpecificNetworksBlock(
|
||||
specificNetworks: ImmutableList<EarnFilterNetworkUM.Network>,
|
||||
onOptionClick: (EarnFilterNetworkUM) -> Unit,
|
||||
) {
|
||||
if (specificNetworks.isNotEmpty()) {
|
||||
CardFilterBlock {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.padding(top = 16.dp, bottom = 8.dp),
|
||||
text = stringResourceSafe(id = R.string.earn_filter_networks),
|
||||
style = TangemTheme.typography2.bodyRegular14,
|
||||
color = TangemTheme.colors2.text.neutral.tertiary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
specificNetworks.fastForEachIndexed { index, item ->
|
||||
TangemRowContainer(
|
||||
modifier = Modifier
|
||||
.roundedShapeItemDecoration(
|
||||
currentIndex = index,
|
||||
lastIndex = specificNetworks.lastIndex,
|
||||
addDefaultPadding = false,
|
||||
)
|
||||
.clickable { onOptionClick(item) },
|
||||
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 12.dp),
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.size(40.dp)
|
||||
.layoutId(TangemRowLayoutId.HEAD),
|
||||
imageVector = ImageVector.vectorResource(item.iconRes),
|
||||
contentDescription = item.symbol,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(start = 8.dp)
|
||||
.layoutId(layoutId = TangemRowLayoutId.START_TOP),
|
||||
text = item.text,
|
||||
style = TangemTheme.typography2.bodySemibold16,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
|
||||
TangemCheckbox(
|
||||
modifier = Modifier
|
||||
.padding(start = 8.dp)
|
||||
.layoutId(layoutId = TangemRowLayoutId.TAIL),
|
||||
isChecked = item.isSelected,
|
||||
onCheckedChange = { onOptionClick(item) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.allMyNetworksList(
|
||||
allMyNetworks: List<EarnFilterNetworkUM>,
|
||||
onOptionClicked: (EarnFilterNetworkUM) -> Unit,
|
||||
|
|
@ -201,7 +359,7 @@ private fun LazyListScope.specificNetworksList(
|
|||
@Preview(widthDp = 360, heightDp = 800)
|
||||
@Preview(widthDp = 360, heightDp = 800, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun Preview() {
|
||||
private fun PreviewV1() {
|
||||
TangemThemePreview(
|
||||
alwaysShowBottomSheets = true,
|
||||
) {
|
||||
|
|
@ -235,4 +393,43 @@ private fun Preview() {
|
|||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 360, heightDp = 800)
|
||||
@Preview(widthDp = 360, heightDp = 800, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun PreviewV2() {
|
||||
TangemThemePreviewRedesign(
|
||||
alwaysShowBottomSheets = true,
|
||||
) {
|
||||
Box(Modifier.background(TangemTheme.colors.background.secondary)) {
|
||||
EarnFilterByNetworkBottomSheet(
|
||||
TangemBottomSheetConfig(
|
||||
isShown = true,
|
||||
onDismissRequest = {},
|
||||
content = EarnFilterByNetworkBottomSheetContentUM(
|
||||
networks = persistentListOf(
|
||||
EarnFilterNetworkUM.AllNetworks(isSelected = true),
|
||||
EarnFilterNetworkUM.MyNetworks(isSelected = false),
|
||||
EarnFilterNetworkUM.Network(
|
||||
id = "ethereum",
|
||||
text = "Ethereum",
|
||||
symbol = "ETH",
|
||||
iconRes = R.drawable.img_btc_22,
|
||||
isSelected = false,
|
||||
),
|
||||
EarnFilterNetworkUM.Network(
|
||||
id = "polygon",
|
||||
text = "Polygon",
|
||||
symbol = "MATIC",
|
||||
iconRes = R.drawable.img_btc_22,
|
||||
isSelected = false,
|
||||
),
|
||||
),
|
||||
onOptionClick = {},
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,34 +5,62 @@ import androidx.compose.foundation.background
|
|||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.layoutId
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.inputrow.InputRowChecked
|
||||
import com.tangem.core.ui.components.inputrow.inner.DividerContainer
|
||||
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
|
||||
import com.tangem.core.ui.ds.checkbox.TangemCheckbox
|
||||
import com.tangem.core.ui.ds.row.TangemRowContainer
|
||||
import com.tangem.core.ui.ds.row.TangemRowLayoutId
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
import com.tangem.features.feed.impl.R
|
||||
import com.tangem.features.feed.ui.earn.state.EarnFilterByTypeBottomSheetContentUM
|
||||
import com.tangem.features.feed.ui.earn.state.EarnFilterTypeUM
|
||||
|
||||
@Composable
|
||||
internal fun EarnFilterByTypeBottomSheet(config: TangemBottomSheetConfig) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
EarnFilterByTypeBottomSheetV2(config)
|
||||
} else {
|
||||
EarnFilterByTypeBottomSheetV1(config)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun EarnFilterByTypeBottomSheetV1(config: TangemBottomSheetConfig) {
|
||||
TangemBottomSheet<EarnFilterByTypeBottomSheetContentUM>(
|
||||
config = config,
|
||||
titleText = resourceReference(R.string.earn_filter_by),
|
||||
containerColor = TangemTheme.colors.background.tertiary,
|
||||
content = { Content(it) },
|
||||
content = { ContentV1(it) },
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Content(content: EarnFilterByTypeBottomSheetContentUM) {
|
||||
private fun EarnFilterByTypeBottomSheetV2(config: TangemBottomSheetConfig) {
|
||||
EarnFilterBottomSheet<EarnFilterByTypeBottomSheetContentUM>(
|
||||
config = config,
|
||||
content = { ContentV2(it) },
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ContentV1(content: EarnFilterByTypeBottomSheetContentUM) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
|
|
@ -62,10 +90,49 @@ private fun Content(content: EarnFilterByTypeBottomSheetContentUM) {
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ContentV2(content: EarnFilterByTypeBottomSheetContentUM) {
|
||||
CardFilterBlock(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens2.x4)
|
||||
.padding(bottom = TangemTheme.dimens2.x4),
|
||||
) {
|
||||
EarnFilterTypeUM.entries.forEachIndexed { index, type ->
|
||||
TangemRowContainer(
|
||||
modifier = Modifier
|
||||
.roundedShapeItemDecoration(
|
||||
currentIndex = index,
|
||||
lastIndex = EarnFilterTypeUM.entries.lastIndex,
|
||||
addDefaultPadding = false,
|
||||
)
|
||||
.clickable { content.onOptionClick(type) },
|
||||
contentPadding = PaddingValues(horizontal = TangemTheme.dimens2.x4, vertical = TangemTheme.dimens2.x3),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.layoutId(layoutId = TangemRowLayoutId.START_TOP),
|
||||
text = type.text.resolveReference(),
|
||||
style = TangemTheme.typography2.bodySemibold16,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
|
||||
TangemCheckbox(
|
||||
modifier = Modifier
|
||||
.padding(start = 8.dp)
|
||||
.layoutId(layoutId = TangemRowLayoutId.TAIL),
|
||||
isChecked = type == content.selectedOption,
|
||||
onCheckedChange = { content.onOptionClick(type) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 360, heightDp = 640)
|
||||
@Preview(widthDp = 360, heightDp = 640, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun Preview() {
|
||||
private fun PreviewV1() {
|
||||
TangemThemePreview(
|
||||
alwaysShowBottomSheets = true,
|
||||
) {
|
||||
|
|
@ -82,4 +149,26 @@ private fun Preview() {
|
|||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 360, heightDp = 640)
|
||||
@Preview(widthDp = 360, heightDp = 640, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun PreviewV2() {
|
||||
TangemThemePreviewRedesign(
|
||||
alwaysShowBottomSheets = true,
|
||||
) {
|
||||
Box(Modifier.background(TangemTheme.colors.background.secondary)) {
|
||||
EarnFilterByTypeBottomSheet(
|
||||
TangemBottomSheetConfig(
|
||||
isShown = true,
|
||||
onDismissRequest = {},
|
||||
content = EarnFilterByTypeBottomSheetContentUM(
|
||||
selectedOption = EarnFilterTypeUM.All,
|
||||
onOptionClick = {},
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,12 +4,16 @@ import android.content.res.Configuration
|
|||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.layout.layoutId
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
|
@ -18,15 +22,33 @@ import com.tangem.core.ui.components.SpacerH
|
|||
import com.tangem.core.ui.components.SpacerW
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIcon
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.ds.image.TangemIcon
|
||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||
import com.tangem.core.ui.ds.row.TangemRowContainer
|
||||
import com.tangem.core.ui.ds.row.TangemRowLayoutId
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemColorPalette
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.res.*
|
||||
import com.tangem.features.feed.ui.earn.state.EarnListItemUM
|
||||
|
||||
@Composable
|
||||
internal fun EarnListItem(item: EarnListItemUM, modifier: Modifier = Modifier) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
EarnListItemV2(
|
||||
item = item,
|
||||
modifier = modifier,
|
||||
)
|
||||
} else {
|
||||
EarnListItemV1(
|
||||
item = item,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun EarnListItemV1(item: EarnListItemUM, modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
|
|
@ -82,27 +104,117 @@ internal fun EarnListItem(item: EarnListItemUM, modifier: Modifier = Modifier) {
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun EarnListItemV2(item: EarnListItemUM, modifier: Modifier = Modifier) {
|
||||
TangemRowContainer(
|
||||
modifier = modifier.clickable { item.onItemClick() },
|
||||
contentPadding = PaddingValues(12.dp),
|
||||
content = {
|
||||
TangemIcon(
|
||||
tangemIconUM = TangemIconUM.Currency(item.currencyIconState),
|
||||
modifier = Modifier
|
||||
.layoutId(layoutId = TangemRowLayoutId.HEAD)
|
||||
.padding(end = TangemTheme.dimens2.x2),
|
||||
)
|
||||
|
||||
TokenTitle(
|
||||
modifier = Modifier
|
||||
.layoutId(layoutId = TangemRowLayoutId.START_TOP)
|
||||
.padding(end = TangemTheme.dimens2.x2),
|
||||
name = item.tokenName.resolveReference(),
|
||||
symbol = item.symbol.resolveReference(),
|
||||
)
|
||||
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.layoutId(layoutId = TangemRowLayoutId.START_BOTTOM)
|
||||
.padding(end = TangemTheme.dimens2.x2),
|
||||
text = item.network.resolveReference(),
|
||||
color = TangemTheme.colors2.text.neutral.secondary,
|
||||
style = TangemTheme.typography2.captionSemibold12,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
|
||||
Text(
|
||||
modifier = Modifier.layoutId(layoutId = TangemRowLayoutId.END_TOP),
|
||||
text = item.earnValue.resolveReference(),
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
style = TangemTheme.typography2.bodySemibold16,
|
||||
maxLines = 1,
|
||||
)
|
||||
|
||||
ModeBlock(
|
||||
modifier = Modifier.layoutId(layoutId = TangemRowLayoutId.END_BOTTOM),
|
||||
earnType = item.earnType,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TokenTitle(name: String, symbol: String, modifier: Modifier = Modifier) {
|
||||
Row(modifier = modifier) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.weight(1f, fill = false)
|
||||
.alignByBaseline(),
|
||||
text = name,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
if (LocalRedesignEnabled.current) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.weight(1f, fill = false)
|
||||
.alignByBaseline(),
|
||||
text = name,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
style = TangemTheme.typography2.bodySemibold16,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
SpacerW(4.dp)
|
||||
Text(
|
||||
modifier = Modifier.alignByBaseline(),
|
||||
text = symbol,
|
||||
color = TangemTheme.colors2.text.neutral.secondary,
|
||||
style = TangemTheme.typography2.captionSemibold12,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Visible,
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.weight(1f, fill = false)
|
||||
.alignByBaseline(),
|
||||
text = name,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
SpacerW(4.dp)
|
||||
Text(
|
||||
modifier = Modifier.alignByBaseline(),
|
||||
text = symbol,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.body2,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Visible,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ModeBlock(earnType: TextReference, modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(2.dp),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_staking_new_16),
|
||||
tint = TangemTheme.colors2.markers.iconGray,
|
||||
contentDescription = null,
|
||||
)
|
||||
SpacerW(4.dp)
|
||||
Text(
|
||||
modifier = Modifier.alignByBaseline(),
|
||||
text = symbol,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.body2,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Visible,
|
||||
text = earnType.resolveReference(),
|
||||
style = TangemTheme.typography2.captionSemibold12,
|
||||
color = TangemTheme.colors2.text.neutral.tertiary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -110,7 +222,7 @@ private fun TokenTitle(name: String, symbol: String, modifier: Modifier = Modifi
|
|||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun EarnListItemPreview() {
|
||||
private fun EarnListItemPreviewV1() {
|
||||
TangemThemePreview {
|
||||
Column(modifier = Modifier.background(TangemTheme.colors.background.primary)) {
|
||||
EarnListItem(
|
||||
|
|
@ -151,4 +263,53 @@ private fun EarnListItemPreview() {
|
|||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun EarnListItemPreviewV2() {
|
||||
TangemThemePreviewRedesign {
|
||||
Column(modifier = Modifier.background(TangemTheme.colors.background.primary)) {
|
||||
SpacerH(12.dp)
|
||||
EarnListItem(
|
||||
item = EarnListItemUM(
|
||||
network = stringReference("Ethereum"),
|
||||
symbol = stringReference("ETH"),
|
||||
tokenName = stringReference("Ethereum"),
|
||||
currencyIconState = CurrencyIconState.TokenIcon(
|
||||
url = null,
|
||||
topBadgeIconResId = R.drawable.img_eth_22,
|
||||
fallbackTint = TangemColorPalette.Black,
|
||||
fallbackBackground = TangemColorPalette.Meadow,
|
||||
isGrayscale = false,
|
||||
shouldShowCustomBadge = false,
|
||||
),
|
||||
earnValue = stringReference("APY 8.50%"),
|
||||
earnType = stringReference("Yield"),
|
||||
onItemClick = {},
|
||||
),
|
||||
)
|
||||
SpacerH(12.dp)
|
||||
EarnListItem(
|
||||
item = EarnListItemUM(
|
||||
network = stringReference("Ethereum"),
|
||||
symbol = stringReference("USDT"),
|
||||
tokenName = stringReference("Tether"),
|
||||
currencyIconState = CurrencyIconState.TokenIcon(
|
||||
url = null,
|
||||
topBadgeIconResId = R.drawable.img_eth_22,
|
||||
fallbackTint = TangemColorPalette.Black,
|
||||
fallbackBackground = TangemColorPalette.Meadow,
|
||||
isGrayscale = false,
|
||||
shouldShowCustomBadge = false,
|
||||
),
|
||||
earnValue = stringReference("APY 8.50%"),
|
||||
earnType = stringReference("Yield"),
|
||||
onItemClick = {},
|
||||
),
|
||||
)
|
||||
SpacerH(12.dp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,27 +8,13 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.CircleShimmer
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.components.SpacerW
|
||||
import com.tangem.core.ui.components.SpacerWMax
|
||||
import com.tangem.core.ui.components.*
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
|
||||
@Composable
|
||||
internal fun EarnListPlaceholder(modifier: Modifier = Modifier, placeholderCount: Int = PLACEHOLDER_ITEMS_COUNT) {
|
||||
Column(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
) {
|
||||
repeat(placeholderCount) {
|
||||
EarnItemPlaceholder()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun EarnItemPlaceholder(modifier: Modifier = Modifier) {
|
||||
internal fun EarnItemPlaceholderV1(modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
|
|
@ -38,9 +24,7 @@ internal fun EarnItemPlaceholder(modifier: Modifier = Modifier) {
|
|||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
CircleShimmer(
|
||||
modifier = Modifier.size(36.dp),
|
||||
)
|
||||
CircleShimmer(modifier = Modifier.size(36.dp))
|
||||
|
||||
SpacerW(12.dp)
|
||||
|
||||
|
|
@ -91,18 +75,86 @@ internal fun EarnItemPlaceholder(modifier: Modifier = Modifier) {
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun EarnItemPlaceholderV2(modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
CircleShimmer(modifier = Modifier.size(40.dp))
|
||||
|
||||
SpacerW(4.dp)
|
||||
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Row(modifier = Modifier.fillMaxWidth()) {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(96.dp)
|
||||
.height(20.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
SpacerWMax()
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(56.dp)
|
||||
.height(20.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
}
|
||||
|
||||
SpacerH(4.dp)
|
||||
|
||||
Row(modifier = Modifier.fillMaxWidth()) {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(46.dp)
|
||||
.height(16.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
SpacerWMax()
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(46.dp)
|
||||
.height(16.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val PLACEHOLDER_ITEMS_COUNT = 8
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun EarnListPlaceholderPreview() {
|
||||
private fun EarnListPlaceholderPreviewV1() {
|
||||
TangemThemePreview {
|
||||
Box(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(TangemTheme.colors.background.tertiary),
|
||||
) {
|
||||
EarnListPlaceholder()
|
||||
repeat(PLACEHOLDER_ITEMS_COUNT) {
|
||||
EarnItemPlaceholderV1()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun EarnListPlaceholderPreviewV2() {
|
||||
TangemThemePreviewRedesign {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(TangemTheme.colors2.surface.level3),
|
||||
) {
|
||||
repeat(PLACEHOLDER_ITEMS_COUNT) {
|
||||
EarnItemPlaceholderV2()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
package com.tangem.features.feed.ui.earn.components
|
||||
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.SpacerWMax
|
||||
import com.tangem.core.ui.components.buttons.SecondarySmallButton
|
||||
import com.tangem.core.ui.components.buttons.SmallButtonConfig
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition
|
||||
import com.tangem.core.ui.ds.button.PrimaryInverseTangemButton
|
||||
import com.tangem.core.ui.ds.button.TangemButtonShape
|
||||
import com.tangem.core.ui.ds.button.TangemButtonSize
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.features.feed.ui.earn.state.EarnFilterNetworkUM
|
||||
import com.tangem.features.feed.ui.earn.state.EarnFilterUM
|
||||
|
||||
@Composable
|
||||
internal fun FilterButtons(
|
||||
earnFilterUM: EarnFilterUM,
|
||||
onNetworkFilterClick: () -> Unit,
|
||||
onTypeFilterClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
FilterButtonsV2(
|
||||
earnFilterUM = earnFilterUM,
|
||||
onNetworkFilterClick = onNetworkFilterClick,
|
||||
onTypeFilterClick = onTypeFilterClick,
|
||||
modifier = modifier,
|
||||
)
|
||||
} else {
|
||||
FilterButtonsV1(
|
||||
earnFilterUM = earnFilterUM,
|
||||
onNetworkFilterClick = onNetworkFilterClick,
|
||||
onTypeFilterClick = onTypeFilterClick,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FilterButtonsV1(
|
||||
earnFilterUM: EarnFilterUM,
|
||||
onNetworkFilterClick: () -> Unit,
|
||||
onTypeFilterClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier.padding(horizontal = 16.dp),
|
||||
) {
|
||||
SecondarySmallButton(
|
||||
config = SmallButtonConfig(
|
||||
text = when (earnFilterUM.selectedNetworkFilter) {
|
||||
is EarnFilterNetworkUM.AllNetworks -> TextReference.Res(R.string.earn_filter_all_networks)
|
||||
is EarnFilterNetworkUM.MyNetworks -> TextReference.Res(R.string.earn_filter_my_networks)
|
||||
is EarnFilterNetworkUM.Network -> TextReference.Str(earnFilterUM.selectedNetworkFilter.text)
|
||||
},
|
||||
onClick = onNetworkFilterClick,
|
||||
icon = TangemButtonIconPosition.End(iconResId = R.drawable.ic_chevron_24),
|
||||
isEnabled = earnFilterUM.isNetworkFilterEnabled,
|
||||
),
|
||||
)
|
||||
|
||||
SpacerWMax()
|
||||
|
||||
SecondarySmallButton(
|
||||
config = SmallButtonConfig(
|
||||
text = earnFilterUM.selectedTypeFilter.text,
|
||||
onClick = onTypeFilterClick,
|
||||
icon = TangemButtonIconPosition.End(iconResId = R.drawable.ic_chevron_24),
|
||||
isEnabled = earnFilterUM.isTypeFilterEnabled,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FilterButtonsV2(
|
||||
earnFilterUM: EarnFilterUM,
|
||||
onNetworkFilterClick: () -> Unit,
|
||||
onTypeFilterClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(modifier = modifier.padding(horizontal = 16.dp, vertical = 4.dp)) {
|
||||
PrimaryInverseTangemButton(
|
||||
text = when (earnFilterUM.selectedNetworkFilter) {
|
||||
is EarnFilterNetworkUM.AllNetworks -> TextReference.Res(R.string.earn_filter_all_networks)
|
||||
is EarnFilterNetworkUM.MyNetworks -> TextReference.Res(R.string.earn_filter_my_networks)
|
||||
is EarnFilterNetworkUM.Network -> TextReference.Str(earnFilterUM.selectedNetworkFilter.text)
|
||||
},
|
||||
onClick = onNetworkFilterClick,
|
||||
iconRes = R.drawable.ic_chewron_down_20,
|
||||
iconPosition = com.tangem.core.ui.ds.button.TangemButtonIconPosition.End,
|
||||
size = TangemButtonSize.X9,
|
||||
shape = TangemButtonShape.Rounded,
|
||||
isEnabled = earnFilterUM.isNetworkFilterEnabled,
|
||||
)
|
||||
|
||||
SpacerWMax()
|
||||
|
||||
PrimaryInverseTangemButton(
|
||||
text = earnFilterUM.selectedTypeFilter.text,
|
||||
onClick = onTypeFilterClick,
|
||||
iconRes = R.drawable.ic_chewron_down_20,
|
||||
iconPosition = com.tangem.core.ui.ds.button.TangemButtonIconPosition.End,
|
||||
size = TangemButtonSize.X9,
|
||||
shape = TangemButtonShape.Rounded,
|
||||
isEnabled = earnFilterUM.isTypeFilterEnabled,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -44,7 +44,7 @@ internal fun MostlyUsedCard(item: EarnListItemUM, onClick: () -> Unit, modifier:
|
|||
private fun MostlyUsedCardV2(item: EarnListItemUM, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
OpportunitiesBG(
|
||||
modifier = modifier
|
||||
.width(148.dp)
|
||||
.width(178.dp)
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.clickable(onClick = onClick),
|
||||
icon = TangemIconUM.Currency(item.currencyIconState),
|
||||
|
|
|
|||
|
|
@ -2,14 +2,9 @@ package com.tangem.features.feed.ui.earn.components
|
|||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
|
@ -17,27 +12,36 @@ import androidx.compose.ui.unit.dp
|
|||
import com.tangem.core.ui.components.CircleShimmer
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
|
||||
@Composable
|
||||
internal fun MostlyUsedPlaceholder(modifier: Modifier = Modifier) {
|
||||
internal fun MostlyUsedPlaceholder(
|
||||
modifier: Modifier = Modifier,
|
||||
contentPadding: PaddingValues = PaddingValues(
|
||||
horizontal = 16.dp,
|
||||
vertical = 12.dp,
|
||||
),
|
||||
) {
|
||||
LazyRow(
|
||||
modifier = modifier,
|
||||
contentPadding = PaddingValues(
|
||||
horizontal = 16.dp,
|
||||
vertical = 12.dp,
|
||||
),
|
||||
contentPadding = contentPadding,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
items(PLACEHOLDER_ITEMS_COUNT) {
|
||||
MostlyUsedItemPlaceholder()
|
||||
if (LocalRedesignEnabled.current) {
|
||||
MostlyUsedItemPlaceholderV2()
|
||||
} else {
|
||||
MostlyUsedItemPlaceholderV1()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MostlyUsedItemPlaceholder(modifier: Modifier = Modifier) {
|
||||
private fun MostlyUsedItemPlaceholderV1(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.width(148.dp)
|
||||
|
|
@ -69,13 +73,52 @@ fun MostlyUsedItemPlaceholder(modifier: Modifier = Modifier) {
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MostlyUsedItemPlaceholderV2(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.width(178.dp)
|
||||
.height(130.dp)
|
||||
.background(
|
||||
color = TangemTheme.colors2.surface.level3,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens2.x4),
|
||||
)
|
||||
.padding(12.dp),
|
||||
) {
|
||||
CircleShimmer(modifier = Modifier.size(40.dp))
|
||||
SpacerH(22.dp)
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(56.dp)
|
||||
.height(20.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
SpacerH(4.dp)
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(46.dp)
|
||||
.height(16.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private const val PLACEHOLDER_ITEMS_COUNT = 3
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun MostlyUsedPlaceholderPreview() {
|
||||
private fun MostlyUsedPlaceholderPreviewV1() {
|
||||
TangemThemePreview {
|
||||
MostlyUsedPlaceholder()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun MostlyUsedPlaceholderPreviewV2() {
|
||||
TangemThemePreviewRedesign {
|
||||
MostlyUsedPlaceholder()
|
||||
}
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@ import androidx.compose.ui.platform.testTag
|
|||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.components.SpacerW
|
||||
|
|
@ -56,7 +57,11 @@ internal fun FeedListHeader(
|
|||
}
|
||||
|
||||
@Composable
|
||||
internal fun FeedList(state: FeedListUM, modifier: Modifier = Modifier) {
|
||||
internal fun FeedList(
|
||||
state: FeedListUM,
|
||||
modifier: Modifier = Modifier,
|
||||
promoBannersBlockComponent: ComposableContentComponent? = null,
|
||||
) {
|
||||
val background = LocalMainBottomSheetColor.current.value
|
||||
AnimatedContent(
|
||||
modifier = modifier,
|
||||
|
|
@ -83,6 +88,7 @@ internal fun FeedList(state: FeedListUM, modifier: Modifier = Modifier) {
|
|||
FeedListContent(
|
||||
modifier = Modifier,
|
||||
state = state,
|
||||
promoBannersBlockComponent = promoBannersBlockComponent,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -123,7 +129,11 @@ private fun FeedSearchBar(
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun FeedListContent(state: FeedListUM, modifier: Modifier = Modifier) {
|
||||
private fun FeedListContent(
|
||||
state: FeedListUM,
|
||||
modifier: Modifier = Modifier,
|
||||
promoBannersBlockComponent: ComposableContentComponent? = null,
|
||||
) {
|
||||
val background = LocalMainBottomSheetColor.current.value
|
||||
Column(
|
||||
modifier = modifier
|
||||
|
|
@ -140,6 +150,8 @@ private fun FeedListContent(state: FeedListUM, modifier: Modifier = Modifier) {
|
|||
feedListCallbacks = state.feedListCallbacks,
|
||||
)
|
||||
|
||||
promoBannersBlockComponent?.Content(modifier = Modifier.padding(horizontal = 16.dp))
|
||||
|
||||
NewsBlock(
|
||||
news = state.news,
|
||||
feedListCallbacks = state.feedListCallbacks,
|
||||
|
|
|
|||
|
|
@ -2,10 +2,15 @@ package com.tangem.features.feed.ui.feed.components
|
|||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
|
|
@ -13,6 +18,9 @@ import com.tangem.core.ui.components.SpacerW
|
|||
import com.tangem.core.ui.components.buttons.SecondarySmallButton
|
||||
import com.tangem.core.ui.components.buttons.SmallButtonConfig
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
@Composable
|
||||
internal fun Header(
|
||||
|
|
@ -21,6 +29,7 @@ internal fun Header(
|
|||
shouldShowSeeAll: Boolean,
|
||||
title: @Composable () -> Unit,
|
||||
) {
|
||||
val isRedesignEnabled = LocalRedesignEnabled.current
|
||||
AnimatedContent(isLoading) { animatedState ->
|
||||
Row(
|
||||
modifier = Modifier
|
||||
|
|
@ -30,19 +39,47 @@ internal fun Header(
|
|||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
if (animatedState) {
|
||||
RectangleShimmer(modifier = Modifier.size(width = 104.dp, height = 18.dp))
|
||||
if (isRedesignEnabled) {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier.size(width = 130.dp, height = 24.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
} else {
|
||||
RectangleShimmer(modifier = Modifier.size(width = 104.dp, height = 18.dp))
|
||||
}
|
||||
} else {
|
||||
Box(modifier = Modifier.weight(1f)) {
|
||||
title()
|
||||
}
|
||||
SpacerW(8.dp)
|
||||
AnimatedVisibility(shouldShowSeeAll) {
|
||||
SecondarySmallButton(
|
||||
config = SmallButtonConfig(
|
||||
text = TextReference.Res(R.string.common_see_all),
|
||||
onClick = onSeeAllClick,
|
||||
),
|
||||
)
|
||||
if (isRedesignEnabled) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(start = 8.dp)
|
||||
.clickable(onClick = onSeeAllClick),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.common_see_all),
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
style = TangemTheme.typography2.bodySemibold16,
|
||||
)
|
||||
Icon(
|
||||
modifier = Modifier.size(24.dp),
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_chevron_small_right_24),
|
||||
tint = TangemTheme.colors2.markers.iconGray,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
SecondarySmallButton(
|
||||
config = SmallButtonConfig(
|
||||
text = TextReference.Res(R.string.common_see_all),
|
||||
onClick = onSeeAllClick,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
package com.tangem.features.feed.ui.feed.components
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -16,9 +16,12 @@ import com.tangem.core.ui.components.UnableToLoadData
|
|||
import com.tangem.core.ui.components.block.BlockCard
|
||||
import com.tangem.core.ui.components.block.TangemBlockCardColors
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.feed.ui.earn.components.EarnItemPlaceholderV1
|
||||
import com.tangem.features.feed.ui.earn.components.EarnListItem
|
||||
import com.tangem.features.feed.ui.earn.components.EarnListPlaceholder
|
||||
import com.tangem.features.feed.ui.earn.components.MostlyUsedCard
|
||||
import com.tangem.features.feed.ui.earn.components.MostlyUsedPlaceholder
|
||||
import com.tangem.features.feed.ui.earn.state.EarnListItemUM
|
||||
import com.tangem.features.feed.ui.earn.state.EarnListUM
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
|
@ -26,7 +29,15 @@ import kotlinx.collections.immutable.ImmutableList
|
|||
@Composable
|
||||
internal fun EarnBlock(onSeeAllClick: () -> Unit, earnListUM: EarnListUM, modifier: Modifier = Modifier) {
|
||||
if (earnListUM is EarnListUM.Empty) return
|
||||
if (LocalRedesignEnabled.current) {
|
||||
EarnBlockV2(onSeeAllClick = onSeeAllClick, earnListUM = earnListUM, modifier = modifier)
|
||||
} else {
|
||||
EarnBlockV1(onSeeAllClick = onSeeAllClick, earnListUM = earnListUM, modifier = modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun EarnBlockV1(onSeeAllClick: () -> Unit, earnListUM: EarnListUM, modifier: Modifier = Modifier) {
|
||||
Column(modifier = modifier) {
|
||||
Header(
|
||||
title = {
|
||||
|
|
@ -56,7 +67,11 @@ internal fun EarnBlock(onSeeAllClick: () -> Unit, earnListUM: EarnListUM, modifi
|
|||
when (earnListUM) {
|
||||
is EarnListUM.Content -> EarnContentBlock(items = earnListUM.items)
|
||||
is EarnListUM.Error -> EarnErrorBlock(onRetryClick = earnListUM.onRetryClicked)
|
||||
EarnListUM.Loading -> EarnListPlaceholder(placeholderCount = PLACEHOLDER_ITEM_COUNT)
|
||||
EarnListUM.Loading -> {
|
||||
repeat(PLACEHOLDER_ITEM_COUNT) {
|
||||
EarnItemPlaceholderV1()
|
||||
}
|
||||
}
|
||||
EarnListUM.Empty -> Unit
|
||||
}
|
||||
}
|
||||
|
|
@ -65,6 +80,55 @@ internal fun EarnBlock(onSeeAllClick: () -> Unit, earnListUM: EarnListUM, modifi
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun EarnBlockV2(onSeeAllClick: () -> Unit, earnListUM: EarnListUM, modifier: Modifier = Modifier) {
|
||||
Column(modifier = modifier) {
|
||||
Header(
|
||||
title = {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.markets_earn_common_title),
|
||||
style = TangemTheme.typography2.headingSemibold20,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
)
|
||||
},
|
||||
onSeeAllClick = onSeeAllClick,
|
||||
isLoading = earnListUM is EarnListUM.Loading,
|
||||
shouldShowSeeAll = earnListUM is EarnListUM.Content,
|
||||
)
|
||||
|
||||
SpacerH(12.dp)
|
||||
AnimatedContent(
|
||||
targetState = earnListUM,
|
||||
contentKey = { it::class.java },
|
||||
) { earnListUM ->
|
||||
when (earnListUM) {
|
||||
is EarnListUM.Content -> EarnContentBlock(items = earnListUM.items)
|
||||
is EarnListUM.Error -> {
|
||||
BlockCard(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
colors = TangemBlockCardColors.copy(containerColor = TangemTheme.colors2.surface.level3),
|
||||
) {
|
||||
EarnErrorBlock(onRetryClick = earnListUM.onRetryClicked)
|
||||
}
|
||||
}
|
||||
EarnListUM.Loading -> {
|
||||
MostlyUsedPlaceholder(
|
||||
contentPadding = PaddingValues(
|
||||
start = 16.dp,
|
||||
end = 16.dp,
|
||||
bottom = 8.dp,
|
||||
),
|
||||
)
|
||||
}
|
||||
EarnListUM.Empty -> Unit
|
||||
}
|
||||
}
|
||||
SpacerH(32.dp)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun EarnErrorBlock(onRetryClick: () -> Unit) {
|
||||
UnableToLoadData(
|
||||
|
|
@ -77,9 +141,30 @@ private fun EarnErrorBlock(onRetryClick: () -> Unit) {
|
|||
|
||||
@Composable
|
||||
private fun EarnContentBlock(items: ImmutableList<EarnListItemUM>) {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
items.fastForEach { item ->
|
||||
EarnListItem(item = item)
|
||||
if (LocalRedesignEnabled.current) {
|
||||
LazyRow(
|
||||
contentPadding = PaddingValues(
|
||||
start = 16.dp,
|
||||
end = 16.dp,
|
||||
bottom = 8.dp,
|
||||
),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
items(
|
||||
items = items,
|
||||
key = { item -> "${item.tokenName}-${item.network}" },
|
||||
) { item ->
|
||||
MostlyUsedCard(
|
||||
item = item,
|
||||
onClick = item.onItemClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
items.fastForEach { item ->
|
||||
EarnListItem(item = item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,14 +9,15 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.common.ui.markets.MarketsListItemPlaceholder
|
||||
import com.tangem.features.feed.ui.feed.components.articles.DefaultLoadingArticle
|
||||
import com.tangem.features.feed.ui.feed.components.articles.TrendingLoadingArticle
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.components.block.BlockCard
|
||||
import com.tangem.core.ui.components.block.TangemBlockCardColors
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.feed.ui.feed.components.articles.DefaultLoadingArticle
|
||||
import com.tangem.features.feed.ui.feed.components.articles.TrendingLoadingArticle
|
||||
|
||||
@Composable
|
||||
internal fun FeedListLoading(modifier: Modifier = Modifier) {
|
||||
|
|
@ -35,70 +36,142 @@ internal fun FeedListLoading(modifier: Modifier = Modifier) {
|
|||
|
||||
@Composable
|
||||
internal fun MarketLoadingBlock() {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp)
|
||||
.size(width = 104.dp, height = 18.dp),
|
||||
)
|
||||
SpacerH(15.dp)
|
||||
ChartsLoading(modifier = Modifier.padding(horizontal = 16.dp))
|
||||
SpacerH(35.dp)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun MarketPulseLoadingBlock() {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp)
|
||||
.size(width = 104.dp, height = 18.dp),
|
||||
)
|
||||
SpacerH(15.dp)
|
||||
LazyRow(
|
||||
modifier = Modifier.padding(vertical = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
state = rememberLazyListState(),
|
||||
) {
|
||||
items(DEFAULT_CHART_SIZE_IN_MARKET) {
|
||||
RectangleShimmer(modifier = Modifier.size(width = 124.dp, height = 36.dp))
|
||||
}
|
||||
}
|
||||
SpacerH(16.dp)
|
||||
ChartsLoading(modifier = Modifier.padding(horizontal = 16.dp))
|
||||
SpacerH(32.dp)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun NewsLoadingBlock() {
|
||||
Column {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp, top = 16.dp)
|
||||
.size(width = 132.dp, height = 24.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
SpacerH(20.dp)
|
||||
ChartsLoading(modifier = Modifier.padding(horizontal = 16.dp))
|
||||
SpacerH(40.dp)
|
||||
} else {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp)
|
||||
.size(width = 104.dp, height = 18.dp),
|
||||
)
|
||||
SpacerH(15.dp)
|
||||
TrendingLoadingArticle(modifier = Modifier.padding(horizontal = 16.dp))
|
||||
SpacerH(12.dp)
|
||||
LazyRow(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
state = rememberLazyListState(),
|
||||
) {
|
||||
items(DEFAULT_CHART_SIZE_IN_MARKET) {
|
||||
DefaultLoadingArticle()
|
||||
}
|
||||
}
|
||||
ChartsLoading(modifier = Modifier.padding(horizontal = 16.dp))
|
||||
SpacerH(35.dp)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun MarketPulseLoadingBlock() {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp, top = 16.dp)
|
||||
.size(width = 132.dp, height = 24.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
SpacerH(15.dp)
|
||||
LazyRow(
|
||||
modifier = Modifier.padding(vertical = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(6.dp),
|
||||
state = rememberLazyListState(),
|
||||
) {
|
||||
items(DEFAULT_CHART_SIZE_IN_MARKET) {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier.size(width = 112.dp, height = 36.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
}
|
||||
}
|
||||
SpacerH(16.dp)
|
||||
ChartsLoading(modifier = Modifier.padding(horizontal = 16.dp))
|
||||
SpacerH(32.dp)
|
||||
} else {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp)
|
||||
.size(width = 104.dp, height = 18.dp),
|
||||
)
|
||||
SpacerH(15.dp)
|
||||
LazyRow(
|
||||
modifier = Modifier.padding(vertical = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
state = rememberLazyListState(),
|
||||
) {
|
||||
items(DEFAULT_CHART_SIZE_IN_MARKET) {
|
||||
RectangleShimmer(modifier = Modifier.size(width = 124.dp, height = 36.dp))
|
||||
}
|
||||
}
|
||||
SpacerH(16.dp)
|
||||
ChartsLoading(modifier = Modifier.padding(horizontal = 16.dp))
|
||||
SpacerH(32.dp)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun NewsLoadingBlock() {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
Column {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp, top = 16.dp)
|
||||
.size(width = 132.dp, height = 24.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
SpacerH(20.dp)
|
||||
TrendingLoadingArticle(modifier = Modifier.padding(horizontal = 16.dp))
|
||||
SpacerH(12.dp)
|
||||
LazyRow(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
contentPadding = PaddingValues(horizontal = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
state = rememberLazyListState(),
|
||||
) {
|
||||
items(DEFAULT_CHART_SIZE_IN_MARKET) {
|
||||
DefaultLoadingArticle()
|
||||
}
|
||||
}
|
||||
SpacerH(40.dp)
|
||||
}
|
||||
} else {
|
||||
Column {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp)
|
||||
.size(width = 104.dp, height = 18.dp),
|
||||
)
|
||||
SpacerH(15.dp)
|
||||
TrendingLoadingArticle(modifier = Modifier.padding(horizontal = 16.dp))
|
||||
SpacerH(12.dp)
|
||||
LazyRow(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
state = rememberLazyListState(),
|
||||
) {
|
||||
items(DEFAULT_CHART_SIZE_IN_MARKET) {
|
||||
DefaultLoadingArticle()
|
||||
}
|
||||
}
|
||||
SpacerH(35.dp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ChartsLoading(modifier: Modifier = Modifier) {
|
||||
val isRedesignEnabled = LocalRedesignEnabled.current
|
||||
BlockCard(
|
||||
modifier = modifier,
|
||||
colors = TangemBlockCardColors.copy(containerColor = TangemTheme.colors.background.action),
|
||||
colors = TangemBlockCardColors.copy(
|
||||
containerColor = if (isRedesignEnabled) {
|
||||
TangemTheme.colors2.surface.level3
|
||||
} else {
|
||||
TangemTheme.colors.background.action
|
||||
},
|
||||
|
||||
),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
|
|
|||
|
|
@ -25,7 +25,9 @@ import com.tangem.core.ui.components.SpacerH
|
|||
import com.tangem.core.ui.components.UnableToLoadData
|
||||
import com.tangem.core.ui.components.block.BlockCard
|
||||
import com.tangem.core.ui.components.block.TangemBlockCardColors
|
||||
import com.tangem.core.ui.ds.tabs.TangemTab
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.feed.model.market.list.state.SortByTypeUM
|
||||
import com.tangem.features.feed.ui.feed.state.FeedListCallbacks
|
||||
|
|
@ -34,6 +36,7 @@ import com.tangem.features.feed.ui.feed.state.MarketChartUM
|
|||
|
||||
@Composable
|
||||
internal fun MarketBlock(marketChart: MarketChartUM?, feedListCallbacks: FeedListCallbacks) {
|
||||
val isRedesignEnabled = LocalRedesignEnabled.current
|
||||
AnimatedContent(
|
||||
targetState = marketChart,
|
||||
label = "MarketBlockChartAnimation",
|
||||
|
|
@ -49,8 +52,16 @@ internal fun MarketBlock(marketChart: MarketChartUM?, feedListCallbacks: FeedLis
|
|||
title = {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.markets_common_title),
|
||||
style = TangemTheme.typography.h3,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = if (isRedesignEnabled) {
|
||||
TangemTheme.typography2.headingSemibold20
|
||||
} else {
|
||||
TangemTheme.typography.h3
|
||||
},
|
||||
color = if (isRedesignEnabled) {
|
||||
TangemTheme.colors2.text.neutral.primary
|
||||
} else {
|
||||
TangemTheme.colors.text.primary1
|
||||
},
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
)
|
||||
|
|
@ -76,8 +87,10 @@ internal fun MarketBlock(marketChart: MarketChartUM?, feedListCallbacks: FeedLis
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
internal fun MarketPulseBlock(marketChartConfig: MarketChartConfig, feedListCallbacks: FeedListCallbacks) {
|
||||
val isRedesignEnabled = LocalRedesignEnabled.current
|
||||
val onSeeAllClick by rememberUpdatedState {
|
||||
feedListCallbacks.onMarketOpenClick(marketChartConfig.currentSortByType)
|
||||
}
|
||||
|
|
@ -86,8 +99,16 @@ internal fun MarketPulseBlock(marketChartConfig: MarketChartConfig, feedListCall
|
|||
title = {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.markets_pulse_common_title),
|
||||
style = TangemTheme.typography.h3,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = if (isRedesignEnabled) {
|
||||
TangemTheme.typography2.headingSemibold20
|
||||
} else {
|
||||
TangemTheme.typography.h3
|
||||
},
|
||||
color = if (isRedesignEnabled) {
|
||||
TangemTheme.colors2.text.neutral.primary
|
||||
} else {
|
||||
TangemTheme.colors.text.primary1
|
||||
},
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
)
|
||||
|
|
@ -98,9 +119,13 @@ internal fun MarketPulseBlock(marketChartConfig: MarketChartConfig, feedListCall
|
|||
)
|
||||
|
||||
LazyRow(
|
||||
modifier = Modifier.padding(vertical = 4.dp),
|
||||
modifier = Modifier.padding(vertical = if (isRedesignEnabled) 12.dp else 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
contentPadding = if (isRedesignEnabled) {
|
||||
PaddingValues(horizontal = 16.dp, vertical = 6.dp)
|
||||
} else {
|
||||
PaddingValues(16.dp)
|
||||
},
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
state = rememberLazyListState(),
|
||||
) {
|
||||
|
|
@ -108,16 +133,22 @@ internal fun MarketPulseBlock(marketChartConfig: MarketChartConfig, feedListCall
|
|||
items = marketChartConfig.getFilterPreset(),
|
||||
key = SortByTypeUM::name,
|
||||
) { sortByTypeUM ->
|
||||
ChartsFilterChip(
|
||||
sortByTypeUM = sortByTypeUM,
|
||||
isSelected = sortByTypeUM == marketChartConfig.currentSortByType,
|
||||
onClick = { feedListCallbacks.onSortTypeClick(sortByTypeUM) },
|
||||
)
|
||||
if (isRedesignEnabled) {
|
||||
TangemTab(
|
||||
text = sortByTypeUM.text,
|
||||
isChecked = sortByTypeUM == marketChartConfig.currentSortByType,
|
||||
onCheckedChange = { feedListCallbacks.onSortTypeClick(sortByTypeUM) },
|
||||
)
|
||||
} else {
|
||||
ChartsFilterChip(
|
||||
sortByTypeUM = sortByTypeUM,
|
||||
isSelected = sortByTypeUM == marketChartConfig.currentSortByType,
|
||||
onClick = { feedListCallbacks.onSortTypeClick(sortByTypeUM) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SpacerH(12.dp)
|
||||
|
||||
AnimatedContent(
|
||||
targetState = marketChartConfig.currentSortByType,
|
||||
label = "MarketPulseChartAnimation",
|
||||
|
|
@ -141,9 +172,16 @@ private fun Charts(
|
|||
onItemClick: (MarketsListItemUM) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val isRedesignEnabled = LocalRedesignEnabled.current
|
||||
BlockCard(
|
||||
modifier = modifier,
|
||||
colors = TangemBlockCardColors.copy(containerColor = TangemTheme.colors.background.action),
|
||||
colors = TangemBlockCardColors.copy(
|
||||
containerColor = if (isRedesignEnabled) {
|
||||
TangemTheme.colors2.surface.level3
|
||||
} else {
|
||||
TangemTheme.colors.background.action
|
||||
},
|
||||
),
|
||||
) {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
when (marketChart) {
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ private fun TrendingArticleBackground(
|
|||
Color(0xFF7C16F1).copy(alpha = .8f),
|
||||
Color.Transparent,
|
||||
),
|
||||
center = Offset(w / 2f, 2.4f * h),
|
||||
center = Offset(w / 2f, 2.6f * h),
|
||||
radius = radiusScale * 1.57f,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -154,7 +154,10 @@ private fun ArticleHeaderV2(
|
|||
text = tag.text,
|
||||
tangemIconUM = when (val content = tag.leadingContent) {
|
||||
LabelLeadingContentUM.None -> null
|
||||
is LabelLeadingContentUM.Token -> TangemIconUM.Url(content.iconUrl)
|
||||
is LabelLeadingContentUM.Token -> TangemIconUM.Url(
|
||||
url = content.iconUrl,
|
||||
fallbackRes = R.drawable.ic_alert_24,
|
||||
)
|
||||
},
|
||||
shape = TangemBadgeShape.Rounded,
|
||||
size = TangemBadgeSize.X9,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import androidx.compose.ui.layout.SubcomposeLayout
|
|||
import androidx.compose.ui.layout.SubcomposeMeasureScope
|
||||
import androidx.compose.ui.unit.Constraints
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.label.Label
|
||||
import com.tangem.core.ui.components.label.entity.LabelLeadingContentUM
|
||||
import com.tangem.core.ui.components.label.entity.LabelUM
|
||||
|
|
@ -40,7 +41,10 @@ internal fun Tags(tags: ImmutableList<LabelUM>, modifier: Modifier = Modifier) {
|
|||
text = tag.text,
|
||||
tangemIconUM = when (val content = tag.leadingContent) {
|
||||
LabelLeadingContentUM.None -> null
|
||||
is LabelLeadingContentUM.Token -> TangemIconUM.Url(content.iconUrl)
|
||||
is LabelLeadingContentUM.Token -> TangemIconUM.Url(
|
||||
url = content.iconUrl,
|
||||
fallbackRes = R.drawable.ic_alert_24,
|
||||
)
|
||||
},
|
||||
shape = TangemBadgeShape.Rounded,
|
||||
size = TangemBadgeSize.X6,
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import com.tangem.core.ui.event.StateEvent
|
|||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.domain.markets.PriceChangeInterval
|
||||
|
|
@ -81,6 +82,7 @@ private fun Content(
|
|||
modifier: Modifier = Modifier,
|
||||
portfolioBlock: @Composable ((Modifier) -> Unit)?,
|
||||
) {
|
||||
val isRedesignEnabled = LocalRedesignEnabled.current
|
||||
val density = LocalDensity.current
|
||||
val bottomBarHeight = with(density) { WindowInsets.systemBars.getBottom(this).toDp() }
|
||||
val lazyListState = rememberLazyListState()
|
||||
|
|
@ -133,6 +135,7 @@ private fun Content(
|
|||
state = state.body,
|
||||
portfolioBlock = portfolioBlock,
|
||||
relatedNews = state.relatedNews,
|
||||
isRedesignEnabled = isRedesignEnabled,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,28 +2,31 @@ package com.tangem.features.feed.ui.market.detailed.components
|
|||
|
||||
import android.content.res.Configuration
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.systemBars
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.layout.layoutId
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.CircleShimmer
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.components.SpacerH12
|
||||
import com.tangem.core.ui.components.appbar.TangemTopAppBar
|
||||
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
|
||||
|
|
@ -35,16 +38,22 @@ import com.tangem.core.ui.components.buttons.SmallButtonConfig
|
|||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.components.token.TokenItem
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
|
||||
import com.tangem.core.ui.ds.button.*
|
||||
import com.tangem.core.ui.ds.image.TangemIcon
|
||||
import com.tangem.core.ui.ds.row.TangemRowContainer
|
||||
import com.tangem.core.ui.ds.row.TangemRowLayoutId
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBar
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBarType
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.feed.impl.R
|
||||
import com.tangem.features.feed.ui.market.detailed.state.ExchangeItemUM
|
||||
import com.tangem.features.feed.ui.market.detailed.state.ExchangesBottomSheetContent
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet as TangemBottomSheetV2
|
||||
|
||||
/**
|
||||
* Exchanges bottom sheet
|
||||
|
|
@ -55,6 +64,15 @@ import kotlinx.collections.immutable.toImmutableList
|
|||
*/
|
||||
@Composable
|
||||
internal fun ExchangesBottomSheet(config: TangemBottomSheetConfig) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
ExchangesBottomSheetV2(config)
|
||||
} else {
|
||||
ExchangesBottomSheetV1(config)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ExchangesBottomSheetV1(config: TangemBottomSheetConfig) {
|
||||
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
|
||||
|
||||
TangemBottomSheet<ExchangesBottomSheetContent>(
|
||||
|
|
@ -98,6 +116,77 @@ internal fun ExchangesBottomSheet(config: TangemBottomSheetConfig) {
|
|||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ExchangesBottomSheetV2(config: TangemBottomSheetConfig) {
|
||||
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
|
||||
|
||||
TangemBottomSheetV2<ExchangesBottomSheetContent>(
|
||||
config = config,
|
||||
title = { content ->
|
||||
TangemTopBar(
|
||||
title = resourceReference(content.titleResId),
|
||||
type = TangemTopBarType.BottomSheet,
|
||||
startContent = {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.ic_arrow_back_28),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors2.graphic.neutral.primary,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens2.x11)
|
||||
.background(
|
||||
color = TangemTheme.colors2.button.backgroundSecondary,
|
||||
shape = CircleShape,
|
||||
)
|
||||
.clickableSingle(onClick = config.onDismissRequest)
|
||||
.padding(TangemTheme.dimens2.x2),
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
content = { content ->
|
||||
Box {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentPadding = PaddingValues(bottom = bottomBarHeight),
|
||||
) {
|
||||
item(key = "subtitle") {
|
||||
Subtitle(
|
||||
subtitleRes = content.subtitleResId,
|
||||
volumeReference = content.volumeReference,
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens2.x7),
|
||||
)
|
||||
}
|
||||
|
||||
if (content.exchangeItemsV2.isNotEmpty()) {
|
||||
val lastIndex = content.exchangeItemsV2.lastIndex
|
||||
itemsIndexed(
|
||||
items = content.exchangeItemsV2,
|
||||
key = { _, item -> item.id },
|
||||
) { index, item ->
|
||||
ExchangeItemRow(
|
||||
exchangeItemUM = item,
|
||||
modifier = Modifier
|
||||
.roundedShapeItemDecoration(
|
||||
currentIndex = index,
|
||||
lastIndex = lastIndex,
|
||||
backgroundColor = TangemTheme.colors2.surface.level3,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (content is ExchangesBottomSheetContent.Error) {
|
||||
Error(
|
||||
content = content,
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Title(@StringRes textResId: Int, onBackClick: () -> Unit) {
|
||||
TangemTopAppBar(
|
||||
|
|
@ -120,17 +209,36 @@ private fun Subtitle(@StringRes subtitleRes: Int, volumeReference: TextReference
|
|||
|
||||
@Composable
|
||||
private fun SubtitleText(textReference: TextReference) {
|
||||
Text(
|
||||
text = textReference.resolveReference(),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.body2,
|
||||
)
|
||||
if (LocalRedesignEnabled.current) {
|
||||
Text(
|
||||
text = textReference.resolveReference(),
|
||||
color = TangemTheme.colors2.text.neutral.secondary,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography2.captionSemibold12,
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
text = textReference.resolveReference(),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.body2,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Error(content: ExchangesBottomSheetContent.Error, modifier: Modifier = Modifier) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
ErrorV2(content, modifier)
|
||||
} else {
|
||||
ErrorV1(content, modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ErrorV1(content: ExchangesBottomSheetContent.Error, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
|
|
@ -154,6 +262,155 @@ private fun Error(content: ExchangesBottomSheetContent.Error, modifier: Modifier
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ErrorV2(content: ExchangesBottomSheetContent.Error, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(id = content.message),
|
||||
color = TangemTheme.colors2.text.neutral.tertiary,
|
||||
textAlign = TextAlign.Center,
|
||||
style = TangemTheme.typography2.bodyRegular14,
|
||||
)
|
||||
|
||||
SpacerH(8.dp)
|
||||
|
||||
TangemButton(
|
||||
buttonUM = TangemButtonUM(
|
||||
text = resourceReference(com.tangem.core.ui.R.string.try_to_load_data_again_button_title),
|
||||
onClick = content.onRetryClick,
|
||||
type = TangemButtonType.Secondary,
|
||||
size = TangemButtonSize.X8,
|
||||
shape = TangemButtonShape.Rounded,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ExchangeItemRow(exchangeItemUM: ExchangeItemUM, modifier: Modifier = Modifier) {
|
||||
when (exchangeItemUM) {
|
||||
is ExchangeItemUM.Content -> {
|
||||
ExchangeItemRowContent(exchangeItemUM, modifier)
|
||||
}
|
||||
is ExchangeItemUM.Loading -> {
|
||||
ExchangeItemRowPlaceholder(modifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ExchangeItemRowContent(exchangeItemUM: ExchangeItemUM.Content, modifier: Modifier = Modifier) {
|
||||
TangemRowContainer(
|
||||
modifier = modifier,
|
||||
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 14.dp),
|
||||
) {
|
||||
TangemIcon(
|
||||
tangemIconUM = exchangeItemUM.icon,
|
||||
modifier = Modifier
|
||||
.layoutId(layoutId = TangemRowLayoutId.HEAD)
|
||||
.size(TangemTheme.dimens2.x10),
|
||||
)
|
||||
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(start = TangemTheme.dimens2.x2)
|
||||
.layoutId(TangemRowLayoutId.START_TOP),
|
||||
text = exchangeItemUM.title.resolveReference(),
|
||||
style = TangemTheme.typography2.bodySemibold16,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
)
|
||||
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(start = TangemTheme.dimens2.x2)
|
||||
.layoutId(TangemRowLayoutId.START_BOTTOM),
|
||||
text = exchangeItemUM.subTitle.resolveReference(),
|
||||
style = TangemTheme.typography2.captionSemibold12,
|
||||
color = TangemTheme.colors2.text.neutral.secondary,
|
||||
)
|
||||
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(start = TangemTheme.dimens2.x2)
|
||||
.layoutId(TangemRowLayoutId.END_TOP),
|
||||
text = exchangeItemUM.volumeInUsd.resolveReference(),
|
||||
style = TangemTheme.typography2.bodySemibold16,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
)
|
||||
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = getColorByTrustValue(exchangeItemUM.auditLabel.type).copy(alpha = .1f),
|
||||
shape = CircleShape,
|
||||
)
|
||||
.padding(vertical = 2.dp, horizontal = 6.dp)
|
||||
.layoutId(TangemRowLayoutId.END_BOTTOM),
|
||||
text = exchangeItemUM.auditLabel.text.resolveReference(),
|
||||
style = TangemTheme.typography2.captionSemibold11,
|
||||
color = getColorByTrustValue(exchangeItemUM.auditLabel.type),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ExchangeItemRowPlaceholder(modifier: Modifier = Modifier) {
|
||||
TangemRowContainer(
|
||||
modifier = modifier,
|
||||
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 14.dp),
|
||||
) {
|
||||
CircleShimmer(
|
||||
modifier = Modifier
|
||||
.size(40.dp)
|
||||
.layoutId(TangemRowLayoutId.HEAD),
|
||||
)
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(108.dp)
|
||||
.height(20.dp)
|
||||
.padding(start = 4.dp)
|
||||
.layoutId(TangemRowLayoutId.START_TOP),
|
||||
)
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(52.dp)
|
||||
.height(16.dp)
|
||||
.padding(start = 4.dp)
|
||||
.layoutId(TangemRowLayoutId.START_BOTTOM),
|
||||
)
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(106.dp)
|
||||
.height(20.dp)
|
||||
.padding(start = 4.dp)
|
||||
.layoutId(TangemRowLayoutId.END_TOP),
|
||||
)
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(52.dp)
|
||||
.height(16.dp)
|
||||
.padding(start = 4.dp)
|
||||
.layoutId(TangemRowLayoutId.END_BOTTOM),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun getColorByTrustValue(type: AuditLabelUM.Type): Color {
|
||||
return when (type) {
|
||||
AuditLabelUM.Type.Prohibition -> TangemTheme.colors2.fill.status.attention
|
||||
AuditLabelUM.Type.Warning -> TangemTheme.colors2.fill.status.warning
|
||||
AuditLabelUM.Type.Permit,
|
||||
AuditLabelUM.Type.Info,
|
||||
AuditLabelUM.Type.General,
|
||||
-> TangemTheme.colors2.fill.status.accent
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview(name = "Dark Theme", uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
|
|
@ -175,7 +432,7 @@ private class ExchangesBottomSheetContentProvider : CollectionPreviewParameterPr
|
|||
listOf(
|
||||
ExchangesBottomSheetContent.Loading(exchangesCount = 13),
|
||||
ExchangesBottomSheetContent.Error(onRetryClick = {}),
|
||||
ExchangesBottomSheetContent.Content(
|
||||
ExchangesBottomSheetContent.ContentV1(
|
||||
exchangeItems = List(size = 13) { index ->
|
||||
TokenItemState.Content(
|
||||
id = index.toString(),
|
||||
|
|
|
|||
|
|
@ -1,30 +1,52 @@
|
|||
package com.tangem.features.feed.ui.market.detailed.components
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.systemBars
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.layout.layoutId
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetType
|
||||
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheetTitle
|
||||
import com.tangem.core.ui.components.notifications.Notification
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig
|
||||
import com.tangem.core.ui.ds.row.TangemRowContainer
|
||||
import com.tangem.core.ui.ds.row.TangemRowLayoutId
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBar
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBarType
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.clickableSingle
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.feed.impl.R
|
||||
import com.tangem.features.feed.ui.market.detailed.state.InfoBottomSheetContent
|
||||
import dev.jeziellago.compose.markdowntext.MarkdownText
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet as TangemBottomSheetV2
|
||||
|
||||
@Composable
|
||||
internal fun InfoBottomSheet(config: TangemBottomSheetConfig) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
InfoBottomSheetV2(config)
|
||||
} else {
|
||||
InfoBottomSheetV1(config)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InfoBottomSheetV1(config: TangemBottomSheetConfig) {
|
||||
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
|
||||
|
||||
TangemBottomSheet<InfoBottomSheetContent>(
|
||||
|
|
@ -62,6 +84,89 @@ internal fun InfoBottomSheet(config: TangemBottomSheetConfig) {
|
|||
)
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun InfoBottomSheetV2(config: TangemBottomSheetConfig) {
|
||||
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
|
||||
|
||||
TangemBottomSheetV2<InfoBottomSheetContent>(
|
||||
config = config,
|
||||
type = TangemBottomSheetType.Modal,
|
||||
containerColor = TangemTheme.colors2.surface.level3,
|
||||
title = { content ->
|
||||
TangemTopBar(
|
||||
title = content.title,
|
||||
type = TangemTopBarType.BottomSheet,
|
||||
endContent = {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.ic_close_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors2.graphic.neutral.primary,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens2.x11)
|
||||
.background(
|
||||
color = TangemTheme.colors2.button.backgroundSecondary,
|
||||
shape = CircleShape,
|
||||
)
|
||||
.clickableSingle(onClick = config.onDismissRequest)
|
||||
.padding(TangemTheme.dimens2.x2_5),
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
content = { content ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(horizontal = TangemTheme.dimens2.x4),
|
||||
) {
|
||||
MarkdownText(
|
||||
modifier = Modifier.padding(bottom = TangemTheme.dimens2.x3),
|
||||
markdown = content.body.resolveReference(),
|
||||
disableLinkMovementMethod = true,
|
||||
linkifyMask = 0,
|
||||
syntaxHighlightColor = TangemTheme.colors2.text.neutral.secondary,
|
||||
style = TangemTheme.typography2.bodyRegular16.copy(
|
||||
TangemTheme.colors2.text.neutral.secondary,
|
||||
),
|
||||
)
|
||||
|
||||
if (content.generatedAINotificationUM != null) {
|
||||
TangemRowContainer(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = TangemTheme.colors2.surface.level4,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens2.x5),
|
||||
)
|
||||
.clickableSingle(onClick = content.generatedAINotificationUM.onClick),
|
||||
contentPadding = PaddingValues(TangemTheme.dimens2.x3),
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens2.x10)
|
||||
.padding(TangemTheme.dimens2.x1)
|
||||
.layoutId(TangemRowLayoutId.HEAD),
|
||||
tint = TangemTheme.colors2.markers.iconBlue,
|
||||
contentDescription = null,
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_magic_28),
|
||||
)
|
||||
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(start = TangemTheme.dimens2.x1)
|
||||
.layoutId(TangemRowLayoutId.START_TOP),
|
||||
text = stringResourceSafe(R.string.information_generated_with_ai),
|
||||
style = TangemTheme.typography2.captionSemibold12,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
)
|
||||
}
|
||||
}
|
||||
SpacerH(bottomBarHeight)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AdditionalInfoNotification(onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
Notification(
|
||||
|
|
|
|||
|
|
@ -2,17 +2,11 @@ package com.tangem.features.feed.ui.market.detailed.components
|
|||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredHeight
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
|
|
@ -21,18 +15,30 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.SpacerW4
|
||||
import com.tangem.core.ui.components.TextShimmer
|
||||
import com.tangem.core.ui.components.text.TooltipText
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
import com.tangem.core.ui.utils.PreviewShimmerContainer
|
||||
import com.tangem.features.feed.ui.market.detailed.state.InfoPointUM
|
||||
|
||||
@Composable
|
||||
internal fun InfoPoint(infoPointUM: InfoPointUM, modifier: Modifier = Modifier) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
InfoPointV2(infoPointUM, modifier)
|
||||
} else {
|
||||
InfoPointV1(infoPointUM, modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InfoPointV1(infoPointUM: InfoPointUM, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier.padding(vertical = TangemTheme.dimens.spacing8),
|
||||
horizontalAlignment = Alignment.Start,
|
||||
|
|
@ -81,8 +87,68 @@ internal fun InfoPoint(infoPointUM: InfoPointUM, modifier: Modifier = Modifier)
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InfoPointV2(infoPointUM: InfoPointUM, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier.padding(vertical = TangemTheme.dimens.spacing8),
|
||||
horizontalAlignment = Alignment.Start,
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
Row {
|
||||
Text(
|
||||
text = infoPointUM.value,
|
||||
style = TangemTheme.typography2.headingSemibold20,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
)
|
||||
if (infoPointUM.change != null) {
|
||||
SpacerW4()
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens2.x2)
|
||||
.align(Alignment.CenterVertically),
|
||||
imageVector = ImageVector.vectorResource(
|
||||
id = when (infoPointUM.change) {
|
||||
InfoPointUM.ChangeType.UP -> R.drawable.ic_arrow_up_8
|
||||
InfoPointUM.ChangeType.DOWN -> R.drawable.ic_arrow_down_8
|
||||
},
|
||||
),
|
||||
tint = when (infoPointUM.change) {
|
||||
InfoPointUM.ChangeType.UP -> TangemTheme.colors2.markers.iconGreen
|
||||
InfoPointUM.ChangeType.DOWN -> TangemTheme.colors2.markers.iconRed
|
||||
},
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
if (infoPointUM.onInfoClick != null) {
|
||||
InformationTextBlock(
|
||||
text = infoPointUM.title,
|
||||
onInfoClick = infoPointUM.onInfoClick,
|
||||
informationTextBlockIconPosition = InformationTextBlockIconPosition.START,
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
text = infoPointUM.title.resolveReference(),
|
||||
style = TangemTheme.typography2.captionSemibold12,
|
||||
color = TangemTheme.colors2.text.neutral.tertiary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun InfoPointShimmer(modifier: Modifier = Modifier, withTooltip: Boolean = false) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
InfoPointShimmerV2(modifier)
|
||||
} else {
|
||||
InfoPointShimmerV1(modifier, withTooltip)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InfoPointShimmerV1(modifier: Modifier = Modifier, withTooltip: Boolean = false) {
|
||||
Column(
|
||||
modifier = modifier.padding(vertical = TangemTheme.dimens.spacing8),
|
||||
horizontalAlignment = Alignment.Start,
|
||||
|
|
@ -115,10 +181,34 @@ internal fun InfoPointShimmer(modifier: Modifier = Modifier, withTooltip: Boolea
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InfoPointShimmerV2(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier.padding(vertical = TangemTheme.dimens2.x6),
|
||||
horizontalAlignment = Alignment.Start,
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x1),
|
||||
) {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(24.dp)
|
||||
.padding(end = 10.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(68.dp)
|
||||
.height(16.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview("Dark Theme", uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun ContentPreview() {
|
||||
private fun ContentPreviewV1() {
|
||||
TangemThemePreview {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
|
@ -158,6 +248,53 @@ private fun ContentPreview() {
|
|||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview("Dark Theme", uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun ContentPreviewV2() {
|
||||
CompositionLocalProvider(
|
||||
LocalRedesignEnabled provides true,
|
||||
) {
|
||||
TangemThemePreviewRedesign {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.width(150.dp)
|
||||
.background(TangemTheme.colors.background.tertiary),
|
||||
) {
|
||||
InfoPoint(
|
||||
infoPointUM = InfoPointUM(
|
||||
title = stringReference("Market Cap"),
|
||||
value = "$1,000,000,000",
|
||||
),
|
||||
)
|
||||
InfoPoint(
|
||||
infoPointUM = InfoPointUM(
|
||||
title = stringReference("Market Cap"),
|
||||
value = "$1,000,000,000",
|
||||
onInfoClick = { },
|
||||
),
|
||||
)
|
||||
InfoPoint(
|
||||
infoPointUM = InfoPointUM(
|
||||
title = stringReference("Market Cap"),
|
||||
value = "$1,000,000",
|
||||
change = InfoPointUM.ChangeType.UP,
|
||||
onInfoClick = { },
|
||||
),
|
||||
)
|
||||
InfoPoint(
|
||||
infoPointUM = InfoPointUM(
|
||||
title = stringReference("Market Cap"),
|
||||
value = "$1,000,000",
|
||||
change = InfoPointUM.ChangeType.DOWN,
|
||||
onInfoClick = { },
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview("Dark Theme", uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
|
|
@ -186,7 +323,7 @@ private fun PreviewShimmer() {
|
|||
}
|
||||
},
|
||||
actualContent = {
|
||||
ContentPreview()
|
||||
ContentPreviewV1()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,86 @@
|
|||
package com.tangem.features.feed.ui.market.detailed.components
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.conditional
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
@Composable
|
||||
internal fun InformationTextBlock(
|
||||
text: TextReference,
|
||||
modifier: Modifier = Modifier,
|
||||
onInfoClick: (() -> Unit)? = null,
|
||||
textColor: Color = TangemTheme.colors2.text.neutral.tertiary,
|
||||
infoIconColor: Color = TangemTheme.colors2.markers.iconGray,
|
||||
informationTextBlockIconPosition: InformationTextBlockIconPosition = InformationTextBlockIconPosition.START,
|
||||
) {
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
|
||||
val infoIcon: @Composable () -> Unit = {
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens2.x4),
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.ic_information_24),
|
||||
tint = infoIconColor,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
|
||||
val contentText: @Composable () -> Unit = {
|
||||
Text(
|
||||
text = text.resolveReference(),
|
||||
style = TangemTheme.typography2.captionSemibold12,
|
||||
color = textColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = modifier
|
||||
.conditional(
|
||||
condition = onInfoClick != null,
|
||||
modifier = {
|
||||
onInfoClick?.let { infoClick ->
|
||||
clickable(
|
||||
interactionSource = interactionSource,
|
||||
indication = null,
|
||||
onClick = infoClick,
|
||||
)
|
||||
} ?: this
|
||||
},
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x1),
|
||||
) {
|
||||
when (informationTextBlockIconPosition) {
|
||||
InformationTextBlockIconPosition.START -> {
|
||||
infoIcon()
|
||||
contentText()
|
||||
}
|
||||
InformationTextBlockIconPosition.END -> {
|
||||
contentText()
|
||||
infoIcon()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal enum class InformationTextBlockIconPosition {
|
||||
START, END
|
||||
}
|
||||
|
|
@ -1,37 +1,33 @@
|
|||
package com.tangem.features.feed.ui.market.detailed.components
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.SpacerW
|
||||
import com.tangem.core.ui.components.block.information.GridItems
|
||||
import com.tangem.core.ui.components.block.information.InformationBlock
|
||||
import com.tangem.core.ui.components.buttons.segmentedbutton.SegmentedButtons
|
||||
import com.tangem.core.ui.components.text.TooltipText
|
||||
import com.tangem.core.ui.ds.tabs.TangemSegmentUM
|
||||
import com.tangem.core.ui.ds.tabs.TangemSegmentedPicker
|
||||
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.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
import com.tangem.core.ui.utils.PreviewShimmerContainer
|
||||
import com.tangem.domain.markets.PriceChangeInterval
|
||||
import com.tangem.features.feed.impl.R
|
||||
import com.tangem.features.feed.ui.components.TokenMarketInformationBlock
|
||||
import com.tangem.features.feed.ui.market.detailed.getText
|
||||
import com.tangem.features.feed.ui.market.detailed.state.InfoPointUM
|
||||
import com.tangem.features.feed.ui.market.detailed.state.InsightsUM
|
||||
|
|
@ -40,6 +36,15 @@ import kotlinx.collections.immutable.toImmutableList
|
|||
|
||||
@Composable
|
||||
internal fun InsightsBlock(state: InsightsUM, modifier: Modifier = Modifier) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
InsightsBlockV2(state, modifier)
|
||||
} else {
|
||||
InsightsBlockV1(state, modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InsightsBlockV1(state: InsightsUM, modifier: Modifier = Modifier) {
|
||||
var currentInterval by remember { mutableStateOf(PriceChangeInterval.H24) }
|
||||
|
||||
InformationBlock(
|
||||
|
|
@ -104,8 +109,84 @@ internal fun InsightsBlock(state: InsightsUM, modifier: Modifier = Modifier) {
|
|||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InsightsBlockV2(state: InsightsUM, modifier: Modifier = Modifier) {
|
||||
val segmentItems = persistentListOf(
|
||||
TangemSegmentUM(
|
||||
id = PriceChangeInterval.H24.name,
|
||||
title = resourceReference(R.string.markets_token_details_insight_day_timeline),
|
||||
),
|
||||
TangemSegmentUM(
|
||||
id = PriceChangeInterval.WEEK.name,
|
||||
title = resourceReference(R.string.markets_token_details_insight_week_timeline),
|
||||
),
|
||||
TangemSegmentUM(
|
||||
id = PriceChangeInterval.MONTH.name,
|
||||
title = resourceReference(R.string.markets_token_details_insight_month_timeline),
|
||||
),
|
||||
)
|
||||
var currentInterval by remember { mutableStateOf(segmentItems.first()) }
|
||||
|
||||
TokenMarketInformationBlock(
|
||||
modifier = modifier,
|
||||
title = {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.markets_token_details_insights),
|
||||
style = TangemTheme.typography2.headingSemibold20,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
)
|
||||
TangemSegmentedPicker(
|
||||
items = segmentItems,
|
||||
initialSelectedItem = segmentItems.first(),
|
||||
hasSeparator = true,
|
||||
isFixed = false,
|
||||
isAltSurface = true,
|
||||
minSegmentWidth = 48.dp,
|
||||
onClick = { segment ->
|
||||
currentInterval = segment
|
||||
state.onIntervalChanged(PriceChangeInterval.valueOf(segment.id))
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
content = {
|
||||
val infoPoints = when (currentInterval.id) {
|
||||
PriceChangeInterval.H24.name -> state.h24Info
|
||||
PriceChangeInterval.WEEK.name -> state.weekInfo
|
||||
PriceChangeInterval.MONTH.name -> state.monthInfo
|
||||
else -> state.h24Info
|
||||
}
|
||||
|
||||
GridItems(
|
||||
modifier = Modifier.padding(top = 24.dp),
|
||||
items = infoPoints,
|
||||
itemContent = { infoPointUM ->
|
||||
InfoPoint(
|
||||
modifier = Modifier.align(Alignment.CenterStart),
|
||||
infoPointUM = infoPointUM,
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun InsightsBlockPlaceholder(modifier: Modifier = Modifier) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
InsightsBlockPlaceholderV2(modifier)
|
||||
} else {
|
||||
InsightsBlockPlaceholderV1(modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun InsightsBlockPlaceholderV1(modifier: Modifier = Modifier) {
|
||||
val subtitle2dp = with(LocalDensity.current) { TangemTheme.typography.subtitle2.lineHeight.toDp() }
|
||||
val caption1dp = with(LocalDensity.current) { TangemTheme.typography.caption1.lineHeight.toDp() }
|
||||
val headerHeight = maxOf(subtitle2dp, caption1dp) + TangemTheme.dimens.spacing4
|
||||
|
|
@ -134,10 +215,47 @@ internal fun InsightsBlockPlaceholder(modifier: Modifier = Modifier) {
|
|||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun InsightsBlockPlaceholderV2(modifier: Modifier = Modifier) {
|
||||
TokenMarketInformationBlock(
|
||||
modifier = modifier,
|
||||
title = {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.height(24.dp)
|
||||
.width(120.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
|
||||
SpacerW(62.dp)
|
||||
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.height(36.dp)
|
||||
.weight(1f),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
}
|
||||
},
|
||||
content = {
|
||||
GridItems(
|
||||
items = List(size = 4) { it }.toImmutableList(),
|
||||
horizontalArragement = Arrangement.spacedBy(10.dp),
|
||||
itemContent = {
|
||||
InfoPointShimmer(modifier = Modifier.fillMaxWidth())
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun ContentPreview() {
|
||||
private fun ContentPreviewV1() {
|
||||
TangemThemePreview {
|
||||
InsightsBlock(
|
||||
state = InsightsUM(
|
||||
|
|
@ -205,11 +323,95 @@ private fun ContentPreview() {
|
|||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun PreviewPlaceholder() {
|
||||
private fun ContentPreviewV2() {
|
||||
CompositionLocalProvider(LocalRedesignEnabled provides true) {
|
||||
TangemThemePreviewRedesign {
|
||||
InsightsBlock(
|
||||
state = InsightsUM(
|
||||
h24Info = persistentListOf(
|
||||
InfoPointUM(
|
||||
title = resourceReference(R.string.markets_token_details_experienced_buyers),
|
||||
value = "1 000 000 000",
|
||||
),
|
||||
InfoPointUM(
|
||||
title = resourceReference(R.string.markets_token_details_buy_pressure),
|
||||
value = "1 000 000 000",
|
||||
),
|
||||
InfoPointUM(
|
||||
title = resourceReference(R.string.markets_token_details_holders),
|
||||
value = "1 000 000 000",
|
||||
),
|
||||
InfoPointUM(
|
||||
title = resourceReference(R.string.markets_token_details_liquidity),
|
||||
value = "1 000 000 000",
|
||||
),
|
||||
),
|
||||
weekInfo = persistentListOf(
|
||||
InfoPointUM(
|
||||
title = resourceReference(R.string.markets_token_details_experienced_buyers),
|
||||
value = "1 000 000",
|
||||
),
|
||||
InfoPointUM(
|
||||
title = resourceReference(R.string.markets_token_details_buy_pressure),
|
||||
value = "1 000 000",
|
||||
),
|
||||
InfoPointUM(
|
||||
title = resourceReference(R.string.markets_token_details_holders),
|
||||
value = "1 000 000",
|
||||
),
|
||||
InfoPointUM(
|
||||
title = resourceReference(R.string.markets_token_details_liquidity),
|
||||
value = "1 000 000",
|
||||
),
|
||||
),
|
||||
monthInfo = persistentListOf(
|
||||
InfoPointUM(
|
||||
title = resourceReference(R.string.markets_token_details_experienced_buyers),
|
||||
value = "1 000",
|
||||
),
|
||||
InfoPointUM(
|
||||
title = resourceReference(R.string.markets_token_details_buy_pressure),
|
||||
value = "1 000",
|
||||
),
|
||||
InfoPointUM(
|
||||
title = resourceReference(R.string.markets_token_details_holders),
|
||||
value = "1 000",
|
||||
),
|
||||
InfoPointUM(
|
||||
title = resourceReference(R.string.markets_token_details_liquidity),
|
||||
value = "1 000",
|
||||
),
|
||||
),
|
||||
onInfoClick = {},
|
||||
onIntervalChanged = {},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun PreviewPlaceholderV1() {
|
||||
TangemThemePreview {
|
||||
PreviewShimmerContainer(
|
||||
actualContent = { ContentPreview() },
|
||||
actualContent = { ContentPreviewV1() },
|
||||
shimmerContent = { InsightsBlockPlaceholder() },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun PreviewPlaceholderV2() {
|
||||
TangemThemePreviewRedesign {
|
||||
CompositionLocalProvider(LocalRedesignEnabled provides true) {
|
||||
PreviewShimmerContainer(
|
||||
actualContent = { ContentPreviewV2() },
|
||||
shimmerContent = { InsightsBlockPlaceholder() },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,7 @@
|
|||
package com.tangem.features.feed.ui.market.detailed.components
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -21,18 +14,35 @@ import com.tangem.core.ui.components.TextShimmer
|
|||
import com.tangem.core.ui.components.block.information.InformationBlock
|
||||
import com.tangem.core.ui.components.buttons.chip.Chip
|
||||
import com.tangem.core.ui.components.inputrow.inner.DividerContainer
|
||||
import com.tangem.core.ui.ds.badge.TangemBadge
|
||||
import com.tangem.core.ui.ds.badge.TangemBadgeIconPosition
|
||||
import com.tangem.core.ui.ds.badge.TangemBadgeShape
|
||||
import com.tangem.core.ui.ds.badge.TangemBadgeSize
|
||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
import com.tangem.core.ui.utils.PreviewShimmerContainer
|
||||
import com.tangem.features.feed.impl.R
|
||||
import com.tangem.features.feed.ui.components.ContainerWithDivider
|
||||
import com.tangem.features.feed.ui.market.detailed.state.LinksUM
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
@Composable
|
||||
internal fun LinksBlock(state: LinksUM, modifier: Modifier = Modifier) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
LinksBlockV2(state, modifier)
|
||||
} else {
|
||||
LinksBlockV1(state, modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LinksBlockV1(state: LinksUM, modifier: Modifier = Modifier) {
|
||||
InformationBlock(
|
||||
modifier = modifier,
|
||||
contentHorizontalPadding = 0.dp,
|
||||
|
|
@ -47,22 +57,22 @@ internal fun LinksBlock(state: LinksUM, modifier: Modifier = Modifier) {
|
|||
},
|
||||
content = {
|
||||
Column {
|
||||
SubBlock(
|
||||
SubBlockV1(
|
||||
title = stringResourceSafe(id = R.string.markets_token_details_official_links),
|
||||
links = state.officialLinks,
|
||||
onLinkClick = state.onLinkClick,
|
||||
)
|
||||
SubBlock(
|
||||
SubBlockV1(
|
||||
title = stringResourceSafe(id = R.string.markets_token_details_social),
|
||||
links = state.social,
|
||||
onLinkClick = state.onLinkClick,
|
||||
)
|
||||
SubBlock(
|
||||
SubBlockV1(
|
||||
title = stringResourceSafe(id = R.string.markets_token_details_repository),
|
||||
links = state.repository,
|
||||
onLinkClick = state.onLinkClick,
|
||||
)
|
||||
SubBlock(
|
||||
SubBlockV1(
|
||||
title = stringResourceSafe(id = R.string.markets_token_details_blockchain_site),
|
||||
links = state.blockchainSite,
|
||||
onLinkClick = state.onLinkClick,
|
||||
|
|
@ -73,9 +83,36 @@ internal fun LinksBlock(state: LinksUM, modifier: Modifier = Modifier) {
|
|||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LinksBlockV2(state: LinksUM, modifier: Modifier = Modifier) {
|
||||
Column(modifier = modifier) {
|
||||
SubBlockV2(
|
||||
title = stringResourceSafe(id = R.string.markets_token_details_official_links),
|
||||
links = state.officialLinks,
|
||||
onLinkClick = state.onLinkClick,
|
||||
)
|
||||
SubBlockV2(
|
||||
title = stringResourceSafe(id = R.string.markets_token_details_social),
|
||||
links = state.social,
|
||||
onLinkClick = state.onLinkClick,
|
||||
)
|
||||
SubBlockV2(
|
||||
title = stringResourceSafe(id = R.string.markets_token_details_repository),
|
||||
links = state.repository,
|
||||
onLinkClick = state.onLinkClick,
|
||||
)
|
||||
SubBlockV2(
|
||||
title = stringResourceSafe(id = R.string.markets_token_details_blockchain_site),
|
||||
links = state.blockchainSite,
|
||||
onLinkClick = state.onLinkClick,
|
||||
lastBlock = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
private fun SubBlock(
|
||||
private fun SubBlockV1(
|
||||
links: ImmutableList<LinksUM.Link>,
|
||||
onLinkClick: (LinksUM.Link) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
|
|
@ -115,8 +152,67 @@ private fun SubBlock(
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
private fun SubBlockV2(
|
||||
title: String,
|
||||
links: ImmutableList<LinksUM.Link>,
|
||||
onLinkClick: (LinksUM.Link) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
lastBlock: Boolean = false,
|
||||
) {
|
||||
if (links.isEmpty()) return
|
||||
|
||||
ContainerWithDivider(
|
||||
modifier = modifier,
|
||||
showDivider = !lastBlock,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(vertical = TangemTheme.dimens2.x2),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 10.dp, top = TangemTheme.dimens2.x4),
|
||||
text = title,
|
||||
style = TangemTheme.typography2.bodySemibold16,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
FlowRow(
|
||||
modifier = Modifier.padding(vertical = TangemTheme.dimens2.x2, horizontal = 3.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2),
|
||||
) {
|
||||
links.fastForEach { link ->
|
||||
TangemBadge(
|
||||
text = stringReference(link.title),
|
||||
onClick = { onLinkClick(link) },
|
||||
iconPosition = TangemBadgeIconPosition.Start,
|
||||
tangemIconUM = TangemIconUM.Icon(
|
||||
iconRes = link.iconRes,
|
||||
tintReference = { TangemTheme.colors2.markers.iconGray },
|
||||
),
|
||||
size = TangemBadgeSize.X9,
|
||||
shape = TangemBadgeShape.Rounded,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LinksBlockPlaceholder(modifier: Modifier = Modifier) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
LinksBlockPlaceholderV2(modifier)
|
||||
} else {
|
||||
LinksBlockPlaceholderV1(modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LinksBlockPlaceholderV1(modifier: Modifier = Modifier) {
|
||||
InformationBlock(
|
||||
modifier = modifier,
|
||||
contentHorizontalPadding = 0.dp,
|
||||
|
|
@ -128,22 +224,31 @@ fun LinksBlockPlaceholder(modifier: Modifier = Modifier) {
|
|||
},
|
||||
content = {
|
||||
Column {
|
||||
SubBlockPlaceholder()
|
||||
SubBlockPlaceholder()
|
||||
SubBlockPlaceholder(lastBlock = true)
|
||||
SubBlockPlaceholderV1()
|
||||
SubBlockPlaceholderV1()
|
||||
SubBlockPlaceholderV1(lastBlock = true)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SubBlockPlaceholder(modifier: Modifier = Modifier, lastBlock: Boolean = false) {
|
||||
private fun LinksBlockPlaceholderV2(modifier: Modifier = Modifier) {
|
||||
Column(modifier = modifier) {
|
||||
SubBlockPlaceholderV2()
|
||||
SubBlockPlaceholderV2()
|
||||
SubBlockPlaceholderV2(lastBlock = true)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SubBlockPlaceholderV1(modifier: Modifier = Modifier, lastBlock: Boolean = false) {
|
||||
DividerContainer(
|
||||
modifier = modifier,
|
||||
showDivider = !lastBlock,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(TangemTheme.dimens.spacing12),
|
||||
modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing12),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
TextShimmer(
|
||||
|
|
@ -163,12 +268,42 @@ private fun SubBlockPlaceholder(modifier: Modifier = Modifier, lastBlock: Boolea
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SubBlockPlaceholderV2(modifier: Modifier = Modifier, lastBlock: Boolean = false) {
|
||||
ContainerWithDivider(
|
||||
modifier = modifier,
|
||||
showDivider = !lastBlock,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(TangemTheme.dimens2.x2),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2),
|
||||
) {
|
||||
TextShimmer(
|
||||
modifier = Modifier.width(56.dp),
|
||||
style = TangemTheme.typography2.bodySemibold16,
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2),
|
||||
) {
|
||||
repeat(times = 3) {
|
||||
ChipShimmer(
|
||||
modifier = Modifier
|
||||
.height(36.dp)
|
||||
.weight(1f),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun ContentPreview() {
|
||||
private fun ContentPreviewV1() {
|
||||
TangemThemePreview {
|
||||
LinksBlock(
|
||||
LinksBlockV1(
|
||||
state = LinksUM(
|
||||
officialLinks = persistentListOf(
|
||||
LinksUM.Link(
|
||||
|
|
@ -216,11 +351,73 @@ private fun ContentPreview() {
|
|||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun PlaceholderPreview() {
|
||||
TangemThemePreview {
|
||||
PreviewShimmerContainer(
|
||||
shimmerContent = { LinksBlockPlaceholder() },
|
||||
actualContent = { ContentPreview() },
|
||||
private fun ContentPreviewV2() {
|
||||
TangemThemePreviewRedesign {
|
||||
LinksBlockV2(
|
||||
state = LinksUM(
|
||||
officialLinks = persistentListOf(
|
||||
LinksUM.Link(
|
||||
title = "Website",
|
||||
iconRes = R.drawable.ic_plus_24,
|
||||
url = "https://tangem.com",
|
||||
),
|
||||
LinksUM.Link(
|
||||
title = "Website",
|
||||
iconRes = R.drawable.ic_plus_24,
|
||||
url = "https://tangem.com",
|
||||
),
|
||||
LinksUM.Link(
|
||||
title = "Website",
|
||||
iconRes = R.drawable.ic_plus_24,
|
||||
url = "https://tangem.com",
|
||||
),
|
||||
),
|
||||
social = persistentListOf(
|
||||
LinksUM.Link(
|
||||
title = "Twitter",
|
||||
iconRes = R.drawable.ic_plus_24,
|
||||
url = "https://tangem.com",
|
||||
),
|
||||
LinksUM.Link(
|
||||
title = "Facebook",
|
||||
iconRes = R.drawable.ic_plus_24,
|
||||
url = "https://tangem.com",
|
||||
),
|
||||
),
|
||||
repository = persistentListOf(
|
||||
LinksUM.Link(
|
||||
title = "Github",
|
||||
iconRes = R.drawable.ic_plus_24,
|
||||
url = "https://tangem.com",
|
||||
),
|
||||
),
|
||||
blockchainSite = persistentListOf(),
|
||||
onLinkClick = {},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun PlaceholderPreviewV1() {
|
||||
TangemThemePreview {
|
||||
PreviewShimmerContainer(
|
||||
shimmerContent = { LinksBlockPlaceholderV1() },
|
||||
actualContent = { ContentPreviewV1() },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun PlaceholderPreviewV2() {
|
||||
TangemThemePreviewRedesign {
|
||||
Column {
|
||||
LinksBlockPlaceholderV2()
|
||||
ContentPreviewV2()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,31 +2,33 @@ package com.tangem.features.feed.ui.market.detailed.components
|
|||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.semantics.testTag
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.common.ui.R
|
||||
import com.tangem.core.ui.components.SpacerWMax
|
||||
import com.tangem.core.ui.components.TextShimmer
|
||||
import com.tangem.core.ui.components.block.information.InformationBlock
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.MarketsTestTags
|
||||
import com.tangem.features.feed.ui.components.TokenMarketInformationBlock
|
||||
import com.tangem.features.feed.ui.market.detailed.state.ListedOnUM
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
|
|
@ -39,6 +41,15 @@ import kotlinx.coroutines.delay
|
|||
*/
|
||||
@Composable
|
||||
internal fun ListedOnBlock(state: ListedOnUM, modifier: Modifier = Modifier) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
ListedOnBlockV2(state, modifier)
|
||||
} else {
|
||||
ListedOnBlockV1(state, modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ListedOnBlockV1(state: ListedOnUM, modifier: Modifier = Modifier) {
|
||||
Box(modifier = modifier) {
|
||||
InformationBlock(
|
||||
title = {
|
||||
|
|
@ -75,8 +86,55 @@ internal fun ListedOnBlock(state: ListedOnUM, modifier: Modifier = Modifier) {
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ListedOnBlockV2(state: ListedOnUM, modifier: Modifier = Modifier) {
|
||||
TokenMarketInformationBlock(
|
||||
modifier = modifier.clickable(enabled = state is ListedOnUM.Content) {
|
||||
(state as? ListedOnUM.Content)?.onClick?.invoke()
|
||||
},
|
||||
title = {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
Text(
|
||||
text = state.title.resolveReference(),
|
||||
style = TangemTheme.typography2.headingSemibold20,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
Text(
|
||||
text = state.description.resolveReference(),
|
||||
style = TangemTheme.typography2.captionSemibold13,
|
||||
color = TangemTheme.colors2.text.neutral.secondary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
|
||||
SpacerWMax()
|
||||
|
||||
Icon(
|
||||
modifier = Modifier.size(20.dp),
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_chevron_small_right_24),
|
||||
tint = TangemTheme.colors2.markers.iconGray,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun ListedOnBlockPlaceholder(modifier: Modifier = Modifier) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
ListedOnBlockPlaceholderV2(modifier)
|
||||
} else {
|
||||
ListedOnBlockPlaceholderV1(modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun ListedOnBlockPlaceholderV1(modifier: Modifier = Modifier) {
|
||||
InformationBlock(
|
||||
title = {
|
||||
TextShimmer(
|
||||
|
|
@ -95,11 +153,32 @@ internal fun ListedOnBlockPlaceholder(modifier: Modifier = Modifier) {
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun ListedOnBlockPlaceholderV2(modifier: Modifier = Modifier) {
|
||||
TokenMarketInformationBlock(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
title = {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
TextShimmer(
|
||||
style = TangemTheme.typography2.headingSemibold20,
|
||||
modifier = Modifier.width(120.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
TextShimmer(
|
||||
style = TangemTheme.typography2.captionSemibold13,
|
||||
modifier = Modifier.width(66.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Description(state: ListedOnUM, modifier: Modifier = Modifier) {
|
||||
Text(
|
||||
text = state.description.resolveReference(),
|
||||
modifier = modifier,
|
||||
modifier = modifier.semantics { testTag = MarketsTestTags.LISTED_ON_EXCHANGES_COUNT },
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
package com.tangem.features.feed.ui.market.detailed.components
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.TextButton
|
||||
import com.tangem.core.ui.components.TextShimmer
|
||||
import com.tangem.core.ui.components.block.information.GridItems
|
||||
|
|
@ -16,12 +16,16 @@ import com.tangem.core.ui.components.block.information.InformationBlock
|
|||
import com.tangem.core.ui.components.buttons.common.TangemButtonsDefaults
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.utils.PreviewShimmerContainer
|
||||
import com.tangem.features.feed.impl.R
|
||||
import com.tangem.features.feed.ui.components.MetricsCard
|
||||
import com.tangem.features.feed.ui.market.detailed.state.InfoPointUM
|
||||
import com.tangem.features.feed.ui.market.detailed.state.InfoPointUMV2
|
||||
import com.tangem.features.feed.ui.market.detailed.state.MetricsUM
|
||||
import com.tangem.features.feed.ui.market.detailed.state.MetricsV2UM
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
|
|
@ -29,6 +33,17 @@ const val MAX_METRICS_COUNT = 6
|
|||
|
||||
@Composable
|
||||
internal fun MetricsBlock(state: MetricsUM, modifier: Modifier = Modifier) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
state.metricsV2?.let {
|
||||
MetricsBlockV2(it, modifier)
|
||||
}
|
||||
} else {
|
||||
MetricsBlockV1(state, modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MetricsBlockV1(state: MetricsUM, modifier: Modifier = Modifier) {
|
||||
var isExpanded by remember { mutableStateOf(false) }
|
||||
|
||||
InformationBlock(
|
||||
|
|
@ -64,6 +79,50 @@ internal fun MetricsBlock(state: MetricsUM, modifier: Modifier = Modifier) {
|
|||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MetricsBlockV2(state: MetricsV2UM, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2),
|
||||
) {
|
||||
state.rows.forEach { row ->
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2),
|
||||
) {
|
||||
Box(Modifier.weight(1f)) {
|
||||
MetricRowItem(row.first)
|
||||
}
|
||||
|
||||
row.second?.let { second ->
|
||||
Box(Modifier.weight(1f)) {
|
||||
MetricRowItem(second)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MetricRowItem(item: InfoPointUMV2) {
|
||||
when (item) {
|
||||
is InfoPointUMV2.CirculatingSupply -> CirculatingSupplyCard(item)
|
||||
else -> MetricCard(item)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MetricCard(item: InfoPointUMV2) {
|
||||
when (item) {
|
||||
is InfoPointUMV2.MarketCap -> MarketCapCard(item)
|
||||
is InfoPointUMV2.TradingVolume -> TradingVolumeCard(item)
|
||||
is InfoPointUMV2.MarketPosition -> MarketPositionCard(item)
|
||||
is InfoPointUMV2.FullyDilutedValuation -> FDVCard(item)
|
||||
is InfoPointUMV2.CirculatingSupply -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
// TODO make TextButton clickable area smaller and remove paddings for an action in InformationBlock
|
||||
@Composable
|
||||
private fun ShowLessMoreButton(expanded: Boolean, onClick: () -> Unit) {
|
||||
|
|
@ -84,6 +143,15 @@ private fun ShowLessMoreButton(expanded: Boolean, onClick: () -> Unit) {
|
|||
|
||||
@Composable
|
||||
internal fun MetricsBlockPlaceholder(modifier: Modifier = Modifier) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
MetricsBlockPlaceholderV2(modifier)
|
||||
} else {
|
||||
MetricsBlockPlaceholderV1(modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MetricsBlockPlaceholderV1(modifier: Modifier = Modifier) {
|
||||
InformationBlock(
|
||||
modifier = modifier,
|
||||
title = {
|
||||
|
|
@ -111,6 +179,108 @@ internal fun MetricsBlockPlaceholder(modifier: Modifier = Modifier) {
|
|||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MetricsBlockPlaceholderV2(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2),
|
||||
) {
|
||||
repeat(2) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2),
|
||||
) {
|
||||
repeat(2) {
|
||||
Box(Modifier.weight(1f)) {
|
||||
MetricsCard(
|
||||
modifier = Modifier
|
||||
.heightIn(120.dp)
|
||||
.fillMaxWidth(),
|
||||
title = {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(28.dp)
|
||||
.padding(end = 10.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
},
|
||||
content = {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(16.dp)
|
||||
.padding(end = 74.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CirculatingSupplyCardPlaceholder()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CirculatingSupplyCardPlaceholder() {
|
||||
MetricsCard(
|
||||
modifier = Modifier
|
||||
.heightIn(120.dp)
|
||||
.fillMaxWidth(),
|
||||
title = {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(104.dp)
|
||||
.height(16.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(64.dp)
|
||||
.height(16.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(160.dp)
|
||||
.height(28.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(48.dp)
|
||||
.height(28.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
content = {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(6.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview("Dark Theme", uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
|
|
@ -150,6 +320,7 @@ private fun BlockPreview() {
|
|||
onInfoClick = {},
|
||||
),
|
||||
),
|
||||
metricsV2 = null,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,417 @@
|
|||
package com.tangem.features.feed.ui.market.detailed.components
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LinearProgressIndicator
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.StrokeCap
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.layout.layoutId
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.components.progressbar.TangemLinearProgressIndicator
|
||||
import com.tangem.core.ui.ds.progress.TangemLinearProgressIndicatorWithDot
|
||||
import com.tangem.core.ui.ds.row.TangemRowContainer
|
||||
import com.tangem.core.ui.ds.row.TangemRowLayoutId
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.LocalIsInDarkTheme
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
import com.tangem.features.feed.impl.R
|
||||
import com.tangem.features.feed.ui.components.MetricsCard
|
||||
import com.tangem.features.feed.ui.market.detailed.state.InfoPointUMV2
|
||||
import com.tangem.features.feed.ui.market.detailed.state.MarketRatingType
|
||||
import com.tangem.features.feed.ui.market.detailed.state.TrendingVolumeLiquidityType
|
||||
|
||||
@Composable
|
||||
internal fun MarketCapCard(item: InfoPointUMV2.MarketCap) {
|
||||
MetricsCard(
|
||||
modifier = Modifier
|
||||
.heightIn(120.dp)
|
||||
.fillMaxWidth(),
|
||||
title = {
|
||||
Text(
|
||||
text = item.capitalizationValue.resolveReference(),
|
||||
style = TangemTheme.typography2.headingSemibold20,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
},
|
||||
content = {
|
||||
InformationTextBlock(
|
||||
text = resourceReference(R.string.markets_token_details_market_capitalization),
|
||||
onInfoClick = item.onInfoClick,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun TradingVolumeCard(item: InfoPointUMV2.TradingVolume) {
|
||||
val tradingColor = when (item.trendingVolumeLiquidityType) {
|
||||
TrendingVolumeLiquidityType.HIGH -> TangemTheme.colors2.markers.backgroundSolidGreen
|
||||
TrendingVolumeLiquidityType.MEDIUM -> TangemTheme.colors2.graphic.status.attention
|
||||
TrendingVolumeLiquidityType.LOW -> TangemTheme.colors2.graphic.status.warning
|
||||
}
|
||||
MetricsCard(
|
||||
modifier = Modifier
|
||||
.heightIn(120.dp)
|
||||
.fillMaxWidth(),
|
||||
title = {
|
||||
Row {
|
||||
Text(
|
||||
text = item.tradingValue.resolveReference(),
|
||||
style = TangemTheme.typography2.headingSemibold20,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(TangemTheme.dimens2.x1),
|
||||
text = stringResourceSafe(R.string.markets_token_details_trading_interval),
|
||||
style = TangemTheme.typography2.captionSemibold11,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
},
|
||||
content = {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
TangemLinearProgressIndicator(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(6.dp),
|
||||
progress = { item.liquidity },
|
||||
color = tradingColor,
|
||||
backgroundColor = TangemTheme.colors2.graphic.neutral.primaryInvertedConstant.copy(alpha = .1f),
|
||||
)
|
||||
SpacerH(12.dp)
|
||||
InformationTextBlock(
|
||||
text = resourceReference(R.string.markets_token_details_trading_volume),
|
||||
textColor = tradingColor,
|
||||
infoIconColor = tradingColor,
|
||||
onInfoClick = item.onInfoClick,
|
||||
)
|
||||
}
|
||||
},
|
||||
cardColor = tradingColor.copy(alpha = .2f),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun MarketPositionCard(item: InfoPointUMV2.MarketPosition) {
|
||||
val ratingCardColor = mapRatingToCardColor(marketRatingType = item.marketRatingType)
|
||||
val ratingColor = mapRatingToColor(marketRatingType = item.marketRatingType)
|
||||
|
||||
MetricsCard(
|
||||
modifier = Modifier
|
||||
.heightIn(120.dp)
|
||||
.fillMaxWidth(),
|
||||
title = {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_big_laurel_left_20),
|
||||
tint = ratingColor,
|
||||
contentDescription = null,
|
||||
)
|
||||
|
||||
Text(
|
||||
textAlign = TextAlign.Center,
|
||||
text = item.position,
|
||||
color = ratingColor,
|
||||
style = TangemTheme.typography2.headingSemibold20.copy(letterSpacing = 0.sp),
|
||||
maxLines = 1,
|
||||
)
|
||||
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_big_laurel_right_20),
|
||||
tint = ratingColor,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
},
|
||||
content = {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
TangemLinearProgressIndicatorWithDot(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(6.dp),
|
||||
progress = { item.rangeValue },
|
||||
dotColor = TangemTheme.colors2.fill.neutral.primaryInvertedConstant,
|
||||
backgroundColor = TangemTheme.colors2.graphic.neutral.primaryInvertedConstant.copy(alpha = .1f),
|
||||
)
|
||||
SpacerH(12.dp)
|
||||
InformationTextBlock(
|
||||
text = resourceReference(R.string.markets_token_details_market_rating),
|
||||
textColor = ratingColor,
|
||||
infoIconColor = ratingColor,
|
||||
onInfoClick = item.onInfoClick,
|
||||
)
|
||||
}
|
||||
},
|
||||
cardColor = ratingCardColor,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun FDVCard(item: InfoPointUMV2.FullyDilutedValuation) {
|
||||
MetricsCard(
|
||||
modifier = Modifier
|
||||
.heightIn(120.dp)
|
||||
.fillMaxWidth(),
|
||||
title = {
|
||||
Text(
|
||||
text = item.value.resolveReference(),
|
||||
style = TangemTheme.typography2.headingSemibold20,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
},
|
||||
content = {
|
||||
InformationTextBlock(
|
||||
text = resourceReference(R.string.markets_token_details_fully_diluted_valuation),
|
||||
onInfoClick = item.onInfoClick,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun CirculatingSupplyCard(item: InfoPointUMV2.CirculatingSupply) {
|
||||
MetricsCard(
|
||||
modifier = Modifier
|
||||
.heightIn(min = if (item.fillValue == null) 88.dp else 114.dp)
|
||||
.fillMaxWidth(),
|
||||
title = {
|
||||
TangemRowContainer(contentPadding = PaddingValues(0.dp)) {
|
||||
Text(
|
||||
modifier = Modifier.layoutId(TangemRowLayoutId.START_TOP),
|
||||
text = stringResourceSafe(R.string.markets_token_details_circulating_supply),
|
||||
style = TangemTheme.typography2.captionSemibold13,
|
||||
color = TangemTheme.colors2.text.neutral.tertiary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(top = 12.dp)
|
||||
.layoutId(TangemRowLayoutId.START_BOTTOM),
|
||||
text = item.currentValue.resolveReference(),
|
||||
style = TangemTheme.typography2.headingSemibold20,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
|
||||
Text(
|
||||
modifier = Modifier.layoutId(TangemRowLayoutId.END_TOP),
|
||||
text = stringResourceSafe(R.string.markets_token_details_max_supply),
|
||||
style = TangemTheme.typography2.captionSemibold13,
|
||||
color = TangemTheme.colors2.text.neutral.tertiary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
|
||||
if (item.maxValue != null) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(top = 12.dp)
|
||||
.layoutId(TangemRowLayoutId.END_BOTTOM),
|
||||
text = item.maxValue.resolveReference(),
|
||||
style = TangemTheme.typography2.headingSemibold20,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
content = {
|
||||
if (item.fillValue != null) {
|
||||
LinearProgressIndicator(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(6.dp),
|
||||
color = TangemTheme.colors2.graphic.status.accent,
|
||||
trackColor = TangemTheme.colors2.graphic.neutral.primaryInvertedConstant.copy(alpha = .1f),
|
||||
progress = { item.fillValue },
|
||||
strokeCap = StrokeCap.Round,
|
||||
drawStopIndicator = {},
|
||||
gapSize = 4.dp,
|
||||
)
|
||||
}
|
||||
},
|
||||
onClick = item.onInfoClick,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MarketRatingType.baseColor(): Color {
|
||||
val isDarkTheme = LocalIsInDarkTheme.current
|
||||
|
||||
return when (this) {
|
||||
MarketRatingType.GOLD ->
|
||||
if (isDarkTheme) Color(GOLD_PLACE_COLOR_NIGHT) else Color(GOLD_PLACE_COLOR_LIGHT)
|
||||
|
||||
MarketRatingType.SILVER ->
|
||||
if (isDarkTheme) Color(SILVER_PLACE_COLOR_NIGHT) else Color(SILVER_PLACE_COLOR_LIGHT)
|
||||
|
||||
MarketRatingType.BRONZE ->
|
||||
if (isDarkTheme) Color(BRONZE_PLACE_COLOR_NIGHT) else Color(BRONZE_PLACE_COLOR_LIGHT)
|
||||
|
||||
MarketRatingType.OTHER ->
|
||||
TangemTheme.colors2.graphic.neutral.primary
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun mapRatingToColor(marketRatingType: MarketRatingType): Color = marketRatingType.baseColor()
|
||||
|
||||
@Composable
|
||||
private fun mapRatingToCardColor(marketRatingType: MarketRatingType): Color {
|
||||
return when (marketRatingType) {
|
||||
MarketRatingType.OTHER -> TangemTheme.colors2.surface.level3
|
||||
else -> marketRatingType.baseColor().copy(alpha = 0.3f)
|
||||
}
|
||||
}
|
||||
|
||||
private const val GOLD_PLACE_COLOR_NIGHT = 0xFFFBEE76
|
||||
private const val GOLD_PLACE_COLOR_LIGHT = 0xFFD9B900
|
||||
private const val SILVER_PLACE_COLOR_NIGHT = 0xFFAABEF7
|
||||
private const val SILVER_PLACE_COLOR_LIGHT = 0xFF6680CC
|
||||
private const val BRONZE_PLACE_COLOR_NIGHT = 0xFFFF9976
|
||||
private const val BRONZE_PLACE_COLOR_LIGHT = 0xFFCC7F66
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Preview(widthDp = 360, heightDp = 1500, showBackground = true)
|
||||
@Preview(widthDp = 360, heightDp = 1500, showBackground = true, locale = "ru")
|
||||
@Preview(widthDp = 360, heightDp = 1500, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun MetricsCardsPreview() {
|
||||
CompositionLocalProvider(LocalRedesignEnabled provides true) {
|
||||
TangemThemePreviewRedesign {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(TangemTheme.colors2.surface.level2)
|
||||
.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
MarketCapCard(
|
||||
item = InfoPointUMV2.MarketCap(
|
||||
capitalizationValue = stringReference("$ 1.2 T"),
|
||||
onInfoClick = {},
|
||||
),
|
||||
)
|
||||
|
||||
TradingVolumeCard(
|
||||
item = InfoPointUMV2.TradingVolume(
|
||||
tradingValue = stringReference("$ 45.2 M"),
|
||||
liquidity = 0.75f,
|
||||
trendingVolumeLiquidityType = TrendingVolumeLiquidityType.HIGH,
|
||||
onInfoClick = {},
|
||||
),
|
||||
)
|
||||
|
||||
TradingVolumeCard(
|
||||
item = InfoPointUMV2.TradingVolume(
|
||||
tradingValue = stringReference("$ 12.1 M"),
|
||||
liquidity = 0.45f,
|
||||
trendingVolumeLiquidityType = TrendingVolumeLiquidityType.MEDIUM,
|
||||
onInfoClick = {},
|
||||
),
|
||||
)
|
||||
|
||||
TradingVolumeCard(
|
||||
item = InfoPointUMV2.TradingVolume(
|
||||
tradingValue = stringReference("$ 2.3 M"),
|
||||
liquidity = 0.15f,
|
||||
trendingVolumeLiquidityType = TrendingVolumeLiquidityType.LOW,
|
||||
onInfoClick = {},
|
||||
),
|
||||
)
|
||||
|
||||
MarketPositionCard(
|
||||
item = InfoPointUMV2.MarketPosition(
|
||||
position = "1",
|
||||
rangeValue = 0.02f,
|
||||
marketRatingType = MarketRatingType.GOLD,
|
||||
onInfoClick = {},
|
||||
),
|
||||
)
|
||||
|
||||
MarketPositionCard(
|
||||
item = InfoPointUMV2.MarketPosition(
|
||||
position = "2",
|
||||
rangeValue = 0.05f,
|
||||
marketRatingType = MarketRatingType.SILVER,
|
||||
onInfoClick = {},
|
||||
),
|
||||
)
|
||||
|
||||
MarketPositionCard(
|
||||
item = InfoPointUMV2.MarketPosition(
|
||||
position = "3",
|
||||
rangeValue = 0.08f,
|
||||
marketRatingType = MarketRatingType.BRONZE,
|
||||
onInfoClick = {},
|
||||
),
|
||||
)
|
||||
|
||||
MarketPositionCard(
|
||||
item = InfoPointUMV2.MarketPosition(
|
||||
position = "42",
|
||||
rangeValue = 0.42f,
|
||||
marketRatingType = MarketRatingType.OTHER,
|
||||
onInfoClick = {},
|
||||
),
|
||||
)
|
||||
|
||||
FDVCard(
|
||||
item = InfoPointUMV2.FullyDilutedValuation(
|
||||
value = stringReference("$ 1.5 T"),
|
||||
onInfoClick = {},
|
||||
),
|
||||
)
|
||||
|
||||
CirculatingSupplyCard(
|
||||
item = InfoPointUMV2.CirculatingSupply(
|
||||
currentValue = stringReference("12.5 B POL"),
|
||||
maxValue = stringReference("21 B POL"),
|
||||
fillValue = 0.6f,
|
||||
onInfoClick = {},
|
||||
),
|
||||
)
|
||||
|
||||
CirculatingSupplyCard(
|
||||
item = InfoPointUMV2.CirculatingSupply(
|
||||
currentValue = stringReference("18.9 M ETH"),
|
||||
maxValue = null,
|
||||
fillValue = null,
|
||||
onInfoClick = {},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,7 @@
|
|||
package com.tangem.features.feed.ui.market.detailed.components
|
||||
|
||||
import androidx.annotation.FloatRange
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.requiredSize
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -22,6 +18,7 @@ import androidx.compose.ui.res.vectorResource
|
|||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.feed.impl.R
|
||||
import kotlin.math.round
|
||||
|
|
@ -34,6 +31,28 @@ internal fun ScoreStarsBlock(
|
|||
horizontalSpacing: Dp,
|
||||
scoreTextStyle: TextStyle,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
ScoreStarsBlockV2(
|
||||
score = score,
|
||||
modifier = modifier,
|
||||
)
|
||||
} else {
|
||||
ScoreStarsBlockV1(
|
||||
score = score,
|
||||
horizontalSpacing = horizontalSpacing,
|
||||
scoreTextStyle = scoreTextStyle,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ScoreStarsBlockV1(
|
||||
score: Float,
|
||||
horizontalSpacing: Dp,
|
||||
scoreTextStyle: TextStyle,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val rounded = score.roundTo1decimal()
|
||||
val percentage = rounded / STARS_COUNT
|
||||
|
|
@ -51,16 +70,38 @@ internal fun ScoreStarsBlock(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ScoreStarsBlockV2(score: Float, modifier: Modifier = Modifier) {
|
||||
val rounded = score.roundTo1decimal()
|
||||
val percentage = rounded / STARS_COUNT
|
||||
Box(
|
||||
modifier = modifier,
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Stars(fraction = percentage)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Composable
|
||||
private fun Stars(@FloatRange(0.0, 1.0) fraction: Float = 0f) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
StarsV2(fraction)
|
||||
} else {
|
||||
StarsV1(fraction)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Composable
|
||||
private fun StarsV1(@FloatRange(0.0, 1.0) fraction: Float = 0f) {
|
||||
val grayColor = TangemTheme.colors.icon.inactive
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
repeat(times = 5) { i ->
|
||||
repeat(times = STARS_COUNT) { i ->
|
||||
Box(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size16),
|
||||
contentAlignment = Alignment.Center,
|
||||
|
|
@ -94,6 +135,49 @@ private fun Stars(@FloatRange(0.0, 1.0) fraction: Float = 0f) {
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Composable
|
||||
private fun StarsV2(@FloatRange(0.0, 1.0) fraction: Float = 0f) {
|
||||
val grayColor = TangemTheme.colors2.markers.iconGray
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x1),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
repeat(times = STARS_COUNT) { i ->
|
||||
Box(
|
||||
modifier = Modifier.size(TangemTheme.dimens2.x5),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.requiredSize(16.dp)
|
||||
.graphicsLayer(compositingStrategy = CompositingStrategy.Offscreen)
|
||||
.drawWithCache {
|
||||
onDrawWithContent {
|
||||
val starFraction = ((fraction - i * 0.2) / 0.2).coerceIn(0.0, 1.0)
|
||||
val starFractionFloat = starFraction
|
||||
.toFloat()
|
||||
.roundTo1decimal()
|
||||
|
||||
drawContent()
|
||||
drawRect(
|
||||
color = grayColor,
|
||||
topLeft = Offset(x = size.width * starFractionFloat, y = 0f),
|
||||
size = Size(size.width * (1 - starFractionFloat), size.height),
|
||||
blendMode = BlendMode.SrcIn,
|
||||
)
|
||||
}
|
||||
},
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_star_filled_20),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors2.markers.iconBlue,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private fun Float.roundTo1decimal(): Float {
|
||||
return round(this * 10) / 10
|
||||
|
|
|
|||
|
|
@ -2,33 +2,45 @@ package com.tangem.features.feed.ui.market.detailed.components
|
|||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.layout.layoutId
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.components.TextShimmer
|
||||
import com.tangem.core.ui.components.text.TooltipText
|
||||
import com.tangem.core.ui.ds.row.TangemRowContainer
|
||||
import com.tangem.core.ui.ds.row.TangemRowLayoutId
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
import com.tangem.core.ui.utils.PreviewShimmerContainer
|
||||
import com.tangem.features.feed.impl.R
|
||||
import com.tangem.features.feed.ui.components.ContainerWithDivider
|
||||
import com.tangem.features.feed.ui.market.detailed.state.SecurityScoreUM
|
||||
|
||||
@Composable
|
||||
internal fun SecurityScoreBlock(state: SecurityScoreUM, modifier: Modifier = Modifier) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
SecurityScoreBlockV2(state, modifier)
|
||||
} else {
|
||||
SecurityScoreBlockV1(state, modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SecurityScoreBlockV1(state: SecurityScoreUM, modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
|
|
@ -66,8 +78,99 @@ internal fun SecurityScoreBlock(state: SecurityScoreUM, modifier: Modifier = Mod
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SecurityScoreBlockV2(state: SecurityScoreUM, modifier: Modifier = Modifier) {
|
||||
ContainerWithDivider(
|
||||
modifier = modifier,
|
||||
showDivider = true,
|
||||
) {
|
||||
TangemRowContainer(modifier = Modifier.padding(top = 20.dp, bottom = 24.dp)) {
|
||||
Text(
|
||||
modifier = Modifier.layoutId(layoutId = TangemRowLayoutId.START_TOP),
|
||||
text = "${state.score}",
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
style = TangemTheme.typography2.headingBold28,
|
||||
)
|
||||
|
||||
InformationTextBlock(
|
||||
modifier = Modifier.layoutId(layoutId = TangemRowLayoutId.START_BOTTOM),
|
||||
text = resourceReference(R.string.markets_token_details_security_score),
|
||||
onInfoClick = state.onInfoClick,
|
||||
textColor = TangemTheme.colors2.text.neutral.primary,
|
||||
informationTextBlockIconPosition = InformationTextBlockIconPosition.END,
|
||||
)
|
||||
|
||||
Text(
|
||||
modifier = Modifier.layoutId(layoutId = TangemRowLayoutId.END_BOTTOM),
|
||||
text = state.description.resolveReference(),
|
||||
style = TangemTheme.typography2.captionSemibold12,
|
||||
color = TangemTheme.colors2.text.neutral.tertiary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
|
||||
ScoreStarsBlock(
|
||||
modifier = Modifier
|
||||
.padding(bottom = 16.dp)
|
||||
.layoutId(layoutId = TangemRowLayoutId.END_TOP),
|
||||
score = state.score,
|
||||
scoreTextStyle = TangemTheme.typography.body1,
|
||||
horizontalSpacing = TangemTheme.dimens.spacing8,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun SecurityScoreBlockPlaceholder(modifier: Modifier = Modifier) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
SecurityScoreBlockPlaceholderV2(modifier)
|
||||
} else {
|
||||
SecurityScoreBlockPlaceholderV1(modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SecurityScoreBlockPlaceholderV2(modifier: Modifier = Modifier) {
|
||||
TangemRowContainer(modifier = modifier) {
|
||||
TextShimmer(
|
||||
modifier = Modifier
|
||||
.width(114.dp)
|
||||
.layoutId(layoutId = TangemRowLayoutId.START_TOP),
|
||||
style = TangemTheme.typography2.headingBold28,
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
|
||||
TextShimmer(
|
||||
modifier = Modifier
|
||||
.width(74.dp)
|
||||
.padding(top = 8.dp)
|
||||
.layoutId(layoutId = TangemRowLayoutId.START_BOTTOM),
|
||||
style = TangemTheme.typography2.captionSemibold12,
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
|
||||
TextShimmer(
|
||||
modifier = Modifier
|
||||
.width(116.dp)
|
||||
.layoutId(layoutId = TangemRowLayoutId.END_TOP),
|
||||
style = TangemTheme.typography2.headingBold28,
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
|
||||
TextShimmer(
|
||||
modifier = Modifier
|
||||
.width(96.dp)
|
||||
.padding(top = 8.dp)
|
||||
.layoutId(layoutId = TangemRowLayoutId.END_BOTTOM),
|
||||
style = TangemTheme.typography2.captionSemibold12,
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SecurityScoreBlockPlaceholderV1(modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
|
|
@ -109,7 +212,7 @@ internal fun SecurityScoreBlockPlaceholder(modifier: Modifier = Modifier) {
|
|||
@Preview(widthDp = 328, showBackground = true, locale = "ru")
|
||||
@Preview(widthDp = 328, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun ContentPreview() {
|
||||
private fun ContentPreviewV1() {
|
||||
TangemThemePreview {
|
||||
SecurityScoreBlock(
|
||||
state = SecurityScoreUM(
|
||||
|
|
@ -124,7 +227,7 @@ private fun ContentPreview() {
|
|||
@Preview(widthDp = 328, showBackground = true)
|
||||
@Preview(widthDp = 328, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun PreviewPlaceholder() {
|
||||
private fun PreviewPlaceholderV1() {
|
||||
TangemThemePreview {
|
||||
PreviewShimmerContainer(
|
||||
shimmerContent = {
|
||||
|
|
@ -133,8 +236,36 @@ private fun PreviewPlaceholder() {
|
|||
)
|
||||
},
|
||||
actualContent = {
|
||||
ContentPreview()
|
||||
ContentPreviewV1()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 328, showBackground = true)
|
||||
@Preview(widthDp = 328, showBackground = true, locale = "ru")
|
||||
@Preview(widthDp = 328, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun ContentPreviewV2() {
|
||||
CompositionLocalProvider(
|
||||
LocalRedesignEnabled provides true,
|
||||
) {
|
||||
TangemThemePreviewRedesign {
|
||||
Column(modifier = Modifier.background(TangemTheme.colors2.surface.level2)) {
|
||||
SecurityScoreBlock(
|
||||
state = SecurityScoreUM(
|
||||
score = 3.5f,
|
||||
description = stringReference("Based on 3 ratings"),
|
||||
onInfoClick = {},
|
||||
),
|
||||
)
|
||||
|
||||
SpacerH(10.dp)
|
||||
|
||||
SecurityScoreBlockPlaceholder(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,42 +1,71 @@
|
|||
package com.tangem.features.feed.ui.market.detailed.components
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.drawWithCache
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.BlendMode
|
||||
import androidx.compose.ui.graphics.CompositingStrategy
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.layout.layoutId
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import androidx.compose.ui.util.fastForEachIndexed
|
||||
import coil.compose.SubcomposeAsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.tangem.core.ui.components.*
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetType
|
||||
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheetTitle
|
||||
import com.tangem.core.ui.components.inputrow.inner.DividerContainer
|
||||
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
|
||||
import com.tangem.core.ui.ds.row.TangemRowContainer
|
||||
import com.tangem.core.ui.ds.row.TangemRowLayoutId
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBar
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBarType
|
||||
import com.tangem.core.ui.extensions.clickableSingle
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
import com.tangem.features.feed.impl.R
|
||||
import com.tangem.features.feed.ui.market.detailed.preview.SecurityScorePreviewData
|
||||
import com.tangem.features.feed.ui.market.detailed.state.SecurityScoreBottomSheetContent
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet as TangemBottomSheetV2
|
||||
|
||||
@Composable
|
||||
internal fun SecurityScoreBottomSheet(config: TangemBottomSheetConfig) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
SecurityScoreBottomSheetV2(config)
|
||||
} else {
|
||||
SecurityScoreBottomSheetV1(config)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SecurityScoreBottomSheetV1(config: TangemBottomSheetConfig) {
|
||||
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
|
||||
|
||||
TangemBottomSheet<SecurityScoreBottomSheetContent>(
|
||||
|
|
@ -82,6 +111,62 @@ internal fun SecurityScoreBottomSheet(config: TangemBottomSheetConfig) {
|
|||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SecurityScoreBottomSheetV2(config: TangemBottomSheetConfig) {
|
||||
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
|
||||
|
||||
TangemBottomSheetV2<SecurityScoreBottomSheetContent>(
|
||||
config = config,
|
||||
type = TangemBottomSheetType.Modal,
|
||||
title = { content ->
|
||||
TangemTopBar(
|
||||
title = content.title,
|
||||
type = TangemTopBarType.BottomSheet,
|
||||
endContent = {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.ic_close_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors2.graphic.neutral.primary,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens2.x11)
|
||||
.background(
|
||||
color = TangemTheme.colors2.button.backgroundSecondary,
|
||||
shape = CircleShape,
|
||||
)
|
||||
.clickableSingle(onClick = config.onDismissRequest)
|
||||
.padding(TangemTheme.dimens2.x2_5),
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
content = { content ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(horizontal = TangemTheme.dimens2.x4),
|
||||
) {
|
||||
Text(
|
||||
text = content.description.resolveReference(),
|
||||
style = TangemTheme.typography2.bodyRegular15.copy(
|
||||
color = TangemTheme.colors2.text.neutral.tertiary,
|
||||
),
|
||||
)
|
||||
SpacerH12()
|
||||
content.providers.fastForEach { provider ->
|
||||
SecurityScoreProviderRowV2(
|
||||
providerUM = provider,
|
||||
onLinkClick = { content.onProviderLinkClick(provider) },
|
||||
)
|
||||
SpacerH(TangemTheme.dimens2.x2)
|
||||
}
|
||||
|
||||
SpacerH16()
|
||||
SpacerH(bottomBarHeight)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SecurityScoreProviderRow(
|
||||
providerUM: SecurityScoreBottomSheetContent.SecurityScoreProviderUM,
|
||||
|
|
@ -145,17 +230,91 @@ private fun SecurityScoreProviderRow(
|
|||
horizontalSpacing = TangemTheme.dimens.spacing3,
|
||||
)
|
||||
|
||||
UrlBlock(providerUM)
|
||||
UrlBlockV1(providerUM)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UrlBlock(providerUM: SecurityScoreBottomSheetContent.SecurityScoreProviderUM) {
|
||||
private fun SecurityScoreProviderRowV2(
|
||||
providerUM: SecurityScoreBottomSheetContent.SecurityScoreProviderUM,
|
||||
onLinkClick: () -> Unit,
|
||||
) {
|
||||
TangemRowContainer(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = TangemTheme.colors2.surface.level4,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens2.x5),
|
||||
)
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = TangemTheme.colors2.border.neutral.primary,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens2.x5),
|
||||
),
|
||||
) {
|
||||
SubcomposeAsyncImage(
|
||||
modifier = Modifier
|
||||
.size(size = TangemTheme.dimens.size40)
|
||||
.clip(TangemTheme.shapes.roundedCorners8)
|
||||
.layoutId(TangemRowLayoutId.HEAD),
|
||||
model = ImageRequest.Builder(context = LocalContext.current)
|
||||
.data(providerUM.iconUrl)
|
||||
.crossfade(enable = true)
|
||||
.allowHardware(false)
|
||||
.build(),
|
||||
loading = { RectangleShimmer(radius = TangemTheme.dimens2.x3) },
|
||||
error = { RectangleShimmer(radius = TangemTheme.dimens2.x3) },
|
||||
contentDescription = null,
|
||||
)
|
||||
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(start = TangemTheme.dimens2.x2)
|
||||
.layoutId(TangemRowLayoutId.START_TOP),
|
||||
text = providerUM.name,
|
||||
style = TangemTheme.typography2.bodySemibold16,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
)
|
||||
|
||||
providerUM.lastAuditDate?.let { auditDate ->
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(start = TangemTheme.dimens2.x2)
|
||||
.layoutId(TangemRowLayoutId.START_BOTTOM),
|
||||
text = auditDate,
|
||||
style = TangemTheme.typography2.captionSemibold12,
|
||||
color = TangemTheme.colors2.text.neutral.secondary,
|
||||
)
|
||||
}
|
||||
|
||||
StarBlock(
|
||||
score = providerUM.score,
|
||||
modifier = Modifier
|
||||
.padding(start = TangemTheme.dimens2.x2)
|
||||
.clickableSingle(onClick = onLinkClick)
|
||||
.layoutId(TangemRowLayoutId.END_TOP),
|
||||
)
|
||||
|
||||
UrlBlockV2(
|
||||
modifier = Modifier
|
||||
.padding(start = TangemTheme.dimens2.x2)
|
||||
.clickableSingle(onClick = onLinkClick)
|
||||
.layoutId(TangemRowLayoutId.END_BOTTOM),
|
||||
providerUM = providerUM,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UrlBlockV1(
|
||||
providerUM: SecurityScoreBottomSheetContent.SecurityScoreProviderUM,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val urlData = providerUM.urlData
|
||||
val rootHost = urlData?.rootHost
|
||||
if (urlData != null && rootHost != null) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4),
|
||||
) {
|
||||
Text(
|
||||
|
|
@ -174,10 +333,75 @@ private fun UrlBlock(providerUM: SecurityScoreBottomSheetContent.SecurityScorePr
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UrlBlockV2(
|
||||
providerUM: SecurityScoreBottomSheetContent.SecurityScoreProviderUM,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val urlData = providerUM.urlData
|
||||
val rootHost = urlData?.rootHost
|
||||
if (urlData != null && rootHost != null) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x0_5),
|
||||
) {
|
||||
Text(
|
||||
text = urlData.rootHost,
|
||||
style = TangemTheme.typography2.captionSemibold12,
|
||||
color = TangemTheme.colors2.text.neutral.secondary,
|
||||
)
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens2.x4),
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.ic_arrow_top_right_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors2.markers.iconGray,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Composable
|
||||
fun StarBlock(score: Float, modifier: Modifier = Modifier) {
|
||||
val grayColor = TangemTheme.colors2.markers.iconGray
|
||||
val percentage = score / 5
|
||||
Row(
|
||||
modifier = modifier,
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x1),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = score.toString(),
|
||||
style = TangemTheme.typography2.bodySemibold16,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
)
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.requiredSize(TangemTheme.dimens2.x5)
|
||||
.graphicsLayer(compositingStrategy = CompositingStrategy.Offscreen)
|
||||
.drawWithCache {
|
||||
onDrawWithContent {
|
||||
drawContent()
|
||||
drawRect(
|
||||
color = grayColor,
|
||||
topLeft = Offset(x = size.width * percentage, y = 0f),
|
||||
size = Size(size.width * (1 - percentage), size.height),
|
||||
blendMode = BlendMode.SrcIn,
|
||||
)
|
||||
}
|
||||
},
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_star_filled_20),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors2.markers.iconBlue,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun SecurityScoreBottomSheetPreview() {
|
||||
private fun SecurityScoreBottomSheetPreviewV1() {
|
||||
TangemThemePreview {
|
||||
SecurityScoreBottomSheet(
|
||||
config = TangemBottomSheetConfig(
|
||||
|
|
@ -187,4 +411,21 @@ private fun SecurityScoreBottomSheetPreview() {
|
|||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun SecurityScoreBottomSheetPreviewV2() {
|
||||
TangemThemePreviewRedesign {
|
||||
CompositionLocalProvider(LocalRedesignEnabled provides true) {
|
||||
SecurityScoreBottomSheet(
|
||||
config = TangemBottomSheetConfig(
|
||||
isShown = true,
|
||||
onDismissRequest = {},
|
||||
content = SecurityScorePreviewData.bottomSheetContent,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -22,6 +22,27 @@ import com.tangem.features.feed.ui.market.detailed.state.MarketsTokenDetailsUM.R
|
|||
|
||||
@Suppress("CanBeNonNullable")
|
||||
internal fun LazyListScope.tokenMarketDetailsBody(
|
||||
isRedesignEnabled: Boolean,
|
||||
state: MarketsTokenDetailsUM.Body,
|
||||
portfolioBlock: @Composable ((Modifier) -> Unit)?,
|
||||
relatedNews: RelatedNews,
|
||||
) {
|
||||
if (isRedesignEnabled) {
|
||||
tokenMarketDetailsBodyV2(
|
||||
state = state,
|
||||
relatedNews = relatedNews,
|
||||
)
|
||||
} else {
|
||||
tokenMarketDetailsBodyV1(
|
||||
state = state,
|
||||
portfolioBlock = portfolioBlock,
|
||||
relatedNews = relatedNews,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("CanBeNonNullable")
|
||||
private fun LazyListScope.tokenMarketDetailsBodyV1(
|
||||
state: MarketsTokenDetailsUM.Body,
|
||||
portfolioBlock: @Composable ((Modifier) -> Unit)?,
|
||||
relatedNews: RelatedNews,
|
||||
|
|
@ -40,7 +61,7 @@ internal fun LazyListScope.tokenMarketDetailsBody(
|
|||
|
||||
aboutCoinHeader()
|
||||
|
||||
loadingInfoBlocks()
|
||||
loadingInfoBlocks(false)
|
||||
}
|
||||
is MarketsTokenDetailsUM.Body.Content -> {
|
||||
if (state.description != null) {
|
||||
|
|
@ -59,7 +80,36 @@ internal fun LazyListScope.tokenMarketDetailsBody(
|
|||
|
||||
aboutCoinHeader()
|
||||
|
||||
infoBlocksList(state.infoBlocks)
|
||||
infoBlocksListV1(state = state.infoBlocks)
|
||||
}
|
||||
is MarketsTokenDetailsUM.Body.Error -> {
|
||||
error(state)
|
||||
}
|
||||
MarketsTokenDetailsUM.Body.Nothing -> {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("CanBeNonNullable")
|
||||
private fun LazyListScope.tokenMarketDetailsBodyV2(state: MarketsTokenDetailsUM.Body, relatedNews: RelatedNews) {
|
||||
when (state) {
|
||||
MarketsTokenDetailsUM.Body.Loading -> {
|
||||
item("description-loading") {
|
||||
DescriptionPlaceholder(modifier = Modifier.blockPaddings())
|
||||
}
|
||||
|
||||
loadingInfoBlocks(true)
|
||||
}
|
||||
is MarketsTokenDetailsUM.Body.Content -> {
|
||||
if (state.description != null) {
|
||||
description(state.description)
|
||||
}
|
||||
|
||||
infoBlocksListV2(
|
||||
state = state.infoBlocks,
|
||||
relatedNews = relatedNews,
|
||||
)
|
||||
}
|
||||
is MarketsTokenDetailsUM.Body.Error -> {
|
||||
error(state)
|
||||
|
|
@ -112,7 +162,7 @@ private fun LazyListScope.description(description: MarketsTokenDetailsUM.Descrip
|
|||
}
|
||||
}
|
||||
|
||||
internal fun LazyListScope.infoBlocksList(state: MarketsTokenDetailsUM.InformationBlocks) {
|
||||
internal fun LazyListScope.infoBlocksListV1(state: MarketsTokenDetailsUM.InformationBlocks) {
|
||||
if (state.insights != null) {
|
||||
item("insights") {
|
||||
InsightsBlock(
|
||||
|
|
@ -166,7 +216,64 @@ internal fun LazyListScope.infoBlocksList(state: MarketsTokenDetailsUM.Informati
|
|||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.loadingInfoBlocks() {
|
||||
internal fun LazyListScope.infoBlocksListV2(state: MarketsTokenDetailsUM.InformationBlocks, relatedNews: RelatedNews) {
|
||||
if (state.metrics != null) {
|
||||
item("metrics") {
|
||||
MetricsBlock(
|
||||
modifier = Modifier.blockPaddings(),
|
||||
state = state.metrics,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (state.insights != null) {
|
||||
item("insights") {
|
||||
InsightsBlock(
|
||||
modifier = Modifier.blockPaddings(),
|
||||
state = state.insights,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item(key = "listedOn") {
|
||||
ListedOnBlock(
|
||||
state = state.listedOn,
|
||||
modifier = Modifier.blockPaddings(),
|
||||
)
|
||||
}
|
||||
|
||||
if (relatedNews.articles.isNotEmpty()) {
|
||||
relatedNews(relatedNews)
|
||||
}
|
||||
|
||||
if (state.securityScore != null) {
|
||||
item("securityScore") {
|
||||
SecurityScoreBlock(
|
||||
modifier = Modifier.blockPaddings(),
|
||||
state = state.securityScore,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (state.links != null) {
|
||||
item("links") {
|
||||
LinksBlock(
|
||||
modifier = Modifier.blockPaddings(),
|
||||
state = state.links,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.loadingInfoBlocks(isRedesignEnabled: Boolean) {
|
||||
if (isRedesignEnabled) {
|
||||
loadingInfoBlocksV2()
|
||||
} else {
|
||||
loadingInfoBlocksV1()
|
||||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.loadingInfoBlocksV1() {
|
||||
item("insights-loading") {
|
||||
InsightsBlockPlaceholder(
|
||||
modifier = Modifier.blockPaddings(),
|
||||
|
|
@ -194,6 +301,28 @@ private fun LazyListScope.loadingInfoBlocks() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.loadingInfoBlocksV2() {
|
||||
item("metrics-loading") {
|
||||
MetricsBlockPlaceholder(modifier = Modifier.blockPaddings())
|
||||
}
|
||||
|
||||
item("insights-loading") {
|
||||
InsightsBlockPlaceholder(modifier = Modifier.blockPaddings())
|
||||
}
|
||||
|
||||
item(key = "listedOn-loading") {
|
||||
ListedOnBlockPlaceholder(modifier = Modifier.blockPaddings())
|
||||
}
|
||||
|
||||
item("securityScore-loading") {
|
||||
SecurityScoreBlockPlaceholder(modifier = Modifier.blockPaddings())
|
||||
}
|
||||
|
||||
item("links-loading") {
|
||||
LinksBlockPlaceholder(modifier = Modifier.blockPaddings())
|
||||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.relatedNews(relatedNews: RelatedNews) {
|
||||
item("related-news") {
|
||||
Column(
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ internal object MarketsTokenDetailsPreview {
|
|||
infoPoint,
|
||||
infoPoint,
|
||||
),
|
||||
metricsV2 = null,
|
||||
),
|
||||
pricePerformance = PricePerformanceUM(
|
||||
h24 = PricePerformanceUM.Value(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
package com.tangem.features.feed.ui.market.detailed.state
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.components.audits.AuditLabelUM
|
||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
||||
@Immutable
|
||||
internal sealed interface ExchangeItemUM {
|
||||
|
||||
val id: String
|
||||
|
||||
data class Content(
|
||||
override val id: String,
|
||||
val title: TextReference,
|
||||
val subTitle: TextReference,
|
||||
val icon: TangemIconUM,
|
||||
val volumeInUsd: TextReference,
|
||||
val auditLabel: AuditLabelUM,
|
||||
) : ExchangeItemUM
|
||||
|
||||
data class Loading(override val id: String) : ExchangeItemUM
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.features.feed.ui.market.detailed.state
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
|
@ -18,6 +19,7 @@ import kotlinx.collections.immutable.toImmutableList
|
|||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Immutable
|
||||
internal sealed interface ExchangesBottomSheetContent : TangemBottomSheetConfigContent {
|
||||
|
||||
/** Title of bottom sheet. Like, app bar. */
|
||||
|
|
@ -39,6 +41,8 @@ internal sealed interface ExchangesBottomSheetContent : TangemBottomSheetConfigC
|
|||
/** Exchange items */
|
||||
val exchangeItems: ImmutableList<TokenItemState>
|
||||
|
||||
val exchangeItemsV2: ImmutableList<ExchangeItemUM>
|
||||
|
||||
/**
|
||||
* Loading state
|
||||
*
|
||||
|
|
@ -49,6 +53,10 @@ internal sealed interface ExchangesBottomSheetContent : TangemBottomSheetConfigC
|
|||
override val exchangeItems: ImmutableList<TokenItemState>
|
||||
get() = List(size = exchangesCount) { index -> TokenItemState.Loading(id = "loading#$index") }
|
||||
.toImmutableList()
|
||||
|
||||
override val exchangeItemsV2: ImmutableList<ExchangeItemUM>
|
||||
get() = List(size = exchangesCount) { index -> ExchangeItemUM.Loading(id = "loading#$index") }
|
||||
.toImmutableList()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -56,8 +64,14 @@ internal sealed interface ExchangesBottomSheetContent : TangemBottomSheetConfigC
|
|||
*
|
||||
* @property exchangeItems exchanges
|
||||
*/
|
||||
data class Content(
|
||||
data class ContentV1(
|
||||
override val exchangeItems: ImmutableList<TokenItemState>,
|
||||
override val exchangeItemsV2: ImmutableList<ExchangeItemUM> = persistentListOf(),
|
||||
) : ExchangesBottomSheetContent
|
||||
|
||||
data class ContentV2(
|
||||
override val exchangeItemsV2: ImmutableList<ExchangeItemUM>,
|
||||
override val exchangeItems: ImmutableList<TokenItemState> = persistentListOf(),
|
||||
) : ExchangesBottomSheetContent
|
||||
|
||||
/** Error state */
|
||||
|
|
@ -65,6 +79,7 @@ internal sealed interface ExchangesBottomSheetContent : TangemBottomSheetConfigC
|
|||
val onRetryClick: () -> Unit,
|
||||
) : ExchangesBottomSheetContent {
|
||||
override val exchangeItems: ImmutableList<TokenItemState> = persistentListOf()
|
||||
override val exchangeItemsV2: ImmutableList<ExchangeItemUM> = persistentListOf()
|
||||
|
||||
@StringRes
|
||||
val message: Int = R.string.markets_loading_error_title
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.features.feed.ui.market.detailed.state
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.pluralReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
|
|
@ -11,6 +12,7 @@ import com.tangem.features.feed.impl.R
|
|||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Immutable
|
||||
internal sealed interface ListedOnUM {
|
||||
|
||||
/** Title */
|
||||
|
|
@ -20,6 +22,7 @@ internal sealed interface ListedOnUM {
|
|||
/** Description */
|
||||
val description: TextReference
|
||||
|
||||
@Immutable
|
||||
/** Empty state. No exchanges found */
|
||||
data object Empty : ListedOnUM {
|
||||
override val description = resourceReference(id = R.string.markets_token_details_empty_exchanges)
|
||||
|
|
@ -31,6 +34,7 @@ internal sealed interface ListedOnUM {
|
|||
* @property onClick lambda be invoked when button is clicked
|
||||
* @property amount amount of exchanges
|
||||
*/
|
||||
@Immutable
|
||||
data class Content(
|
||||
val onClick: () -> Unit,
|
||||
private val amount: Int,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,68 @@
|
|||
package com.tangem.features.feed.ui.market.detailed.state
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
internal data class MetricsUM(
|
||||
val metrics: ImmutableList<InfoPointUM>,
|
||||
)
|
||||
val metricsV2: MetricsV2UM?,
|
||||
)
|
||||
|
||||
@Immutable
|
||||
internal sealed interface InfoPointUMV2 {
|
||||
|
||||
@Immutable
|
||||
data class MarketCap(
|
||||
val capitalizationValue: TextReference,
|
||||
val onInfoClick: () -> Unit,
|
||||
) : InfoPointUMV2
|
||||
|
||||
@Immutable
|
||||
data class TradingVolume(
|
||||
val tradingValue: TextReference,
|
||||
val liquidity: Float,
|
||||
val trendingVolumeLiquidityType: TrendingVolumeLiquidityType,
|
||||
val onInfoClick: () -> Unit,
|
||||
) : InfoPointUMV2
|
||||
|
||||
@Immutable
|
||||
data class MarketPosition(
|
||||
val position: String,
|
||||
val rangeValue: Float,
|
||||
val marketRatingType: MarketRatingType,
|
||||
val onInfoClick: () -> Unit,
|
||||
) : InfoPointUMV2
|
||||
|
||||
@Immutable
|
||||
data class FullyDilutedValuation(
|
||||
val value: TextReference,
|
||||
val onInfoClick: () -> Unit,
|
||||
) : InfoPointUMV2
|
||||
|
||||
@Immutable
|
||||
data class CirculatingSupply(
|
||||
val currentValue: TextReference,
|
||||
val maxValue: TextReference?,
|
||||
val fillValue: Float?,
|
||||
val onInfoClick: () -> Unit,
|
||||
) : InfoPointUMV2
|
||||
}
|
||||
|
||||
internal data class MetricsV2UM(
|
||||
val rows: ImmutableList<Row>,
|
||||
) {
|
||||
|
||||
internal data class Row(
|
||||
val first: InfoPointUMV2,
|
||||
val second: InfoPointUMV2?,
|
||||
)
|
||||
}
|
||||
|
||||
internal enum class TrendingVolumeLiquidityType {
|
||||
HIGH, MEDIUM, LOW,
|
||||
}
|
||||
|
||||
internal enum class MarketRatingType {
|
||||
GOLD, SILVER, BRONZE, OTHER
|
||||
}
|
||||
|
|
@ -15,6 +15,8 @@ import androidx.compose.ui.unit.dp
|
|||
import com.tangem.core.ui.components.UnableToLoadData
|
||||
import com.tangem.core.ui.components.haze.hazeSourceTangem
|
||||
import com.tangem.core.ui.components.pager.PagerIndicator
|
||||
import com.tangem.core.ui.ds.TangemPagerIndicator
|
||||
import com.tangem.core.ui.ds.TangemPagerIndicatorColors
|
||||
import com.tangem.core.ui.extensions.conditionalCompose
|
||||
import com.tangem.core.ui.res.*
|
||||
import com.tangem.features.feed.ui.news.details.components.ArticleDetail
|
||||
|
|
@ -105,12 +107,24 @@ private fun Content(state: NewsDetailsUM, background: Color) {
|
|||
)
|
||||
}
|
||||
if (state.articles.size > 1) {
|
||||
PagerIndicator(
|
||||
pagerState = pagerState,
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.windowInsetsPadding(WindowInsets.navigationBars),
|
||||
)
|
||||
if (isRedesignEnabled) {
|
||||
TangemPagerIndicator(
|
||||
pagerState = pagerState,
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.windowInsetsPadding(WindowInsets.navigationBars),
|
||||
colors = TangemPagerIndicatorColors.copy(
|
||||
overlay = TangemTheme.colors2.tabs.backgroundSecondary.copy(alpha = .1f),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
PagerIndicator(
|
||||
pagerState = pagerState,
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.windowInsetsPadding(WindowInsets.navigationBars),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ internal fun RelatedTokensBlock(
|
|||
onItemClick: ((MarketsListItemUM) -> Unit)?,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val isRedesignEnabled = LocalRedesignEnabled.current
|
||||
val isVisible = remember(relatedTokensUM) {
|
||||
when (relatedTokensUM) {
|
||||
is RelatedTokensUM.Content -> relatedTokensUM.items.isNotEmpty()
|
||||
|
|
@ -40,7 +41,7 @@ internal fun RelatedTokensBlock(
|
|||
|
||||
Column(modifier = modifier) {
|
||||
SpacerH(40.dp)
|
||||
if (LocalRedesignEnabled.current) {
|
||||
if (isRedesignEnabled) {
|
||||
Text(
|
||||
modifier = Modifier.padding(horizontal = 8.dp),
|
||||
text = stringResourceSafe(R.string.news_related_tokens),
|
||||
|
|
@ -58,7 +59,11 @@ internal fun RelatedTokensBlock(
|
|||
|
||||
BlockCard(
|
||||
colors = TangemBlockCardColors.copy(
|
||||
containerColor = TangemTheme.colors.background.action,
|
||||
containerColor = if (isRedesignEnabled) {
|
||||
TangemTheme.colors2.surface.level3
|
||||
} else {
|
||||
TangemTheme.colors.background.action
|
||||
},
|
||||
),
|
||||
) {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue