Updated on 2026-08-14
This commit is contained in:
parent
0680fb9e7e
commit
84198eb103
84 changed files with 601 additions and 7129 deletions
|
|
@ -5,7 +5,6 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
|
|||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.navigation.Route
|
||||
import com.tangem.core.ui.DesignFeatureToggles
|
||||
import com.tangem.core.ui.decompose.ComposableModularBottomSheetContentComponent
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
|
@ -15,7 +14,6 @@ import com.tangem.features.feed.components.earn.DefaultEarnComponent
|
|||
import com.tangem.features.feed.components.feed.DefaultFeedComponent
|
||||
import com.tangem.features.feed.components.feed.DefaultFeedComponent.FeedParams
|
||||
import com.tangem.features.feed.components.market.details.DefaultMarketsTokenDetailsComponent
|
||||
import com.tangem.features.feed.components.market.details.portfolio.api.MarketsPortfolioComponent
|
||||
import com.tangem.features.feed.components.market.details.portfolioblock.PortfolioBlockComponent
|
||||
import com.tangem.features.feed.components.market.list.DefaultMarketsTokenListComponent
|
||||
import com.tangem.features.feed.components.news.details.DefaultNewsDetailsComponent
|
||||
|
|
@ -32,12 +30,10 @@ import javax.inject.Inject
|
|||
@Suppress("LongParameterList")
|
||||
internal class FeedEntryChildFactory @Inject constructor(
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val portfolioComponentFactory: MarketsPortfolioComponent.Factory,
|
||||
private val portfolioBlockComponentFactory: PortfolioBlockComponent.Factory,
|
||||
private val addToPortfolioComponentFactory: AddToPortfolioComponent.Factory,
|
||||
private val manageFundsComponentFactory: ManageFundsComponent.Factory,
|
||||
private val promoBannersBlockComponentFactory: PromoBannersBlockComponent.Factory,
|
||||
private val designFeatureToggles: DesignFeatureToggles,
|
||||
private val forYouComponentFactory: ForYouComponent.Factory,
|
||||
private val tokenSummaryComponentFactory: TokenSummaryComponent.Factory,
|
||||
private val marketingBannerComponentFactory: MarketingBannerComponent.Factory,
|
||||
|
|
@ -100,9 +96,7 @@ internal class FeedEntryChildFactory @Inject constructor(
|
|||
appComponentContext = appComponentContext,
|
||||
params = child.params,
|
||||
analyticsEventHandler = analyticsEventHandler,
|
||||
portfolioComponentFactory = portfolioComponentFactory,
|
||||
portfolioBlockComponentFactory = portfolioBlockComponentFactory,
|
||||
designFeatureToggles = designFeatureToggles,
|
||||
addToPortfolioComponentFactory = addToPortfolioComponentFactory,
|
||||
manageFundsComponentFactory = manageFundsComponentFactory,
|
||||
marketingBannerComponentFactory = marketingBannerComponentFactory,
|
||||
|
|
|
|||
|
|
@ -21,8 +21,6 @@ import com.tangem.core.decompose.context.AppComponentContext
|
|||
import com.tangem.core.decompose.context.childByContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.R
|
||||
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.components.haze.hazeEffectTangem
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
|
|
@ -31,9 +29,6 @@ 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.models.earn.PreselectedEarnType
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioComponent
|
||||
|
|
@ -60,44 +55,31 @@ internal class DefaultEarnComponent(
|
|||
|
||||
@Composable
|
||||
override fun Title(bottomSheetState: State<BottomSheetState>) {
|
||||
val background = LocalMainBottomSheetColor.current.value
|
||||
val state by earnModel.state.collectAsStateWithLifecycle()
|
||||
if (LocalRedesignEnabled.current) {
|
||||
TangemTopBar(
|
||||
title = resourceReference(R.string.earn_title),
|
||||
type = if (LocalIsOpenedInBottomSheet.current) {
|
||||
TangemTopBarType.BottomSheet
|
||||
} else {
|
||||
TangemTopBarType.Default
|
||||
},
|
||||
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)
|
||||
.clip(CircleShape)
|
||||
.hazeEffectTangem { blurRadius = 8.dp }
|
||||
.clickableSingle(
|
||||
onClick = state.onBackClick,
|
||||
enabled = bottomSheetState.value == BottomSheetState.EXPANDED,
|
||||
)
|
||||
.padding(TangemTheme.dimens2.x2),
|
||||
)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
TangemTopAppBar(
|
||||
containerColor = background,
|
||||
title = stringResourceSafe(R.string.earn_title),
|
||||
startButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = R.drawable.ic_back_24,
|
||||
onClicked = state.onBackClick,
|
||||
isEnabled = bottomSheetState.value == BottomSheetState.EXPANDED,
|
||||
),
|
||||
)
|
||||
}
|
||||
TangemTopBar(
|
||||
title = resourceReference(R.string.earn_title),
|
||||
type = if (LocalIsOpenedInBottomSheet.current) {
|
||||
TangemTopBarType.BottomSheet
|
||||
} else {
|
||||
TangemTopBarType.Default
|
||||
},
|
||||
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)
|
||||
.clip(CircleShape)
|
||||
.hazeEffectTangem { blurRadius = 8.dp }
|
||||
.clickableSingle(
|
||||
onClick = state.onBackClick,
|
||||
enabled = bottomSheetState.value == BottomSheetState.EXPANDED,
|
||||
)
|
||||
.padding(TangemTheme.dimens2.x2),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ 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.DesignFeatureToggles
|
||||
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.core.ui.decompose.ComposableModularBottomSheetContentComponent
|
||||
|
|
@ -29,7 +28,6 @@ import com.tangem.domain.markets.TokenMarketParams
|
|||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioComponent
|
||||
import com.tangem.features.commonfeatures.api.managefunds.ManageFundsComponent
|
||||
import com.tangem.features.feed.components.market.details.portfolio.api.MarketsPortfolioComponent
|
||||
import com.tangem.features.feed.components.market.details.portfolioblock.PortfolioBlockComponent
|
||||
import com.tangem.features.feed.components.market.details.portfolioblock.PortfolioBlockParentClickIntents
|
||||
import com.tangem.features.feed.model.market.details.MarketsTokenDetailsModel
|
||||
|
|
@ -46,8 +44,6 @@ import kotlinx.serialization.Serializable
|
|||
internal class DefaultMarketsTokenDetailsComponent(
|
||||
appComponentContext: AppComponentContext,
|
||||
analyticsEventHandler: AnalyticsEventHandler,
|
||||
designFeatureToggles: DesignFeatureToggles,
|
||||
portfolioComponentFactory: MarketsPortfolioComponent.Factory,
|
||||
portfolioBlockComponentFactory: PortfolioBlockComponent.Factory,
|
||||
val params: Params,
|
||||
private val addToPortfolioComponentFactory: AddToPortfolioComponent.Factory,
|
||||
|
|
@ -69,21 +65,8 @@ internal class DefaultMarketsTokenDetailsComponent(
|
|||
params = MarketingBannerComponent.Params.Standalone(requestFlow = model.marketingRequest),
|
||||
)
|
||||
|
||||
private val portfolioComponent: MarketsPortfolioComponent? =
|
||||
if (updatedParams.shouldShowPortfolio && !designFeatureToggles.isRedesignEnabled) {
|
||||
portfolioComponentFactory.create(
|
||||
context = child("my_portfolio"),
|
||||
params = MarketsPortfolioComponent.Params(
|
||||
updatedParams.token,
|
||||
analyticsParams = analyticsParams?.source?.let { MarketsPortfolioComponent.AnalyticsParams(it) },
|
||||
),
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
private val portfolioBlockComponent: PortfolioBlockComponent? =
|
||||
if (updatedParams.shouldShowPortfolio && designFeatureToggles.isRedesignEnabled) {
|
||||
if (updatedParams.shouldShowPortfolio) {
|
||||
portfolioBlockComponentFactory.create(
|
||||
context = child("portfolio_block"),
|
||||
params = PortfolioBlockComponent.Params(token = updatedParams.token),
|
||||
|
|
@ -126,11 +109,9 @@ internal class DefaultMarketsTokenDetailsComponent(
|
|||
when (state) {
|
||||
is TokenNetworksState.NetworksAvailable -> {
|
||||
portfolioBlockComponent?.setTokenNetworks(state.networks)
|
||||
portfolioComponent?.setTokenNetworks(state.networks)
|
||||
}
|
||||
TokenNetworksState.NoNetworksAvailable -> {
|
||||
portfolioBlockComponent?.setNoNetworksAvailable()
|
||||
portfolioComponent?.setNoNetworksAvailable()
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
|
|
@ -180,7 +161,6 @@ internal class DefaultMarketsTokenDetailsComponent(
|
|||
val state by model.state.collectAsStateWithLifecycle()
|
||||
MarketsTokenDetailsTitle(
|
||||
state = state,
|
||||
backgroundColor = LocalMainBottomSheetColor.current.value,
|
||||
isBackButtonEnabled = bottomSheetState.value == BottomSheetState.EXPANDED,
|
||||
onBackClick = { params.onBackClicked() },
|
||||
)
|
||||
|
|
@ -211,11 +191,6 @@ internal class DefaultMarketsTokenDetailsComponent(
|
|||
modifier = modifier,
|
||||
backgroundColor = LocalMainBottomSheetColor.current.value,
|
||||
state = state,
|
||||
portfolioBlock = portfolioComponent?.let { component ->
|
||||
{ blockModifier ->
|
||||
component.Content(blockModifier)
|
||||
}
|
||||
},
|
||||
portfolioFloatingBlock = portfolioBlockComponent?.let { component ->
|
||||
{ blockModifier ->
|
||||
component.Content(blockModifier)
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
package com.tangem.features.feed.components.market.details.portfolio.api
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.domain.markets.TokenMarketInfo
|
||||
import com.tangem.domain.markets.TokenMarketParams
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Deprecated("Will be removed in favor of PortfolioComponent with redesigned UI")
|
||||
@Stable
|
||||
interface MarketsPortfolioComponent : ComposableContentComponent {
|
||||
|
||||
@Serializable
|
||||
data class Params(
|
||||
val token: TokenMarketParams,
|
||||
val analyticsParams: AnalyticsParams?,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class AnalyticsParams(
|
||||
val source: String,
|
||||
)
|
||||
|
||||
fun setTokenNetworks(networks: List<TokenMarketInfo.Network>)
|
||||
|
||||
fun setNoNetworksAvailable()
|
||||
|
||||
interface Factory : ComponentFactory<Params, MarketsPortfolioComponent>
|
||||
}
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
package com.tangem.features.feed.components.market.details.portfolio.impl
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
import com.arkivanov.decompose.extensions.compose.subscribeAsState
|
||||
import com.arkivanov.decompose.router.slot.childSlot
|
||||
import com.arkivanov.decompose.router.slot.dismiss
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.context.childByContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.domain.markets.TokenMarketInfo
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioComponent
|
||||
import com.tangem.features.feed.components.market.details.portfolio.api.MarketsPortfolioComponent
|
||||
import com.tangem.features.feed.components.market.details.portfolio.impl.model.MarketsPortfolioModel
|
||||
import com.tangem.features.feed.components.market.details.portfolio.impl.model.MarketsPortfolioRoute
|
||||
import com.tangem.features.feed.components.market.details.portfolio.impl.ui.MyPortfolio
|
||||
import com.tangem.features.tokenreceive.TokenReceiveComponent
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
@Stable
|
||||
internal class DefaultMarketsPortfolioComponent @AssistedInject constructor(
|
||||
@Assisted context: AppComponentContext,
|
||||
@Assisted private val params: MarketsPortfolioComponent.Params,
|
||||
private val tokenReceiveComponentFactory: TokenReceiveComponent.Factory,
|
||||
private val addToPortfolioComponentFactory: AddToPortfolioComponent.Factory,
|
||||
) : AppComponentContext by context, MarketsPortfolioComponent {
|
||||
|
||||
private val model: MarketsPortfolioModel = getOrCreateModel(params)
|
||||
|
||||
private val bottomSheetSlot = childSlot(
|
||||
source = model.bottomSheetNavigation,
|
||||
serializer = MarketsPortfolioRoute.serializer(),
|
||||
handleBackButton = false,
|
||||
childFactory = ::bottomSheetChild,
|
||||
)
|
||||
|
||||
override fun setTokenNetworks(networks: List<TokenMarketInfo.Network>) {
|
||||
model.setTokenNetworks(networks)
|
||||
}
|
||||
|
||||
override fun setNoNetworksAvailable() {
|
||||
model.setNoNetworksAvailable()
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state by model.state.collectAsStateWithLifecycle()
|
||||
val bottomSheet by bottomSheetSlot.subscribeAsState()
|
||||
|
||||
MyPortfolio(modifier = modifier, state = state)
|
||||
bottomSheet.child?.instance?.BottomSheet()
|
||||
}
|
||||
|
||||
private fun bottomSheetChild(
|
||||
config: MarketsPortfolioRoute,
|
||||
componentContext: ComponentContext,
|
||||
): ComposableBottomSheetComponent = when (config) {
|
||||
MarketsPortfolioRoute.AddToPortfolio -> addToPortfolioComponentFactory.create(
|
||||
context = childByContext(componentContext),
|
||||
params = AddToPortfolioComponent.Params(
|
||||
addToPortfolioManager = model.addToPortfolioManager,
|
||||
),
|
||||
)
|
||||
is MarketsPortfolioRoute.TokenReceive -> tokenReceiveComponentFactory.create(
|
||||
context = childByContext(componentContext),
|
||||
params = TokenReceiveComponent.Params(
|
||||
config = config.config,
|
||||
onDismiss = model.bottomSheetNavigation::dismiss,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : MarketsPortfolioComponent.Factory {
|
||||
override fun create(
|
||||
context: AppComponentContext,
|
||||
params: MarketsPortfolioComponent.Params,
|
||||
): DefaultMarketsPortfolioComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
package com.tangem.features.feed.components.market.details.portfolio.impl.analytics
|
||||
|
||||
import com.tangem.common.ui.markets.action.TokenActionsBSContentUM
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
|
||||
internal class PortfolioAnalyticsEvent(
|
||||
event: String,
|
||||
params: Map<String, String> = emptyMap(),
|
||||
) : AnalyticsEvent(category = "Markets / Chart", event = event, params = params) {
|
||||
|
||||
data class EventBuilder(
|
||||
val tokenSymbol: String,
|
||||
val source: String?,
|
||||
) {
|
||||
|
||||
fun addToPortfolioClicked() = PortfolioAnalyticsEvent(
|
||||
event = "Button - Add To Portfolio",
|
||||
params = buildMap {
|
||||
put("Token", tokenSymbol)
|
||||
if (source != null) put("Source", source)
|
||||
},
|
||||
)
|
||||
|
||||
fun quickActionClick(actionUM: TokenActionsBSContentUM.Action, blockchainName: String) =
|
||||
PortfolioAnalyticsEvent(
|
||||
event = when (actionUM) {
|
||||
TokenActionsBSContentUM.Action.Buy -> "Button - Buy"
|
||||
TokenActionsBSContentUM.Action.Receive -> "Button - Receive"
|
||||
TokenActionsBSContentUM.Action.Exchange -> "Button - Swap"
|
||||
TokenActionsBSContentUM.Action.Stake -> "Button - Stake"
|
||||
TokenActionsBSContentUM.Action.YieldMode -> "Button - Yield Mode"
|
||||
else -> "error"
|
||||
},
|
||||
params = buildMap {
|
||||
put("Token", tokenSymbol)
|
||||
if (source != null) put("Source", source)
|
||||
put("blockchain", blockchainName)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
package com.tangem.features.feed.components.market.details.portfolio.impl.di
|
||||
|
||||
import com.tangem.features.feed.components.market.details.portfolio.api.MarketsPortfolioComponent
|
||||
import com.tangem.features.feed.components.market.details.portfolio.impl.DefaultMarketsPortfolioComponent
|
||||
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 ComponentModule {
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindMarketsPortfolioComponent(
|
||||
factory: DefaultMarketsPortfolioComponent.Factory,
|
||||
): MarketsPortfolioComponent.Factory
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
package com.tangem.features.feed.components.market.details.portfolio.impl.di
|
||||
|
||||
import com.tangem.core.decompose.di.ModelComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.feed.components.market.details.portfolio.impl.model.MarketsPortfolioModel
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.multibindings.ClassKey
|
||||
import dagger.multibindings.IntoMap
|
||||
|
||||
@Module
|
||||
@InstallIn(ModelComponent::class)
|
||||
internal interface ModelModule {
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(MarketsPortfolioModel::class)
|
||||
fun provideMarketsPortfolioModel(model: MarketsPortfolioModel): Model
|
||||
}
|
||||
|
|
@ -1,347 +0,0 @@
|
|||
package com.tangem.features.feed.components.market.details.portfolio.impl.model
|
||||
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.blockchainsdk.compatibility.getTokenIdIfL2Network
|
||||
import com.tangem.common.ui.account.AccountTitleUM
|
||||
import com.tangem.common.ui.account.CryptoPortfolioIconConverter
|
||||
import com.tangem.common.ui.account.toUM
|
||||
import com.tangem.common.ui.markets.action.CryptoCurrencyData
|
||||
import com.tangem.common.ui.markets.action.TokenActionsHandler
|
||||
import com.tangem.core.ui.DesignFeatureToggles
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.domain.account.models.AccountStatusList
|
||||
import com.tangem.domain.account.status.supplier.MultiAccountStatusListSupplier
|
||||
import com.tangem.domain.account.status.usecase.GetCryptoCurrencyActionsUseCaseV2
|
||||
import com.tangem.domain.account.status.usecase.IsAccountsModeEnabledUseCase
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
import com.tangem.domain.markets.TokenMarketInfo
|
||||
import com.tangem.domain.markets.TokenMarketParams
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.account.filterCryptoPortfolio
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.models.wallet.isMultiCurrency
|
||||
import com.tangem.domain.tokens.model.TokenActionsState
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.domain.yield.supply.models.YieldSupplyAvailability
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyGetAvailabilityUseCase
|
||||
import com.tangem.features.feed.components.market.details.portfolio.impl.ui.state.MyPortfolioUM
|
||||
import com.tangem.features.feed.components.market.details.portfolio.impl.ui.state.PortfolioHeader
|
||||
import com.tangem.features.feed.components.market.details.portfolio.impl.ui.state.PortfolioListItem
|
||||
import com.tangem.features.feed.components.market.details.portfolio.impl.ui.state.WalletHeader
|
||||
import com.tangem.utils.extensions.isZero
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Suppress("LongParameterList")
|
||||
internal class MarketsPortfolioDelegate @AssistedInject constructor(
|
||||
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
||||
private val allAccountSupplier: MultiAccountStatusListSupplier,
|
||||
private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCaseV2,
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||
private val yieldSupplyGetAvailabilityUseCase: YieldSupplyGetAvailabilityUseCase,
|
||||
private val designFeatureToggles: DesignFeatureToggles,
|
||||
isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
|
||||
@Assisted private val scope: CoroutineScope,
|
||||
@Assisted private val token: TokenMarketParams,
|
||||
@Assisted private val tokenActionsHandler: TokenActionsHandler,
|
||||
@Assisted private val buttonState: Flow<MyPortfolioUM.Tokens.AddButtonState>,
|
||||
@Assisted private val onAddClick: () -> Unit,
|
||||
) {
|
||||
|
||||
private val currencyRawId: CryptoCurrency.RawID = token.id
|
||||
private var expandedHolder: MutableStateFlow<Set<Pair<UserWalletId, CryptoCurrency.ID>>>? = null
|
||||
|
||||
private val settingsFlow: Flow<SettingsBox> = combine(
|
||||
flow = getSelectedAppCurrencyUseCase.invokeOrDefault(),
|
||||
flow2 = getBalanceHidingSettingsUseCase.isBalanceHidden(),
|
||||
flow3 = isAccountsModeEnabledUseCase(),
|
||||
transform = ::SettingsBox,
|
||||
).shareIn(
|
||||
replay = 1,
|
||||
started = SharingStarted.Eagerly,
|
||||
scope = scope,
|
||||
).distinctUntilChanged()
|
||||
|
||||
private val availableNetworks = MutableSharedFlow<List<TokenMarketInfo.Network>>(
|
||||
replay = 1,
|
||||
onBufferOverflow = BufferOverflow.DROP_OLDEST,
|
||||
)
|
||||
|
||||
fun setTokenNetworks(networks: List<TokenMarketInfo.Network>) {
|
||||
availableNetworks.tryEmit(networks)
|
||||
}
|
||||
|
||||
fun combineData(): Flow<MyPortfolioUM> {
|
||||
return availableNetworks.transformLatest { availableNetworks ->
|
||||
when {
|
||||
availableNetworks.isEmpty() -> emit(MyPortfolioUM.Unavailable)
|
||||
else -> emitAll(onAvailableNetworksFlow().distinctUntilChanged())
|
||||
}
|
||||
}.distinctUntilChanged()
|
||||
}
|
||||
|
||||
private fun onAvailableNetworksFlow(): Flow<MyPortfolioUM> =
|
||||
portfolioWithThisCurrencyFLow().transformLatest { portfolioWithCurrency ->
|
||||
when (portfolioWithCurrency.flattenAddedCurrency.isEmpty()) {
|
||||
false -> emitAll(contentFlow(portfolioWithCurrency).distinctUntilChanged())
|
||||
true -> when (portfolioWithCurrency.hasMultiWallets) {
|
||||
true -> emitAll(addFirstTokenFlow())
|
||||
false -> emit(MyPortfolioUM.UnavailableForWallet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addFirstTokenFlow(): Flow<MyPortfolioUM> = buttonState.map { state ->
|
||||
when (state) {
|
||||
MyPortfolioUM.Tokens.AddButtonState.Loading -> MyPortfolioUM.Loading
|
||||
MyPortfolioUM.Tokens.AddButtonState.Available -> MyPortfolioUM.AddFirstToken(onAddClick = onAddClick)
|
||||
MyPortfolioUM.Tokens.AddButtonState.Unavailable -> MyPortfolioUM.Unavailable
|
||||
}
|
||||
}
|
||||
|
||||
private fun contentFlow(portfoliosWithThisCurrency: PortfoliosWithThisCurrency): Flow<MyPortfolioUM.Content> {
|
||||
fun Portfolio.actionsFoAccountCurrencies(): List<Flow<Pair<CryptoCurrency, TokenActionsState>>> =
|
||||
accountsWithAdded.map { account ->
|
||||
fun CryptoCurrencyStatus.actionsFlow(): Flow<Pair<CryptoCurrency, TokenActionsState>> = flow {
|
||||
val yieldSupplyAvailability = yieldSupplyGetAvailabilityUseCase(this@actionsFlow.currency)
|
||||
.getOrElse { YieldSupplyAvailability.Unavailable }
|
||||
emitAll(
|
||||
getCryptoCurrencyActionsUseCase(
|
||||
accountId = account.accountStatus.account.accountId,
|
||||
currency = this@actionsFlow.currency,
|
||||
yieldSupplyAvailability = yieldSupplyAvailability,
|
||||
).map { actionsState -> actionsState.cryptoCurrencyStatus.currency to actionsState },
|
||||
)
|
||||
}
|
||||
account.addedCurrency.map { it.actionsFlow() }
|
||||
}.flatten()
|
||||
|
||||
val allAddedTokenActions =
|
||||
portfoliosWithThisCurrency.portfolios.map { portfolio -> portfolio.actionsFoAccountCurrencies() }.flatten()
|
||||
|
||||
return combine(
|
||||
flow = combine(allAddedTokenActions) { it.toMap() }.distinctUntilChanged(),
|
||||
flow2 = buttonState.distinctUntilChanged(),
|
||||
flow3 = getExpandedHolder(portfoliosWithThisCurrency),
|
||||
flow4 = settingsFlow.distinctUntilChanged(),
|
||||
transform = { actions, addButtonState, expanded, settings ->
|
||||
buildContentState(
|
||||
portfolio = portfoliosWithThisCurrency,
|
||||
allActions = actions,
|
||||
addButtonState = addButtonState,
|
||||
expanded = expanded,
|
||||
settings = settings,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun getExpandedHolder(
|
||||
portfolio: PortfoliosWithThisCurrency,
|
||||
): StateFlow<Set<Pair<UserWalletId, CryptoCurrency.ID>>> {
|
||||
val expandedHolder = this.expandedHolder
|
||||
if (expandedHolder != null) return expandedHolder
|
||||
val allAddedCurrency = portfolio.flattenAddedCurrency
|
||||
val shouldForceExpand = allAddedCurrency.size == 1 &&
|
||||
allAddedCurrency.first().value.amount?.isZero() == true
|
||||
|
||||
val initValue = when {
|
||||
shouldForceExpand -> {
|
||||
val currency = allAddedCurrency.first()
|
||||
// find userWallet than have this single added token
|
||||
portfolio.portfolios
|
||||
.find { it.accountsWithAdded.any { account -> account.addedCurrency.isNotEmpty() } }
|
||||
?.userWallet
|
||||
?.let { setOf(it.walletId to currency.currency.id) }
|
||||
.orEmpty()
|
||||
}
|
||||
else -> emptySet()
|
||||
}
|
||||
return MutableStateFlow(initValue)
|
||||
.also { this.expandedHolder = it }
|
||||
}
|
||||
|
||||
private fun portfolioWithThisCurrencyFLow(): Flow<PortfoliosWithThisCurrency> =
|
||||
allAccountSupplier().map { list -> list.map { it.addedAccountsFlow() } }.flatMapLatest { flows ->
|
||||
combine(flows) { portfolios ->
|
||||
PortfoliosWithThisCurrency(
|
||||
currencyRawId = currencyRawId,
|
||||
portfolios = portfolios.toList(),
|
||||
)
|
||||
}
|
||||
}.distinctUntilChanged()
|
||||
|
||||
private fun AccountStatusList.addedAccountsFlow(): Flow<Portfolio> =
|
||||
getUserWalletUseCase.invokeFlow(this.userWalletId).mapNotNull { it.getOrNull() }.map { wallet ->
|
||||
Portfolio(
|
||||
userWallet = wallet,
|
||||
accountStatusList = this,
|
||||
accountsWithAdded = this.filterByRawID(),
|
||||
)
|
||||
}.distinctUntilChanged()
|
||||
|
||||
private fun AccountStatusList.filterByRawID(): List<AccountWithAdded> {
|
||||
fun AccountStatus.CryptoPortfolio.filterByRawID(): List<CryptoCurrencyStatus> = tokenList.flattenCurrencies()
|
||||
.filter { status ->
|
||||
val currencyId = status.currency.id.rawCurrencyId ?: return@filter false
|
||||
getTokenIdIfL2Network(currencyId.value) == currencyRawId.value
|
||||
}
|
||||
return accountStatuses.filterCryptoPortfolio().map { accountStatus ->
|
||||
AccountWithAdded(
|
||||
accountStatus = accountStatus,
|
||||
addedCurrency = accountStatus.filterByRawID(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildContentState(
|
||||
portfolio: PortfoliosWithThisCurrency,
|
||||
allActions: Map<CryptoCurrency, TokenActionsState>,
|
||||
addButtonState: MyPortfolioUM.Tokens.AddButtonState,
|
||||
expanded: Set<Pair<UserWalletId, CryptoCurrency.ID>>,
|
||||
settings: SettingsBox,
|
||||
): MyPortfolioUM.Content {
|
||||
val appCurrency = settings.appCurrency
|
||||
val isBalanceHidden = settings.isBalanceHidden
|
||||
val isAccountMode = settings.isAccountMode
|
||||
val uiItems: MutableList<PortfolioListItem> = mutableListOf()
|
||||
|
||||
fun toggleQuickActions(key: Pair<UserWalletId, CryptoCurrency.ID>) = expandedHolder?.update { expanded ->
|
||||
val isExpand = expanded.contains(key)
|
||||
if (isExpand) expanded.minus(key) else expanded.plus(key)
|
||||
}
|
||||
|
||||
val tokenUMConverter = PortfolioTokenUMConverter(
|
||||
appCurrency = appCurrency,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
onTokenItemClick = { },
|
||||
tokenActionsHandler = tokenActionsHandler,
|
||||
isRedesignEnabled = designFeatureToggles.isRedesignEnabled,
|
||||
)
|
||||
|
||||
portfolio.portfolios.forEach { portfolioItem ->
|
||||
if (portfolioItem.flattenAddedCurrency.isEmpty()) return@forEach
|
||||
val userWallet = portfolioItem.userWallet
|
||||
if (isAccountMode) {
|
||||
uiItems.add(portfolioItem.userWallet.toWalletHeader())
|
||||
} else {
|
||||
uiItems.add(portfolioItem.userWallet.toWalletPortfolioHeader())
|
||||
}
|
||||
|
||||
portfolioItem.accountsWithAdded.forEach { accountWithAdded ->
|
||||
if (accountWithAdded.addedCurrency.isEmpty()) return@forEach
|
||||
if (isAccountMode) {
|
||||
val account = accountWithAdded.accountStatus.account
|
||||
uiItems.add(account.toAccountPortfolioHeader())
|
||||
}
|
||||
|
||||
accountWithAdded.addedCurrency.forEach { currencyStatus ->
|
||||
val actions = allActions[currencyStatus.currency]?.states.orEmpty()
|
||||
val value = CryptoCurrencyData(
|
||||
userWallet = userWallet,
|
||||
status = currencyStatus,
|
||||
actions = actions,
|
||||
isAccountMode = isAccountMode,
|
||||
account = accountWithAdded.accountStatus,
|
||||
)
|
||||
val expandedKey = portfolioItem.userWallet.walletId to currencyStatus.currency.id
|
||||
val isExpand = expanded.contains(expandedKey)
|
||||
|
||||
val tokenItem = tokenUMConverter.convertV2(
|
||||
onTokenItemClick = { wallet, status ->
|
||||
toggleQuickActions(wallet.walletId to status.currency.id)
|
||||
},
|
||||
value = value,
|
||||
isQuickActionsShown = isExpand,
|
||||
)
|
||||
uiItems.add(tokenItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return MyPortfolioUM.Content(
|
||||
items = uiItems.toImmutableList(),
|
||||
buttonState = addButtonState,
|
||||
onAddClick = onAddClick,
|
||||
)
|
||||
}
|
||||
|
||||
private fun Account.CryptoPortfolio.toAccountPortfolioHeader(): PortfolioHeader = PortfolioHeader(
|
||||
id = this.accountId.value,
|
||||
state = AccountTitleUM.Account(
|
||||
prefixText = TextReference.EMPTY,
|
||||
name = this.accountName.toUM().value,
|
||||
icon = CryptoPortfolioIconConverter.convert(this.icon),
|
||||
),
|
||||
)
|
||||
|
||||
private fun UserWallet.toWalletPortfolioHeader(): PortfolioHeader = PortfolioHeader(
|
||||
id = this.walletId.stringValue,
|
||||
state = AccountTitleUM.Text(
|
||||
title = stringReference(this.name),
|
||||
),
|
||||
)
|
||||
|
||||
private fun UserWallet.toWalletHeader(): WalletHeader = WalletHeader(
|
||||
id = this.walletId.stringValue,
|
||||
name = stringReference(this.name),
|
||||
)
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(
|
||||
scope: CoroutineScope,
|
||||
token: TokenMarketParams,
|
||||
tokenActionsHandler: TokenActionsHandler,
|
||||
buttonState: Flow<MyPortfolioUM.Tokens.AddButtonState>,
|
||||
onAddClick: () -> Unit,
|
||||
): MarketsPortfolioDelegate
|
||||
}
|
||||
}
|
||||
|
||||
private data class PortfoliosWithThisCurrency(
|
||||
val currencyRawId: CryptoCurrency.RawID,
|
||||
val portfolios: List<Portfolio>,
|
||||
) {
|
||||
|
||||
val hasMultiWallets: Boolean = portfolios.any { it.userWallet.isMultiCurrency }
|
||||
|
||||
val flattenAddedCurrency: List<CryptoCurrencyStatus> =
|
||||
portfolios.map { portfolio -> portfolio.flattenAddedCurrency }.flatten()
|
||||
}
|
||||
|
||||
private data class Portfolio(
|
||||
val userWallet: UserWallet,
|
||||
val accountStatusList: AccountStatusList,
|
||||
val accountsWithAdded: List<AccountWithAdded>,
|
||||
) {
|
||||
val flattenAddedCurrency: List<CryptoCurrencyStatus> =
|
||||
accountsWithAdded.map { it.addedCurrency }.flatten()
|
||||
}
|
||||
|
||||
private data class AccountWithAdded(
|
||||
val addedCurrency: List<CryptoCurrencyStatus>,
|
||||
val accountStatus: AccountStatus.CryptoPortfolio,
|
||||
)
|
||||
|
||||
private data class SettingsBox(
|
||||
val appCurrency: AppCurrency,
|
||||
val isBalanceHidden: Boolean,
|
||||
val isAccountMode: Boolean,
|
||||
)
|
||||
|
|
@ -1,170 +0,0 @@
|
|||
package com.tangem.features.feed.components.market.details.portfolio.impl.model
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import arrow.core.getOrElse
|
||||
import com.arkivanov.decompose.router.slot.SlotNavigation
|
||||
import com.arkivanov.decompose.router.slot.activate
|
||||
import com.arkivanov.decompose.router.slot.dismiss
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.common.ui.markets.action.TokenActionsBSContentUM
|
||||
import com.tangem.common.ui.markets.action.TokenActionsHandler
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.markets.TokenMarketInfo
|
||||
import com.tangem.domain.transaction.usecase.ReceiveAddressesFactory
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager
|
||||
import com.tangem.features.feed.components.market.details.portfolio.api.MarketsPortfolioComponent
|
||||
import com.tangem.features.feed.components.market.details.portfolio.impl.analytics.PortfolioAnalyticsEvent
|
||||
import com.tangem.features.feed.components.market.details.portfolio.impl.ui.state.MyPortfolioUM
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Stable
|
||||
@ModelScoped
|
||||
internal class MarketsPortfolioModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
private val addToPortfolioManagerFactory: AddToPortfolioManager.Factory,
|
||||
private val marketsPortfolioDelegateFactory: MarketsPortfolioDelegate.Factory,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val tokenActionsHandlerFactory: TokenActionsHandler.Factory,
|
||||
private val receiveAddressesFactory: ReceiveAddressesFactory,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val appRouter: AppRouter,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
) : Model() {
|
||||
|
||||
val state: StateFlow<MyPortfolioUM>
|
||||
field = MutableStateFlow<MyPortfolioUM>(value = MyPortfolioUM.Loading)
|
||||
|
||||
private val params = paramsContainer.require<MarketsPortfolioComponent.Params>()
|
||||
|
||||
private val analyticsEventBuilder = PortfolioAnalyticsEvent.EventBuilder(
|
||||
tokenSymbol = params.token.symbol,
|
||||
source = params.analyticsParams?.source,
|
||||
)
|
||||
|
||||
private val currentAppCurrency = createAppCurrencyFlow()
|
||||
private val tokenActionsHandler = createTokenActionsHandler()
|
||||
|
||||
val addToPortfolioManager: AddToPortfolioManager = createAddToPortfolioManager()
|
||||
private val marketsPortfolioDelegate: MarketsPortfolioDelegate = createMarketsPortfolioDelegate()
|
||||
|
||||
val bottomSheetNavigation: SlotNavigation<MarketsPortfolioRoute> = SlotNavigation()
|
||||
|
||||
init {
|
||||
marketsPortfolioDelegate.combineData()
|
||||
.onEach { state.value = it }
|
||||
.flowOn(dispatchers.default)
|
||||
.launchIn(modelScope)
|
||||
addToPortfolioManager.onDismiss.receiveAsFlow()
|
||||
.onEach { bottomSheetNavigation.dismiss() }
|
||||
.launchIn(modelScope)
|
||||
addToPortfolioManager.onSuccessAdded.receiveAsFlow()
|
||||
.onEach { bottomSheetNavigation.dismiss() }
|
||||
.launchIn(modelScope)
|
||||
addToPortfolioManager.onAddedTokenClick.receiveAsFlow()
|
||||
.onEach { result ->
|
||||
bottomSheetNavigation.dismiss()
|
||||
appRouter.push(
|
||||
AppRoute.CurrencyDetails(
|
||||
userWalletId = result.wallet.walletId,
|
||||
currency = result.addedCurrency.currency,
|
||||
),
|
||||
)
|
||||
}
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
fun setTokenNetworks(networks: List<TokenMarketInfo.Network>) {
|
||||
addToPortfolioManager.setTokenNetworks(networks)
|
||||
marketsPortfolioDelegate.setTokenNetworks(networks)
|
||||
}
|
||||
|
||||
fun setNoNetworksAvailable() {
|
||||
addToPortfolioManager.setTokenNetworks(emptyList())
|
||||
marketsPortfolioDelegate.setTokenNetworks(emptyList())
|
||||
}
|
||||
|
||||
private fun createAddToPortfolioManager(): AddToPortfolioManager {
|
||||
return addToPortfolioManagerFactory.create(
|
||||
scope = modelScope,
|
||||
settings = AddToPortfolioManager.Settings.DefaultMarket,
|
||||
analyticsParams = AddToPortfolioManager.AnalyticsParams(params.analyticsParams?.source),
|
||||
).also { addToPortfolioManager -> addToPortfolioManager.setTokenParams(params.token) }
|
||||
}
|
||||
|
||||
private fun createMarketsPortfolioDelegate(): MarketsPortfolioDelegate {
|
||||
return marketsPortfolioDelegateFactory.create(
|
||||
scope = modelScope,
|
||||
token = params.token,
|
||||
tokenActionsHandler = tokenActionsHandler,
|
||||
buttonState = addToPortfolioManager.state.map { state ->
|
||||
when (state) {
|
||||
is AddToPortfolioManager.State.Ready -> {
|
||||
if (state.isAvailableToAdd) {
|
||||
MyPortfolioUM.Tokens.AddButtonState.Available
|
||||
} else {
|
||||
MyPortfolioUM.Tokens.AddButtonState.Unavailable
|
||||
}
|
||||
}
|
||||
AddToPortfolioManager.State.Loading -> MyPortfolioUM.Tokens.AddButtonState.Loading
|
||||
}
|
||||
},
|
||||
onAddClick = {
|
||||
analyticsEventHandler.send(analyticsEventBuilder.addToPortfolioClicked())
|
||||
bottomSheetNavigation.activate(MarketsPortfolioRoute.AddToPortfolio)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun createAppCurrencyFlow(): StateFlow<AppCurrency> {
|
||||
return getSelectedAppCurrencyUseCase()
|
||||
.map { maybeAppCurrency ->
|
||||
maybeAppCurrency.getOrElse { AppCurrency.Default }
|
||||
}
|
||||
.stateIn(
|
||||
scope = modelScope,
|
||||
started = SharingStarted.Eagerly,
|
||||
initialValue = AppCurrency.Default,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createTokenActionsHandler(): TokenActionsHandler {
|
||||
return tokenActionsHandlerFactory.create(
|
||||
currentAppCurrency = Provider { currentAppCurrency.value },
|
||||
onHandleQuickAction = { handledAction, _ ->
|
||||
val currency = handledAction.cryptoCurrencyData.status.currency
|
||||
analyticsEventHandler.send(
|
||||
analyticsEventBuilder.quickActionClick(
|
||||
actionUM = handledAction.action,
|
||||
blockchainName = currency.network.name,
|
||||
),
|
||||
)
|
||||
configureReceiveAddresses(handledAction)
|
||||
},
|
||||
coroutineScope = modelScope,
|
||||
)
|
||||
}
|
||||
|
||||
private fun configureReceiveAddresses(quickAction: TokenActionsHandler.HandledQuickAction) {
|
||||
val isNewReceive = quickAction.action == TokenActionsBSContentUM.Action.Receive
|
||||
if (isNewReceive) {
|
||||
modelScope.launch {
|
||||
val tokenConfig = receiveAddressesFactory.create(
|
||||
status = quickAction.cryptoCurrencyData.status,
|
||||
userWalletId = quickAction.cryptoCurrencyData.userWallet.walletId,
|
||||
) ?: return@launch
|
||||
bottomSheetNavigation.activate(MarketsPortfolioRoute.TokenReceive(tokenConfig))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package com.tangem.features.feed.components.market.details.portfolio.impl.model
|
||||
|
||||
import com.tangem.core.decompose.navigation.Route
|
||||
import com.tangem.domain.models.TokenReceiveConfig
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
sealed interface MarketsPortfolioRoute : Route {
|
||||
|
||||
@Serializable
|
||||
data object AddToPortfolio : MarketsPortfolioRoute
|
||||
|
||||
@Serializable
|
||||
data class TokenReceive(
|
||||
val config: TokenReceiveConfig,
|
||||
) : MarketsPortfolioRoute
|
||||
}
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
package com.tangem.features.feed.components.market.details.portfolio.impl.model
|
||||
|
||||
import com.tangem.common.ui.markets.action.CryptoCurrencyData
|
||||
import com.tangem.common.ui.markets.action.QuickActionsConverter.quickActions
|
||||
import com.tangem.common.ui.markets.action.TokenActionsHandler
|
||||
import com.tangem.common.ui.tokens.TokenItemStateConverter
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.features.feed.components.market.details.portfolio.impl.ui.state.PortfolioTokenUM
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
/**
|
||||
* Converter from [UserWallet] and [CryptoCurrencyStatus] to [PortfolioTokenUM]
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class PortfolioTokenUMConverter(
|
||||
private val appCurrency: AppCurrency,
|
||||
private val isBalanceHidden: Boolean,
|
||||
private val onTokenItemClick: (CryptoCurrencyStatus) -> Unit,
|
||||
private val tokenActionsHandler: TokenActionsHandler,
|
||||
private val isRedesignEnabled: Boolean,
|
||||
) : Converter<CryptoCurrencyData, PortfolioTokenUM> {
|
||||
|
||||
fun convertV2(
|
||||
value: CryptoCurrencyData,
|
||||
isQuickActionsShown: Boolean,
|
||||
onTokenItemClick: (UserWallet, CryptoCurrencyStatus) -> Unit,
|
||||
): PortfolioTokenUM {
|
||||
val tokenItemStateConverter = TokenItemStateConverter(
|
||||
appCurrency = appCurrency,
|
||||
onItemClick = { _, status -> onTokenItemClick(value.userWallet, status) },
|
||||
)
|
||||
return PortfolioTokenUM(
|
||||
tokenItemState = tokenItemStateConverter.convert(value = value.status),
|
||||
walletId = value.userWallet.walletId,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
isQuickActionsShown = isQuickActionsShown,
|
||||
quickActions = quickActions(
|
||||
cryptoData = value,
|
||||
tokenActionsHandler = tokenActionsHandler,
|
||||
isRedesignEnabled = isRedesignEnabled,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
override fun convert(value: CryptoCurrencyData): PortfolioTokenUM {
|
||||
val tokenItemStateConverter = TokenItemStateConverter(
|
||||
appCurrency = appCurrency,
|
||||
titleStateProvider = { TokenItemState.TitleState.Content(text = stringReference(value.userWallet.name)) },
|
||||
subtitleStateProvider = {
|
||||
TokenItemState.SubtitleState.TextContent(value = stringReference(value.status.currency.name))
|
||||
},
|
||||
onItemClick = { _, status -> onTokenItemClick(status) },
|
||||
)
|
||||
|
||||
return PortfolioTokenUM(
|
||||
tokenItemState = tokenItemStateConverter.convert(value = value.status),
|
||||
walletId = value.userWallet.walletId,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
isQuickActionsShown = false,
|
||||
quickActions = quickActions(
|
||||
cryptoData = value,
|
||||
tokenActionsHandler = tokenActionsHandler,
|
||||
isRedesignEnabled = isRedesignEnabled,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,333 +0,0 @@
|
|||
package com.tangem.features.feed.components.market.details.portfolio.impl.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastForEachIndexed
|
||||
import com.tangem.common.ui.account.AccountTitle
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.SmallButtonShimmer
|
||||
import com.tangem.core.ui.components.TextShimmer
|
||||
import com.tangem.core.ui.components.block.information.InformationBlock
|
||||
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.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.feed.components.market.details.portfolio.impl.ui.preview.PreviewMyPortfolioUMProvider
|
||||
import com.tangem.features.feed.components.market.details.portfolio.impl.ui.state.*
|
||||
import com.tangem.features.feed.impl.R
|
||||
|
||||
@Composable
|
||||
internal fun MyPortfolio(state: MyPortfolioUM, modifier: Modifier = Modifier) {
|
||||
if (state is MyPortfolioUM.Content) {
|
||||
val contentModifier = Modifier.padding(
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
top = TangemTheme.dimens.spacing20,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
bottom = TangemTheme.dimens.spacing32,
|
||||
)
|
||||
PortfolioList(state, contentModifier)
|
||||
return
|
||||
}
|
||||
InformationBlock(
|
||||
modifier = modifier,
|
||||
contentHorizontalPadding = TangemTheme.dimens.spacing0,
|
||||
title = { Title() },
|
||||
action = {
|
||||
if (state !is MyPortfolioUM.Tokens) return@InformationBlock
|
||||
|
||||
AddButton(state = state.buttonState, onClick = state.onAddClick)
|
||||
},
|
||||
) {
|
||||
val contentModifier = Modifier.padding(
|
||||
start = TangemTheme.dimens.spacing12,
|
||||
end = TangemTheme.dimens.spacing12,
|
||||
bottom = TangemTheme.dimens.spacing12,
|
||||
)
|
||||
|
||||
when (state) {
|
||||
is MyPortfolioUM.Tokens -> TokenList(state = state)
|
||||
is MyPortfolioUM.AddFirstToken -> AddFirstTokenContent(state = state, modifier = contentModifier)
|
||||
MyPortfolioUM.Loading -> LoadingPlaceholder(modifier = contentModifier)
|
||||
MyPortfolioUM.Unavailable -> UnavailableAsset(modifier = contentModifier)
|
||||
MyPortfolioUM.UnavailableForWallet -> UnavailableAssetForWallet(modifier = contentModifier)
|
||||
is MyPortfolioUM.Content -> PortfolioList(state = state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Title() {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.markets_common_my_portfolio),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AddButton(state: MyPortfolioUM.Tokens.AddButtonState, onClick: () -> Unit) {
|
||||
when (state) {
|
||||
MyPortfolioUM.Tokens.AddButtonState.Loading -> {
|
||||
Box {
|
||||
SmallButtonShimmer(
|
||||
modifier = Modifier.width(width = TangemTheme.dimens.size63),
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.radius3),
|
||||
withIcon = true,
|
||||
)
|
||||
|
||||
Box(
|
||||
Modifier
|
||||
.matchParentSize()
|
||||
.background(TangemTheme.colors.background.action),
|
||||
)
|
||||
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.align(Alignment.Center)
|
||||
.size(width = TangemTheme.dimens.size63, height = TangemTheme.dimens.size18),
|
||||
radius = TangemTheme.dimens.radius3,
|
||||
)
|
||||
}
|
||||
}
|
||||
MyPortfolioUM.Tokens.AddButtonState.Available,
|
||||
MyPortfolioUM.Tokens.AddButtonState.Unavailable,
|
||||
-> {
|
||||
SecondarySmallButton(
|
||||
config = SmallButtonConfig(
|
||||
text = resourceReference(R.string.markets_add_token),
|
||||
icon = TangemButtonIconPosition.Start(R.drawable.ic_plus_24),
|
||||
onClick = onClick,
|
||||
isEnabled = state == MyPortfolioUM.Tokens.AddButtonState.Available,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TokenList(state: MyPortfolioUM.Tokens, modifier: Modifier = Modifier) {
|
||||
Column(modifier) {
|
||||
state.tokens.fastForEachIndexed { index, token ->
|
||||
key(token.tokenItemState.id) {
|
||||
PortfolioItem(
|
||||
modifier = Modifier.background(color = TangemTheme.colors.background.action),
|
||||
state = token,
|
||||
lastInList = index == state.tokens.size - 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PortfolioList(state: MyPortfolioUM.Content, modifier: Modifier = Modifier) {
|
||||
Column(modifier) {
|
||||
key("PortfolioListHeader") {
|
||||
Row(
|
||||
modifier = Modifier.padding(horizontal = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = stringResourceSafe(R.string.markets_common_my_portfolio),
|
||||
style = TangemTheme.typography.h3,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
AddButton(state = state.buttonState, onClick = state.onAddClick)
|
||||
}
|
||||
}
|
||||
|
||||
state.items.fastForEachIndexed { index, item ->
|
||||
val previousItem = state.items.getOrNull(index.dec())
|
||||
val nextItem = state.items.getOrNull(index.inc())
|
||||
val itemModifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.getOffsetModifier(item, previousItem)
|
||||
.getBackgroundModifier(item, previousItem, nextItem)
|
||||
|
||||
key(item.id) {
|
||||
PortfolioItem(
|
||||
item = item,
|
||||
modifier = itemModifier,
|
||||
lastInList = index == state.items.size - 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Modifier.getBackgroundModifier(
|
||||
item: PortfolioListItem,
|
||||
previousItem: PortfolioListItem?,
|
||||
nextItem: PortfolioListItem?,
|
||||
): Modifier {
|
||||
val color = TangemTheme.colors.background.action
|
||||
val radius = 14.dp
|
||||
val topRound = RoundedCornerShape(topStart = radius, topEnd = radius)
|
||||
val bottomRound = RoundedCornerShape(bottomStart = radius, bottomEnd = radius)
|
||||
val allRound = RoundedCornerShape(size = radius)
|
||||
val backgroundModifier = when (item) {
|
||||
is WalletHeader -> this
|
||||
is PortfolioHeader -> this
|
||||
.clip(topRound)
|
||||
.background(color = color)
|
||||
is PortfolioTokenUM -> when {
|
||||
previousItem is PortfolioHeader && nextItem !is PortfolioTokenUM -> this
|
||||
.clip(bottomRound)
|
||||
.background(color = color)
|
||||
previousItem is WalletHeader && nextItem !is PortfolioTokenUM -> this
|
||||
.clip(allRound)
|
||||
.background(color = color)
|
||||
previousItem is PortfolioTokenUM && nextItem !is PortfolioTokenUM -> this
|
||||
.clip(bottomRound)
|
||||
.background(color = color)
|
||||
else -> this.background(color = color)
|
||||
}
|
||||
}
|
||||
return backgroundModifier
|
||||
}
|
||||
|
||||
private fun Modifier.getOffsetModifier(item: PortfolioListItem, previousItem: PortfolioListItem?): Modifier = when {
|
||||
item is WalletHeader -> this.padding(top = 20.dp, start = 4.dp, end = 4.dp)
|
||||
item is PortfolioHeader && previousItem is PortfolioTokenUM -> this.padding(top = 12.dp)
|
||||
item is PortfolioHeader && previousItem == null -> this.padding(top = 20.dp)
|
||||
previousItem is WalletHeader -> this.padding(top = 12.dp)
|
||||
previousItem is PortfolioTokenUM -> this
|
||||
else -> this
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PortfolioItem(item: PortfolioListItem, lastInList: Boolean, modifier: Modifier = Modifier) {
|
||||
when (item) {
|
||||
is PortfolioHeader -> AccountTitle(
|
||||
modifier = modifier.padding(
|
||||
start = 12.dp,
|
||||
top = 12.dp,
|
||||
bottom = 8.dp,
|
||||
),
|
||||
accountTitleUM = item.state,
|
||||
textStyle = TangemTheme.typography.caption1,
|
||||
textColor = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
is PortfolioTokenUM -> PortfolioItem(
|
||||
state = item,
|
||||
modifier = modifier,
|
||||
lastInList = lastInList,
|
||||
)
|
||||
is WalletHeader -> Text(
|
||||
modifier = modifier,
|
||||
text = item.name.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UnavailableAsset(modifier: Modifier = Modifier) {
|
||||
UnavailableContent(
|
||||
textId = R.string.markets_add_to_my_portfolio_unavailable_description,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UnavailableAssetForWallet(modifier: Modifier = Modifier) {
|
||||
UnavailableContent(
|
||||
textId = R.string.markets_add_to_my_portfolio_unavailable_for_wallet_description,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UnavailableContent(@StringRes textId: Int, modifier: Modifier = Modifier) {
|
||||
Text(
|
||||
modifier = modifier,
|
||||
text = stringResourceSafe(textId),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AddFirstTokenContent(state: MyPortfolioUM.AddFirstToken, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.markets_add_to_my_portfolio_description),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
|
||||
PrimaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResourceSafe(R.string.common_add_to_portfolio),
|
||||
onClick = state.onAddClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LoadingPlaceholder(modifier: Modifier = Modifier) {
|
||||
Column(modifier = modifier) {
|
||||
TextShimmer(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
style = TangemTheme.typography.body2,
|
||||
textSizeHeight = true,
|
||||
)
|
||||
|
||||
TextShimmer(
|
||||
modifier = Modifier.fillMaxWidth(fraction = 0.7f),
|
||||
style = TangemTheme.typography.body2,
|
||||
textSizeHeight = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun Preview(@PreviewParameter(PreviewMyPortfolioUMProvider::class) state: MyPortfolioUM) {
|
||||
TangemThemePreview {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(TangemTheme.colors.background.tertiary),
|
||||
) {
|
||||
MyPortfolio(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun PreviewRtl(@PreviewParameter(PreviewMyPortfolioUMProvider::class) state: MyPortfolioUM) {
|
||||
TangemThemePreview(rtl = true) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(TangemTheme.colors.background.tertiary)
|
||||
.padding(TangemTheme.dimens.spacing8),
|
||||
) {
|
||||
MyPortfolio(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,156 +0,0 @@
|
|||
package com.tangem.features.feed.components.market.details.portfolio.impl.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import com.tangem.core.ui.components.icons.IconTint
|
||||
import com.tangem.core.ui.components.token.TokenItem
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.haptic.TangemHapticEffect
|
||||
import com.tangem.core.ui.res.LocalHapticManager
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.MarketTokenDetailsBottomSheetTestTags
|
||||
import com.tangem.features.feed.components.market.details.portfolio.impl.ui.preview.PreviewMyPortfolioUMProvider
|
||||
import com.tangem.features.feed.components.market.details.portfolio.impl.ui.state.PortfolioTokenUM
|
||||
import com.tangem.features.feed.impl.R
|
||||
import com.tangem.utils.StringsSigns.DASH_SIGN
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState.FiatAmountState as TokenFiatAmountState
|
||||
|
||||
@Composable
|
||||
internal fun PortfolioItem(state: PortfolioTokenUM, lastInList: Boolean, modifier: Modifier = Modifier) {
|
||||
Column(modifier) {
|
||||
val hapticManager = LocalHapticManager.current
|
||||
val tokenItemState = remember(state.tokenItemState) {
|
||||
when (state.tokenItemState) {
|
||||
is TokenItemState.Content -> state.tokenItemState.copy(
|
||||
onItemClick = { tokenItemState ->
|
||||
val onClick = state.tokenItemState.onItemClick
|
||||
if (onClick != null) {
|
||||
hapticManager.perform(TangemHapticEffect.View.ContextClick)
|
||||
onClick.invoke(tokenItemState)
|
||||
}
|
||||
},
|
||||
)
|
||||
else -> state.tokenItemState
|
||||
}
|
||||
}
|
||||
TokenItem(
|
||||
state = tokenItemState,
|
||||
isBalanceHidden = state.isBalanceHidden,
|
||||
itemPaddingValues = PaddingValues(
|
||||
start = TangemTheme.dimens.spacing10,
|
||||
end = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
modifier = Modifier.testTag(MarketTokenDetailsBottomSheetTestTags.PORTFOLIO_TOKEN_ITEM),
|
||||
)
|
||||
|
||||
PortfolioQuickActions(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
bottom = if (lastInList) {
|
||||
TangemTheme.dimens.spacing12
|
||||
} else {
|
||||
TangemTheme.dimens.spacing24
|
||||
},
|
||||
),
|
||||
actions = state.quickActions.actions,
|
||||
isVisible = state.isQuickActionsShown,
|
||||
onActionClick = state.quickActions.onQuickActionClick,
|
||||
onActionLongClick = state.quickActions.onQuickActionLongClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun Preview(@PreviewParameter(PortfolioTokenUMProvider::class) tokenUM: PortfolioTokenUM) {
|
||||
TangemThemePreview {
|
||||
var isQuickActionsShown by remember { mutableStateOf(value = false) }
|
||||
|
||||
val onItemClick = {
|
||||
isQuickActionsShown = isQuickActionsShown.not()
|
||||
}
|
||||
|
||||
PortfolioItem(
|
||||
modifier = Modifier.background(color = TangemTheme.colors.background.action),
|
||||
state = tokenUM.copy(
|
||||
tokenItemState = when (tokenUM.tokenItemState) {
|
||||
is TokenItemState.Content -> tokenUM.tokenItemState.copy(onItemClick = { onItemClick() })
|
||||
is TokenItemState.Unreachable -> tokenUM.tokenItemState.copy(onItemClick = { onItemClick() })
|
||||
else -> tokenUM.tokenItemState
|
||||
},
|
||||
isQuickActionsShown = isQuickActionsShown,
|
||||
),
|
||||
lastInList = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class PortfolioTokenUMProvider : CollectionPreviewParameterProvider<PortfolioTokenUM>(
|
||||
collection = listOf(
|
||||
tokenUM.copy(
|
||||
tokenItemState = (tokenUM.tokenItemState as TokenItemState.Content).copy(
|
||||
fiatAmountState = contentFiatAmount?.copy(
|
||||
icons = persistentListOf(
|
||||
TokenFiatAmountState.Content.IconUM(
|
||||
iconRes = R.drawable.ic_staking_24,
|
||||
tint = IconTint.Accent,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
tokenUM.copy(
|
||||
tokenItemState = tokenUM.tokenItemState.copy(
|
||||
fiatAmountState = contentFiatAmount?.copy(text = DASH_SIGN),
|
||||
subtitle2State = (tokenUM.tokenItemState.subtitle2State as? TokenItemState.Subtitle2State.TextContent)
|
||||
?.copy(text = DASH_SIGN),
|
||||
),
|
||||
),
|
||||
tokenUM.copy(isBalanceHidden = true),
|
||||
tokenUM.copy(
|
||||
tokenItemState = TokenItemState.Unreachable(
|
||||
id = tokenUM.tokenItemState.id,
|
||||
iconState = tokenUM.tokenItemState.iconState,
|
||||
titleState = tokenUM.tokenItemState.titleState,
|
||||
subtitleState = tokenUM.tokenItemState.subtitleState,
|
||||
onItemClick = {},
|
||||
onItemLongClick = {},
|
||||
),
|
||||
),
|
||||
tokenUM.copy(
|
||||
tokenItemState = TokenItemState.NoAddress(
|
||||
id = tokenUM.tokenItemState.id,
|
||||
iconState = tokenUM.tokenItemState.iconState,
|
||||
titleState = tokenUM.tokenItemState.titleState,
|
||||
subtitleState = tokenUM.tokenItemState.subtitleState,
|
||||
onItemLongClick = {},
|
||||
),
|
||||
),
|
||||
tokenUM.copy(
|
||||
tokenItemState = TokenItemState.Loading(
|
||||
id = tokenUM.tokenItemState.id,
|
||||
iconState = tokenUM.tokenItemState.iconState,
|
||||
titleState = tokenUM.tokenItemState.titleState as TokenItemState.TitleState.Content,
|
||||
subtitleState = tokenUM.tokenItemState.subtitleState,
|
||||
),
|
||||
),
|
||||
),
|
||||
) {
|
||||
|
||||
companion object {
|
||||
val tokenUM = PreviewMyPortfolioUMProvider().sampleToken
|
||||
val contentFiatAmount = tokenUM.tokenItemState.fiatAmountState as? TokenFiatAmountState.Content
|
||||
}
|
||||
}
|
||||
|
|
@ -1,263 +0,0 @@
|
|||
package com.tangem.features.feed.components.market.details.portfolio.impl.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.core.Spring
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.drawWithContent
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import com.tangem.common.ui.markets.action.QuickActionUM
|
||||
import com.tangem.core.ui.components.SpacerH4
|
||||
import com.tangem.core.ui.components.icons.badge.drawBadge
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.haptic.TangemHapticEffect
|
||||
import com.tangem.core.ui.res.LocalHapticManager
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.MarketTokenDetailsBottomSheetTestTags
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
@Composable
|
||||
internal fun PortfolioQuickActions(
|
||||
actions: ImmutableList<QuickActionUM>,
|
||||
isVisible: Boolean,
|
||||
onActionClick: (QuickActionUM) -> Unit,
|
||||
onActionLongClick: (QuickActionUM) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (actions.isEmpty()) return
|
||||
|
||||
AnimatedVisibility(
|
||||
modifier = modifier,
|
||||
visible = isVisible,
|
||||
enter = expandVertically(expandFrom = Alignment.Top),
|
||||
exit = shrinkVertically(shrinkTowards = Alignment.Top),
|
||||
) {
|
||||
Column {
|
||||
actions.fastForEach { action ->
|
||||
LineSeparator()
|
||||
QuickActionItem(
|
||||
state = action,
|
||||
onClick = { onActionClick(action) },
|
||||
onLongClick = { onActionLongClick(action) }.takeIf { action.isLongClickAvailable },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AnimatedVisibilityScope.LineSeparator(modifier: Modifier = Modifier) {
|
||||
val lineColor = TangemTheme.colors.stroke.primary
|
||||
val strokeWidth = TangemTheme.dimens.size1
|
||||
val isLtr = LocalLayoutDirection.current == LayoutDirection.Ltr
|
||||
val startPadding = TangemTheme.dimens.spacing30
|
||||
|
||||
val height = TangemTheme.dimens.size16
|
||||
|
||||
Canvas(
|
||||
modifier = modifier
|
||||
.animateEnterExit(
|
||||
enter = expandVertically(
|
||||
animationSpec = spring(
|
||||
stiffness = Spring.StiffnessLow,
|
||||
),
|
||||
expandFrom = Alignment.Top,
|
||||
) + fadeIn(),
|
||||
exit = shrinkVertically(
|
||||
spring(
|
||||
stiffness = Spring.StiffnessLow,
|
||||
),
|
||||
shrinkTowards = Alignment.Top,
|
||||
) + fadeOut(),
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.height(height),
|
||||
) {
|
||||
val x = if (isLtr) startPadding.toPx() else size.width - startPadding.toPx()
|
||||
|
||||
drawLine(
|
||||
color = lineColor,
|
||||
start = Offset(x, 0f),
|
||||
end = Offset(x, size.height),
|
||||
strokeWidth = strokeWidth.toPx(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
private fun AnimatedVisibilityScope.QuickActionItem(
|
||||
state: QuickActionUM,
|
||||
onClick: () -> Unit,
|
||||
onLongClick: (() -> Unit)?,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val hapticManager = LocalHapticManager.current
|
||||
val onLongClickInternal: (() -> Unit)? = if (onLongClick != null) {
|
||||
{
|
||||
hapticManager.perform(TangemHapticEffect.View.LongPress)
|
||||
onLongClick()
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.combinedClickable(
|
||||
onLongClick = onLongClickInternal,
|
||||
onClick = {
|
||||
hapticManager.perform(TangemHapticEffect.View.SegmentTick)
|
||||
onClick()
|
||||
},
|
||||
)
|
||||
.padding(horizontal = TangemTheme.dimens.spacing14, vertical = TangemTheme.dimens.spacing4)
|
||||
.testTag(MarketTokenDetailsBottomSheetTestTags.PORTFOLIO_QUICK_ACTION_BUTTON),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing18),
|
||||
) {
|
||||
QuickActionIcon(state)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.animateEnterExit(
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut(),
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing2),
|
||||
) {
|
||||
Text(
|
||||
text = state.title.resolveReference(),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
modifier = Modifier.testTag(MarketTokenDetailsBottomSheetTestTags.PORTFOLIO_QUICK_ACTION_BUTTON_TITLE),
|
||||
)
|
||||
Text(
|
||||
text = state.description.resolveReference(),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AnimatedVisibilityScope.QuickActionIcon(state: QuickActionUM) {
|
||||
val containerColor = TangemTheme.colors.background.action
|
||||
Box(
|
||||
Modifier
|
||||
.animateEnterExit(
|
||||
enter = scaleIn(),
|
||||
exit = scaleOut(),
|
||||
)
|
||||
.background(
|
||||
color = TangemTheme.colors.button.secondary,
|
||||
shape = CircleShape,
|
||||
)
|
||||
.size(TangemTheme.dimens.size32)
|
||||
.semantics {
|
||||
contentDescription = if (state is QuickActionUM.V1.Exchange && state.shouldShowBadge) {
|
||||
"Badge shown"
|
||||
} else {
|
||||
"Badge hidden"
|
||||
}
|
||||
}
|
||||
.drawWithContent {
|
||||
drawContent()
|
||||
if (state is QuickActionUM.V1.Exchange && state.shouldShowBadge) {
|
||||
drawBadge(containerColor = containerColor, offset = 4.dp)
|
||||
}
|
||||
},
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.requiredSize(TangemTheme.dimens.size16)
|
||||
.testTag(MarketTokenDetailsBottomSheetTestTags.PORTFOLIO_QUICK_ACTION_BUTTON_ICON),
|
||||
imageVector = ImageVector.vectorResource(id = state.icon),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.button.primary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun Preview() {
|
||||
TangemThemePreview {
|
||||
var isVisible by remember { mutableStateOf(true) }
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(680.dp),
|
||||
) {
|
||||
Button(
|
||||
onClick = { isVisible = !isVisible },
|
||||
modifier = Modifier.padding(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
Text(text = "Toggle")
|
||||
}
|
||||
SpacerH4()
|
||||
Box(
|
||||
modifier = Modifier.background(color = TangemTheme.colors.background.action),
|
||||
) {
|
||||
PortfolioQuickActions(
|
||||
actions = persistentListOf(
|
||||
QuickActionUM.V1.Buy,
|
||||
QuickActionUM.V1.Exchange(shouldShowBadge = true),
|
||||
QuickActionUM.V1.Receive,
|
||||
),
|
||||
isVisible = isVisible,
|
||||
onActionClick = {},
|
||||
onActionLongClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun PreviewRtl() {
|
||||
TangemThemePreview(rtl = true) {
|
||||
Box(modifier = Modifier.background(color = TangemTheme.colors.background.action)) {
|
||||
PortfolioQuickActions(
|
||||
actions = persistentListOf(
|
||||
QuickActionUM.V1.Buy,
|
||||
QuickActionUM.V1.Exchange(shouldShowBadge = true),
|
||||
QuickActionUM.V1.Receive,
|
||||
),
|
||||
isVisible = true,
|
||||
onActionClick = {},
|
||||
onActionLongClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,142 +0,0 @@
|
|||
package com.tangem.features.feed.components.market.details.portfolio.impl.ui.preview
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import com.tangem.common.ui.account.AccountIconPreviewData
|
||||
import com.tangem.common.ui.account.AccountTitleUM
|
||||
import com.tangem.common.ui.markets.action.QuickActionUM
|
||||
import com.tangem.common.ui.markets.action.QuickActions
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState.FiatAmountState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.features.feed.components.market.details.portfolio.impl.ui.state.*
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import java.util.UUID
|
||||
|
||||
@Suppress("PropertyUsedBeforeDeclaration")
|
||||
internal class PreviewMyPortfolioUMProvider : PreviewParameterProvider<MyPortfolioUM> {
|
||||
|
||||
override val values: Sequence<MyPortfolioUM>
|
||||
get() = sequenceOf(
|
||||
MyPortfolioUM.Tokens(
|
||||
tokens = persistentListOf(sampleToken, sampleToken),
|
||||
buttonState = MyPortfolioUM.Tokens.AddButtonState.Available,
|
||||
onAddClick = {},
|
||||
),
|
||||
MyPortfolioUM.Tokens(
|
||||
tokens = persistentListOf(sampleToken, sampleToken.copy(isQuickActionsShown = true)),
|
||||
buttonState = MyPortfolioUM.Tokens.AddButtonState.Unavailable,
|
||||
onAddClick = {},
|
||||
),
|
||||
MyPortfolioUM.Tokens(
|
||||
tokens = persistentListOf(sampleToken.copy(isQuickActionsShown = true), sampleToken),
|
||||
buttonState = MyPortfolioUM.Tokens.AddButtonState.Loading,
|
||||
onAddClick = {},
|
||||
),
|
||||
MyPortfolioUM.AddFirstToken(onAddClick = {}),
|
||||
MyPortfolioUM.Content(
|
||||
items = persistentListOf(
|
||||
walletPortfolioHeader,
|
||||
accountToken,
|
||||
accountToken,
|
||||
),
|
||||
buttonState = MyPortfolioUM.Tokens.AddButtonState.Available,
|
||||
onAddClick = {},
|
||||
),
|
||||
MyPortfolioUM.Content(
|
||||
items = persistentListOf(
|
||||
walletHeader,
|
||||
accountHeader,
|
||||
accountToken,
|
||||
accountToken,
|
||||
),
|
||||
buttonState = MyPortfolioUM.Tokens.AddButtonState.Available,
|
||||
onAddClick = {},
|
||||
),
|
||||
MyPortfolioUM.Content(
|
||||
items = persistentListOf(
|
||||
walletHeader,
|
||||
accountHeader,
|
||||
accountToken.copy(isQuickActionsShown = true),
|
||||
accountToken,
|
||||
),
|
||||
buttonState = MyPortfolioUM.Tokens.AddButtonState.Available,
|
||||
onAddClick = {},
|
||||
),
|
||||
MyPortfolioUM.Loading,
|
||||
MyPortfolioUM.Unavailable,
|
||||
MyPortfolioUM.UnavailableForWallet,
|
||||
)
|
||||
|
||||
val walletHeader
|
||||
get() = WalletHeader(
|
||||
id = UUID.randomUUID().toString(),
|
||||
name = stringReference("Wallet 1"),
|
||||
)
|
||||
|
||||
val walletPortfolioHeader
|
||||
get() = PortfolioHeader(
|
||||
state = AccountTitleUM.Text(title = stringReference("Wallet 1")),
|
||||
id = UUID.randomUUID().toString(),
|
||||
)
|
||||
|
||||
val accountHeader
|
||||
get() = PortfolioHeader(
|
||||
state = AccountTitleUM.Account(
|
||||
icon = AccountIconPreviewData.randomAccountIcon(),
|
||||
name = stringReference("Main Account"),
|
||||
prefixText = TextReference.EMPTY,
|
||||
),
|
||||
id = UUID.randomUUID().toString(),
|
||||
)
|
||||
val coinIconState
|
||||
get() = CurrencyIconState.CoinIcon(
|
||||
url = null,
|
||||
fallbackResId = com.tangem.core.ui.R.drawable.img_polygon_22,
|
||||
isGrayscale = false,
|
||||
shouldShowCustomBadge = false,
|
||||
)
|
||||
val accountToken
|
||||
get() = sampleToken.copy(
|
||||
tokenItemState = TokenItemState.Content(
|
||||
id = UUID.randomUUID().toString(),
|
||||
iconState = coinIconState,
|
||||
titleState = TokenItemState.TitleState.Content(text = stringReference(value = "Polygon")),
|
||||
fiatAmountState = FiatAmountState.Content(text = "321 $"),
|
||||
subtitle2State = TokenItemState.Subtitle2State.TextContent(text = "5,412 MATIC"),
|
||||
subtitleState = TokenItemState.SubtitleState.TextContent(stringReference(value = "Token")),
|
||||
onItemClick = {},
|
||||
onItemLongClick = {},
|
||||
),
|
||||
)
|
||||
|
||||
val sampleToken
|
||||
get() = PortfolioTokenUM(
|
||||
tokenItemState = TokenItemState.Content(
|
||||
id = UUID.randomUUID().toString(),
|
||||
iconState = CurrencyIconState.Locked,
|
||||
titleState = TokenItemState.TitleState.Content(text = stringReference(value = "My wallet")),
|
||||
fiatAmountState = FiatAmountState.Content(text = "486,65 \$"),
|
||||
subtitle2State = TokenItemState.Subtitle2State.TextContent(text = "733,71097 MATIC"),
|
||||
subtitleState = TokenItemState.SubtitleState.TextContent(
|
||||
value = stringReference(value = "XRP Ledger token"),
|
||||
),
|
||||
onItemClick = {},
|
||||
onItemLongClick = {},
|
||||
),
|
||||
isQuickActionsShown = false,
|
||||
quickActions = QuickActions(
|
||||
actions = persistentListOf(
|
||||
QuickActionUM.V1.Buy,
|
||||
QuickActionUM.V1.Exchange(shouldShowBadge = true),
|
||||
QuickActionUM.V1.Receive,
|
||||
),
|
||||
onQuickActionClick = {},
|
||||
onQuickActionLongClick = {},
|
||||
),
|
||||
isBalanceHidden = false,
|
||||
walletId = UserWalletId(""),
|
||||
)
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
package com.tangem.features.feed.components.market.details.portfolio.impl.ui.state
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Immutable
|
||||
internal sealed class MyPortfolioUM {
|
||||
|
||||
data class Tokens(
|
||||
val tokens: ImmutableList<PortfolioTokenUM>,
|
||||
val buttonState: AddButtonState,
|
||||
val onAddClick: () -> Unit,
|
||||
) : MyPortfolioUM() {
|
||||
|
||||
enum class AddButtonState {
|
||||
Loading,
|
||||
Available,
|
||||
Unavailable,
|
||||
}
|
||||
}
|
||||
|
||||
data class Content(
|
||||
val items: ImmutableList<PortfolioListItem>,
|
||||
val buttonState: Tokens.AddButtonState,
|
||||
val onAddClick: () -> Unit,
|
||||
) : MyPortfolioUM()
|
||||
|
||||
data class AddFirstToken(
|
||||
val onAddClick: () -> Unit,
|
||||
) : MyPortfolioUM()
|
||||
|
||||
data object Loading : MyPortfolioUM()
|
||||
|
||||
data object Unavailable : MyPortfolioUM()
|
||||
|
||||
data object UnavailableForWallet : MyPortfolioUM()
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
package com.tangem.features.feed.components.market.details.portfolio.impl.ui.state
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.common.ui.account.AccountTitleUM
|
||||
import com.tangem.common.ui.markets.action.QuickActions
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
||||
@Immutable
|
||||
internal sealed interface PortfolioListItem {
|
||||
val id: String
|
||||
}
|
||||
|
||||
internal data class WalletHeader(
|
||||
override val id: String,
|
||||
val name: TextReference,
|
||||
) : PortfolioListItem
|
||||
|
||||
internal data class PortfolioHeader(
|
||||
override val id: String,
|
||||
val state: AccountTitleUM,
|
||||
) : PortfolioListItem
|
||||
|
||||
internal data class PortfolioTokenUM(
|
||||
val tokenItemState: TokenItemState,
|
||||
val walletId: UserWalletId,
|
||||
val isBalanceHidden: Boolean,
|
||||
val isQuickActionsShown: Boolean,
|
||||
val quickActions: QuickActions,
|
||||
) : PortfolioListItem {
|
||||
override val id: String = tokenItemState.id
|
||||
}
|
||||
|
|
@ -27,7 +27,6 @@ import com.tangem.core.ui.decompose.ComposableModularBottomSheetContentComponent
|
|||
import com.tangem.core.ui.extensions.clickableSingle
|
||||
import com.tangem.core.ui.extensions.conditional
|
||||
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
|
||||
|
|
@ -36,7 +35,6 @@ import com.tangem.features.feed.model.market.list.state.MarketsListUM
|
|||
import com.tangem.features.feed.model.market.list.state.SortByTypeUM
|
||||
import com.tangem.features.feed.ui.components.FeedSearchBar
|
||||
import com.tangem.features.feed.ui.market.list.MarketsList
|
||||
import com.tangem.features.feed.ui.market.list.TopBarWithSearch
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
internal class DefaultMarketsTokenListComponent(
|
||||
|
|
@ -55,41 +53,31 @@ internal class DefaultMarketsTokenListComponent(
|
|||
@Composable
|
||||
override fun Title(bottomSheetState: State<BottomSheetState>) {
|
||||
val state by model.state.collectAsStateWithLifecycle()
|
||||
val bsState by bottomSheetState
|
||||
|
||||
if (LocalRedesignEnabled.current) {
|
||||
val bottomSheetColor = LocalMainBottomSheetColor.current.value
|
||||
FeedSearchBar(
|
||||
isSearchBarClickable = bottomSheetState.value == BottomSheetState.EXPANDED,
|
||||
feedListSearchBar = state.feedListSearchBar,
|
||||
modifier = Modifier.conditional(bottomSheetColor.isSpecified) {
|
||||
background(bottomSheetColor.copy(alpha = .95f))
|
||||
},
|
||||
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)
|
||||
.clip(CircleShape)
|
||||
.hazeEffectTangem { blurRadius = 8.dp }
|
||||
.clickableSingle(
|
||||
onClick = clickIntents.onBackClicked,
|
||||
enabled = bottomSheetState.value == BottomSheetState.EXPANDED,
|
||||
)
|
||||
.padding(TangemTheme.dimens2.x2),
|
||||
)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
TopBarWithSearch(
|
||||
onBackClick = clickIntents.onBackClicked,
|
||||
onSearchClick = state.onSearchClicked,
|
||||
marketsSearchBar = state.marketsSearchBar,
|
||||
bottomSheetState = bsState,
|
||||
)
|
||||
}
|
||||
val bottomSheetColor = LocalMainBottomSheetColor.current.value
|
||||
FeedSearchBar(
|
||||
isSearchBarClickable = bottomSheetState.value == BottomSheetState.EXPANDED,
|
||||
feedListSearchBar = state.feedListSearchBar,
|
||||
modifier = Modifier.conditional(bottomSheetColor.isSpecified) {
|
||||
background(bottomSheetColor.copy(alpha = .95f))
|
||||
},
|
||||
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)
|
||||
.clip(CircleShape)
|
||||
.hazeEffectTangem { blurRadius = 8.dp }
|
||||
.clickableSingle(
|
||||
onClick = clickIntents.onBackClicked,
|
||||
enabled = bottomSheetState.value == BottomSheetState.EXPANDED,
|
||||
)
|
||||
.padding(TangemTheme.dimens2.x2),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -17,16 +17,12 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.R
|
||||
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.components.haze.hazeEffectTangem
|
||||
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.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
|
||||
|
|
@ -46,65 +42,47 @@ internal class DefaultNewsDetailsComponent(
|
|||
|
||||
@Composable
|
||||
override fun Title(bottomSheetState: State<BottomSheetState>) {
|
||||
val background = LocalMainBottomSheetColor.current.value
|
||||
val state by newsDetailsModel.state.collectAsStateWithLifecycle()
|
||||
if (LocalRedesignEnabled.current) {
|
||||
TangemTopBar(
|
||||
type = if (LocalIsOpenedInBottomSheet.current) {
|
||||
TangemTopBarType.BottomSheet
|
||||
} else {
|
||||
TangemTopBarType.Default
|
||||
},
|
||||
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)
|
||||
.clip(CircleShape)
|
||||
.hazeEffectTangem { blurRadius = 8.dp }
|
||||
.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)
|
||||
.clip(CircleShape)
|
||||
.hazeEffectTangem { blurRadius = 8.dp }
|
||||
.clickableSingle(
|
||||
onClick = state.onShareClick,
|
||||
enabled = bottomSheetState.value == BottomSheetState.EXPANDED &&
|
||||
state.articlesStateUM is ArticlesStateUM.Content,
|
||||
)
|
||||
.padding(TangemTheme.dimens2.x2_5),
|
||||
)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
TangemTopAppBar(
|
||||
containerColor = background,
|
||||
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,
|
||||
),
|
||||
)
|
||||
}
|
||||
TangemTopBar(
|
||||
type = if (LocalIsOpenedInBottomSheet.current) {
|
||||
TangemTopBarType.BottomSheet
|
||||
} else {
|
||||
TangemTopBarType.Default
|
||||
},
|
||||
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)
|
||||
.clip(CircleShape)
|
||||
.hazeEffectTangem { blurRadius = 8.dp }
|
||||
.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)
|
||||
.clip(CircleShape)
|
||||
.hazeEffectTangem { blurRadius = 8.dp }
|
||||
.clickableSingle(
|
||||
onClick = state.onShareClick,
|
||||
enabled = bottomSheetState.value == BottomSheetState.EXPANDED &&
|
||||
state.articlesStateUM is ArticlesStateUM.Content,
|
||||
)
|
||||
.padding(TangemTheme.dimens2.x2_5),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -18,8 +18,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.R
|
||||
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.components.haze.hazeEffectTangem
|
||||
import com.tangem.core.ui.decompose.ComposableModularBottomSheetContentComponent
|
||||
|
|
@ -27,9 +25,7 @@ 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
|
||||
|
|
@ -48,43 +44,31 @@ internal class DefaultNewsListComponent(
|
|||
override fun Title(bottomSheetState: State<BottomSheetState>) {
|
||||
val background = LocalMainBottomSheetColor.current.value
|
||||
val state by newsListModel.state.collectAsStateWithLifecycle()
|
||||
if (LocalRedesignEnabled.current) {
|
||||
TangemTopBar(
|
||||
modifier = Modifier.background(background.copy(alpha = .95f)),
|
||||
title = resourceReference(R.string.common_news),
|
||||
type = if (LocalIsOpenedInBottomSheet.current) {
|
||||
TangemTopBarType.BottomSheet
|
||||
} else {
|
||||
TangemTopBarType.Default
|
||||
},
|
||||
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)
|
||||
.clip(CircleShape)
|
||||
.hazeEffectTangem { blurRadius = 8.dp }
|
||||
.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,
|
||||
),
|
||||
)
|
||||
}
|
||||
TangemTopBar(
|
||||
modifier = Modifier.background(background.copy(alpha = .95f)),
|
||||
title = resourceReference(R.string.common_news),
|
||||
type = if (LocalIsOpenedInBottomSheet.current) {
|
||||
TangemTopBarType.BottomSheet
|
||||
} else {
|
||||
TangemTopBarType.Default
|
||||
},
|
||||
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)
|
||||
.clip(CircleShape)
|
||||
.hazeEffectTangem { blurRadius = 8.dp }
|
||||
.clickableSingle(
|
||||
onClick = state.onBackClick,
|
||||
enabled = bottomSheetState.value == BottomSheetState.EXPANDED,
|
||||
)
|
||||
.padding(TangemTheme.dimens2.x2),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import com.tangem.core.analytics.models.AnalyticsParam
|
|||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.ui.DesignFeatureToggles
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.utils.DateTimeFormatters
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
|
|
@ -60,7 +59,6 @@ internal class FeedComponentModel @Inject constructor(
|
|||
private val fetchTopEarnTokensUseCase: FetchTopEarnTokensUseCase,
|
||||
private val getTopEarnTokensUseCase: GetTopEarnTokensUseCase,
|
||||
private val appRouter: AppRouter,
|
||||
private val designFeatureToggles: DesignFeatureToggles,
|
||||
private val addToPortfolioManagerFactory: AddToPortfolioManager.Factory,
|
||||
private val forYouFeatureToggles: ForYouFeatureToggles,
|
||||
getTopFiveMarketTokenUseCase: GetTopFiveMarketTokenUseCase,
|
||||
|
|
@ -229,19 +227,11 @@ internal class FeedComponentModel @Inject constructor(
|
|||
currentDate = getCurrentDate(),
|
||||
feedListSearchBar = FeedListSearchBar(
|
||||
placeholderText = resourceReference(
|
||||
id = if (designFeatureToggles.isRedesignEnabled) {
|
||||
R.string.markets_search_title_placeholder
|
||||
} else {
|
||||
R.string.markets_search_header_title
|
||||
},
|
||||
id = R.string.markets_search_title_placeholder,
|
||||
),
|
||||
onBarClick = {
|
||||
analyticsEventHandler.send(FeedAnalyticsEvent.TokenSearchedClicked())
|
||||
if (designFeatureToggles.isRedesignEnabled) {
|
||||
params.feedClickIntents.openSearch(AnalyticsParam.ScreensSources.Markets.value)
|
||||
} else {
|
||||
params.feedClickIntents.onMarketOpenClick(null)
|
||||
}
|
||||
params.feedClickIntents.openSearch(AnalyticsParam.ScreensSources.Markets.value)
|
||||
},
|
||||
),
|
||||
feedListCallbacks = FeedListCallbacks(
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ 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
|
||||
|
|
@ -59,7 +58,6 @@ import com.tangem.features.feed.impl.R
|
|||
import com.tangem.features.feed.model.converter.ShortArticleToArticleConfigUMConverter
|
||||
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.*
|
||||
|
|
@ -92,7 +90,6 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
|||
getUserCountryUseCase: GetUserCountryUseCase,
|
||||
paramsContainer: ParamsContainer,
|
||||
addToPortfolioManagerFactory: AddToPortfolioManager.Factory,
|
||||
private val designFeatureToggles: DesignFeatureToggles,
|
||||
private val getTokenPriceChartUseCase: GetTokenPriceChartUseCase,
|
||||
private val getTokenMarketInfoUseCase: GetTokenMarketInfoUseCase,
|
||||
private val getTokenFullQuotesUseCase: GetTokenFullQuotesUseCase,
|
||||
|
|
@ -165,7 +162,6 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
|||
needApplyFCARestrictions = Provider {
|
||||
userCountry.needApplyFCARestrictions()
|
||||
},
|
||||
isRedesignEnabled = designFeatureToggles.isRedesignEnabled,
|
||||
// ==================
|
||||
)
|
||||
|
||||
|
|
@ -203,7 +199,7 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
|||
|
||||
marketChartLook.copy(
|
||||
type = percentChangeType.toChartType(),
|
||||
isMinMaxLook = designFeatureToggles.isRedesignEnabled,
|
||||
isMinMaxLook = true,
|
||||
xAxisFormatter = MarketsDateTimeFormatters.getChartXFormatterByInterval(PriceChangeInterval.H24),
|
||||
yAxisFormatter = { value ->
|
||||
value.format {
|
||||
|
|
@ -244,7 +240,7 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
|||
val addFundsSheetNavigation = SlotNavigation<AddFundsSlotRoute>()
|
||||
|
||||
private val isAddToPortfolioAvailable: Boolean =
|
||||
params.shouldShowPortfolio && designFeatureToggles.isRedesignEnabled
|
||||
params.shouldShowPortfolio
|
||||
val addToPortfolioManager: AddToPortfolioManager = addToPortfolioManagerFactory.create(
|
||||
scope = modelScope,
|
||||
settings = AddToPortfolioManager.Settings(
|
||||
|
|
@ -846,15 +842,9 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
|||
ExchangesBottomSheetContent.Error(onRetryClick = { onListedOnClick(exchangesCount) })
|
||||
},
|
||||
ifRight = { list ->
|
||||
if (designFeatureToggles.isRedesignEnabled) {
|
||||
ExchangesBottomSheetContent.ContentV2(
|
||||
exchangeItemsV2 = ExchangeItemStateConverterV2.convertList(list).toImmutableList(),
|
||||
)
|
||||
} else {
|
||||
ExchangesBottomSheetContent.ContentV1(
|
||||
exchangeItems = ExchangeItemStateConverter.convertList(list).toImmutableList(),
|
||||
)
|
||||
}
|
||||
ExchangesBottomSheetContent.ContentV2(
|
||||
exchangeItemsV2 = ExchangeItemStateConverterV2.convertList(list).toImmutableList(),
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ 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")
|
||||
|
|
@ -118,11 +117,7 @@ internal class MetricsConverter(
|
|||
},
|
||||
),
|
||||
),
|
||||
metricsV2 = if (isRedesignEnabled) {
|
||||
convertToMetricsV2UM(value)
|
||||
} else {
|
||||
null
|
||||
},
|
||||
metricsV2 = convertToMetricsV2UM(value),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ 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,
|
||||
|
|
@ -48,7 +47,6 @@ internal class TokenMarketInfoConverter(
|
|||
tokenSymbol = value.symbol,
|
||||
appCurrency = appCurrency,
|
||||
onInfoClick = onInfoClick,
|
||||
isRedesignEnabled = isRedesignEnabled,
|
||||
)
|
||||
|
||||
val exchangesAmount = value.exchangesAmount
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ 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.datasource.api.common.response.ApiResponseError
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
|
|
@ -54,7 +53,6 @@ internal class NewsDetailsModel @Inject constructor(
|
|||
private val markArticleAsViewedUseCase: MarkArticleAsViewedUseCase,
|
||||
private val toggleArticleLikedUseCase: ToggleArticleLikedUseCase,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val designFeatureToggles: DesignFeatureToggles,
|
||||
paramsContainer: ParamsContainer,
|
||||
) : Model() {
|
||||
|
||||
|
|
@ -70,7 +68,6 @@ internal class NewsDetailsModel @Inject constructor(
|
|||
dispatchers = dispatchers,
|
||||
observeNewsDetailsUseCase = observeNewsDetailsUseCase,
|
||||
prefetchedIds = params.preselectedArticlesId.toSet(),
|
||||
isRedesignEnabled = designFeatureToggles.isRedesignEnabled,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,13 +19,11 @@ internal class NewsDetailsPaginationManager(
|
|||
currentCategoryIds: Provider<List<Int>>,
|
||||
modelScope: CoroutineScope,
|
||||
prefetchedIds: Set<Int>,
|
||||
isRedesignEnabled: Boolean,
|
||||
) : NewsListBatchFlowManager(
|
||||
getNewsListBatchFlowUseCase = getNewsListBatchFlowUseCase,
|
||||
currentCategoryIds = currentCategoryIds,
|
||||
modelScope = modelScope,
|
||||
dispatchers = dispatchers,
|
||||
isRedesignEnabled = isRedesignEnabled,
|
||||
) {
|
||||
|
||||
private val _cachedPrefetchedIds = MutableStateFlow(prefetchedIds)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
|
|||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.ui.DesignFeatureToggles
|
||||
import com.tangem.core.ui.components.chip.entity.ChipUM
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
import com.tangem.core.ui.event.triggeredEvent
|
||||
|
|
@ -36,7 +35,6 @@ internal class NewsListModel @Inject constructor(
|
|||
private val getNewsCategoriesUseCase: GetNewsCategoriesUseCase,
|
||||
private val getNewsListBatchFlowUseCase: GetNewsListBatchFlowUseCase,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val designFeatureToggles: DesignFeatureToggles,
|
||||
paramsContainer: ParamsContainer,
|
||||
) : Model() {
|
||||
|
||||
|
|
@ -59,7 +57,6 @@ internal class NewsListModel @Inject constructor(
|
|||
},
|
||||
modelScope = modelScope,
|
||||
dispatchers = dispatchers,
|
||||
isRedesignEnabled = designFeatureToggles.isRedesignEnabled,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import kotlinx.coroutines.flow.*
|
|||
import kotlinx.coroutines.launch
|
||||
|
||||
internal open class NewsListBatchFlowManager(
|
||||
private val isRedesignEnabled: Boolean,
|
||||
getNewsListBatchFlowUseCase: GetNewsListBatchFlowUseCase,
|
||||
private val currentCategoryIds: Provider<List<Int>>,
|
||||
protected val modelScope: CoroutineScope,
|
||||
|
|
@ -30,11 +29,7 @@ internal open class NewsListBatchFlowManager(
|
|||
private val actionsFlow = MutableSharedFlow<BatchAction<Int, NewsListConfig, Nothing>>()
|
||||
private val converter by lazy {
|
||||
ShortArticleToArticleConfigUMConverter(
|
||||
isTrending = if (isRedesignEnabled) {
|
||||
null
|
||||
} else {
|
||||
false
|
||||
},
|
||||
isTrending = null,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package com.tangem.features.feed.ui
|
|||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
|
|
@ -22,9 +21,7 @@ import com.tangem.core.ui.components.topFade
|
|||
import com.tangem.core.ui.decompose.ComposableModularBottomSheetContentComponent
|
||||
import com.tangem.core.ui.res.LocalHazeState
|
||||
import com.tangem.core.ui.res.LocalMainBottomSheetColor
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.utils.WindowInsetsZero
|
||||
import com.tangem.features.feed.components.FeedEntryChildFactory
|
||||
import com.tangem.features.feed.ui.utils.FadeConstants.BASE_FADE_LEVEL
|
||||
import com.tangem.features.feed.ui.utils.contentFeedEntryStackAnimation
|
||||
|
|
@ -51,82 +48,11 @@ internal fun EntryContent(
|
|||
isOpenedInBottomSheet: Boolean,
|
||||
) {
|
||||
CompositionLocalProvider(LocalIsOpenedInBottomSheet provides isOpenedInBottomSheet) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
EntryContentV2(
|
||||
bottomSheetState = bottomSheetState,
|
||||
stackState = stackState,
|
||||
onHeaderSizeChange = onHeaderSizeChange,
|
||||
onExpandSheet = onExpandSheet,
|
||||
)
|
||||
} else {
|
||||
EntryContentV1(
|
||||
bottomSheetState = bottomSheetState,
|
||||
stackState = stackState,
|
||||
onHeaderSizeChange = onHeaderSizeChange,
|
||||
onExpandSheet = onExpandSheet,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun EntryContentV1(
|
||||
bottomSheetState: State<BottomSheetState>,
|
||||
stackState: State<ChildStack<FeedEntryChildFactory.Child, ComposableModularBottomSheetContentComponent>>,
|
||||
onHeaderSizeChange: (Dp) -> Unit,
|
||||
onExpandSheet: () -> Unit,
|
||||
) {
|
||||
val density = LocalDensity.current
|
||||
val background = LocalMainBottomSheetColor.current.value
|
||||
val stackAnimation = remember { contentFeedEntryStackAnimation() }
|
||||
val isOpenedInBottomSheet = LocalIsOpenedInBottomSheet.current
|
||||
|
||||
Surface(contentColor = background) {
|
||||
Scaffold(
|
||||
containerColor = background,
|
||||
contentWindowInsets = WindowInsetsZero,
|
||||
topBar = {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.then(
|
||||
if (!isOpenedInBottomSheet) {
|
||||
Modifier.statusBarsPadding()
|
||||
} else {
|
||||
Modifier
|
||||
},
|
||||
)
|
||||
.onGloballyPositioned { coordinates ->
|
||||
if (coordinates.size.height > 0) {
|
||||
with(density) {
|
||||
onHeaderSizeChange(coordinates.size.height.toDp())
|
||||
}
|
||||
}
|
||||
},
|
||||
) {
|
||||
Children(
|
||||
stack = stackState.value,
|
||||
animation = stackAnimation,
|
||||
) { child ->
|
||||
child.instance.Title(bottomSheetState)
|
||||
}
|
||||
CollapsedTitleClickOverlay(
|
||||
bottomSheetState = bottomSheetState,
|
||||
onExpandSheet = onExpandSheet,
|
||||
)
|
||||
}
|
||||
},
|
||||
content = { contentPadding ->
|
||||
Children(
|
||||
stack = stackState.value,
|
||||
animation = stackAnimation,
|
||||
) { child ->
|
||||
child.instance.Content(
|
||||
modifier = Modifier.padding(contentPadding),
|
||||
bottomSheetState = bottomSheetState,
|
||||
contentPadding = PaddingValues(),
|
||||
)
|
||||
}
|
||||
},
|
||||
EntryContentV2(
|
||||
bottomSheetState = bottomSheetState,
|
||||
stackState = stackState,
|
||||
onHeaderSizeChange = onHeaderSizeChange,
|
||||
onExpandSheet = onExpandSheet,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
package com.tangem.features.feed.ui.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
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.runtime.Composable
|
||||
|
|
@ -21,7 +19,6 @@ import com.tangem.core.ui.ds.topbar.TangemTopBar
|
|||
import com.tangem.core.ui.ds.topbar.TangemTopBarType
|
||||
import com.tangem.core.ui.extensions.conditional
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.feed.ui.LocalIsOpenedInBottomSheet
|
||||
import com.tangem.features.feed.ui.feed.state.FeedListSearchBar
|
||||
|
|
@ -34,56 +31,13 @@ internal fun FeedSearchBar(
|
|||
startContent: @Composable (() -> Unit)? = null,
|
||||
endContent: @Composable (() -> Unit)? = null,
|
||||
) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
FeedSearchBarV2(
|
||||
isSearchBarClickable = isSearchBarClickable,
|
||||
feedListSearchBar = feedListSearchBar,
|
||||
modifier = modifier,
|
||||
startContent = startContent,
|
||||
endContent = endContent,
|
||||
)
|
||||
} else {
|
||||
FeedSearchBarV1(
|
||||
isSearchBarClickable = isSearchBarClickable,
|
||||
feedListSearchBar = feedListSearchBar,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FeedSearchBarV1(
|
||||
isSearchBarClickable: Boolean,
|
||||
feedListSearchBar: FeedListSearchBar,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.padding(bottom = 8.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(36.dp))
|
||||
.background(color = TangemTheme.colors.field.focused)
|
||||
.conditional(condition = isSearchBarClickable) {
|
||||
clickable(onClick = feedListSearchBar.onBarClick)
|
||||
}
|
||||
.padding(14.dp),
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size20),
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.ic_search_24),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
)
|
||||
|
||||
SpacerW(14.dp)
|
||||
|
||||
Text(
|
||||
text = feedListSearchBar.placeholderText.resolveReference(),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
FeedSearchBarV2(
|
||||
isSearchBarClickable = isSearchBarClickable,
|
||||
feedListSearchBar = feedListSearchBar,
|
||||
modifier = modifier,
|
||||
startContent = startContent,
|
||||
endContent = endContent,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ 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.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.*
|
||||
|
|
@ -40,7 +39,6 @@ internal fun EarnContent(contentPadding: PaddingValues, state: EarnUM, 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(
|
||||
|
|
@ -60,7 +58,7 @@ internal fun EarnContent(contentPadding: PaddingValues, state: EarnUM, modifier:
|
|||
SectionHeader(
|
||||
title = stringResourceSafe(R.string.earn_mostly_used),
|
||||
modifier = Modifier.padding(
|
||||
top = if (isRedesignEnabled) 10.dp else 16.dp,
|
||||
top = 10.dp,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -76,7 +74,7 @@ internal fun EarnContent(contentPadding: PaddingValues, state: EarnUM, modifier:
|
|||
SectionHeader(
|
||||
title = stringResourceSafe(R.string.earn_best_opportunities),
|
||||
modifier = Modifier.padding(
|
||||
top = if (isRedesignEnabled) 32.dp else 20.dp,
|
||||
top = 32.dp,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -91,17 +89,12 @@ internal fun EarnContent(contentPadding: PaddingValues, state: EarnUM, modifier:
|
|||
)
|
||||
}
|
||||
|
||||
if (isRedesignEnabled) {
|
||||
bestOpportunitiesItemsV2(state = state.bestOpportunities)
|
||||
} else {
|
||||
bestOpportunitiesItemsV1(state = state.bestOpportunities)
|
||||
}
|
||||
bestOpportunitiesItemsV2(state = state.bestOpportunities)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MostlyUsedContent(state: EarnListUM, onScroll: () -> Unit) {
|
||||
val isRedesignEnabled = LocalRedesignEnabled.current
|
||||
AnimatedContent(
|
||||
targetState = state,
|
||||
contentKey = { it::class.java },
|
||||
|
|
@ -136,20 +129,9 @@ private fun MostlyUsedContent(state: EarnListUM, onScroll: () -> Unit) {
|
|||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp)
|
||||
.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,
|
||||
)
|
||||
},
|
||||
.background(
|
||||
color = TangemTheme.colors2.surface.level3,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens2.x4),
|
||||
)
|
||||
.padding(vertical = 32.dp, horizontal = 12.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
|
|
@ -177,76 +159,6 @@ private fun BestOpportunitiesFilters(
|
|||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.bestOpportunitiesItemsV1(state: EarnBestOpportunitiesUM) {
|
||||
when (state) {
|
||||
is EarnBestOpportunitiesUM.Loading -> {
|
||||
val lastIndex = PLACEHOLDER_ITEMS_COUNT - 1
|
||||
items(
|
||||
count = PLACEHOLDER_ITEMS_COUNT,
|
||||
key = { "placeholder_$it" },
|
||||
) { index ->
|
||||
EarnItemPlaceholderV1(
|
||||
modifier = Modifier
|
||||
.roundedShapeItemDecoration(
|
||||
currentIndex = index,
|
||||
lastIndex = lastIndex,
|
||||
backgroundColor = TangemTheme.colors.background.action,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
is EarnBestOpportunitiesUM.Empty -> {
|
||||
item(key = "best_opportunities_empty") {
|
||||
SpacerH(12.dp)
|
||||
BestOpportunitiesEmpty()
|
||||
}
|
||||
}
|
||||
is EarnBestOpportunitiesUM.EmptyFiltered -> {
|
||||
item(key = "best_opportunities_empty_filtered") {
|
||||
SpacerH(12.dp)
|
||||
BestOpportunitiesEmptyFiltered(onClearFilterClick = state.onClearFilterClick)
|
||||
}
|
||||
}
|
||||
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.colors.background.action,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
is EarnBestOpportunitiesUM.Error -> {
|
||||
item(key = "best_opportunities_error") {
|
||||
SpacerH(12.dp)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.background(
|
||||
color = TangemTheme.colors.background.action,
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
)
|
||||
.padding(vertical = 32.dp, horizontal = 12.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
UnableToLoadData(onRetryClick = state.onRetryClicked)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.bestOpportunitiesItemsV2(state: EarnBestOpportunitiesUM) {
|
||||
when (state) {
|
||||
is EarnBestOpportunitiesUM.Loading -> {
|
||||
|
|
@ -320,53 +232,32 @@ private fun LazyListScope.bestOpportunitiesItemsV2(state: EarnBestOpportunitiesU
|
|||
@Composable
|
||||
private fun FilterButtonsShimmer(modifier: Modifier = Modifier) {
|
||||
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,
|
||||
)
|
||||
SpacerWMax()
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(106.dp)
|
||||
.height(36.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
} else {
|
||||
SmallButtonShimmer(
|
||||
modifier = Modifier.width(110.dp),
|
||||
)
|
||||
SpacerWMax()
|
||||
SmallButtonShimmer(
|
||||
modifier = Modifier.width(90.dp),
|
||||
)
|
||||
}
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(130.dp)
|
||||
.height(36.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
SpacerWMax()
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(106.dp)
|
||||
.height(36.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SectionHeader(title: String, modifier: Modifier = Modifier) {
|
||||
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,
|
||||
)
|
||||
}
|
||||
Text(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 24.dp),
|
||||
text = title,
|
||||
style = TangemTheme.typography2.headingSemibold20,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -10,55 +10,16 @@ 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,
|
||||
)
|
||||
}
|
||||
BestOpportunitiesEmptyV2(modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -9,24 +9,16 @@ 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)
|
||||
}
|
||||
BestOpportunitiesEmptyFilteredV2(onClearFilterClick, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -58,32 +50,4 @@ private fun BestOpportunitiesEmptyFilteredV2(onClearFilterClick: () -> Unit, mod
|
|||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@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,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -3,39 +3,25 @@ package com.tangem.features.feed.ui.earn.components
|
|||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
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
|
||||
import com.tangem.core.ui.components.inputrow.InputRowChecked
|
||||
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
|
||||
|
|
@ -48,62 +34,12 @@ 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 = { ContentV1(it) },
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
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>()
|
||||
}
|
||||
val specificNetworks = remember(content) { content.networks.filterIsInstance<EarnFilterNetworkUM.Network>() }
|
||||
|
||||
LazyColumn(
|
||||
contentPadding = PaddingValues(
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
bottom = TangemTheme.dimens.spacing16,
|
||||
),
|
||||
) {
|
||||
allMyNetworksList(
|
||||
allMyNetworks = allMyNetworks,
|
||||
onOptionClicked = content.onOptionClick,
|
||||
)
|
||||
|
||||
if (specificNetworks.isNotEmpty()) {
|
||||
networksHeader()
|
||||
|
||||
specificNetworksList(
|
||||
specificNetworks = specificNetworks,
|
||||
onOptionClicked = content.onOptionClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ContentV2(content: EarnFilterByNetworkBottomSheetContentUM) {
|
||||
val allMyNetworks = remember(content) {
|
||||
|
|
@ -250,125 +186,6 @@ private fun SpecificNetworksBlock(
|
|||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.allMyNetworksList(
|
||||
allMyNetworks: List<EarnFilterNetworkUM>,
|
||||
onOptionClicked: (EarnFilterNetworkUM) -> Unit,
|
||||
) {
|
||||
itemsIndexed(
|
||||
items = allMyNetworks,
|
||||
key = { _, item ->
|
||||
when (item) {
|
||||
is EarnFilterNetworkUM.AllNetworks -> "all_networks"
|
||||
is EarnFilterNetworkUM.MyNetworks -> "my_networks"
|
||||
is EarnFilterNetworkUM.Network -> item.id
|
||||
}
|
||||
},
|
||||
) { index, item ->
|
||||
DividerContainer(
|
||||
modifier = Modifier
|
||||
.roundedShapeItemDecoration(
|
||||
currentIndex = index,
|
||||
lastIndex = allMyNetworks.lastIndex,
|
||||
addDefaultPadding = false,
|
||||
)
|
||||
.background(TangemTheme.colors.background.action)
|
||||
.clickable { onOptionClicked(item) },
|
||||
showDivider = index != allMyNetworks.lastIndex,
|
||||
) {
|
||||
InputRowChecked(
|
||||
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)
|
||||
},
|
||||
checked = item.isSelected,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.networksHeader() {
|
||||
item(key = "networks_header") {
|
||||
SpacerH(TangemTheme.dimens.spacing16)
|
||||
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(
|
||||
color = TangemTheme.colors.background.action,
|
||||
shape = RoundedCornerShape(
|
||||
topStart = 16.dp,
|
||||
topEnd = 16.dp,
|
||||
),
|
||||
)
|
||||
.padding(
|
||||
start = 12.dp,
|
||||
end = 12.dp,
|
||||
top = 12.dp,
|
||||
bottom = 4.dp,
|
||||
),
|
||||
text = stringResourceSafe(id = R.string.earn_filter_networks),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.specificNetworksList(
|
||||
specificNetworks: List<EarnFilterNetworkUM.Network>,
|
||||
onOptionClicked: (EarnFilterNetworkUM) -> Unit,
|
||||
) {
|
||||
itemsIndexed(
|
||||
items = specificNetworks,
|
||||
key = { _, item -> item.id },
|
||||
) { index, item ->
|
||||
DividerContainer(
|
||||
modifier = Modifier
|
||||
.height(52.dp)
|
||||
.roundedShapeItemDecoration(
|
||||
currentIndex = index + 1,
|
||||
lastIndex = specificNetworks.lastIndex + 1,
|
||||
addDefaultPadding = false,
|
||||
)
|
||||
.background(TangemTheme.colors.background.action)
|
||||
.clickable { onOptionClicked(item) },
|
||||
showDivider = false,
|
||||
) {
|
||||
RowContentContainer(
|
||||
modifier = Modifier
|
||||
.heightIn(52.dp)
|
||||
.padding(horizontal = 12.dp),
|
||||
icon = {
|
||||
Image(
|
||||
modifier = Modifier.size(22.dp),
|
||||
imageVector = ImageVector.vectorResource(item.iconRes),
|
||||
contentDescription = item.symbol,
|
||||
)
|
||||
},
|
||||
text = {
|
||||
RowText(
|
||||
mainText = item.text,
|
||||
secondText = item.symbol,
|
||||
accentMainText = true,
|
||||
accentSecondText = false,
|
||||
)
|
||||
},
|
||||
action = {
|
||||
if (item.isSelected) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_check_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.accent,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 360, heightDp = 800)
|
||||
@Preview(widthDp = 360, heightDp = 800, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import android.content.res.Configuration
|
|||
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
|
||||
|
|
@ -15,81 +14,25 @@ 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 = { ContentV1(it) },
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun EarnFilterByTypeBottomSheetV2(config: TangemBottomSheetConfig) {
|
||||
EarnFilterBottomSheet<EarnFilterByTypeBottomSheetContentUM>(
|
||||
config = config,
|
||||
content = { ContentV2(it) },
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ContentV1(content: EarnFilterByTypeBottomSheetContentUM) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
bottom = TangemTheme.dimens.spacing16,
|
||||
),
|
||||
) {
|
||||
EarnFilterTypeUM.entries.forEachIndexed { index, type ->
|
||||
DividerContainer(
|
||||
modifier = Modifier
|
||||
.roundedShapeItemDecoration(
|
||||
currentIndex = index,
|
||||
lastIndex = EarnFilterTypeUM.entries.lastIndex,
|
||||
addDefaultPadding = false,
|
||||
)
|
||||
.background(TangemTheme.colors.background.action)
|
||||
.clickable { content.onOptionClick(type) },
|
||||
showDivider = index != EarnFilterTypeUM.entries.lastIndex,
|
||||
) {
|
||||
InputRowChecked(
|
||||
text = type.text,
|
||||
checked = type == content.selectedOption,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ContentV2(content: EarnFilterByTypeBottomSheetContentUM) {
|
||||
CardFilterBlock(
|
||||
|
|
|
|||
|
|
@ -9,8 +9,6 @@ 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
|
||||
|
|
@ -20,7 +18,6 @@ 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.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
|
||||
|
|
@ -35,74 +32,10 @@ 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()
|
||||
.clip(RectangleShape)
|
||||
.clickable { item.onItemClick() }
|
||||
.padding(
|
||||
horizontal = 12.dp,
|
||||
vertical = 15.dp,
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
CurrencyIcon(
|
||||
modifier = Modifier.size(36.dp),
|
||||
state = item.currencyIconState,
|
||||
shouldDisplayNetwork = true,
|
||||
networkBadgeBackground = TangemTheme.colors.background.action,
|
||||
)
|
||||
|
||||
SpacerW(12.dp)
|
||||
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
TokenTitle(
|
||||
name = item.tokenName.resolveReference(),
|
||||
symbol = item.symbol.resolveReference(),
|
||||
)
|
||||
SpacerH(2.dp)
|
||||
Text(
|
||||
text = item.network.resolveReference(),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.caption2,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
|
||||
SpacerW(8.dp)
|
||||
|
||||
Column(horizontalAlignment = Alignment.End) {
|
||||
Text(
|
||||
text = item.earnValue.resolveReference(),
|
||||
color = TangemTheme.colors.text.accent,
|
||||
style = TangemTheme.typography.body2,
|
||||
maxLines = 1,
|
||||
)
|
||||
SpacerH(2.dp)
|
||||
Text(
|
||||
text = item.earnTypeTitle.resolveReference(),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.caption2,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
EarnListItemV2(
|
||||
item = item,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -158,47 +91,25 @@ private fun EarnListItemV2(item: EarnListItemUM, modifier: Modifier = Modifier)
|
|||
@Composable
|
||||
private fun TokenTitle(name: String, symbol: String, modifier: Modifier = Modifier) {
|
||||
Row(modifier = modifier) {
|
||||
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,
|
||||
)
|
||||
}
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,71 +10,8 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.compose.ui.unit.dp
|
||||
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 EarnItemPlaceholderV1(modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = 16.dp,
|
||||
vertical = 12.dp,
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
CircleShimmer(modifier = Modifier.size(36.dp))
|
||||
|
||||
SpacerW(12.dp)
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(top = 4.dp),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(70.dp)
|
||||
.height(12.dp),
|
||||
radius = 4.dp,
|
||||
)
|
||||
SpacerWMax()
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(40.dp)
|
||||
.height(12.dp),
|
||||
radius = 4.dp,
|
||||
)
|
||||
}
|
||||
|
||||
SpacerH(8.dp)
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(52.dp)
|
||||
.height(12.dp),
|
||||
radius = 4.dp,
|
||||
)
|
||||
SpacerWMax()
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(40.dp)
|
||||
.height(12.dp),
|
||||
radius = 4.dp,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun EarnItemPlaceholderV2(modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
|
|
@ -127,22 +64,6 @@ internal fun EarnItemPlaceholderV2(modifier: Modifier = Modifier) {
|
|||
|
||||
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 EarnListPlaceholderPreviewV1() {
|
||||
TangemThemePreview {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(TangemTheme.colors.background.tertiary),
|
||||
) {
|
||||
repeat(PLACEHOLDER_ITEMS_COUNT) {
|
||||
EarnItemPlaceholderV1()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -7,15 +7,11 @@ 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.ds.image.TangemIconUM
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.feed.ui.earn.state.EarnFilterNetworkUM
|
||||
import com.tangem.features.feed.ui.earn.state.EarnFilterUM
|
||||
|
|
@ -27,57 +23,12 @@ internal fun FilterButtons(
|
|||
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,
|
||||
),
|
||||
)
|
||||
}
|
||||
FilterButtonsV2(
|
||||
earnFilterUM = earnFilterUM,
|
||||
onNetworkFilterClick = onNetworkFilterClick,
|
||||
onTypeFilterClick = onTypeFilterClick,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -25,19 +25,11 @@ import com.tangem.features.feed.ui.earn.state.EarnListItemUM
|
|||
|
||||
@Composable
|
||||
internal fun MostlyUsedCard(item: EarnListItemUM, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
MostlyUsedCardV2(
|
||||
modifier = modifier,
|
||||
item = item,
|
||||
onClick = onClick,
|
||||
)
|
||||
} else {
|
||||
MostlyUsedCardV1(
|
||||
modifier = modifier,
|
||||
item = item,
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
MostlyUsedCardV2(
|
||||
modifier = modifier,
|
||||
item = item,
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -91,84 +83,6 @@ private fun MostlyUsedCardV2(item: EarnListItemUM, onClick: () -> Unit, modifier
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MostlyUsedCardV1(item: EarnListItemUM, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.width(148.dp)
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(TangemTheme.colors.background.action)
|
||||
.clickable(onClick = onClick)
|
||||
.padding(12.dp),
|
||||
) {
|
||||
CurrencyIcon(
|
||||
modifier = Modifier.size(32.dp),
|
||||
state = item.currencyIconState,
|
||||
shouldDisplayNetwork = true,
|
||||
networkBadgeSize = 12.dp,
|
||||
networkBadgeBackground = TangemTheme.colors.background.action,
|
||||
)
|
||||
|
||||
SpacerH(8.dp)
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.weight(weight = 1f, fill = false),
|
||||
text = item.tokenName.resolveReference(),
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
)
|
||||
SpacerW(4.dp)
|
||||
Text(
|
||||
text = item.symbol.resolveReference(),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
|
||||
SpacerH(2.dp)
|
||||
|
||||
Text(
|
||||
text = item.earnValue.resolveReference(),
|
||||
color = TangemTheme.colors.text.accent,
|
||||
style = TangemTheme.typography.caption1,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun EarnListItemPreviewV1() {
|
||||
TangemThemePreview {
|
||||
MostlyUsedCardV1(
|
||||
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 6.54%"),
|
||||
earnTypeTitle = stringReference("Yield"),
|
||||
earnType = EarnType.YIELD,
|
||||
onItemClick = {},
|
||||
),
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun EarnListItemPreviewV2() {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ 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
|
||||
|
|
@ -31,48 +30,11 @@ internal fun MostlyUsedPlaceholder(
|
|||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
items(PLACEHOLDER_ITEMS_COUNT) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
MostlyUsedItemPlaceholderV2()
|
||||
} else {
|
||||
MostlyUsedItemPlaceholderV1()
|
||||
}
|
||||
MostlyUsedItemPlaceholderV2()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MostlyUsedItemPlaceholderV1(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.width(148.dp)
|
||||
.height(102.dp)
|
||||
.background(
|
||||
color = TangemTheme.colors.background.action,
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
)
|
||||
.padding(12.dp),
|
||||
) {
|
||||
CircleShimmer(
|
||||
modifier = Modifier
|
||||
.size(32.dp),
|
||||
)
|
||||
SpacerH(8.dp)
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(74.dp)
|
||||
.height(20.dp),
|
||||
radius = 4.dp,
|
||||
)
|
||||
SpacerH(2.dp)
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(84.dp)
|
||||
.height(16.dp),
|
||||
radius = 4.dp,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MostlyUsedItemPlaceholderV2(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import androidx.compose.foundation.rememberScrollState
|
|||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.drawBehind
|
||||
import androidx.compose.ui.platform.testTag
|
||||
|
|
@ -106,12 +105,10 @@ private fun FeedListContent(
|
|||
.padding(bottom = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding())
|
||||
.drawBehind { drawRect(background) },
|
||||
) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
SpacerH(contentPadding.calculateTopPadding())
|
||||
}
|
||||
SpacerH(contentPadding.calculateTopPadding())
|
||||
DateBlock(state.currentDate)
|
||||
|
||||
if (state.forYouBannerUM is ForYouBannerUM.Content && LocalRedesignEnabled.current) {
|
||||
if (state.forYouBannerUM is ForYouBannerUM.Content) {
|
||||
SpacerH(16.dp)
|
||||
TangemMessageBanner(
|
||||
title = resourceReference(R.string.for_you_title),
|
||||
|
|
@ -164,17 +161,7 @@ private fun FeedListContent(
|
|||
@Preview(showBackground = true, heightDp = 1500)
|
||||
@Composable
|
||||
private fun FeedListPreview() {
|
||||
TangemThemePreview {
|
||||
TangemThemePreviewRedesign {
|
||||
FeedList(state = createFeedPreviewState(), contentPadding = PaddingValues())
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, heightDp = 1500)
|
||||
@Composable
|
||||
private fun FeedListPreviewV2() {
|
||||
TangemThemePreviewRedesign {
|
||||
CompositionLocalProvider(LocalRedesignEnabled provides true) {
|
||||
FeedList(state = createFeedPreviewState(), contentPadding = PaddingValues())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,11 +16,7 @@ import com.tangem.core.ui.R
|
|||
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.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
|
||||
|
|
@ -31,10 +27,7 @@ internal fun ColumnScope.Header(
|
|||
modifier: Modifier = Modifier,
|
||||
title: @Composable () -> Unit,
|
||||
) {
|
||||
val isRedesignEnabled = LocalRedesignEnabled.current
|
||||
if (isRedesignEnabled) {
|
||||
SpacerH(12.dp)
|
||||
}
|
||||
SpacerH(12.dp)
|
||||
AnimatedContent(
|
||||
targetState = isLoading,
|
||||
modifier = modifier,
|
||||
|
|
@ -45,45 +38,32 @@ internal fun ColumnScope.Header(
|
|||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
if (animatedState) {
|
||||
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))
|
||||
}
|
||||
RectangleShimmer(
|
||||
modifier = Modifier.size(width = 130.dp, height = 24.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
} else {
|
||||
Box(modifier = Modifier.weight(1f)) {
|
||||
title()
|
||||
}
|
||||
SpacerW(8.dp)
|
||||
AnimatedVisibility(shouldShowSeeAll) {
|
||||
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,
|
||||
),
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,51 +0,0 @@
|
|||
package com.tangem.features.feed.ui.feed.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
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.unit.dp
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.feed.model.market.list.state.SortByTypeUM
|
||||
|
||||
@Composable
|
||||
internal fun ChartsFilterChip(sortByTypeUM: SortByTypeUM, isSelected: Boolean, onClick: () -> Unit) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.clip(shape = RoundedCornerShape(12.dp))
|
||||
.background(
|
||||
color = if (isSelected) {
|
||||
TangemTheme.colors.button.primary
|
||||
} else {
|
||||
TangemTheme.colors.button.secondary
|
||||
},
|
||||
)
|
||||
.clickable(
|
||||
onClick = onClick,
|
||||
indication = ripple(),
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
)
|
||||
.padding(vertical = 8.dp, horizontal = 24.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Text(
|
||||
text = sortByTypeUM.text.resolveReference(),
|
||||
style = TangemTheme.typography.button,
|
||||
color = if (isSelected) {
|
||||
TangemTheme.colors.text.primary2
|
||||
} else {
|
||||
TangemTheme.colors.text.primary1
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -5,45 +5,13 @@ 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.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 DateBlock(currentDate: String) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
DateBlockV2(currentDate)
|
||||
} else {
|
||||
DateBlockV1(currentDate)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DateBlockV1(currentDate: String) {
|
||||
SpacerH(20.dp)
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 20.dp),
|
||||
text = stringResourceSafe(R.string.feed_market_and_news),
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 20.dp),
|
||||
text = currentDate,
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DateBlockV2(currentDate: String) {
|
||||
SpacerH(TangemTheme.dimens2.x1)
|
||||
Text(
|
||||
modifier = Modifier
|
||||
|
|
|
|||
|
|
@ -9,17 +9,13 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import com.tangem.core.ui.R
|
||||
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.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.MostlyUsedCard
|
||||
import com.tangem.features.feed.ui.earn.components.MostlyUsedPlaceholder
|
||||
import com.tangem.features.feed.ui.earn.state.EarnListItemUM
|
||||
|
|
@ -29,56 +25,7 @@ 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 = {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.markets_earn_common_title),
|
||||
style = TangemTheme.typography.h3,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
)
|
||||
},
|
||||
onSeeAllClick = onSeeAllClick,
|
||||
isLoading = earnListUM is EarnListUM.Loading,
|
||||
shouldShowSeeAll = earnListUM is EarnListUM.Content,
|
||||
modifier = Modifier.padding(horizontal = 20.dp),
|
||||
)
|
||||
|
||||
SpacerH(12.dp)
|
||||
|
||||
BlockCard(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
colors = TangemBlockCardColors.copy(containerColor = TangemTheme.colors.background.action),
|
||||
) {
|
||||
AnimatedContent(
|
||||
targetState = earnListUM,
|
||||
contentKey = { it::class.java },
|
||||
) { earnListUM ->
|
||||
when (earnListUM) {
|
||||
is EarnListUM.Content -> EarnContentBlock(items = earnListUM.items)
|
||||
is EarnListUM.Error -> EarnErrorBlock(onRetryClick = earnListUM.onRetryClicked)
|
||||
EarnListUM.Loading -> {
|
||||
repeat(PLACEHOLDER_ITEM_COUNT) {
|
||||
EarnItemPlaceholderV1()
|
||||
}
|
||||
}
|
||||
EarnListUM.Empty -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
SpacerH(32.dp)
|
||||
}
|
||||
EarnBlockV2(onSeeAllClick = onSeeAllClick, earnListUM = earnListUM, modifier = modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -143,32 +90,22 @@ private fun EarnErrorBlock(onRetryClick: () -> Unit) {
|
|||
|
||||
@Composable
|
||||
private fun EarnContentBlock(items: ImmutableList<EarnListItemUM>) {
|
||||
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.id },
|
||||
) { item ->
|
||||
MostlyUsedCard(
|
||||
item = item,
|
||||
onClick = item.onItemClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
items.fastForEach { item ->
|
||||
EarnListItem(item = item)
|
||||
}
|
||||
LazyRow(
|
||||
contentPadding = PaddingValues(
|
||||
start = 16.dp,
|
||||
end = 16.dp,
|
||||
bottom = 8.dp,
|
||||
),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
items(
|
||||
items = items,
|
||||
key = { item -> item.id },
|
||||
) { item ->
|
||||
MostlyUsedCard(
|
||||
item = item,
|
||||
onClick = item.onItemClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val PLACEHOLDER_ITEM_COUNT = 5
|
||||
}
|
||||
|
|
@ -13,9 +13,8 @@ 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.core.ui.res.TangemThemePreviewRedesign
|
||||
import com.tangem.features.feed.ui.feed.components.articles.DefaultLoadingArticle
|
||||
import com.tangem.features.feed.ui.feed.components.articles.TrendingLoadingArticle
|
||||
|
||||
|
|
@ -36,7 +35,48 @@ internal fun FeedListLoading(modifier: Modifier = Modifier) {
|
|||
|
||||
@Composable
|
||||
internal fun MarketLoadingBlock() {
|
||||
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)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun MarketPulseLoadingBlock() {
|
||||
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)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun NewsLoadingBlock() {
|
||||
Column {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp, top = 16.dp)
|
||||
|
|
@ -44,133 +84,28 @@ internal fun MarketLoadingBlock() {
|
|||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
SpacerH(20.dp)
|
||||
ChartsLoading(modifier = Modifier.padding(horizontal = 16.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 {
|
||||
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() {
|
||||
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 = if (isRedesignEnabled) {
|
||||
TangemTheme.colors2.surface.level3
|
||||
} else {
|
||||
TangemTheme.colors.background.action
|
||||
},
|
||||
|
||||
containerColor = TangemTheme.colors2.surface.level3,
|
||||
),
|
||||
) {
|
||||
Column(
|
||||
|
|
@ -190,7 +125,7 @@ private const val DEFAULT_CHART_SIZE_IN_MARKET = 5
|
|||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun FeedListLoadingPreview() {
|
||||
TangemThemePreview {
|
||||
TangemThemePreviewRedesign {
|
||||
Column {
|
||||
MarketLoadingBlock()
|
||||
NewsLoadingBlock()
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ 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
|
||||
|
|
@ -42,7 +41,6 @@ internal fun MarketBlock(
|
|||
onItemClick: (MarketsListItemUM) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val isRedesignEnabled = LocalRedesignEnabled.current
|
||||
AnimatedContent(
|
||||
targetState = marketChart,
|
||||
label = "MarketBlockChartAnimation",
|
||||
|
|
@ -58,16 +56,8 @@ internal fun MarketBlock(
|
|||
title = {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.markets_common_title),
|
||||
style = if (isRedesignEnabled) {
|
||||
TangemTheme.typography2.headingSemibold20
|
||||
} else {
|
||||
TangemTheme.typography.h3
|
||||
},
|
||||
color = if (isRedesignEnabled) {
|
||||
TangemTheme.colors2.text.neutral.primary
|
||||
} else {
|
||||
TangemTheme.colors.text.primary1
|
||||
},
|
||||
style = TangemTheme.typography2.headingSemibold20,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
)
|
||||
|
|
@ -80,7 +70,7 @@ internal fun MarketBlock(
|
|||
modifier = Modifier.padding(horizontal = MARKET_BLOCK_HEADER_PADDING),
|
||||
)
|
||||
|
||||
SpacerH(if (isRedesignEnabled) 20.dp else 12.dp)
|
||||
SpacerH(20.dp)
|
||||
|
||||
Charts(
|
||||
onItemClick = onItemClick,
|
||||
|
|
@ -96,7 +86,6 @@ internal fun MarketBlock(
|
|||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
internal fun ColumnScope.MarketPulseBlock(marketChartConfig: MarketChartConfig, feedListCallbacks: FeedListCallbacks) {
|
||||
val isRedesignEnabled = LocalRedesignEnabled.current
|
||||
val onSeeAllClick by rememberUpdatedState {
|
||||
feedListCallbacks.onMarketOpenClick(marketChartConfig.currentSortByType)
|
||||
}
|
||||
|
|
@ -105,16 +94,8 @@ internal fun ColumnScope.MarketPulseBlock(marketChartConfig: MarketChartConfig,
|
|||
title = {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.markets_pulse_common_title),
|
||||
style = if (isRedesignEnabled) {
|
||||
TangemTheme.typography2.headingSemibold20
|
||||
} else {
|
||||
TangemTheme.typography.h3
|
||||
},
|
||||
color = if (isRedesignEnabled) {
|
||||
TangemTheme.colors2.text.neutral.primary
|
||||
} else {
|
||||
TangemTheme.colors.text.primary1
|
||||
},
|
||||
style = TangemTheme.typography2.headingSemibold20,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
)
|
||||
|
|
@ -128,11 +109,7 @@ internal fun ColumnScope.MarketPulseBlock(marketChartConfig: MarketChartConfig,
|
|||
LazyRow(
|
||||
modifier = Modifier.padding(vertical = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
contentPadding = if (isRedesignEnabled) {
|
||||
PaddingValues(horizontal = 16.dp, vertical = 6.dp)
|
||||
} else {
|
||||
PaddingValues(16.dp)
|
||||
},
|
||||
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 6.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
state = rememberLazyListState(),
|
||||
) {
|
||||
|
|
@ -140,19 +117,11 @@ internal fun ColumnScope.MarketPulseBlock(marketChartConfig: MarketChartConfig,
|
|||
items = marketChartConfig.getFilterPreset(),
|
||||
key = SortByTypeUM::name,
|
||||
) { 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) },
|
||||
)
|
||||
}
|
||||
TangemTab(
|
||||
text = sortByTypeUM.text,
|
||||
isChecked = sortByTypeUM == marketChartConfig.currentSortByType,
|
||||
onCheckedChange = { feedListCallbacks.onSortTypeClick(sortByTypeUM) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -179,21 +148,12 @@ private fun Charts(
|
|||
onItemClick: (MarketsListItemUM) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val isRedesignEnabled = LocalRedesignEnabled.current
|
||||
BlockCard(
|
||||
modifier = modifier,
|
||||
colors = TangemBlockCardColors.copy(
|
||||
containerColor = if (isRedesignEnabled) {
|
||||
TangemTheme.colors2.surface.level3
|
||||
} else {
|
||||
TangemTheme.colors.background.action
|
||||
},
|
||||
containerColor = TangemTheme.colors2.surface.level3,
|
||||
),
|
||||
shape = if (isRedesignEnabled) {
|
||||
RoundedCornerShape(TangemTheme.dimens2.x6)
|
||||
} else {
|
||||
TangemTheme.shapes.roundedCornersXMedium
|
||||
},
|
||||
shape = RoundedCornerShape(TangemTheme.dimens2.x6),
|
||||
) {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
when (marketChart) {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
package com.tangem.features.feed.ui.feed.components
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.*
|
||||
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.Brush
|
||||
|
|
@ -26,7 +24,6 @@ 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.feed.components.articles.ArticleCard
|
||||
import com.tangem.features.feed.ui.feed.components.articles.ArticleConfigUM
|
||||
|
|
@ -38,9 +35,6 @@ private const val GRADIENT_END = 0.5f
|
|||
private const val LINEAR_GRADIENT_FIRST_PART_V2 = 0xFF7B78FF
|
||||
private const val LINEAR_GRADIENT_SECOND_PART_V2 = 0xFFC56BCD
|
||||
|
||||
private const val LINEAR_GRADIENT_FIRST_PART_V1 = 0xFF635EEC
|
||||
private const val LINEAR_GRADIENT_SECOND_PART_V1 = 0xFFE05AED
|
||||
|
||||
private const val MAGIC_ICON_COLOR = 0xFF7D78FF
|
||||
|
||||
@Composable
|
||||
|
|
@ -65,22 +59,8 @@ internal fun NewsBlock(feedListCallbacks: FeedListCallbacks, news: NewsUM, trend
|
|||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun NewsContentBlock(feedListCallbacks: FeedListCallbacks, news: NewsUM, trendingArticle: ArticleConfigUM?) {
|
||||
val isRedesignEnabled = LocalRedesignEnabled.current
|
||||
val gradientStart = remember(isRedesignEnabled) {
|
||||
if (isRedesignEnabled) {
|
||||
Color(LINEAR_GRADIENT_FIRST_PART_V2)
|
||||
} else {
|
||||
Color(LINEAR_GRADIENT_FIRST_PART_V1)
|
||||
}
|
||||
}
|
||||
|
||||
val gradientEnd = remember(isRedesignEnabled) {
|
||||
if (isRedesignEnabled) {
|
||||
Color(LINEAR_GRADIENT_SECOND_PART_V2)
|
||||
} else {
|
||||
Color(LINEAR_GRADIENT_SECOND_PART_V1)
|
||||
}
|
||||
}
|
||||
val gradientStart = Color(LINEAR_GRADIENT_FIRST_PART_V2)
|
||||
val gradientEnd = Color(LINEAR_GRADIENT_SECOND_PART_V2)
|
||||
|
||||
Column {
|
||||
Header(
|
||||
|
|
@ -88,33 +68,18 @@ private fun NewsContentBlock(feedListCallbacks: FeedListCallbacks, news: NewsUM,
|
|||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.common_news),
|
||||
style = if (isRedesignEnabled) {
|
||||
TangemTheme.typography2.headingSemibold20
|
||||
} else {
|
||||
TangemTheme.typography.h3
|
||||
},
|
||||
color = if (isRedesignEnabled) {
|
||||
TangemTheme.colors2.text.neutral.primary
|
||||
} else {
|
||||
TangemTheme.colors.text.primary1
|
||||
},
|
||||
style = TangemTheme.typography2.headingSemibold20,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
)
|
||||
|
||||
SpacerW(4.dp)
|
||||
|
||||
if (isRedesignEnabled) {
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens2.x6),
|
||||
tint = Color(MAGIC_ICON_COLOR),
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_magic_28),
|
||||
contentDescription = null,
|
||||
)
|
||||
} else {
|
||||
Image(
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_stars_20),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens2.x6),
|
||||
tint = Color(MAGIC_ICON_COLOR),
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_magic_28),
|
||||
contentDescription = null,
|
||||
)
|
||||
|
||||
SpacerW(2.dp)
|
||||
|
||||
|
|
@ -131,11 +96,7 @@ private fun NewsContentBlock(feedListCallbacks: FeedListCallbacks, news: NewsUM,
|
|||
append(stringResourceSafe(R.string.feed_tangem_ai))
|
||||
}
|
||||
},
|
||||
style = if (isRedesignEnabled) {
|
||||
TangemTheme.typography2.bodyRegular16.copy(fontWeight = FontWeight.Medium)
|
||||
} else {
|
||||
TangemTheme.typography.subtitle1
|
||||
},
|
||||
style = TangemTheme.typography2.bodyRegular16.copy(fontWeight = FontWeight.Medium),
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
)
|
||||
|
|
@ -156,7 +117,6 @@ private fun NewsContentBlock(feedListCallbacks: FeedListCallbacks, news: NewsUM,
|
|||
.padding(horizontal = 16.dp),
|
||||
articleConfigUM = trendingArticle,
|
||||
onArticleClick = { feedListCallbacks.onArticleClick(trendingArticle.id) },
|
||||
colors = TangemBlockCardColors.copy(containerColor = TangemTheme.colors.background.action),
|
||||
)
|
||||
SpacerH(12.dp)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,9 +10,7 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.onFirstVisible
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.block.TangemBlockCardColors
|
||||
import com.tangem.core.ui.res.LocalMainBottomSheetColor
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.feed.ui.feed.components.articles.ArticleCard
|
||||
import com.tangem.features.feed.ui.feed.components.articles.ShowMoreArticlesCard
|
||||
import com.tangem.features.feed.ui.feed.state.NewsSliderConfig
|
||||
|
|
@ -48,7 +46,6 @@ internal fun NewsSlider(newsSliderConfig: NewsSliderConfig) {
|
|||
.width(280.dp)
|
||||
.heightIn(min = 172.dp)
|
||||
.fillMaxHeight(),
|
||||
colors = TangemBlockCardColors.copy(containerColor = TangemTheme.colors.background.action),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,30 +1,13 @@
|
|||
package com.tangem.features.feed.ui.feed.components.articles
|
||||
|
||||
import androidx.compose.material3.CardColors
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.core.ui.components.block.TangemBlockCardColors
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
|
||||
@Composable
|
||||
fun ArticleCard(
|
||||
articleConfigUM: ArticleConfigUM,
|
||||
onArticleClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
colors: CardColors = TangemBlockCardColors,
|
||||
) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
ArticleCardV2(
|
||||
articleConfigUM = articleConfigUM,
|
||||
onArticleClick = onArticleClick,
|
||||
modifier = modifier,
|
||||
)
|
||||
} else {
|
||||
ArticleCardV1(
|
||||
articleConfigUM = articleConfigUM,
|
||||
onArticleClick = onArticleClick,
|
||||
modifier = modifier,
|
||||
colors = colors,
|
||||
)
|
||||
}
|
||||
fun ArticleCard(articleConfigUM: ArticleConfigUM, onArticleClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
ArticleCardV2(
|
||||
articleConfigUM = articleConfigUM,
|
||||
onArticleClick = onArticleClick,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,273 +0,0 @@
|
|||
package com.tangem.features.feed.ui.feed.components.articles
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.CardColors
|
||||
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.vector.ImageVector
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.painterResource
|
||||
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 com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.components.SpacerHMax
|
||||
import com.tangem.core.ui.components.block.BlockCard
|
||||
import com.tangem.core.ui.components.block.TangemBlockCardColors
|
||||
import com.tangem.core.ui.components.label.entity.LabelUM
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableSet
|
||||
|
||||
@Composable
|
||||
internal fun ArticleCardV1(
|
||||
articleConfigUM: ArticleConfigUM,
|
||||
onArticleClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
colors: CardColors = TangemBlockCardColors,
|
||||
) {
|
||||
if (articleConfigUM.isTrending) {
|
||||
TrendingArticle(
|
||||
articleConfigUM = articleConfigUM,
|
||||
modifier = modifier,
|
||||
onArticleClick = onArticleClick,
|
||||
colors = colors,
|
||||
)
|
||||
} else {
|
||||
DefaultArticle(
|
||||
articleConfigUM = articleConfigUM,
|
||||
modifier = modifier,
|
||||
onArticleClick = onArticleClick,
|
||||
colors = colors,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TrendingArticle(
|
||||
articleConfigUM: ArticleConfigUM,
|
||||
onArticleClick: () -> Unit,
|
||||
colors: CardColors,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(colors.containerColor)
|
||||
.clickable { onArticleClick() },
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier.matchParentSize(),
|
||||
painter = painterResource(R.drawable.bg_trending_article),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.FillWidth,
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.padding(vertical = 24.dp, horizontal = 16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = TangemTheme.colors.icon.accent.copy(alpha = 0.1f),
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
)
|
||||
.padding(horizontal = 8.dp, vertical = 4.dp),
|
||||
text = stringResourceSafe(R.string.feed_trending_now),
|
||||
style = TangemTheme.typography.caption1,
|
||||
color = TangemTheme.colors.icon.accent,
|
||||
)
|
||||
|
||||
SpacerH(12.dp)
|
||||
|
||||
Text(
|
||||
text = articleConfigUM.title,
|
||||
color = if (articleConfigUM.isViewed) {
|
||||
TangemTheme.colors.text.tertiary
|
||||
} else {
|
||||
TangemTheme.colors.text.primary1
|
||||
},
|
||||
style = TangemTheme.typography.h3,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
|
||||
SpacerH(8.dp)
|
||||
|
||||
ArticleInfo(
|
||||
score = articleConfigUM.score,
|
||||
createdAt = articleConfigUM.createdAt.resolveReference(),
|
||||
)
|
||||
|
||||
SpacerH(32.dp)
|
||||
|
||||
Tags(
|
||||
modifier = Modifier.padding(horizontal = 46.dp),
|
||||
tags = articleConfigUM.tags.toImmutableList(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun ShowMoreArticlesCardV1(modifier: Modifier = Modifier, onClick: () -> Unit) {
|
||||
BlockCard(
|
||||
modifier = modifier,
|
||||
onClick = onClick,
|
||||
colors = TangemBlockCardColors.copy(containerColor = TangemTheme.colors.background.action),
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(vertical = 31.dp, horizontal = 12.dp),
|
||||
) {
|
||||
Image(
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_show_more_news_48),
|
||||
contentDescription = stringResourceSafe(R.string.common_show_more),
|
||||
)
|
||||
|
||||
SpacerH(16.dp)
|
||||
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.news_all_news),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.news_stay_in_the_loop),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DefaultArticle(
|
||||
articleConfigUM: ArticleConfigUM,
|
||||
onArticleClick: () -> Unit,
|
||||
colors: CardColors,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(colors.containerColor)
|
||||
.clickable { onArticleClick() }
|
||||
.padding(12.dp),
|
||||
) {
|
||||
ArticleInfo(
|
||||
score = articleConfigUM.score,
|
||||
createdAt = articleConfigUM.createdAt.resolveReference(),
|
||||
)
|
||||
|
||||
SpacerH(8.dp)
|
||||
|
||||
Text(
|
||||
text = articleConfigUM.title,
|
||||
color = if (articleConfigUM.isViewed) {
|
||||
TangemTheme.colors.text.tertiary
|
||||
} else {
|
||||
TangemTheme.colors.text.primary1
|
||||
},
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
maxLines = 3,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
|
||||
SpacerH(16.dp)
|
||||
|
||||
SpacerHMax()
|
||||
|
||||
Tags(tags = articleConfigUM.tags.toImmutableList())
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun TagsPreview() {
|
||||
TangemThemePreview {
|
||||
Tags(
|
||||
tags = persistentListOf(
|
||||
LabelUM(TextReference.Str("Hype")),
|
||||
LabelUM(TextReference.Str("BTC")),
|
||||
LabelUM(TextReference.Str("Supply")),
|
||||
LabelUM(TextReference.Str("Demand")),
|
||||
LabelUM(TextReference.Str("Best rate")),
|
||||
LabelUM(TextReference.Str("Breaking news")),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360, backgroundColor = 0xFF000000)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun ArticleCardsPreview() {
|
||||
val tags = listOf(
|
||||
LabelUM(TextReference.Str("Hype")),
|
||||
LabelUM(TextReference.Str("BTC")),
|
||||
LabelUM(TextReference.Str("Supply")),
|
||||
LabelUM(TextReference.Str("Demand")),
|
||||
LabelUM(TextReference.Str("Breaking news")),
|
||||
).toImmutableSet()
|
||||
|
||||
val config = ArticleConfigUM(
|
||||
id = 1,
|
||||
title = "Bitcoin ETFs log 4th straight day of inflows (+\$550M)",
|
||||
score = 9.5f,
|
||||
createdAt = TextReference.Str("1h ago"),
|
||||
isTrending = true,
|
||||
tags = tags,
|
||||
isViewed = false,
|
||||
)
|
||||
|
||||
TangemThemePreview {
|
||||
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
|
||||
ArticleCard(
|
||||
articleConfigUM = config,
|
||||
onArticleClick = {},
|
||||
)
|
||||
|
||||
SpacerH(20.dp)
|
||||
|
||||
ArticleCard(
|
||||
articleConfigUM = config.copy(isViewed = true),
|
||||
onArticleClick = {},
|
||||
)
|
||||
|
||||
SpacerH(20.dp)
|
||||
|
||||
ArticleCard(
|
||||
articleConfigUM = config.copy(isTrending = false),
|
||||
onArticleClick = {},
|
||||
)
|
||||
|
||||
SpacerH(20.dp)
|
||||
|
||||
ArticleCard(
|
||||
articleConfigUM = config.copy(isTrending = false, isViewed = true),
|
||||
onArticleClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,14 +11,11 @@ import androidx.compose.ui.res.vectorResource
|
|||
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.label.Label
|
||||
import com.tangem.core.ui.components.label.entity.LabelLeadingContentUM
|
||||
import com.tangem.core.ui.components.label.entity.LabelUM
|
||||
import com.tangem.core.ui.ds.badge.*
|
||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||
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.utils.StringsSigns
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
|
@ -34,63 +31,14 @@ fun ArticleHeader(
|
|||
tags: ImmutableList<LabelUM>,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
ArticleHeaderV2(
|
||||
isTrending = isTrending,
|
||||
title = title,
|
||||
createdAt = createdAt,
|
||||
score = score,
|
||||
tags = tags,
|
||||
modifier = modifier,
|
||||
)
|
||||
} else {
|
||||
ArticleHeaderV1(
|
||||
title = title,
|
||||
createdAt = createdAt,
|
||||
score = score,
|
||||
tags = tags,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
private fun ArticleHeaderV1(
|
||||
title: String,
|
||||
createdAt: String,
|
||||
score: Float,
|
||||
tags: ImmutableList<LabelUM>,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
ArticleInfo(
|
||||
score = score,
|
||||
createdAt = createdAt,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
|
||||
Text(
|
||||
text = title,
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
|
||||
if (tags.isNotEmpty()) {
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
FlowRow(
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
tags.forEach { tag ->
|
||||
Label(
|
||||
state = tag,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ArticleHeaderV2(
|
||||
isTrending = isTrending,
|
||||
title = title,
|
||||
createdAt = createdAt,
|
||||
score = score,
|
||||
tags = tags,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -220,21 +168,7 @@ private fun labelLeadingBadgeIconPosition(content: LabelLeadingContentUM): Tange
|
|||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun ArticleHeaderPreviewV1() {
|
||||
TangemThemePreview {
|
||||
ArticleHeader(
|
||||
title = "Something going good!",
|
||||
createdAt = "1 hour ago",
|
||||
score = 5.5f,
|
||||
tags = persistentListOf(),
|
||||
isTrending = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun ArticleHeaderPreviewV2() {
|
||||
private fun ArticleHeaderPreview() {
|
||||
TangemThemePreviewRedesign {
|
||||
ArticleHeader(
|
||||
title = "Something going good!",
|
||||
|
|
|
|||
|
|
@ -1,61 +0,0 @@
|
|||
package com.tangem.features.feed.ui.feed.components.articles
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.size
|
||||
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.drawWithCache
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
@Composable
|
||||
internal fun ArticleInfo(score: Float, createdAt: String, modifier: Modifier = Modifier) {
|
||||
val dotColor = TangemTheme.colors.text.secondary
|
||||
Row(
|
||||
modifier = modifier,
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Image(
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_start_circle_12),
|
||||
contentDescription = null,
|
||||
)
|
||||
|
||||
Text(
|
||||
text = score.toString(),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
)
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.size(4.dp)
|
||||
.drawWithCache {
|
||||
val radius = size.minDimension / 2f
|
||||
onDrawBehind {
|
||||
drawCircle(
|
||||
color = dotColor,
|
||||
radius = radius,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
Text(
|
||||
text = createdAt,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -11,47 +11,12 @@ 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.core.ui.res.TangemThemePreviewRedesign
|
||||
|
||||
@Composable
|
||||
fun TrendingLoadingArticle(modifier: Modifier = Modifier) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
TrendingLoadingArticleV2(modifier)
|
||||
} else {
|
||||
TrendingLoadingArticleV1(modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TrendingLoadingArticleV1(modifier: Modifier = Modifier) {
|
||||
BlockCard(
|
||||
modifier = modifier,
|
||||
colors = TangemBlockCardColors.copy(containerColor = TangemTheme.colors.background.action),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 24.dp, horizontal = 16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
RectangleShimmer(modifier = Modifier.size(width = 96.dp, height = 24.dp), radius = 8.dp)
|
||||
SpacerH(12.dp)
|
||||
RectangleShimmer(modifier = Modifier.size(width = 285.dp, height = 18.dp), radius = 4.dp)
|
||||
SpacerH(6.dp)
|
||||
RectangleShimmer(modifier = Modifier.size(width = 190.dp, height = 18.dp), radius = 4.dp)
|
||||
SpacerH(14.dp)
|
||||
RectangleShimmer(modifier = Modifier.size(width = 110.dp, height = 18.dp), radius = 4.dp)
|
||||
SpacerH(32.dp)
|
||||
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
RectangleShimmer(modifier = Modifier.size(width = 64.dp, height = 24.dp), radius = 8.dp)
|
||||
RectangleShimmer(modifier = Modifier.size(width = 64.dp, height = 24.dp), radius = 8.dp)
|
||||
RectangleShimmer(modifier = Modifier.size(width = 64.dp, height = 24.dp), radius = 8.dp)
|
||||
}
|
||||
}
|
||||
}
|
||||
TrendingLoadingArticleV2(modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -96,51 +61,7 @@ private fun TrendingLoadingArticleV2(modifier: Modifier = Modifier) {
|
|||
|
||||
@Composable
|
||||
fun DefaultLoadingArticle(modifier: Modifier = Modifier) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
DefaultLoadingArticleV2(modifier)
|
||||
} else {
|
||||
DefaultLoadingArticleV1(modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DefaultLoadingArticleV1(modifier: Modifier = Modifier) {
|
||||
BlockCard(
|
||||
modifier = modifier,
|
||||
colors = TangemBlockCardColors.copy(containerColor = TangemTheme.colors.background.action),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
) {
|
||||
RectangleShimmer(modifier = Modifier.size(width = 46.dp, height = 16.dp), radius = TangemTheme.dimens2.x25)
|
||||
SpacerH(8.dp)
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(20.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
SpacerH(28.dp)
|
||||
RectangleShimmer(modifier = Modifier.size(width = 46.dp, height = 16.dp), radius = TangemTheme.dimens2.x25)
|
||||
SpacerH(8.dp)
|
||||
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier.size(width = 72.dp, height = 24.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
RectangleShimmer(
|
||||
modifier = Modifier.size(width = 62.dp, height = 24.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
RectangleShimmer(
|
||||
modifier = Modifier.size(width = 32.dp, height = 24.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
DefaultLoadingArticleV2(modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -186,16 +107,7 @@ private fun DefaultLoadingArticleV2(modifier: Modifier = Modifier) {
|
|||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun TrendingLoadingArticlePreviewV1() {
|
||||
TangemThemePreview {
|
||||
TrendingLoadingArticle()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun TrendingLoadingArticlePreviewV2() {
|
||||
private fun TrendingLoadingArticlePreview() {
|
||||
TangemThemePreviewRedesign {
|
||||
TrendingLoadingArticle()
|
||||
}
|
||||
|
|
@ -204,16 +116,7 @@ private fun TrendingLoadingArticlePreviewV2() {
|
|||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun DefaultLoadingArticlePreviewV1() {
|
||||
TangemThemePreview {
|
||||
DefaultLoadingArticle()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun DefaultLoadingArticlePreviewV2() {
|
||||
private fun DefaultLoadingArticlePreview() {
|
||||
TangemThemePreviewRedesign {
|
||||
DefaultLoadingArticle()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,14 +2,8 @@ package com.tangem.features.feed.ui.feed.components.articles
|
|||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
|
||||
@Composable
|
||||
fun ShowMoreArticlesCard(modifier: Modifier = Modifier, onClick: () -> Unit) {
|
||||
val isRedesignEnabled: Boolean = LocalRedesignEnabled.current
|
||||
if (isRedesignEnabled) {
|
||||
ShowMoreArticlesCardV2(modifier = modifier, onClick = onClick)
|
||||
} else {
|
||||
ShowMoreArticlesCardV1(modifier = modifier, onClick = onClick)
|
||||
}
|
||||
ShowMoreArticlesCardV2(modifier = modifier, onClick = onClick)
|
||||
}
|
||||
|
|
@ -8,13 +8,11 @@ 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
|
||||
import com.tangem.core.ui.ds.badge.*
|
||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.utils.StringsSigns
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
|
|
@ -36,28 +34,24 @@ internal fun Tags(tags: ImmutableList<LabelUM>, modifier: Modifier = Modifier) {
|
|||
|
||||
val tagPlaceables = subcompose(ContentSlot.Tags) {
|
||||
tags.forEach { tag ->
|
||||
if (LocalRedesignEnabled.current) {
|
||||
TangemBadge(
|
||||
text = tag.text,
|
||||
tangemIconUM = when (val content = tag.leadingContent) {
|
||||
LabelLeadingContentUM.None -> null
|
||||
is LabelLeadingContentUM.Token -> TangemIconUM.Url(
|
||||
url = content.iconUrl,
|
||||
fallbackRes = R.drawable.ic_alert_24,
|
||||
)
|
||||
},
|
||||
shape = TangemBadgeShape.Rounded,
|
||||
size = TangemBadgeSize.X6,
|
||||
type = TangemBadgeType.Tinted,
|
||||
color = TangemBadgeColor.Gray,
|
||||
iconPosition = when (tag.leadingContent) {
|
||||
LabelLeadingContentUM.None -> TangemBadgeIconPosition.None
|
||||
is LabelLeadingContentUM.Token -> TangemBadgeIconPosition.Start
|
||||
},
|
||||
)
|
||||
} else {
|
||||
Label(state = tag)
|
||||
}
|
||||
TangemBadge(
|
||||
text = tag.text,
|
||||
tangemIconUM = when (val content = tag.leadingContent) {
|
||||
LabelLeadingContentUM.None -> null
|
||||
is LabelLeadingContentUM.Token -> TangemIconUM.Url(
|
||||
url = content.iconUrl,
|
||||
fallbackRes = R.drawable.ic_alert_24,
|
||||
)
|
||||
},
|
||||
shape = TangemBadgeShape.Rounded,
|
||||
size = TangemBadgeSize.X6,
|
||||
type = TangemBadgeType.Tinted,
|
||||
color = TangemBadgeColor.Gray,
|
||||
iconPosition = when (tag.leadingContent) {
|
||||
LabelLeadingContentUM.None -> TangemBadgeIconPosition.None
|
||||
is LabelLeadingContentUM.Token -> TangemBadgeIconPosition.Start
|
||||
},
|
||||
)
|
||||
}
|
||||
}.map { it.measure(constraints) }
|
||||
|
||||
|
|
@ -134,22 +128,13 @@ private fun SubcomposeMeasureScope.calculateLayoutInfo(
|
|||
|
||||
@Composable
|
||||
private fun OverflowLabel(count: Int) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
TangemBadge(
|
||||
text = TextReference.Str("${StringsSigns.PLUS}$count"),
|
||||
shape = TangemBadgeShape.Rounded,
|
||||
size = TangemBadgeSize.X6,
|
||||
type = TangemBadgeType.Tinted,
|
||||
color = TangemBadgeColor.Gray,
|
||||
)
|
||||
} else {
|
||||
Label(
|
||||
state = LabelUM(
|
||||
text = TextReference.Str("${StringsSigns.PLUS}$count"),
|
||||
maxLines = 1,
|
||||
),
|
||||
)
|
||||
}
|
||||
TangemBadge(
|
||||
text = TextReference.Str("${StringsSigns.PLUS}$count"),
|
||||
shape = TangemBadgeShape.Rounded,
|
||||
size = TangemBadgeSize.X6,
|
||||
type = TangemBadgeType.Tinted,
|
||||
color = TangemBadgeColor.Gray,
|
||||
)
|
||||
}
|
||||
|
||||
private fun calculateRowWidth(placeables: List<Placeable>, spacingPx: Int): Int {
|
||||
|
|
|
|||
|
|
@ -24,9 +24,6 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
|||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.*
|
||||
import com.tangem.core.ui.components.appbar.TangemTopAppBar
|
||||
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
|
||||
import com.tangem.core.ui.components.buttons.segmentedbutton.SegmentedButtons
|
||||
import com.tangem.core.ui.components.currency.icon.CoinIcon
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeInPercent
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
|
|
@ -38,7 +35,6 @@ import com.tangem.core.ui.extensions.TextReference
|
|||
import com.tangem.core.ui.extensions.resolveAnnotatedReference
|
||||
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.test.MarketsTestTags
|
||||
|
|
@ -50,10 +46,8 @@ import com.tangem.features.feed.ui.market.detailed.state.ExchangesBottomSheetCon
|
|||
import com.tangem.features.feed.ui.market.detailed.state.InfoBottomSheetContent
|
||||
import com.tangem.features.feed.ui.market.detailed.state.MarketsTokenDetailsUM
|
||||
import com.tangem.features.feed.ui.market.detailed.state.SecurityScoreBottomSheetContent
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import com.tangem.core.ui.R as CoreR
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
|
|
@ -62,7 +56,6 @@ internal fun MarketsTokenDetailsContent(
|
|||
state: MarketsTokenDetailsUM,
|
||||
backgroundColor: Color,
|
||||
modifier: Modifier = Modifier,
|
||||
portfolioBlock: @Composable ((Modifier) -> Unit)?,
|
||||
portfolioFloatingBlock: @Composable ((Modifier) -> Unit)?,
|
||||
marketingBanner: @Composable (Modifier) -> Unit,
|
||||
) {
|
||||
|
|
@ -71,7 +64,6 @@ internal fun MarketsTokenDetailsContent(
|
|||
modifier = modifier,
|
||||
backgroundColor = backgroundColor,
|
||||
state = state,
|
||||
portfolioBlock = portfolioBlock,
|
||||
portfolioFloatingBlock = portfolioFloatingBlock,
|
||||
marketingBanner = marketingBanner,
|
||||
)
|
||||
|
|
@ -90,11 +82,9 @@ private fun Content(
|
|||
state: MarketsTokenDetailsUM,
|
||||
backgroundColor: Color,
|
||||
modifier: Modifier = Modifier,
|
||||
portfolioBlock: @Composable ((Modifier) -> Unit)?,
|
||||
portfolioFloatingBlock: @Composable ((Modifier) -> Unit)?,
|
||||
marketingBanner: @Composable (Modifier) -> Unit,
|
||||
) {
|
||||
val isRedesignEnabled = LocalRedesignEnabled.current
|
||||
val density = LocalDensity.current
|
||||
val bottomBarHeight = with(density) { WindowInsets.systemBars.getBottom(this).toDp() }
|
||||
val lazyListState = rememberLazyListState()
|
||||
|
|
@ -128,20 +118,19 @@ private fun Content(
|
|||
Header(state = state)
|
||||
}
|
||||
item {
|
||||
if (isRedesignEnabled) SpacerH(TangemTheme.dimens2.x3) else SpacerH16()
|
||||
SpacerH(TangemTheme.dimens2.x3)
|
||||
}
|
||||
item("intervalSelector") {
|
||||
IntervalSelector(
|
||||
trendInterval = state.selectedInterval,
|
||||
onIntervalClick = state.onSelectedIntervalChange,
|
||||
isEnabled = state.body !is MarketsTokenDetailsUM.Body.Nothing,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
if (isRedesignEnabled) SpacerH(TangemTheme.dimens2.x3) else SpacerH32()
|
||||
SpacerH(TangemTheme.dimens2.x3)
|
||||
}
|
||||
item("chart") {
|
||||
MarketTokenDetailsChart(
|
||||
|
|
@ -153,9 +142,7 @@ private fun Content(
|
|||
item { SpacerH16() }
|
||||
tokenMarketDetailsBody(
|
||||
state = state.body,
|
||||
portfolioBlock = portfolioBlock,
|
||||
relatedNews = state.relatedNews,
|
||||
isRedesignEnabled = isRedesignEnabled,
|
||||
marketingBanner = marketingBanner,
|
||||
)
|
||||
item { SpacerH(bottomSpacing) }
|
||||
|
|
@ -176,86 +163,14 @@ private fun Content(
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
internal fun MarketsTokenDetailsTopBar(
|
||||
backgroundColor: Color,
|
||||
shouldShowPriceSubtitle: Boolean,
|
||||
tokenName: String,
|
||||
tokenPrice: String,
|
||||
isBackButtonEnabled: Boolean,
|
||||
onBackClick: () -> Unit,
|
||||
onShareClick: () -> Unit,
|
||||
) {
|
||||
TangemTopAppBar(
|
||||
modifier = Modifier.drawBehind { drawRect(backgroundColor) },
|
||||
title = tokenName,
|
||||
subtitle = if (shouldShowPriceSubtitle) tokenPrice else null,
|
||||
startButton = TopAppBarButtonUM.Back(
|
||||
onBackClicked = onBackClick,
|
||||
enabled = isBackButtonEnabled,
|
||||
),
|
||||
endButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = CoreR.drawable.ic_share_24,
|
||||
onClicked = onShareClick,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Header(state: MarketsTokenDetailsUM) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
HeaderV2(
|
||||
modifier = Modifier
|
||||
.padding(TangemTheme.dimens2.x4)
|
||||
.fillMaxWidth(),
|
||||
state = state,
|
||||
)
|
||||
} else {
|
||||
HeaderV1(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16)
|
||||
.fillMaxWidth(),
|
||||
state = state,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun HeaderV1(state: MarketsTokenDetailsUM, modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
TokenPriceTextV1(
|
||||
price = state.priceText,
|
||||
triggerPriceChange = state.triggerPriceChange,
|
||||
)
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4)) {
|
||||
Text(
|
||||
text = state.dateTimeText.resolveReference(),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
if (state.priceChangePercentText != null) {
|
||||
PriceChangeInPercent(
|
||||
valueInPercent = state.priceChangePercentText,
|
||||
type = state.priceChangeType,
|
||||
textStyle = TangemTheme.typography.caption2,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
SpacerW4()
|
||||
CoinIcon(
|
||||
modifier = Modifier.requiredSize(TangemTheme.dimens.size48),
|
||||
url = state.iconUrl,
|
||||
alpha = 1f,
|
||||
colorFilter = null,
|
||||
fallbackResId = R.drawable.ic_custom_token_44,
|
||||
)
|
||||
}
|
||||
HeaderV2(
|
||||
modifier = Modifier
|
||||
.padding(TangemTheme.dimens2.x4)
|
||||
.fillMaxWidth(),
|
||||
state = state,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -324,40 +239,6 @@ private fun HeaderV2(state: MarketsTokenDetailsUM, modifier: Modifier = Modifier
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TokenPriceTextV1(
|
||||
price: String,
|
||||
triggerPriceChange: StateEvent<PriceChangeType>,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val growColor = TangemTheme.colors.text.accent
|
||||
val fallColor = TangemTheme.colors.text.warning
|
||||
val generalColor = TangemTheme.colors.text.primary1
|
||||
|
||||
val color = remember(generalColor) { Animatable(generalColor) }
|
||||
|
||||
EventEffect(triggerPriceChange) { priceChangeType ->
|
||||
val nextColor = when (priceChangeType) {
|
||||
PriceChangeType.UP,
|
||||
-> growColor
|
||||
PriceChangeType.DOWN -> fallColor
|
||||
PriceChangeType.NEUTRAL -> return@EventEffect
|
||||
}
|
||||
|
||||
color.animateTo(nextColor, snap())
|
||||
color.animateTo(generalColor, tween(durationMillis = 500))
|
||||
}
|
||||
|
||||
Text(
|
||||
text = price,
|
||||
modifier = modifier,
|
||||
color = color.value,
|
||||
autoSize = TextAutoSize.StepBased(maxFontSize = TangemTheme.typography.head.fontSize),
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.head,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TokenPriceTextV2(
|
||||
priceAnnotated: TextReference,
|
||||
|
|
@ -395,66 +276,24 @@ private fun TokenPriceTextV2(
|
|||
@Composable
|
||||
private fun IntervalSelector(
|
||||
trendInterval: PriceChangeInterval,
|
||||
isEnabled: Boolean,
|
||||
onIntervalClick: (PriceChangeInterval) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
val items = remember {
|
||||
PriceChangeInterval.entries
|
||||
.map { TangemSegmentUM(it.toString(), it.getText()) }.toImmutableList()
|
||||
}
|
||||
val selectedItem = remember(trendInterval) {
|
||||
items.firstOrNull { it.id == trendInterval.toString() }
|
||||
}
|
||||
|
||||
TangemSegmentedPicker(
|
||||
items = items,
|
||||
initialSelectedItem = selectedItem,
|
||||
isFixed = true,
|
||||
modifier = modifier,
|
||||
onClick = { onIntervalClick(PriceChangeInterval.valueOf(it.id)) },
|
||||
)
|
||||
|
||||
return
|
||||
val items = remember {
|
||||
PriceChangeInterval.entries
|
||||
.map { TangemSegmentUM(it.toString(), it.getText()) }.toImmutableList()
|
||||
}
|
||||
val selectedItem = remember(trendInterval) {
|
||||
items.firstOrNull { it.id == trendInterval.toString() }
|
||||
}
|
||||
|
||||
SegmentedButtons(
|
||||
config = persistentListOf(
|
||||
PriceChangeInterval.H24,
|
||||
PriceChangeInterval.WEEK,
|
||||
PriceChangeInterval.MONTH,
|
||||
PriceChangeInterval.MONTH3,
|
||||
PriceChangeInterval.MONTH6,
|
||||
PriceChangeInterval.YEAR,
|
||||
PriceChangeInterval.ALL_TIME,
|
||||
),
|
||||
color = TangemTheme.colors.button.secondary,
|
||||
initialSelectedItem = trendInterval,
|
||||
onClick = onIntervalClick,
|
||||
isEnabled = isEnabled,
|
||||
TangemSegmentedPicker(
|
||||
items = items,
|
||||
initialSelectedItem = selectedItem,
|
||||
isFixed = true,
|
||||
modifier = modifier,
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.align(Alignment.Center)
|
||||
.padding(
|
||||
vertical = TangemTheme.dimens.spacing4,
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
text = it.getText().resolveReference(),
|
||||
style = TangemTheme.typography.caption1,
|
||||
color = if (isEnabled) {
|
||||
TangemTheme.colors.text.primary1
|
||||
} else {
|
||||
TangemTheme.colors.text.disabled
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
onClick = { onIntervalClick(PriceChangeInterval.valueOf(it.id)) },
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -493,7 +332,6 @@ private fun MarketsTokenDetailsContent_Preview(
|
|||
MarketsTokenDetailsContent(
|
||||
state = params,
|
||||
backgroundColor = TangemTheme.colors.background.tertiary,
|
||||
portfolioBlock = {},
|
||||
portfolioFloatingBlock = null,
|
||||
marketingBanner = {},
|
||||
contentPadding = PaddingValues(),
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ 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.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.unit.Dp
|
||||
|
|
@ -21,7 +20,6 @@ import com.tangem.core.ui.components.haze.hazeEffectTangem
|
|||
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.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.generated.icons.Icons
|
||||
import com.tangem.core.ui.res.generated.icons.ic_sign_plus_24
|
||||
|
|
@ -31,29 +29,16 @@ import com.tangem.features.feed.ui.market.detailed.state.MarketsTokenDetailsUM
|
|||
@Composable
|
||||
internal fun MarketsTokenDetailsTitle(
|
||||
state: MarketsTokenDetailsUM,
|
||||
backgroundColor: Color,
|
||||
isBackButtonEnabled: Boolean,
|
||||
onBackClick: () -> Unit,
|
||||
) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
MarketsTokenDetailsRedesignTopBar(
|
||||
isAddToPortfolioButtonVisible = state.isAddToPortfolioButtonVisible,
|
||||
onAddToPortfolioClick = state.onAddToPortfolioClick,
|
||||
onShareClick = state.onShareClick,
|
||||
isBackButtonEnabled = isBackButtonEnabled,
|
||||
onBackClick = onBackClick,
|
||||
)
|
||||
} else {
|
||||
MarketsTokenDetailsTopBar(
|
||||
onBackClick = onBackClick,
|
||||
isBackButtonEnabled = isBackButtonEnabled,
|
||||
shouldShowPriceSubtitle = state.shouldShowPriceSubtitle,
|
||||
tokenName = state.tokenName,
|
||||
tokenPrice = state.priceText,
|
||||
backgroundColor = backgroundColor,
|
||||
onShareClick = state.onShareClick,
|
||||
)
|
||||
}
|
||||
MarketsTokenDetailsRedesignTopBar(
|
||||
isAddToPortfolioButtonVisible = state.isAddToPortfolioButtonVisible,
|
||||
onAddToPortfolioClick = state.onAddToPortfolioClick,
|
||||
onShareClick = state.onShareClick,
|
||||
isBackButtonEnabled = isBackButtonEnabled,
|
||||
onBackClick = onBackClick,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -5,13 +5,11 @@ import androidx.annotation.StringRes
|
|||
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.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
|
@ -29,16 +27,9 @@ 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
|
||||
import com.tangem.core.ui.components.audits.AuditLabelUM
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.buttons.SecondarySmallButton
|
||||
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.decorations.roundedShapeItemDecoration
|
||||
import com.tangem.core.ui.ds.button.*
|
||||
|
|
@ -48,10 +39,8 @@ 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.test.TokenElementsTestTags
|
||||
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.state.ExchangeItemUM
|
||||
|
|
@ -68,56 +57,7 @@ import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet as TangemBot
|
|||
*/
|
||||
@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>(
|
||||
config = config,
|
||||
addBottomInsets = false,
|
||||
title = { Title(textResId = it.titleResId, onBackClick = config.onDismissRequest) },
|
||||
content = { content ->
|
||||
Box {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentPadding = PaddingValues(bottom = bottomBarHeight),
|
||||
) {
|
||||
item(key = "subtitle") {
|
||||
Subtitle(
|
||||
subtitleRes = content.subtitleResId,
|
||||
volumeReference = content.volumeReference,
|
||||
modifier = Modifier.padding(
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
top = TangemTheme.dimens.spacing12,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
bottom = TangemTheme.dimens.spacing8,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
items(
|
||||
items = content.exchangeItems,
|
||||
key = TokenItemState::id,
|
||||
itemContent = { TokenItem(state = it, isBalanceHidden = false) },
|
||||
)
|
||||
}
|
||||
|
||||
if (content is ExchangesBottomSheetContent.Error) {
|
||||
Error(
|
||||
content = content,
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
ExchangesBottomSheetV2(config)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -191,14 +131,6 @@ private fun ExchangesBottomSheetV2(config: TangemBottomSheetConfig) {
|
|||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Title(@StringRes textResId: Int, onBackClick: () -> Unit) {
|
||||
TangemTopAppBar(
|
||||
title = stringResourceSafe(id = textResId),
|
||||
startButton = TopAppBarButtonUM.Back(onBackClicked = onBackClick),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Subtitle(@StringRes subtitleRes: Int, volumeReference: TextReference, modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
|
|
@ -213,57 +145,18 @@ private fun Subtitle(@StringRes subtitleRes: Int, volumeReference: TextReference
|
|||
|
||||
@Composable
|
||||
private fun SubtitleText(textReference: TextReference) {
|
||||
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,
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = textReference.resolveReference(),
|
||||
color = TangemTheme.colors2.text.neutral.secondary,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography2.captionSemibold12,
|
||||
)
|
||||
}
|
||||
|
||||
@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,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(id = content.message),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
textAlign = TextAlign.Center,
|
||||
style = TangemTheme.typography.caption1,
|
||||
)
|
||||
|
||||
SpacerH12()
|
||||
|
||||
SecondarySmallButton(
|
||||
config = SmallButtonConfig(
|
||||
text = resourceReference(id = R.string.alert_button_try_again),
|
||||
onClick = content.onRetryClick,
|
||||
),
|
||||
)
|
||||
}
|
||||
ErrorV2(content, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -427,10 +320,10 @@ private fun getColorByTrustValue(type: AuditLabelUM.Type): Color {
|
|||
@Preview
|
||||
@Preview(name = "Dark Theme", uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun Preview_ExchangesBottomSheet(
|
||||
private fun Preview_ExchangesBottomSheetV2(
|
||||
@PreviewParameter(ExchangesBottomSheetContentProvider::class) content: ExchangesBottomSheetContent,
|
||||
) {
|
||||
TangemThemePreview {
|
||||
TangemThemePreviewRedesign {
|
||||
ExchangesBottomSheet(
|
||||
config = TangemBottomSheetConfig(
|
||||
onDismissRequest = {},
|
||||
|
|
@ -441,25 +334,6 @@ private fun Preview_ExchangesBottomSheet(
|
|||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview(name = "Dark Theme", uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun Preview_ExchangesBottomSheetV2(
|
||||
@PreviewParameter(ExchangesBottomSheetContentProvider::class) content: ExchangesBottomSheetContent,
|
||||
) {
|
||||
TangemThemePreviewRedesign {
|
||||
CompositionLocalProvider(LocalRedesignEnabled provides true) {
|
||||
ExchangesBottomSheet(
|
||||
config = TangemBottomSheetConfig(
|
||||
onDismissRequest = {},
|
||||
content = content,
|
||||
isShown = true,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ExchangesBottomSheetContentProvider : CollectionPreviewParameterProvider<ExchangesBottomSheetContent>(
|
||||
listOf(
|
||||
ExchangesBottomSheetContent.Loading(exchangesCount = 13),
|
||||
|
|
|
|||
|
|
@ -17,19 +17,13 @@ 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
|
||||
|
|
@ -38,50 +32,7 @@ import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet as TangemBot
|
|||
|
||||
@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>(
|
||||
config = config,
|
||||
addBottomInsets = false,
|
||||
title = { TangemBottomSheetTitle(title = it.title) },
|
||||
content = { content ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
) {
|
||||
MarkdownText(
|
||||
markdown = content.body.resolveReference(),
|
||||
disableLinkMovementMethod = true,
|
||||
linkifyMask = 0,
|
||||
syntaxHighlightColor = TangemTheme.colors.text.secondary,
|
||||
style = TangemTheme.typography.body2.copy(
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
),
|
||||
)
|
||||
|
||||
if (content.generatedAINotificationUM != null) {
|
||||
AdditionalInfoNotification(
|
||||
onClick = content.generatedAINotificationUM.onClick,
|
||||
modifier = Modifier
|
||||
.padding(top = TangemTheme.dimens.spacing12, bottom = TangemTheme.dimens.spacing16)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
|
||||
SpacerH(bottomBarHeight)
|
||||
}
|
||||
},
|
||||
)
|
||||
InfoBottomSheetV2(config)
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
|
|
@ -165,20 +116,4 @@ private fun InfoBottomSheetV2(config: TangemBottomSheetConfig) {
|
|||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AdditionalInfoNotification(onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
Notification(
|
||||
config = NotificationConfig(
|
||||
subtitle = TextReference.Res(id = R.string.information_generated_with_ai),
|
||||
iconResId = R.drawable.ic_magic_28,
|
||||
onClick = onClick,
|
||||
shouldShowArrowIcon = false,
|
||||
),
|
||||
modifier = modifier,
|
||||
subtitleColor = TangemTheme.colors.text.primary1,
|
||||
containerColor = TangemTheme.colors.button.disabled,
|
||||
iconTint = TangemTheme.colors.icon.accent,
|
||||
)
|
||||
}
|
||||
|
|
@ -8,7 +8,6 @@ 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.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -20,75 +19,16 @@ 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.conditional
|
||||
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,
|
||||
) {
|
||||
if (infoPointUM.onInfoClick != null) {
|
||||
TooltipText(
|
||||
text = infoPointUM.title,
|
||||
onInfoClick = infoPointUM.onInfoClick,
|
||||
textStyle = TangemTheme.typography.caption2,
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
text = infoPointUM.title.resolveReference(),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
Row {
|
||||
Text(
|
||||
text = infoPointUM.value,
|
||||
style = TangemTheme.typography.body1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
if (infoPointUM.change != null) {
|
||||
SpacerW4()
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size8)
|
||||
.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.colors.icon.accent
|
||||
InfoPointUM.ChangeType.DOWN -> TangemTheme.colors.icon.warning
|
||||
},
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
InfoPointV2(infoPointUM, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -154,46 +94,8 @@ private fun InfoPointV2(infoPointUM: InfoPointUM, modifier: Modifier = Modifier)
|
|||
}
|
||||
|
||||
@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,
|
||||
) {
|
||||
if (withTooltip) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.requiredHeight(TangemTheme.dimens.size16)
|
||||
.fillMaxWidth(),
|
||||
contentAlignment = Alignment.CenterStart,
|
||||
) {
|
||||
TextShimmer(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
style = TangemTheme.typography.caption2,
|
||||
textSizeHeight = false,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
TextShimmer(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
style = TangemTheme.typography.caption2,
|
||||
textSizeHeight = true,
|
||||
)
|
||||
}
|
||||
TextShimmer(
|
||||
modifier = Modifier.fillMaxWidth(fraction = 0.5f),
|
||||
style = TangemTheme.typography.body1,
|
||||
textSizeHeight = true,
|
||||
)
|
||||
}
|
||||
internal fun InfoPointShimmer(modifier: Modifier = Modifier) {
|
||||
InfoPointShimmerV2(modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -223,8 +125,8 @@ private fun InfoPointShimmerV2(modifier: Modifier = Modifier) {
|
|||
@Preview
|
||||
@Preview("Dark Theme", uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun ContentPreviewV1() {
|
||||
TangemThemePreview {
|
||||
private fun ContentPreviewV2() {
|
||||
TangemThemePreviewRedesign {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.width(150.dp)
|
||||
|
|
@ -261,85 +163,4 @@ private fun ContentPreviewV1() {
|
|||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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
|
||||
private fun PreviewShimmer() {
|
||||
TangemThemePreview {
|
||||
PreviewShimmerContainer(
|
||||
shimmerContent = {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.width(150.dp)
|
||||
.background(TangemTheme.colors.background.tertiary),
|
||||
) {
|
||||
InfoPointShimmer(modifier = Modifier.fillMaxWidth())
|
||||
InfoPointShimmer(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
withTooltip = true,
|
||||
)
|
||||
InfoPointShimmer(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
withTooltip = true,
|
||||
)
|
||||
InfoPointShimmer(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
withTooltip = true,
|
||||
)
|
||||
}
|
||||
},
|
||||
actualContent = {
|
||||
ContentPreviewV1()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -6,29 +6,21 @@ import androidx.compose.material3.Text
|
|||
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
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
|
@ -36,77 +28,7 @@ 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(
|
||||
modifier = modifier,
|
||||
title = {
|
||||
TooltipText(
|
||||
text = resourceReference(R.string.markets_token_details_insights),
|
||||
textStyle = TangemTheme.typography.subtitle2,
|
||||
onInfoClick = state.onInfoClick,
|
||||
)
|
||||
},
|
||||
action = {
|
||||
SegmentedButtons(
|
||||
config = persistentListOf(
|
||||
PriceChangeInterval.H24,
|
||||
PriceChangeInterval.WEEK,
|
||||
PriceChangeInterval.MONTH,
|
||||
),
|
||||
initialSelectedItem = PriceChangeInterval.H24,
|
||||
onClick = { interval ->
|
||||
currentInterval = interval
|
||||
state.onIntervalChanged(interval)
|
||||
},
|
||||
modifier = Modifier.width(IntrinsicSize.Min),
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.align(Alignment.Center)
|
||||
.padding(
|
||||
horizontal = 14.dp,
|
||||
vertical = 4.dp,
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
text = it.getText().resolveReference(),
|
||||
style = TangemTheme.typography.caption1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
content = {
|
||||
val infoPoints = when (currentInterval) {
|
||||
PriceChangeInterval.H24 -> state.h24Info
|
||||
PriceChangeInterval.WEEK -> state.weekInfo
|
||||
PriceChangeInterval.MONTH -> state.monthInfo
|
||||
else -> state.h24Info
|
||||
}
|
||||
|
||||
GridItems(
|
||||
items = infoPoints,
|
||||
itemContent = { infoPointUM ->
|
||||
InfoPoint(
|
||||
modifier = Modifier.align(Alignment.CenterStart),
|
||||
infoPointUM = infoPointUM,
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
InsightsBlockV2(state, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -180,41 +102,7 @@ private fun InsightsBlockV2(state: InsightsUM, modifier: Modifier = Modifier) {
|
|||
|
||||
@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
|
||||
|
||||
InformationBlock(
|
||||
modifier = modifier,
|
||||
title = {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.height(headerHeight)
|
||||
.fillMaxWidth(),
|
||||
radius = TangemTheme.dimens.radius3,
|
||||
)
|
||||
},
|
||||
content = {
|
||||
GridItems(
|
||||
items = List(size = 4) { it }.toImmutableList(),
|
||||
horizontalArragement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
itemContent = {
|
||||
InfoPointShimmer(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
InsightsBlockPlaceholderV2(modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -257,8 +145,8 @@ internal fun InsightsBlockPlaceholderV2(modifier: Modifier = Modifier) {
|
|||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun ContentPreviewV1() {
|
||||
TangemThemePreview {
|
||||
private fun ContentPreviewV2() {
|
||||
TangemThemePreviewRedesign {
|
||||
InsightsBlock(
|
||||
state = InsightsUM(
|
||||
h24Info = persistentListOf(
|
||||
|
|
@ -325,95 +213,11 @@ private fun ContentPreviewV1() {
|
|||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
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 {
|
||||
private fun PreviewPlaceholderV2() {
|
||||
TangemThemePreviewRedesign {
|
||||
PreviewShimmerContainer(
|
||||
actualContent = { ContentPreviewV1() },
|
||||
actualContent = { ContentPreviewV2() },
|
||||
shimmerContent = { InsightsBlockPlaceholder() },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun PreviewPlaceholderV2() {
|
||||
TangemThemePreviewRedesign {
|
||||
CompositionLocalProvider(LocalRedesignEnabled provides true) {
|
||||
PreviewShimmerContainer(
|
||||
actualContent = { ContentPreviewV2() },
|
||||
shimmerContent = { InsightsBlockPlaceholder() },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,9 +11,6 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.compose.ui.util.fastForEach
|
||||
import com.tangem.core.ui.components.ChipShimmer
|
||||
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.button.SecondaryTangemButton
|
||||
import com.tangem.core.ui.ds.button.TangemButtonIconPosition
|
||||
import com.tangem.core.ui.ds.button.TangemButtonShape
|
||||
|
|
@ -21,11 +18,8 @@ import com.tangem.core.ui.ds.button.TangemButtonSize
|
|||
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.market.detailed.state.LinksUM
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
|
@ -33,53 +27,7 @@ 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,
|
||||
title = {
|
||||
Text(
|
||||
text = stringResourceSafe(id = R.string.markets_token_details_links),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
},
|
||||
content = {
|
||||
Column {
|
||||
SubBlockV1(
|
||||
title = stringResourceSafe(id = R.string.markets_token_details_official_links),
|
||||
links = state.officialLinks,
|
||||
onLinkClick = state.onLinkClick,
|
||||
)
|
||||
SubBlockV1(
|
||||
title = stringResourceSafe(id = R.string.markets_token_details_social),
|
||||
links = state.social,
|
||||
onLinkClick = state.onLinkClick,
|
||||
)
|
||||
SubBlockV1(
|
||||
title = stringResourceSafe(id = R.string.markets_token_details_repository),
|
||||
links = state.repository,
|
||||
onLinkClick = state.onLinkClick,
|
||||
)
|
||||
SubBlockV1(
|
||||
title = stringResourceSafe(id = R.string.markets_token_details_blockchain_site),
|
||||
links = state.blockchainSite,
|
||||
onLinkClick = state.onLinkClick,
|
||||
lastBlock = true,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
LinksBlockV2(state, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -108,48 +56,6 @@ private fun LinksBlockV2(state: LinksUM, modifier: Modifier = Modifier) {
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
private fun SubBlockV1(
|
||||
links: ImmutableList<LinksUM.Link>,
|
||||
onLinkClick: (LinksUM.Link) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
lastBlock: Boolean = false,
|
||||
title: String = "Official links",
|
||||
) {
|
||||
if (links.isEmpty()) return
|
||||
|
||||
DividerContainer(
|
||||
modifier = modifier,
|
||||
showDivider = !lastBlock,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(TangemTheme.dimens.spacing12),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
FlowRow(
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
links.fastForEach { link ->
|
||||
Chip(
|
||||
text = stringReference(link.title),
|
||||
iconResId = link.iconRes,
|
||||
onClick = { onLinkClick(link) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
private fun SubBlockV2(
|
||||
|
|
@ -196,32 +102,7 @@ private fun SubBlockV2(
|
|||
|
||||
@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,
|
||||
title = {
|
||||
TextShimmer(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
)
|
||||
},
|
||||
content = {
|
||||
Column {
|
||||
SubBlockPlaceholderV1()
|
||||
SubBlockPlaceholderV1()
|
||||
SubBlockPlaceholderV1(lastBlock = true)
|
||||
}
|
||||
},
|
||||
)
|
||||
LinksBlockPlaceholderV2(modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -233,33 +114,6 @@ private fun LinksBlockPlaceholderV2(modifier: Modifier = Modifier) {
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SubBlockPlaceholderV1(modifier: Modifier = Modifier, lastBlock: Boolean = false) {
|
||||
DividerContainer(
|
||||
modifier = modifier,
|
||||
showDivider = !lastBlock,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing12),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
TextShimmer(
|
||||
modifier = Modifier.width(78.dp),
|
||||
style = TangemTheme.typography.caption2,
|
||||
)
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
repeat(times = 3) {
|
||||
ChipShimmer(
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SubBlockPlaceholderV2(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
|
|
@ -285,56 +139,6 @@ private fun SubBlockPlaceholderV2(modifier: Modifier = Modifier) {
|
|||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun ContentPreviewV1() {
|
||||
TangemThemePreview {
|
||||
LinksBlockV1(
|
||||
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
|
||||
|
|
@ -385,18 +189,6 @@ private fun ContentPreviewV2() {
|
|||
}
|
||||
}
|
||||
|
||||
@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
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.features.feed.ui.market.detailed.components
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Icon
|
||||
|
|
@ -8,28 +7,19 @@ import androidx.compose.material3.Text
|
|||
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.platform.testTag
|
||||
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.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
|
||||
|
||||
/**
|
||||
* "Listed on" block
|
||||
|
|
@ -40,50 +30,7 @@ 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 = {
|
||||
Text(
|
||||
text = state.title.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
},
|
||||
modifier = Modifier
|
||||
.clip(shape = TangemTheme.shapes.roundedCornersXMedium)
|
||||
.clickable(enabled = state is ListedOnUM.Content) {
|
||||
(state as? ListedOnUM.Content)?.onClick?.invoke()
|
||||
}
|
||||
.testTag(MarketsTestTags.LISTED_ON_BLOCK),
|
||||
) {
|
||||
Description(
|
||||
state = state,
|
||||
modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12),
|
||||
)
|
||||
}
|
||||
|
||||
if (state is ListedOnUM.Content) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_chevron_right_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterEnd)
|
||||
.padding(end = TangemTheme.dimens.spacing12),
|
||||
)
|
||||
}
|
||||
}
|
||||
ListedOnBlockV2(state, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -129,31 +76,7 @@ private fun ListedOnBlockV2(state: ListedOnUM, modifier: Modifier = Modifier) {
|
|||
|
||||
@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(
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
modifier = Modifier.fillMaxWidth(fraction = 0.5f),
|
||||
)
|
||||
},
|
||||
modifier = modifier,
|
||||
) {
|
||||
TextShimmer(
|
||||
style = TangemTheme.typography.body2,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(fraction = 0.3f)
|
||||
.padding(bottom = TangemTheme.dimens.spacing12),
|
||||
)
|
||||
}
|
||||
ListedOnBlockPlaceholderV2(modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -175,51 +98,4 @@ internal fun ListedOnBlockPlaceholderV2(modifier: Modifier = Modifier) {
|
|||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Description(state: ListedOnUM, modifier: Modifier = Modifier) {
|
||||
Text(
|
||||
text = state.description.resolveReference(),
|
||||
modifier = modifier.testTag(MarketsTestTags.LISTED_ON_EXCHANGES_COUNT),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.body2,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(widthDp = 328, heightDp = 68)
|
||||
@Preview(name = "Dark Theme", widthDp = 328, heightDp = 68, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun Preview_ListedOnBlock(@PreviewParameter(ListenOnUMProvider::class) state: ListedOnUM?) {
|
||||
TangemThemePreview {
|
||||
if (state == null) {
|
||||
ListedOnBlockPlaceholder()
|
||||
} else {
|
||||
ListedOnBlock(state = state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun Preview_ListedOnBlock_StateChanging() {
|
||||
var state by remember { mutableStateOf<ListedOnUM?>(value = null) }
|
||||
|
||||
Preview_ListedOnBlock(state = state)
|
||||
|
||||
LaunchedEffect(key1 = null) {
|
||||
delay(timeMillis = 3000)
|
||||
|
||||
state = ListedOnUM.Empty
|
||||
}
|
||||
}
|
||||
|
||||
private class ListenOnUMProvider : CollectionPreviewParameterProvider<ListedOnUM?>(
|
||||
collection = listOf(
|
||||
ListedOnUM.Empty,
|
||||
ListedOnUM.Content(onClick = {}, amount = 5),
|
||||
null,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -1,84 +1,23 @@
|
|||
package com.tangem.features.feed.ui.market.detailed.components
|
||||
|
||||
import android.content.res.Configuration
|
||||
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
|
||||
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
|
||||
|
||||
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)
|
||||
state.metricsV2?.let {
|
||||
MetricsBlockV2(it, modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MetricsBlockV1(state: MetricsUM, modifier: Modifier = Modifier) {
|
||||
var isExpanded by remember { mutableStateOf(false) }
|
||||
|
||||
InformationBlock(
|
||||
modifier = modifier,
|
||||
title = {
|
||||
Text(
|
||||
text = stringResourceSafe(id = R.string.markets_token_details_metrics),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
},
|
||||
action = {
|
||||
if (state.metrics.size > MAX_METRICS_COUNT) {
|
||||
ShowLessMoreButton(expanded = isExpanded, onClick = { isExpanded = !isExpanded })
|
||||
}
|
||||
},
|
||||
content = {
|
||||
val metrics = if (isExpanded) {
|
||||
state.metrics
|
||||
} else {
|
||||
state.metrics.take(MAX_METRICS_COUNT).toImmutableList()
|
||||
}
|
||||
|
||||
GridItems(
|
||||
items = metrics,
|
||||
itemContent = {
|
||||
InfoPoint(infoPointUM = it)
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MetricsBlockV2(state: MetricsV2UM, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
|
|
@ -123,60 +62,9 @@ private fun MetricCard(item: InfoPointUMV2) {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO make TextButton clickable area smaller and remove paddings for an action in InformationBlock
|
||||
@Composable
|
||||
private fun ShowLessMoreButton(expanded: Boolean, onClick: () -> Unit) {
|
||||
// FIXME add string resources
|
||||
val text = if (expanded) {
|
||||
"See less"
|
||||
} else {
|
||||
"See more"
|
||||
}
|
||||
|
||||
TextButton(
|
||||
text = text,
|
||||
onClick = onClick,
|
||||
colors = TangemButtonsDefaults.positiveButtonColors,
|
||||
textStyle = TangemTheme.typography.body2,
|
||||
)
|
||||
}
|
||||
|
||||
@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 = {
|
||||
TextShimmer(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
radius = TangemTheme.dimens.radius3,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
)
|
||||
},
|
||||
action = {
|
||||
Box(Modifier)
|
||||
},
|
||||
content = {
|
||||
GridItems(
|
||||
items = List(size = 6) { it }.toImmutableList(),
|
||||
horizontalArragement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
itemContent = {
|
||||
InfoPointShimmer(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
withTooltip = true,
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
MetricsBlockPlaceholderV2(modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -279,61 +167,4 @@ private fun CirculatingSupplyCardPlaceholder() {
|
|||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview("Dark Theme", uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun BlockPreview() {
|
||||
TangemThemePreview {
|
||||
MetricsBlock(
|
||||
state = MetricsUM(
|
||||
metrics = persistentListOf(
|
||||
InfoPointUM(
|
||||
title = resourceReference(R.string.markets_token_details_market_capitalization),
|
||||
value = "1.2T",
|
||||
onInfoClick = {},
|
||||
),
|
||||
InfoPointUM(
|
||||
title = resourceReference(R.string.markets_token_details_market_rating),
|
||||
value = "A",
|
||||
onInfoClick = {},
|
||||
),
|
||||
InfoPointUM(
|
||||
title = resourceReference(R.string.markets_token_details_trading_volume),
|
||||
value = "1.2T",
|
||||
onInfoClick = {},
|
||||
),
|
||||
InfoPointUM(
|
||||
title = resourceReference(R.string.markets_token_details_fully_diluted_valuation),
|
||||
value = "1.2T",
|
||||
onInfoClick = {},
|
||||
),
|
||||
InfoPointUM(
|
||||
title = resourceReference(R.string.markets_token_details_circulating_supply),
|
||||
value = "1.2T",
|
||||
onInfoClick = {},
|
||||
),
|
||||
InfoPointUM(
|
||||
title = resourceReference(R.string.markets_token_details_total_supply),
|
||||
value = "1.2T",
|
||||
onInfoClick = {},
|
||||
),
|
||||
),
|
||||
metricsV2 = null,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview("Dark Theme", uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun PreviewPlaceholder() {
|
||||
TangemThemePreview {
|
||||
PreviewShimmerContainer(
|
||||
actualContent = { BlockPreview() },
|
||||
shimmerContent = { MetricsBlockPlaceholder() },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,6 @@ 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
|
||||
|
|
@ -28,7 +27,6 @@ import com.tangem.core.ui.ds.row.TangemRowContainer
|
|||
import com.tangem.core.ui.ds.row.TangemRowLayoutId
|
||||
import com.tangem.core.ui.extensions.*
|
||||
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
|
||||
|
|
@ -380,115 +378,113 @@ private const val BRONZE_PLACE_COLOR_LIGHT = 0xFFCC7F66
|
|||
@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 = {},
|
||||
),
|
||||
)
|
||||
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("$ 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("$ 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 = {},
|
||||
),
|
||||
)
|
||||
TradingVolumeCard(
|
||||
item = InfoPointUMV2.TradingVolume(
|
||||
tradingValue = stringReference("$ 2.3 M"),
|
||||
liquidity = 0.15f,
|
||||
trendingVolumeLiquidityType = TrendingVolumeLiquidityType.LOW,
|
||||
onInfoClick = {},
|
||||
),
|
||||
)
|
||||
|
||||
MarketPositionCard(
|
||||
item = InfoPointUMV2.MarketPosition(
|
||||
position = stringReference("1"),
|
||||
rangeValue = 0.02f,
|
||||
marketRatingType = MarketRatingType.GOLD,
|
||||
onInfoClick = {},
|
||||
marketRatingChange24H = MarketRatingChange24H.NoChanges,
|
||||
),
|
||||
)
|
||||
MarketPositionCard(
|
||||
item = InfoPointUMV2.MarketPosition(
|
||||
position = stringReference("1"),
|
||||
rangeValue = 0.02f,
|
||||
marketRatingType = MarketRatingType.GOLD,
|
||||
onInfoClick = {},
|
||||
marketRatingChange24H = MarketRatingChange24H.NoChanges,
|
||||
),
|
||||
)
|
||||
|
||||
MarketPositionCard(
|
||||
item = InfoPointUMV2.MarketPosition(
|
||||
position = stringReference("2"),
|
||||
rangeValue = 0.05f,
|
||||
marketRatingChange24H = MarketRatingChange24H.Up(1),
|
||||
marketRatingType = MarketRatingType.SILVER,
|
||||
onInfoClick = {},
|
||||
),
|
||||
)
|
||||
MarketPositionCard(
|
||||
item = InfoPointUMV2.MarketPosition(
|
||||
position = stringReference("2"),
|
||||
rangeValue = 0.05f,
|
||||
marketRatingChange24H = MarketRatingChange24H.Up(1),
|
||||
marketRatingType = MarketRatingType.SILVER,
|
||||
onInfoClick = {},
|
||||
),
|
||||
)
|
||||
|
||||
MarketPositionCard(
|
||||
item = InfoPointUMV2.MarketPosition(
|
||||
position = null,
|
||||
rangeValue = null,
|
||||
marketRatingType = MarketRatingType.OTHER,
|
||||
onInfoClick = {},
|
||||
marketRatingChange24H = MarketRatingChange24H.NoChanges,
|
||||
),
|
||||
)
|
||||
MarketPositionCard(
|
||||
item = InfoPointUMV2.MarketPosition(
|
||||
position = null,
|
||||
rangeValue = null,
|
||||
marketRatingType = MarketRatingType.OTHER,
|
||||
onInfoClick = {},
|
||||
marketRatingChange24H = MarketRatingChange24H.NoChanges,
|
||||
),
|
||||
)
|
||||
|
||||
MarketPositionCard(
|
||||
item = InfoPointUMV2.MarketPosition(
|
||||
position = stringReference("42"),
|
||||
rangeValue = 0.42f,
|
||||
marketRatingType = MarketRatingType.OTHER,
|
||||
onInfoClick = {},
|
||||
marketRatingChange24H = MarketRatingChange24H.Down(15),
|
||||
),
|
||||
)
|
||||
MarketPositionCard(
|
||||
item = InfoPointUMV2.MarketPosition(
|
||||
position = stringReference("42"),
|
||||
rangeValue = 0.42f,
|
||||
marketRatingType = MarketRatingType.OTHER,
|
||||
onInfoClick = {},
|
||||
marketRatingChange24H = MarketRatingChange24H.Down(15),
|
||||
),
|
||||
)
|
||||
|
||||
FDVCard(
|
||||
item = InfoPointUMV2.FullyDilutedValuation(
|
||||
value = stringReference("$ 1.5 T"),
|
||||
fullyDilutedValuationChange24 = stringReference("$ 2.44 M in total"),
|
||||
onInfoClick = {},
|
||||
),
|
||||
)
|
||||
FDVCard(
|
||||
item = InfoPointUMV2.FullyDilutedValuation(
|
||||
value = stringReference("$ 1.5 T"),
|
||||
fullyDilutedValuationChange24 = stringReference("$ 2.44 M in total"),
|
||||
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("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 = {},
|
||||
),
|
||||
)
|
||||
}
|
||||
CirculatingSupplyCard(
|
||||
item = InfoPointUMV2.CirculatingSupply(
|
||||
currentValue = stringReference("18.9 M ETH"),
|
||||
maxValue = null,
|
||||
fillValue = null,
|
||||
onInfoClick = {},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,269 +0,0 @@
|
|||
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.Column
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.Row
|
||||
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.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.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.StrokeCap
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
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 com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.SpacerW8
|
||||
import com.tangem.core.ui.components.TextShimmer
|
||||
import com.tangem.core.ui.components.block.information.InformationBlock
|
||||
import com.tangem.core.ui.components.buttons.segmentedbutton.SegmentedButtons
|
||||
import com.tangem.core.ui.components.progressbar.TangemLinearProgressIndicator
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemAnimations
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
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.market.detailed.getText
|
||||
import com.tangem.features.feed.ui.market.detailed.state.PricePerformanceUM
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
@Composable
|
||||
internal fun PricePerformanceBlock(state: PricePerformanceUM, modifier: Modifier = Modifier) {
|
||||
var currentInterval by remember { mutableStateOf(PriceChangeInterval.H24) }
|
||||
|
||||
InformationBlock(
|
||||
modifier = modifier,
|
||||
title = {
|
||||
Text(
|
||||
text = stringResourceSafe(id = R.string.markets_token_details_price_performance),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
},
|
||||
action = {
|
||||
SegmentedButtons(
|
||||
config = persistentListOf(
|
||||
PriceChangeInterval.H24,
|
||||
PriceChangeInterval.MONTH,
|
||||
PriceChangeInterval.ALL_TIME,
|
||||
),
|
||||
initialSelectedItem = PriceChangeInterval.H24,
|
||||
onClick = { interval ->
|
||||
currentInterval = interval
|
||||
state.onIntervalChanged(interval)
|
||||
},
|
||||
modifier = Modifier.width(IntrinsicSize.Min),
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.align(Alignment.Center)
|
||||
.padding(
|
||||
horizontal = 14.dp,
|
||||
vertical = TangemTheme.dimens.spacing4,
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
text = it.getText().resolveReference(),
|
||||
style = TangemTheme.typography.caption1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
content = {
|
||||
val value = when (currentInterval) {
|
||||
PriceChangeInterval.H24 -> state.h24
|
||||
PriceChangeInterval.MONTH -> state.month
|
||||
PriceChangeInterval.ALL_TIME -> state.all
|
||||
else -> error("")
|
||||
}
|
||||
|
||||
Content(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
state = value,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Content(state: PricePerformanceUM.Value, modifier: Modifier = Modifier) {
|
||||
val animatedIndicatorFraction by TangemAnimations.horizontalIndicatorAsState(
|
||||
targetFraction = state.indicatorFraction,
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
.padding(vertical = TangemTheme.dimens.spacing8),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.markets_token_details_low),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
SpacerW8()
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.markets_token_details_high),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
TangemLinearProgressIndicator(
|
||||
modifier = Modifier
|
||||
.height(TangemTheme.dimens.size6)
|
||||
.fillMaxWidth(),
|
||||
progress = { animatedIndicatorFraction },
|
||||
color = TangemTheme.colors.text.accent,
|
||||
backgroundColor = TangemTheme.colors.background.tertiary,
|
||||
strokeCap = StrokeCap.Round,
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = state.low,
|
||||
style = TangemTheme.typography.body1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = state.high,
|
||||
style = TangemTheme.typography.body1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.End,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun PricePerformanceBlockPlaceholder(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
|
||||
|
||||
InformationBlock(
|
||||
modifier = modifier,
|
||||
title = {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.height(headerHeight)
|
||||
.fillMaxWidth(),
|
||||
radius = TangemTheme.dimens.radius3,
|
||||
)
|
||||
},
|
||||
content = {
|
||||
Column(
|
||||
modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
TextShimmer(
|
||||
modifier = Modifier.width(35.dp),
|
||||
style = TangemTheme.typography.caption2,
|
||||
)
|
||||
SpacerW8()
|
||||
TextShimmer(
|
||||
modifier = Modifier.width(35.dp),
|
||||
style = TangemTheme.typography.caption2,
|
||||
)
|
||||
}
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.height(TangemTheme.dimens.size6)
|
||||
.fillMaxWidth(),
|
||||
radius = 27.dp,
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
TextShimmer(
|
||||
modifier = Modifier.width(TangemTheme.dimens.size56),
|
||||
style = TangemTheme.typography.body1,
|
||||
)
|
||||
SpacerW8()
|
||||
TextShimmer(
|
||||
modifier = Modifier.width(TangemTheme.dimens.size56),
|
||||
style = TangemTheme.typography.body1,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview("Dark Theme", uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun ContentPreview() {
|
||||
TangemThemePreview {
|
||||
PricePerformanceBlock(
|
||||
modifier = Modifier,
|
||||
state = PricePerformanceUM(
|
||||
h24 = PricePerformanceUM.Value(
|
||||
low = "\$38,5K",
|
||||
high = "\$58,5K",
|
||||
indicatorFraction = 0.5f,
|
||||
),
|
||||
month = PricePerformanceUM.Value(
|
||||
low = "\$500,5K",
|
||||
high = "\$5800,5K",
|
||||
indicatorFraction = 0.8f,
|
||||
),
|
||||
all = PricePerformanceUM.Value(
|
||||
low = "\$58,52",
|
||||
high = "\$580,5M",
|
||||
indicatorFraction = 0.2f,
|
||||
),
|
||||
onIntervalChanged = {},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun PlaceholderPreview() {
|
||||
TangemThemePreview {
|
||||
PreviewShimmerContainer(
|
||||
shimmerContent = {
|
||||
PricePerformanceBlockPlaceholder()
|
||||
},
|
||||
actualContent = {
|
||||
ContentPreview()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@ package com.tangem.features.feed.ui.market.detailed.components
|
|||
import androidx.annotation.FloatRange
|
||||
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
|
||||
|
|
@ -15,10 +14,7 @@ import androidx.compose.ui.graphics.CompositingStrategy
|
|||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
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
|
||||
|
|
@ -26,52 +22,7 @@ import kotlin.math.round
|
|||
private const val STARS_COUNT = 5
|
||||
|
||||
@Composable
|
||||
internal fun ScoreStarsBlock(
|
||||
score: Float,
|
||||
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
|
||||
Row(
|
||||
modifier = modifier,
|
||||
horizontalArrangement = Arrangement.spacedBy(horizontalSpacing),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = rounded.toString(),
|
||||
style = scoreTextStyle,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
Stars(fraction = percentage)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ScoreStarsBlockV2(score: Float, modifier: Modifier = Modifier) {
|
||||
internal fun ScoreStarsBlock(score: Float, modifier: Modifier = Modifier) {
|
||||
val rounded = score.roundTo1decimal()
|
||||
val percentage = rounded / STARS_COUNT
|
||||
Box(
|
||||
|
|
@ -85,59 +36,6 @@ private fun ScoreStarsBlockV2(score: Float, modifier: Modifier = Modifier) {
|
|||
@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 = STARS_COUNT) { i ->
|
||||
Box(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size16),
|
||||
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_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.accent,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Composable
|
||||
private fun StarsV2(@FloatRange(0.0, 1.0) fraction: Float = 0f) {
|
||||
val grayColor = TangemTheme.colors2.markers.iconGray
|
||||
|
||||
Row(
|
||||
|
|
|
|||
|
|
@ -6,77 +6,27 @@ import androidx.compose.foundation.clickable
|
|||
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.TokenMarketInformationBlock
|
||||
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)
|
||||
.background(TangemTheme.colors.background.action)
|
||||
.fillMaxWidth()
|
||||
.heightIn(max = TangemTheme.dimens.size72)
|
||||
.padding(all = TangemTheme.dimens.spacing12),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1F)
|
||||
.fillMaxHeight(),
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
TooltipText(
|
||||
text = resourceReference(R.string.markets_token_details_security_score),
|
||||
onInfoClick = state.onInfoClick,
|
||||
textStyle = TangemTheme.typography.subtitle2,
|
||||
)
|
||||
|
||||
Text(
|
||||
text = state.description.resolveReference(),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
ScoreStarsBlock(
|
||||
score = state.score,
|
||||
scoreTextStyle = TangemTheme.typography.body1,
|
||||
horizontalSpacing = TangemTheme.dimens.spacing8,
|
||||
)
|
||||
}
|
||||
SecurityScoreBlockV2(state, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -111,8 +61,6 @@ private fun SecurityScoreBlockV2(state: SecurityScoreUM, modifier: Modifier = Mo
|
|||
ScoreStarsBlock(
|
||||
modifier = Modifier.layoutId(layoutId = TangemRowLayoutId.END_TOP),
|
||||
score = state.score,
|
||||
scoreTextStyle = TangemTheme.typography.body1,
|
||||
horizontalSpacing = TangemTheme.dimens.spacing8,
|
||||
)
|
||||
}
|
||||
},
|
||||
|
|
@ -121,11 +69,7 @@ private fun SecurityScoreBlockV2(state: SecurityScoreUM, modifier: Modifier = Mo
|
|||
|
||||
@Composable
|
||||
internal fun SecurityScoreBlockPlaceholder(modifier: Modifier = Modifier) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
SecurityScoreBlockPlaceholderV2(modifier)
|
||||
} else {
|
||||
SecurityScoreBlockPlaceholderV1(modifier)
|
||||
}
|
||||
SecurityScoreBlockPlaceholderV2(modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -167,103 +111,26 @@ private fun SecurityScoreBlockPlaceholderV2(modifier: Modifier = Modifier) {
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SecurityScoreBlockPlaceholderV1(modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
.fillMaxWidth()
|
||||
.heightIn(max = TangemTheme.dimens.size72)
|
||||
.padding(all = TangemTheme.dimens.spacing12),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(fraction = 0.4f)
|
||||
.padding(vertical = TangemTheme.dimens.spacing2)
|
||||
.fillMaxHeight(),
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
TextShimmer(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
textSizeHeight = true,
|
||||
)
|
||||
TextShimmer(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
style = TangemTheme.typography.body2,
|
||||
textSizeHeight = true,
|
||||
)
|
||||
}
|
||||
|
||||
TextShimmer(
|
||||
modifier = Modifier.fillMaxWidth(fraction = 0.5f),
|
||||
style = TangemTheme.typography.body2,
|
||||
textSizeHeight = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@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 ContentPreviewV1() {
|
||||
TangemThemePreview {
|
||||
SecurityScoreBlock(
|
||||
state = SecurityScoreUM(
|
||||
score = 3.5f,
|
||||
description = stringReference("Based on 3 ratings"),
|
||||
onInfoClick = {},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 328, showBackground = true)
|
||||
@Preview(widthDp = 328, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun PreviewPlaceholderV1() {
|
||||
TangemThemePreview {
|
||||
PreviewShimmerContainer(
|
||||
shimmerContent = {
|
||||
SecurityScoreBlockPlaceholder(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
},
|
||||
actualContent = {
|
||||
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 = {},
|
||||
),
|
||||
)
|
||||
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)
|
||||
SpacerH(10.dp)
|
||||
|
||||
SecurityScoreBlockPlaceholder(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
SecurityScoreBlockPlaceholder(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,16 +2,12 @@ package com.tangem.features.feed.ui.market.detailed.components
|
|||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
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
|
||||
|
|
@ -25,30 +21,22 @@ 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
|
||||
|
|
@ -57,58 +45,7 @@ import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet as TangemBot
|
|||
|
||||
@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>(
|
||||
config = config,
|
||||
addBottomInsets = false,
|
||||
title = { TangemBottomSheetTitle(title = it.title) },
|
||||
content = { content ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
) {
|
||||
Text(
|
||||
text = content.description.resolveReference(),
|
||||
style = TangemTheme.typography.body2.copy(
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
),
|
||||
)
|
||||
|
||||
SpacerH12()
|
||||
content.providers.fastForEachIndexed { index, provider ->
|
||||
DividerContainer(
|
||||
modifier = Modifier
|
||||
.roundedShapeItemDecoration(
|
||||
currentIndex = index,
|
||||
lastIndex = content.providers.lastIndex,
|
||||
addDefaultPadding = false,
|
||||
)
|
||||
.background(TangemTheme.colors.background.action),
|
||||
showDivider = index != content.providers.lastIndex,
|
||||
) {
|
||||
SecurityScoreProviderRow(
|
||||
providerUM = provider,
|
||||
onLinkClick = { content.onProviderLinkClick(provider) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
SpacerH16()
|
||||
SpacerH(bottomBarHeight)
|
||||
}
|
||||
},
|
||||
)
|
||||
SecurityScoreBottomSheetV2(config)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -167,74 +104,6 @@ private fun SecurityScoreBottomSheetV2(config: TangemBottomSheetConfig) {
|
|||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SecurityScoreProviderRow(
|
||||
providerUM: SecurityScoreBottomSheetContent.SecurityScoreProviderUM,
|
||||
onLinkClick: () -> Unit,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = TangemTheme.dimens.spacing12)
|
||||
.heightIn(min = TangemTheme.dimens.size68),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
SubcomposeAsyncImage(
|
||||
modifier = Modifier
|
||||
.size(size = TangemTheme.dimens.size40)
|
||||
.clip(TangemTheme.shapes.roundedCorners8),
|
||||
model = ImageRequest.Builder(context = LocalContext.current)
|
||||
.data(providerUM.iconUrl)
|
||||
.crossfade(enable = true)
|
||||
.allowHardware(false)
|
||||
.build(),
|
||||
loading = { RectangleShimmer(radius = TangemTheme.dimens.radius8) },
|
||||
error = { RectangleShimmer(radius = TangemTheme.dimens.radius8) },
|
||||
contentDescription = null,
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier = Modifier.padding(start = TangemTheme.dimens.spacing12),
|
||||
horizontalAlignment = Alignment.Start,
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4),
|
||||
) {
|
||||
Text(
|
||||
text = providerUM.name,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
providerUM.lastAuditDate?.let { auditDate ->
|
||||
Text(
|
||||
text = auditDate,
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
SpacerWMax()
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.End,
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4),
|
||||
modifier = Modifier.clickable(
|
||||
enabled = providerUM.urlData != null,
|
||||
indication = ripple(bounded = false),
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
onClick = onLinkClick,
|
||||
),
|
||||
) {
|
||||
ScoreStarsBlock(
|
||||
score = providerUM.score,
|
||||
scoreTextStyle = TangemTheme.typography.body2,
|
||||
horizontalSpacing = TangemTheme.dimens.spacing3,
|
||||
)
|
||||
|
||||
UrlBlockV1(providerUM)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SecurityScoreProviderRowV2(
|
||||
providerUM: SecurityScoreBottomSheetContent.SecurityScoreProviderUM,
|
||||
|
|
@ -305,34 +174,6 @@ private fun SecurityScoreProviderRowV2(
|
|||
}
|
||||
}
|
||||
|
||||
@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(
|
||||
text = urlData.rootHost,
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size16),
|
||||
painter = painterResource(id = R.drawable.ic_arrow_top_right_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UrlBlockV2(
|
||||
providerUM: SecurityScoreBottomSheetContent.SecurityScoreProviderUM,
|
||||
|
|
@ -401,8 +242,8 @@ fun StarBlock(score: Float, modifier: Modifier = Modifier) {
|
|||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun SecurityScoreBottomSheetPreviewV1() {
|
||||
TangemThemePreview {
|
||||
private fun SecurityScoreBottomSheetPreviewV2() {
|
||||
TangemThemePreviewRedesign {
|
||||
SecurityScoreBottomSheet(
|
||||
config = TangemBottomSheetConfig(
|
||||
isShown = true,
|
||||
|
|
@ -411,21 +252,4 @@ private fun SecurityScoreBottomSheetPreviewV1() {
|
|||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@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,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,9 +11,7 @@ import androidx.compose.ui.unit.dp
|
|||
import com.tangem.core.ui.components.UnableToLoadData
|
||||
import com.tangem.core.ui.components.items.DescriptionItem
|
||||
import com.tangem.core.ui.components.items.DescriptionPlaceholder
|
||||
import com.tangem.core.ui.extensions.conditionalCompose
|
||||
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.feed.components.NewsSlider
|
||||
|
|
@ -22,79 +20,16 @@ import com.tangem.features.feed.ui.feed.state.NewsSliderConfig
|
|||
import com.tangem.features.feed.ui.market.detailed.state.MarketsTokenDetailsUM
|
||||
import com.tangem.features.feed.ui.market.detailed.state.MarketsTokenDetailsUM.RelatedNews
|
||||
|
||||
@Suppress("CanBeNonNullable")
|
||||
internal fun LazyListScope.tokenMarketDetailsBody(
|
||||
isRedesignEnabled: Boolean,
|
||||
state: MarketsTokenDetailsUM.Body,
|
||||
portfolioBlock: @Composable ((Modifier) -> Unit)?,
|
||||
relatedNews: RelatedNews,
|
||||
marketingBanner: @Composable (Modifier) -> Unit,
|
||||
) {
|
||||
if (isRedesignEnabled) {
|
||||
tokenMarketDetailsBodyV2(
|
||||
state = state,
|
||||
relatedNews = relatedNews,
|
||||
marketingBanner = marketingBanner,
|
||||
)
|
||||
} else {
|
||||
tokenMarketDetailsBodyV1(
|
||||
state = state,
|
||||
portfolioBlock = portfolioBlock,
|
||||
relatedNews = relatedNews,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("CanBeNonNullable")
|
||||
private fun LazyListScope.tokenMarketDetailsBodyV1(
|
||||
state: MarketsTokenDetailsUM.Body,
|
||||
portfolioBlock: @Composable ((Modifier) -> Unit)?,
|
||||
relatedNews: RelatedNews,
|
||||
) {
|
||||
when (state) {
|
||||
MarketsTokenDetailsUM.Body.Loading -> {
|
||||
item("description-loading") {
|
||||
DescriptionPlaceholder(modifier = Modifier.blockPaddings())
|
||||
}
|
||||
|
||||
if (portfolioBlock != null) {
|
||||
item(key = "portfolio") {
|
||||
portfolioBlock(Modifier.blockPaddings())
|
||||
}
|
||||
}
|
||||
|
||||
aboutCoinHeader()
|
||||
|
||||
loadingInfoBlocks(false)
|
||||
}
|
||||
is MarketsTokenDetailsUM.Body.Content -> {
|
||||
if (state.description != null) {
|
||||
description(state.description)
|
||||
}
|
||||
|
||||
if (portfolioBlock != null) {
|
||||
item(key = "portfolio") {
|
||||
portfolioBlock(Modifier.blockPaddings())
|
||||
}
|
||||
}
|
||||
|
||||
if (relatedNews.articles.isNotEmpty()) {
|
||||
relatedNews(relatedNews)
|
||||
} else {
|
||||
sectionStub(RelatedNews.SECTION_KEY)
|
||||
}
|
||||
|
||||
aboutCoinHeader()
|
||||
|
||||
infoBlocksListV1(state = state.infoBlocks)
|
||||
}
|
||||
is MarketsTokenDetailsUM.Body.Error -> {
|
||||
error(state)
|
||||
}
|
||||
MarketsTokenDetailsUM.Body.Nothing -> {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
tokenMarketDetailsBodyV2(
|
||||
state = state,
|
||||
relatedNews = relatedNews,
|
||||
marketingBanner = marketingBanner,
|
||||
)
|
||||
}
|
||||
|
||||
private fun LazyListScope.marketingBannerItem(marketingBanner: @Composable (Modifier) -> Unit) {
|
||||
|
|
@ -125,7 +60,7 @@ private fun LazyListScope.tokenMarketDetailsBodyV2(
|
|||
DescriptionPlaceholder(modifier = Modifier.blockPaddings())
|
||||
}
|
||||
|
||||
loadingInfoBlocks(true)
|
||||
loadingInfoBlocks()
|
||||
}
|
||||
is MarketsTokenDetailsUM.Body.Content -> {
|
||||
if (state.description != null) {
|
||||
|
|
@ -164,36 +99,12 @@ private fun LazyListScope.error(state: MarketsTokenDetailsUM.Body.Error) {
|
|||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.aboutCoinHeader() {
|
||||
item("aboutCoinHeader") {
|
||||
Text(
|
||||
modifier = Modifier.padding(
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
bottom = TangemTheme.dimens.spacing20,
|
||||
),
|
||||
text = stringResourceSafe(R.string.markets_about_coin_header),
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = TangemTheme.typography.h3,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.description(description: MarketsTokenDetailsUM.Description) {
|
||||
item("description") {
|
||||
DescriptionItem(
|
||||
modifier = Modifier
|
||||
.conditionalCompose(
|
||||
condition = LocalRedesignEnabled.current,
|
||||
modifier = {
|
||||
this
|
||||
.padding(horizontal = TangemTheme.dimens2.x4)
|
||||
.padding(bottom = 24.dp)
|
||||
},
|
||||
otherModifier = {
|
||||
blockPaddings()
|
||||
},
|
||||
),
|
||||
.padding(horizontal = TangemTheme.dimens2.x4)
|
||||
.padding(bottom = 24.dp),
|
||||
description = description.shortDescription,
|
||||
hasFullDescription = description.fullDescription != null,
|
||||
onReadMoreClick = description.onReadMoreClick,
|
||||
|
|
@ -201,60 +112,6 @@ private fun LazyListScope.description(description: MarketsTokenDetailsUM.Descrip
|
|||
}
|
||||
}
|
||||
|
||||
internal fun LazyListScope.infoBlocksListV1(state: MarketsTokenDetailsUM.InformationBlocks) {
|
||||
if (state.insights != null) {
|
||||
item("insights") {
|
||||
InsightsBlock(
|
||||
modifier = Modifier.blockPaddings(),
|
||||
state = state.insights,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (state.securityScore != null) {
|
||||
item("securityScore") {
|
||||
SecurityScoreBlock(
|
||||
modifier = Modifier.blockPaddings(),
|
||||
state = state.securityScore,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (state.metrics != null) {
|
||||
item("metrics") {
|
||||
MetricsBlock(
|
||||
modifier = Modifier.blockPaddings(),
|
||||
state = state.metrics,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (state.pricePerformance != null) {
|
||||
item("pricePerformance") {
|
||||
PricePerformanceBlock(
|
||||
modifier = Modifier.blockPaddings(),
|
||||
state = state.pricePerformance,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item(key = "listedOn") {
|
||||
ListedOnBlock(
|
||||
state = state.listedOn,
|
||||
modifier = Modifier.blockPaddings(),
|
||||
)
|
||||
}
|
||||
|
||||
if (state.links != null) {
|
||||
item("links") {
|
||||
LinksBlock(
|
||||
modifier = Modifier.blockPaddings(),
|
||||
state = state.links,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun LazyListScope.infoBlocksListV2(state: MarketsTokenDetailsUM.InformationBlocks, relatedNews: RelatedNews) {
|
||||
if (state.metrics != null) {
|
||||
item("metrics") {
|
||||
|
|
@ -306,40 +163,8 @@ internal fun LazyListScope.infoBlocksListV2(state: MarketsTokenDetailsUM.Informa
|
|||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.loadingInfoBlocks(isRedesignEnabled: Boolean) {
|
||||
if (isRedesignEnabled) {
|
||||
loadingInfoBlocksV2()
|
||||
} else {
|
||||
loadingInfoBlocksV1()
|
||||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.loadingInfoBlocksV1() {
|
||||
item("insights-loading") {
|
||||
InsightsBlockPlaceholder(
|
||||
modifier = Modifier.blockPaddings(),
|
||||
)
|
||||
}
|
||||
|
||||
item("securityScore-loading") {
|
||||
SecurityScoreBlockPlaceholder(modifier = Modifier.blockPaddings())
|
||||
}
|
||||
|
||||
item("metrics-loading") {
|
||||
MetricsBlockPlaceholder(modifier = Modifier.blockPaddings())
|
||||
}
|
||||
|
||||
item("pricePerformance-loading") {
|
||||
PricePerformanceBlockPlaceholder(modifier = Modifier.blockPaddings())
|
||||
}
|
||||
|
||||
item(key = "listedOn-loading") {
|
||||
ListedOnBlockPlaceholder(modifier = Modifier.blockPaddings())
|
||||
}
|
||||
|
||||
item("links-loading") {
|
||||
LinksBlockPlaceholder(modifier = Modifier.blockPaddings())
|
||||
}
|
||||
private fun LazyListScope.loadingInfoBlocks() {
|
||||
loadingInfoBlocksV2()
|
||||
}
|
||||
|
||||
private fun LazyListScope.loadingInfoBlocksV2() {
|
||||
|
|
@ -364,41 +189,6 @@ private fun LazyListScope.loadingInfoBlocksV2() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.relatedNews(relatedNews: RelatedNews) {
|
||||
item(RelatedNews.SECTION_KEY) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 32.dp, top = 20.dp)
|
||||
.onFirstVisible(
|
||||
minFractionVisible = 0.5f,
|
||||
callback = relatedNews.onFirstVisible,
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 16.dp),
|
||||
text = stringResourceSafe(R.string.news_related_news),
|
||||
style = TangemTheme.typography.h3,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
|
||||
NewsSlider(
|
||||
NewsSliderConfig(
|
||||
callbacks = NewsSliderCallbacks(
|
||||
onOpenAllNews = {}, // not applicable here
|
||||
onSliderScroll = relatedNews.onScroll,
|
||||
onSliderEndReached = {}, // not applicable here
|
||||
onArticleClick = relatedNews.onArticledClicked,
|
||||
),
|
||||
content = relatedNews.articles,
|
||||
shouldShowSeeAllNewsItem = false,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.relatedNewsV2(relatedNews: RelatedNews) {
|
||||
item(RelatedNews.SECTION_KEY) {
|
||||
Column(
|
||||
|
|
@ -436,15 +226,7 @@ private fun LazyListScope.relatedNewsV2(relatedNews: RelatedNews) {
|
|||
|
||||
@Composable
|
||||
private fun Modifier.blockPaddings(): Modifier {
|
||||
return if (LocalRedesignEnabled.current) {
|
||||
this
|
||||
.padding(horizontal = TangemTheme.dimens2.x4)
|
||||
.padding(bottom = TangemTheme.dimens2.x2)
|
||||
} else {
|
||||
this.padding(
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
bottom = TangemTheme.dimens.spacing12,
|
||||
)
|
||||
}
|
||||
return this
|
||||
.padding(horizontal = TangemTheme.dimens2.x4)
|
||||
.padding(bottom = TangemTheme.dimens2.x2)
|
||||
}
|
||||
|
|
@ -2,17 +2,12 @@ package com.tangem.features.feed.ui.market.list
|
|||
|
||||
import android.content.res.Configuration
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.drawBehind
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.layout.onGloballyPositioned
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
|
|
@ -22,17 +17,11 @@ import androidx.compose.ui.unit.Dp
|
|||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.common.ui.markets.preview.MarketChartListItemPreviewDataProvider
|
||||
import com.tangem.core.ui.components.*
|
||||
import com.tangem.core.ui.components.appbar.AppBarWithBackButtonAndIcon
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
|
||||
import com.tangem.core.ui.components.fields.SearchBar
|
||||
import com.tangem.core.ui.components.fields.TangemSearchBarDefaults
|
||||
import com.tangem.core.ui.components.fields.entity.SearchBarUM
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
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.core.ui.res.TangemThemePreview
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
|
|
@ -45,64 +34,6 @@ import com.tangem.features.feed.ui.market.list.components.MarketsListSortByBotto
|
|||
import com.tangem.features.feed.ui.market.list.components.Options
|
||||
import com.tangem.features.feed.ui.utils.FadeConstants.BASE_FADE_LEVEL
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
private const val DELAY_FOR_FOCUS_REQUEST = 500L
|
||||
|
||||
@Composable
|
||||
internal fun TopBarWithSearch(
|
||||
onBackClick: () -> Unit,
|
||||
onSearchClick: () -> Unit,
|
||||
marketsSearchBar: MarketsSearchBar,
|
||||
bottomSheetState: BottomSheetState = BottomSheetState.EXPANDED,
|
||||
) {
|
||||
val background = LocalMainBottomSheetColor.current.value
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
|
||||
val shouldShowAppBar = !marketsSearchBar.shouldAlwaysShowSearchBar &&
|
||||
!marketsSearchBar.searchBarUM.isActive &&
|
||||
marketsSearchBar.searchBarUM.query.isEmpty()
|
||||
|
||||
AnimatedContent(
|
||||
targetState = shouldShowAppBar,
|
||||
label = "TopBarTransition",
|
||||
) { showAppBar ->
|
||||
if (showAppBar) {
|
||||
AppBarWithBackButtonAndIcon(
|
||||
onBackClick = onBackClick,
|
||||
backButtonEnabled = bottomSheetState == BottomSheetState.EXPANDED,
|
||||
endButtonEnabled = bottomSheetState == BottomSheetState.EXPANDED,
|
||||
text = stringResourceSafe(R.string.markets_common_title),
|
||||
iconRes = R.drawable.ic_search_24,
|
||||
onIconClick = onSearchClick,
|
||||
backgroundColor = background,
|
||||
)
|
||||
} else {
|
||||
SearchBar(
|
||||
modifier = Modifier
|
||||
.drawBehind { drawRect(background) }
|
||||
.padding(horizontal = 16.dp)
|
||||
.padding(bottom = 8.dp),
|
||||
state = marketsSearchBar.searchBarUM,
|
||||
colors = TangemSearchBarDefaults.defaultTextFieldColors.copy(
|
||||
focusedContainerColor = TangemTheme.colors.field.focused,
|
||||
unfocusedContainerColor = TangemTheme.colors.field.focused,
|
||||
),
|
||||
focusRequester = focusRequester,
|
||||
)
|
||||
|
||||
val shouldRequestFocus =
|
||||
(marketsSearchBar.shouldAlwaysShowSearchBar || marketsSearchBar.searchBarUM.isActive) &&
|
||||
bottomSheetState == BottomSheetState.EXPANDED
|
||||
if (shouldRequestFocus) {
|
||||
LaunchedEffect(bottomSheetState) {
|
||||
delay(DELAY_FOR_FOCUS_REQUEST)
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun MarketsList(contentPadding: PaddingValues, state: MarketsListUM, modifier: Modifier = Modifier) {
|
||||
|
|
@ -121,77 +52,10 @@ internal fun MarketsList(contentPadding: PaddingValues, state: MarketsListUM, mo
|
|||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun ColumnScope.Content(contentPadding: PaddingValues, state: MarketsListUM, modifier: Modifier = Modifier) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
ContentV2(
|
||||
contentPadding = contentPadding,
|
||||
state = state,
|
||||
)
|
||||
} else {
|
||||
ContentV1(
|
||||
contentPadding = contentPadding,
|
||||
state = state,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun ColumnScope.ContentV1(contentPadding: PaddingValues, state: MarketsListUM, modifier: Modifier = Modifier) {
|
||||
val strokeColor = TangemTheme.colors.stroke.primary
|
||||
val scrolledState = remember { mutableStateOf(false) }
|
||||
Column(modifier.padding(horizontal = TangemTheme.dimens.size16)) {
|
||||
SpacerH(contentPadding.calculateTopPadding())
|
||||
AnimatedVisibility(
|
||||
visible = scrolledState.value.not() &&
|
||||
state.isInSearchMode &&
|
||||
state.marketsSearchBar.searchBarUM.query.isNotEmpty(),
|
||||
) {
|
||||
Column {
|
||||
SpacerH8()
|
||||
Text(
|
||||
text = stringResourceSafe(id = R.string.markets_search_result_title),
|
||||
style = TangemTheme.typography.h3,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
SpacerH12()
|
||||
}
|
||||
}
|
||||
Column {
|
||||
AnimatedVisibility(!state.isInSearchMode && !state.marketsSearchBar.shouldAlwaysShowSearchBar) {
|
||||
Options(
|
||||
modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12),
|
||||
sortByTypeUM = state.selectedSortBy,
|
||||
trendInterval = state.selectedInterval,
|
||||
onIntervalClick = state.onIntervalClick,
|
||||
onSortByClick = state.onSortByButtonClick,
|
||||
sortMenuUM = state.sortByMenuUM,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
val strokeWidth = TangemTheme.dimens.size0_5
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.height(strokeWidth)
|
||||
.drawBehind {
|
||||
// draw horizontal line
|
||||
if (scrolledState.value) {
|
||||
drawLine(
|
||||
color = strokeColor,
|
||||
start = Offset(0f, size.height),
|
||||
end = Offset(size.width, size.height),
|
||||
strokeWidth = strokeWidth.toPx(),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
ItemsList(
|
||||
scrolledState = scrolledState,
|
||||
isInSearchMode = state.isInSearchMode,
|
||||
state = state.list,
|
||||
private fun ColumnScope.Content(contentPadding: PaddingValues, state: MarketsListUM) {
|
||||
ContentV2(
|
||||
contentPadding = contentPadding,
|
||||
state = state,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -229,10 +93,8 @@ private fun ColumnScope.ContentV2(contentPadding: PaddingValues, state: MarketsL
|
|||
}
|
||||
}
|
||||
},
|
||||
sortByTypeUM = state.selectedSortBy,
|
||||
trendInterval = state.selectedInterval,
|
||||
onIntervalClick = state.onIntervalClick,
|
||||
onSortByClick = state.onSortByButtonClick,
|
||||
sortMenuUM = state.sortByMenuUM,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,10 +23,8 @@ import com.tangem.core.ui.components.buttons.SmallButtonConfig
|
|||
import com.tangem.core.ui.components.list.InfiniteListHandler
|
||||
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
|
||||
import com.tangem.core.ui.event.EventEffect
|
||||
import com.tangem.core.ui.extensions.conditionalCompose
|
||||
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.test.MarketsTestTags
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
|
|
@ -46,7 +44,6 @@ internal fun MarketsListLazyColumn(
|
|||
modifier: Modifier = Modifier,
|
||||
topContentPadding: Dp = 0.dp,
|
||||
) {
|
||||
val isRedesignEnabled = LocalRedesignEnabled.current
|
||||
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
|
|
@ -105,16 +102,11 @@ internal fun MarketsListLazyColumn(
|
|||
key = { _, item -> item.getComposeKey() },
|
||||
) { index, item ->
|
||||
MarketsListItem(
|
||||
modifier = Modifier.conditionalCompose(
|
||||
condition = isRedesignEnabled,
|
||||
modifier = {
|
||||
roundedShapeItemDecoration(
|
||||
currentIndex = index,
|
||||
lastIndex = state.items.lastIndex,
|
||||
backgroundColor = TangemTheme.colors2.surface.level3,
|
||||
radius = TangemTheme.dimens2.x5,
|
||||
)
|
||||
},
|
||||
modifier = Modifier.roundedShapeItemDecoration(
|
||||
currentIndex = index,
|
||||
lastIndex = state.items.lastIndex,
|
||||
backgroundColor = TangemTheme.colors2.surface.level3,
|
||||
radius = TangemTheme.dimens2.x5,
|
||||
),
|
||||
model = item,
|
||||
onClick = { state.onItemClick(item) },
|
||||
|
|
|
|||
|
|
@ -1,16 +1,11 @@
|
|||
package com.tangem.features.feed.ui.market.list.components
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
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.buttons.segmentedbutton.SegmentedButtons
|
||||
import com.tangem.core.ui.components.haze.hazeEffectTangem
|
||||
import com.tangem.core.ui.ds.button.PrimaryInverseTangemButton
|
||||
import com.tangem.core.ui.ds.button.TangemButtonShape
|
||||
|
|
@ -18,97 +13,28 @@ import com.tangem.core.ui.ds.button.TangemButtonSize
|
|||
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||
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.haptic.TangemHapticEffect
|
||||
import com.tangem.core.ui.res.LocalHapticManager
|
||||
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.model.market.list.state.MarketsListUM
|
||||
import com.tangem.features.feed.model.market.list.state.SortByMenuUM
|
||||
import com.tangem.features.feed.model.market.list.state.SortByTypeUM
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import com.tangem.core.ui.ds.button.TangemButtonIconPosition as RedesignTangemButtonIconPosition
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
internal fun Options(
|
||||
sortMenuUM: SortByMenuUM,
|
||||
sortByTypeUM: SortByTypeUM,
|
||||
trendInterval: MarketsListUM.TrendInterval,
|
||||
onSortByClick: () -> Unit,
|
||||
onIntervalClick: (MarketsListUM.TrendInterval) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
OptionsV2(
|
||||
sortMenuUM = sortMenuUM,
|
||||
trendInterval = trendInterval,
|
||||
onIntervalClick = onIntervalClick,
|
||||
modifier = modifier,
|
||||
)
|
||||
} else {
|
||||
OptionsV1(
|
||||
sortByTypeUM = sortByTypeUM,
|
||||
trendInterval = trendInterval,
|
||||
onSortByClick = onSortByClick,
|
||||
onIntervalClick = onIntervalClick,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun OptionsV1(
|
||||
sortByTypeUM: SortByTypeUM,
|
||||
trendInterval: MarketsListUM.TrendInterval,
|
||||
onSortByClick: () -> Unit,
|
||||
onIntervalClick: (MarketsListUM.TrendInterval) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.height(IntrinsicSize.Max)
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
SecondarySmallButton(
|
||||
config = SmallButtonConfig(
|
||||
text = sortByTypeUM.text,
|
||||
onClick = onSortByClick,
|
||||
icon = TangemButtonIconPosition.End(iconResId = R.drawable.ic_chevron_24),
|
||||
),
|
||||
)
|
||||
SegmentedButtons(
|
||||
config = persistentListOf(
|
||||
MarketsListUM.TrendInterval.H24,
|
||||
MarketsListUM.TrendInterval.D7,
|
||||
MarketsListUM.TrendInterval.M1,
|
||||
),
|
||||
color = TangemTheme.colors.button.secondary,
|
||||
initialSelectedItem = trendInterval,
|
||||
onClick = onIntervalClick,
|
||||
modifier = Modifier
|
||||
.width(160.dp)
|
||||
.fillMaxHeight(),
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.align(Alignment.Center)
|
||||
.padding(
|
||||
vertical = TangemTheme.dimens.spacing4,
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
text = it.text.resolveReference(),
|
||||
style = TangemTheme.typography.caption1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
OptionsV2(
|
||||
sortMenuUM = sortMenuUM,
|
||||
trendInterval = trendInterval,
|
||||
onIntervalClick = onIntervalClick,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import androidx.compose.ui.graphics.Color
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.UnableToLoadData
|
||||
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.res.*
|
||||
|
|
@ -62,7 +61,6 @@ internal fun NewsDetailsContent(state: NewsDetailsUM, contentPadding: PaddingVal
|
|||
|
||||
@Composable
|
||||
private fun Content(contentPadding: PaddingValues, state: NewsDetailsUM, background: Color) {
|
||||
val isRedesignEnabled = LocalRedesignEnabled.current
|
||||
val pagerState = rememberPagerState(
|
||||
initialPage = state.selectedArticleIndex,
|
||||
pageCount = { state.articles.size },
|
||||
|
|
@ -106,56 +104,22 @@ private fun Content(contentPadding: PaddingValues, state: NewsDetailsUM, backgro
|
|||
)
|
||||
}
|
||||
if (state.articles.size > 1) {
|
||||
if (isRedesignEnabled) {
|
||||
TangemPagerIndicator(
|
||||
pagerState = pagerState,
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.windowInsetsPadding(WindowInsets.navigationBars),
|
||||
colors = TangemPagerIndicatorColors.copy(
|
||||
overlay = TangemTheme.colors2.tabs.backgroundSecondary.copy(PAGER_BG_ALPHA),
|
||||
),
|
||||
hazeState = localHaze,
|
||||
)
|
||||
} else {
|
||||
PagerIndicator(
|
||||
pagerState = pagerState,
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.windowInsetsPadding(WindowInsets.navigationBars),
|
||||
)
|
||||
}
|
||||
TangemPagerIndicator(
|
||||
pagerState = pagerState,
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.windowInsetsPadding(WindowInsets.navigationBars),
|
||||
colors = TangemPagerIndicatorColors.copy(
|
||||
overlay = TangemTheme.colors2.tabs.backgroundSecondary.copy(PAGER_BG_ALPHA),
|
||||
),
|
||||
hazeState = localHaze,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun PreviewNewsDetailsContent() {
|
||||
TangemThemePreview {
|
||||
val background = TangemTheme.colors.background.tertiary
|
||||
CompositionLocalProvider(
|
||||
LocalMainBottomSheetColor provides remember { mutableStateOf(background) },
|
||||
) {
|
||||
NewsDetailsContent(
|
||||
state = NewsDetailsUM(
|
||||
articlesStateUM = ArticlesStateUM.Content,
|
||||
articles = MockArticlesFactory.createMockArticles(),
|
||||
selectedArticleIndex = 0,
|
||||
onShareClick = {},
|
||||
onLikeClick = {},
|
||||
onBackClick = {},
|
||||
onArticleIndexChanged = {},
|
||||
),
|
||||
contentPadding = PaddingValues(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -11,16 +11,13 @@ 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.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.BottomFade
|
||||
import com.tangem.core.ui.components.SecondaryButtonIconStart
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButtonSize
|
||||
import com.tangem.core.ui.components.haze.hazeSourceTangem
|
||||
import com.tangem.core.ui.ds.button.SecondaryTangemButton
|
||||
import com.tangem.core.ui.ds.button.TangemButtonShape
|
||||
|
|
@ -30,7 +27,6 @@ import com.tangem.core.ui.extensions.resourceReference
|
|||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.LocalHazeState
|
||||
import com.tangem.core.ui.res.LocalMainBottomSheetColor
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.feed.ui.feed.components.articles.ArticleHeader
|
||||
import com.tangem.features.feed.ui.news.details.state.ArticleUM
|
||||
|
|
@ -47,150 +43,14 @@ internal fun ArticleDetail(
|
|||
relatedTokensUM: RelatedTokensUM,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
ArticleDetailV2(
|
||||
contentPadding = contentPadding,
|
||||
article = article,
|
||||
onLikeClick = onLikeClick,
|
||||
relatedTokensUM = relatedTokensUM,
|
||||
modifier = modifier,
|
||||
hazeState = hazeState,
|
||||
)
|
||||
} else {
|
||||
ArticleDetailV1(
|
||||
article = article,
|
||||
onLikeClick = onLikeClick,
|
||||
relatedTokensUM = relatedTokensUM,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun ArticleDetailV1(
|
||||
article: ArticleUM,
|
||||
onLikeClick: () -> Unit,
|
||||
relatedTokensUM: RelatedTokensUM,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val hapticFeedback = LocalHapticFeedback.current
|
||||
val density = LocalDensity.current
|
||||
val background = LocalMainBottomSheetColor.current.value
|
||||
val pagerHeight = 32.dp
|
||||
val contentPadding = pagerHeight + 56.dp + with(density) {
|
||||
WindowInsets.navigationBars.getBottom(this).div(this.density)
|
||||
}.dp
|
||||
|
||||
Box(modifier = modifier) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentPadding = PaddingValues(bottom = contentPadding),
|
||||
) {
|
||||
item("content") {
|
||||
ArticleHeader(
|
||||
title = article.title,
|
||||
createdAt = article.createdAt.resolveReference(),
|
||||
score = article.score,
|
||||
tags = article.tags,
|
||||
isTrending = article.isTrending,
|
||||
modifier = Modifier
|
||||
.padding(top = 16.dp)
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
|
||||
if (article.shortContent.isNotEmpty()) {
|
||||
QuickRecap(
|
||||
content = article.shortContent,
|
||||
modifier = Modifier
|
||||
.padding(top = 32.dp)
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
text = article.content,
|
||||
style = TangemTheme.typography.body1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
modifier = Modifier
|
||||
.padding(top = 16.dp)
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
|
||||
SpacerH(24.dp)
|
||||
|
||||
SecondaryButtonIconStart(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
iconResId = if (article.isLiked) {
|
||||
R.drawable.ic_heart_filled_20
|
||||
} else {
|
||||
R.drawable.ic_heart_20
|
||||
},
|
||||
iconTint = Color.Unspecified,
|
||||
text = stringResourceSafe(R.string.news_like),
|
||||
size = TangemButtonSize.RoundedAction,
|
||||
onClick = {
|
||||
hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
onLikeClick()
|
||||
},
|
||||
)
|
||||
|
||||
RelatedTokensBlock(
|
||||
relatedTokensUM = relatedTokensUM,
|
||||
onItemClick = when (relatedTokensUM) {
|
||||
is RelatedTokensUM.Content -> relatedTokensUM.onTokenClick
|
||||
else -> null
|
||||
},
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
)
|
||||
|
||||
if (article.relatedArticles.isNotEmpty()) {
|
||||
SpacerH(24.dp)
|
||||
Row(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.news_sources),
|
||||
style = TangemTheme.typography.h3,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
Text(
|
||||
text = "${article.relatedArticles.size}",
|
||||
style = TangemTheme.typography.h3,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (article.relatedArticles.isNotEmpty()) {
|
||||
item("relatedArticles") {
|
||||
LazyRow(
|
||||
modifier = Modifier.padding(vertical = 12.dp),
|
||||
state = rememberLazyListState(),
|
||||
contentPadding = PaddingValues(horizontal = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
items(
|
||||
items = article.relatedArticles,
|
||||
key = RelatedArticleUM::id,
|
||||
) { article ->
|
||||
RelatedNewsItem(
|
||||
relatedArticle = article,
|
||||
modifier = Modifier.fillParentMaxHeight(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BottomFade(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter),
|
||||
backgroundColor = background,
|
||||
)
|
||||
}
|
||||
ArticleDetailV2(
|
||||
contentPadding = contentPadding,
|
||||
article = article,
|
||||
onLikeClick = onLikeClick,
|
||||
relatedTokensUM = relatedTokensUM,
|
||||
modifier = modifier,
|
||||
hazeState = hazeState,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
|
|
|
|||
|
|
@ -10,94 +10,12 @@ 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.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
|
||||
fun NewsDetailsPlaceholder(contentPadding: PaddingValues, background: Color, modifier: Modifier = Modifier) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
NewsDetailsPlaceholderV2(contentPadding, background, modifier)
|
||||
} else {
|
||||
NewsDetailsPlaceholderV1(background, modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun NewsDetailsPlaceholderV1(background: Color, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.background(background)
|
||||
.padding(16.dp),
|
||||
) {
|
||||
RectangleShimmer(modifier = Modifier.size(width = 112.dp, height = 20.dp))
|
||||
SpacerH(8.dp)
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(28.dp),
|
||||
)
|
||||
SpacerH(4.dp)
|
||||
RectangleShimmer(modifier = Modifier.size(height = 28.dp, width = 208.dp))
|
||||
SpacerH(20.dp)
|
||||
RectangleShimmer(modifier = Modifier.size(height = 36.dp, width = 99.dp), radius = 12.dp)
|
||||
SpacerH(32.dp)
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(20.dp),
|
||||
)
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(20.dp)
|
||||
.padding(end = 30.dp),
|
||||
)
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(20.dp)
|
||||
.padding(end = 30.dp),
|
||||
)
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(20.dp)
|
||||
.padding(end = 24.dp),
|
||||
)
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(20.dp)
|
||||
.padding(end = 70.dp),
|
||||
)
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(20.dp)
|
||||
.padding(end = 30.dp),
|
||||
)
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(20.dp)
|
||||
.padding(end = 96.dp),
|
||||
)
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(20.dp)
|
||||
.padding(end = 100.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
NewsDetailsPlaceholderV2(contentPadding, background, modifier)
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
|
|
@ -183,18 +101,6 @@ private fun NewsDetailsPlaceholderV2(contentPadding: PaddingValues, background:
|
|||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun NewsDetailsPlaceholderPreviewV1() {
|
||||
TangemThemePreview {
|
||||
NewsDetailsPlaceholder(
|
||||
background = TangemTheme.colors.background.tertiary,
|
||||
contentPadding = PaddingValues(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.tangem.features.feed.ui.news.details.components
|
|||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.VerticalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -11,66 +10,22 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.text.withStyle
|
||||
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.SpacerH
|
||||
import com.tangem.core.ui.components.SpacerW
|
||||
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.TangemThemePreviewRedesign
|
||||
|
||||
@Composable
|
||||
internal fun QuickRecap(content: String, modifier: Modifier = Modifier) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
QuickRecapV2(content, modifier)
|
||||
} else {
|
||||
QuickRecapV1(content, modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun QuickRecapV1(content: String, modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
modifier = modifier.height(IntrinsicSize.Min),
|
||||
) {
|
||||
VerticalDivider(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.padding(start = 8.dp),
|
||||
thickness = 2.dp,
|
||||
color = TangemTheme.colors.stroke.primary,
|
||||
)
|
||||
Column(modifier = Modifier.padding(start = 20.dp)) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_quick_recap_16),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.accent,
|
||||
modifier = Modifier.size(20.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.news_quick_recap),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.accent,
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
Text(
|
||||
text = content,
|
||||
style = TangemTheme.typography.body1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
}
|
||||
}
|
||||
QuickRecapV2(content, modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -24,87 +24,12 @@ import com.tangem.core.ui.components.SpacerH
|
|||
import com.tangem.core.ui.components.SpacerHMax
|
||||
import com.tangem.core.ui.components.SpacerW
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.feed.ui.news.details.state.RelatedArticleUM
|
||||
|
||||
@Composable
|
||||
internal fun RelatedNewsItem(relatedArticle: RelatedArticleUM, modifier: Modifier = Modifier) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
RelatedNewsItemV2(relatedArticle, modifier)
|
||||
} else {
|
||||
RelatedNewsItemV1(relatedArticle, modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RelatedNewsItemV1(relatedArticle: RelatedArticleUM, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.sizeIn(maxWidth = 256.dp, minHeight = 132.dp)
|
||||
.background(color = TangemTheme.colors.background.action, shape = RoundedCornerShape(12.dp))
|
||||
.clickable(onClick = relatedArticle.onClick)
|
||||
.padding(12.dp),
|
||||
) {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(bottom = 4.dp)) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_explore_16),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
modifier = Modifier.size(16.dp),
|
||||
)
|
||||
SpacerW(4.dp)
|
||||
Text(
|
||||
text = relatedArticle.media.name,
|
||||
style = TangemTheme.typography.caption1,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
if (relatedArticle.title.isNotEmpty()) {
|
||||
SpacerH(4.dp)
|
||||
Text(
|
||||
text = relatedArticle.title,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
maxLines = 3,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
}
|
||||
if (relatedArticle.imageUrl != null) {
|
||||
SubcomposeAsyncImage(
|
||||
modifier = Modifier
|
||||
.size(40.dp)
|
||||
.clip(RoundedCornerShape(4.dp)),
|
||||
contentScale = ContentScale.Crop,
|
||||
model = ImageRequest.Builder(context = LocalContext.current)
|
||||
.data(relatedArticle.imageUrl)
|
||||
.crossfade(enable = false)
|
||||
.allowHardware(true)
|
||||
.memoryCachePolicy(CachePolicy.DISABLED)
|
||||
.build(),
|
||||
loading = {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier.size(40.dp),
|
||||
radius = 4.dp,
|
||||
)
|
||||
},
|
||||
error = {},
|
||||
contentDescription = relatedArticle.media.name,
|
||||
)
|
||||
}
|
||||
}
|
||||
SpacerHMax()
|
||||
Text(
|
||||
text = relatedArticle.publishedAt.resolveReference(),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
RelatedNewsItemV2(relatedArticle, modifier)
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
|
|
|
|||
|
|
@ -10,17 +10,13 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import com.tangem.common.ui.markets.MarketsListItem
|
||||
import com.tangem.common.ui.markets.MarketsListItemPlaceholder
|
||||
import com.tangem.common.ui.markets.models.MarketsListItemUM
|
||||
import com.tangem.core.ui.R
|
||||
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.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.feed.model.news.details.NewsDetailsModel.Companion.RELATED_TOKEN_MAX_COUNT
|
||||
import com.tangem.features.feed.ui.news.details.state.RelatedTokensUM
|
||||
|
|
@ -31,69 +27,11 @@ internal fun RelatedTokensBlock(
|
|||
onItemClick: ((MarketsListItemUM) -> Unit)?,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
RelatedTokensBlockV2(
|
||||
relatedTokensUM = relatedTokensUM,
|
||||
onItemClick = onItemClick,
|
||||
modifier = modifier,
|
||||
)
|
||||
} else {
|
||||
RelatedTokensBlockV1(
|
||||
relatedTokensUM = relatedTokensUM,
|
||||
onItemClick = onItemClick,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun RelatedTokensBlockV1(
|
||||
relatedTokensUM: RelatedTokensUM,
|
||||
onItemClick: ((MarketsListItemUM) -> Unit)?,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val isVisible = remember(relatedTokensUM) {
|
||||
when (relatedTokensUM) {
|
||||
is RelatedTokensUM.Content -> relatedTokensUM.items.isNotEmpty()
|
||||
RelatedTokensUM.Loading -> true
|
||||
RelatedTokensUM.LoadingError -> false
|
||||
}
|
||||
}
|
||||
|
||||
if (!isVisible) return
|
||||
|
||||
Column(modifier = modifier) {
|
||||
SpacerH(40.dp)
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.news_related_tokens),
|
||||
style = TangemTheme.typography.h3,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
SpacerH(12.dp)
|
||||
|
||||
BlockCard(
|
||||
colors = TangemBlockCardColors.copy(containerColor = TangemTheme.colors.background.action),
|
||||
) {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
when (relatedTokensUM) {
|
||||
is RelatedTokensUM.Content -> {
|
||||
relatedTokensUM.items.fastForEach { marketsListItemUM ->
|
||||
MarketsListItem(
|
||||
model = marketsListItemUM,
|
||||
onClick = { onItemClick?.invoke(marketsListItemUM) },
|
||||
)
|
||||
}
|
||||
}
|
||||
RelatedTokensUM.Loading -> {
|
||||
repeat(RELATED_TOKEN_MAX_COUNT) {
|
||||
MarketsListItemPlaceholder()
|
||||
}
|
||||
}
|
||||
RelatedTokensUM.LoadingError -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RelatedTokensBlockV2(
|
||||
relatedTokensUM = relatedTokensUM,
|
||||
onItemClick = onItemClick,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -13,16 +13,13 @@ import androidx.compose.ui.layout.onGloballyPositioned
|
|||
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.SpacerH
|
||||
import com.tangem.core.ui.components.TopFade
|
||||
import com.tangem.core.ui.components.chip.Chip
|
||||
import com.tangem.core.ui.components.chip.entity.ChipUM
|
||||
import com.tangem.core.ui.components.label.entity.LabelUM
|
||||
import com.tangem.core.ui.ds.tabs.TangemTab
|
||||
import com.tangem.core.ui.event.EventEffect
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.res.LocalMainBottomSheetColor
|
||||
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.ArticleConfigUM
|
||||
|
|
@ -36,57 +33,11 @@ import kotlinx.collections.immutable.toImmutableSet
|
|||
|
||||
@Composable
|
||||
internal fun NewsListContent(contentPadding: PaddingValues, state: NewsListUM, modifier: Modifier = Modifier) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
NewsListContentV2(
|
||||
contentPadding = contentPadding,
|
||||
state = state,
|
||||
modifier = modifier,
|
||||
)
|
||||
} else {
|
||||
NewsListContentV1(
|
||||
contentPadding = contentPadding,
|
||||
state = state,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun NewsListContentV1(contentPadding: PaddingValues, state: NewsListUM, modifier: Modifier = Modifier) {
|
||||
val background = LocalMainBottomSheetColor.current.value
|
||||
val lazyListState = rememberLazyListState()
|
||||
val chipsListState = rememberLazyListState()
|
||||
|
||||
ScrollChipsToSelected(state = state, chipsListState = chipsListState)
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.background(background),
|
||||
) {
|
||||
SpacerH(contentPadding.calculateTopPadding())
|
||||
LazyRow(
|
||||
state = chipsListState,
|
||||
contentPadding = PaddingValues(horizontal = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
items(
|
||||
items = state.filters,
|
||||
key = { it.id },
|
||||
) { filter ->
|
||||
Chip(state = filter)
|
||||
}
|
||||
}
|
||||
|
||||
SpacerH(16.dp)
|
||||
|
||||
NewsListLazyColumn(
|
||||
newsListState = state.newsListState,
|
||||
listOfArticles = state.listOfArticles,
|
||||
lazyListState = lazyListState,
|
||||
onArticleClick = state.onArticleClick,
|
||||
)
|
||||
}
|
||||
NewsListContentV2(
|
||||
contentPadding = contentPadding,
|
||||
state = state,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -19,9 +19,7 @@ import androidx.compose.ui.unit.Dp
|
|||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.components.UnableToLoadData
|
||||
import com.tangem.core.ui.components.block.TangemBlockCardColors
|
||||
import com.tangem.core.ui.components.list.InfiniteListHandler
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.feed.ui.feed.components.articles.ArticleCard
|
||||
import com.tangem.features.feed.ui.feed.components.articles.ArticleConfigUM
|
||||
import com.tangem.features.feed.ui.feed.components.articles.DefaultLoadingArticle
|
||||
|
|
@ -121,7 +119,6 @@ private fun Content(
|
|||
modifier = Modifier
|
||||
.heightIn(min = 152.dp)
|
||||
.fillMaxWidth(),
|
||||
colors = TangemBlockCardColors.copy(containerColor = TangemTheme.colors.background.action),
|
||||
articleConfigUM = article,
|
||||
onArticleClick = {
|
||||
onArticleClick(article.id)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import com.google.common.truth.Truth.assertThat
|
|||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.model.MutableParamsContainer
|
||||
import com.tangem.core.ui.DesignFeatureToggles
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.earn.usecase.FetchTopEarnTokensUseCase
|
||||
|
|
@ -49,7 +48,6 @@ internal class FeedComponentModelTest {
|
|||
private val fetchTopEarnTokensUseCase: FetchTopEarnTokensUseCase = mockk(relaxed = true)
|
||||
private val getTopEarnTokensUseCase: GetTopEarnTokensUseCase = mockk()
|
||||
private val appRouter: AppRouter = mockk(relaxed = true)
|
||||
private val designFeatureToggles: DesignFeatureToggles = mockk()
|
||||
private val addToPortfolioManagerFactory: AddToPortfolioManager.Factory = mockk(relaxed = true)
|
||||
private val getTopFiveMarketTokenUseCase: GetTopFiveMarketTokenUseCase = mockk(relaxed = true)
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase = mockk()
|
||||
|
|
@ -89,7 +87,6 @@ internal class FeedComponentModelTest {
|
|||
every { getSelectedAppCurrencyUseCase() } returns flowOf(Either.Right(AppCurrency.Default))
|
||||
every { manageTrendingNewsUseCase.observeTrendingNews() } returns emptyFlow()
|
||||
every { getTopEarnTokensUseCase() } returns emptyFlow()
|
||||
every { designFeatureToggles.isRedesignEnabled } returns false
|
||||
|
||||
val paramsContainer = MutableParamsContainer(FeedParams(feedClickIntents = feedClickIntents))
|
||||
|
||||
|
|
@ -102,7 +99,6 @@ internal class FeedComponentModelTest {
|
|||
fetchTopEarnTokensUseCase = fetchTopEarnTokensUseCase,
|
||||
getTopEarnTokensUseCase = getTopEarnTokensUseCase,
|
||||
appRouter = appRouter,
|
||||
designFeatureToggles = designFeatureToggles,
|
||||
addToPortfolioManagerFactory = addToPortfolioManagerFactory,
|
||||
forYouFeatureToggles = forYouFeatureToggles,
|
||||
getTopFiveMarketTokenUseCase = getTopFiveMarketTokenUseCase,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue