From 2d753e14af7d1ae17d11a8512112f25162aa6a78 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 5 Sep 2024 18:33:40 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../tap/di/domain/TokensDomainModule.kt | 3 ++ .../tangem/tap/routing/utils/ChildFactory.kt | 1 + .../com/tangem/common/routing/AppRoute.kt | 3 +- .../tangem/common/ui/charts/MarketChart.kt | 2 ++ .../com/tangem/core/ui/components/Shimmers.kt | 20 +++++++++++ .../src/main/res/drawable/ic_analytics_24.xml | 18 ++++++++++ .../repository/DefaultQuotesRepository.kt | 1 - .../domain/markets/TokenMarketParams.kt | 2 +- domain/tokens/build.gradle.kts | 1 + .../tokens/GetCryptoCurrencyActionsUseCase.kt | 8 +++++ .../domain/tokens/model/TokenActionsState.kt | 2 ++ .../managetokens/ui/ManageTokensScreen.kt | 13 ++++++- .../details/MarketsTokenDetailsComponent.kt | 1 + features/markets/impl/build.gradle.kts | 1 + .../DefaultMarketsTokenDetailsComponent.kt | 34 +++++++++++++------ .../impl/model/MarketsTokenDetailsModel.kt | 13 +++---- .../impl/ui/MarketsTokenDetailsContent.kt | 22 ++++++------ .../impl/ui/components/InfoBottomSheet.kt | 14 +++++--- .../details/impl/ui/components/LinksBlock.kt | 5 ++- .../ui/components/PricePerformanceBlock.kt | 2 +- .../ui/components/TokenMarketDetailsBody.kt | 14 +++++--- .../impl/ui/state/MarketsTokenDetailsUM.kt | 4 +-- .../impl/DefaultMarketsEntryComponent.kt | 1 + .../impl/model/TokenActionsHandler.kt | 7 ++-- .../impl/ui/AddToPortfolioBottomSheet.kt | 14 +++++++- .../block/impl/model/TokenMarketBlockModel.kt | 1 + features/wallet/impl/build.gradle.kts | 1 + .../MultiWalletCurrencyActionsConverter.kt | 5 +++ .../WalletCurrencyActionsClickIntents.kt | 29 ++++++++++++++++ gradle/dependencies.toml | 2 ++ 30 files changed, 189 insertions(+), 55 deletions(-) create mode 100644 core/ui/src/main/res/drawable/ic_analytics_24.xml diff --git a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt index 8ec7199796..47d32be700 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt @@ -7,6 +7,7 @@ import com.tangem.domain.tokens.* import com.tangem.domain.tokens.repository.* import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.feature.swap.domain.api.SwapRepository +import com.tangem.features.markets.MarketsFeatureToggles import com.tangem.features.staking.api.featuretoggles.StakingFeatureToggles import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module @@ -230,6 +231,7 @@ internal object TokensDomainModule { networksRepository: NetworksRepository, stakingRepository: StakingRepository, stakingFeatureToggles: StakingFeatureToggles, + marketsFeatureToggles: MarketsFeatureToggles, dispatchers: CoroutineDispatcherProvider, ): GetCryptoCurrencyActionsUseCase { return GetCryptoCurrencyActionsUseCase( @@ -241,6 +243,7 @@ internal object TokensDomainModule { networksRepository = networksRepository, stakingRepository = stakingRepository, stakingFeatureToggles = stakingFeatureToggles, + marketsFeatureToggles = marketsFeatureToggles, dispatchers = dispatchers, ) } diff --git a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt index c2b8cf249c..f33cf4b1af 100644 --- a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt +++ b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt @@ -193,6 +193,7 @@ internal class ChildFactory @Inject constructor( params = MarketsTokenDetailsComponent.Params( token = route.token, appCurrency = route.appCurrency, + showPortfolio = route.showPortfolio, ), componentFactory = marketsTokenDetailsComponentFactory, ) diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt index d8e383a18e..2362e15686 100644 --- a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt +++ b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt @@ -271,5 +271,6 @@ sealed class AppRoute(val path: String) : Route { data class MarketsTokenDetails( val token: TokenMarketParams, val appCurrency: AppCurrency, - ) : AppRoute(path = "/markets_token_details/${token.id}") + val showPortfolio: Boolean, + ) : AppRoute(path = "/markets_token_details/${token.id}/$showPortfolio") } \ No newline at end of file diff --git a/common/ui-charts/src/main/kotlin/com/tangem/common/ui/charts/MarketChart.kt b/common/ui-charts/src/main/kotlin/com/tangem/common/ui/charts/MarketChart.kt index cd14d8f8c8..992380b05e 100644 --- a/common/ui-charts/src/main/kotlin/com/tangem/common/ui/charts/MarketChart.kt +++ b/common/ui-charts/src/main/kotlin/com/tangem/common/ui/charts/MarketChart.kt @@ -125,6 +125,8 @@ fun MarketChart( // Sometimes the chart is not drawn correctly (ex. in LazyLayout), so we need to force the redraw .drawBehind { state.markerFraction + state.chartColor + state.markerHighlightRightSide }, chart = chart, modelProducer = state.modelProducer, diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/Shimmers.kt b/core/ui/src/main/java/com/tangem/core/ui/components/Shimmers.kt index c0f97514dc..e613aa253a 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/Shimmers.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/Shimmers.kt @@ -22,10 +22,13 @@ import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.text.TextStyle import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp import com.tangem.core.ui.R import com.tangem.core.ui.components.buttons.PrimarySmallButton import com.tangem.core.ui.components.buttons.SmallButtonConfig +import com.tangem.core.ui.components.buttons.chip.Chip import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition +import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.res.* import com.valentinilk.shimmer.* @@ -123,6 +126,22 @@ fun SmallButtonShimmer( ) } +/** + * Shimmer for Chip + * Height and min width will be set automatically + */ +@Composable +fun ChipShimmer(modifier: Modifier = Modifier) { + Chip( + modifier = modifier + .clip(RoundedCornerShape(size = 100.dp)) + .shimmer(LocalTangemShimmer.current), + text = TextReference.EMPTY, + iconResId = R.drawable.ic_plus_24, + onClick = {}, + ) +} + internal val TangemShimmer: Shimmer @Composable get() = rememberShimmer( @@ -195,6 +214,7 @@ private fun ShimmersPreview() { ) SmallButtonShimmer(withIcon = true) SmallButtonShimmer(withIcon = false) + ChipShimmer() } } } diff --git a/core/ui/src/main/res/drawable/ic_analytics_24.xml b/core/ui/src/main/res/drawable/ic_analytics_24.xml new file mode 100644 index 0000000000..743431578d --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_analytics_24.xml @@ -0,0 +1,18 @@ + + + + diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt index cc25c467e0..13e1c3e368 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt @@ -143,7 +143,6 @@ internal class DefaultQuotesRepository( block = { acc.add(rawCurrencyId) }, ) } - acc } } diff --git a/domain/markets/models/src/main/kotlin/com/tangem/domain/markets/TokenMarketParams.kt b/domain/markets/models/src/main/kotlin/com/tangem/domain/markets/TokenMarketParams.kt index 643846c466..e14b1f417b 100644 --- a/domain/markets/models/src/main/kotlin/com/tangem/domain/markets/TokenMarketParams.kt +++ b/domain/markets/models/src/main/kotlin/com/tangem/domain/markets/TokenMarketParams.kt @@ -15,7 +15,7 @@ data class TokenMarketParams( @Serializable data class Quotes( val currentPrice: SerializedBigDecimal, - val h24Percent: SerializedBigDecimal, + val h24Percent: SerializedBigDecimal?, val weekPercent: SerializedBigDecimal?, val monthPercent: SerializedBigDecimal?, ) diff --git a/domain/tokens/build.gradle.kts b/domain/tokens/build.gradle.kts index bbe01868f9..a1c639300b 100644 --- a/domain/tokens/build.gradle.kts +++ b/domain/tokens/build.gradle.kts @@ -28,6 +28,7 @@ dependencies { /** Project - Api */ implementation(projects.features.send.api) implementation(projects.features.staking.api) + implementation(projects.features.markets.api) /** Project - Other */ implementation(projects.core.utils) diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyActionsUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyActionsUseCase.kt index a1e112bb15..c1ac067bff 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyActionsUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyActionsUseCase.kt @@ -12,6 +12,7 @@ import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.tokens.repository.QuotesRepository import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.models.UserWallet +import com.tangem.features.markets.MarketsFeatureToggles import com.tangem.features.staking.api.featuretoggles.StakingFeatureToggles import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.isNullOrZero @@ -33,6 +34,7 @@ class GetCryptoCurrencyActionsUseCase( private val networksRepository: NetworksRepository, private val stakingRepository: StakingRepository, private val stakingFeatureToggles: StakingFeatureToggles, + private val marketsFeatureToggles: MarketsFeatureToggles, private val dispatchers: CoroutineDispatcherProvider, ) { @@ -112,6 +114,12 @@ class GetCryptoCurrencyActionsUseCase( val activeList = mutableListOf() val disabledList = mutableListOf() + // markets + // not a custom token + if (marketsFeatureToggles.isFeatureEnabled && cryptoCurrencyStatus.currency.id.rawCurrencyId != null) { + activeList.add(TokenActionsState.ActionState.Analytics(ScenarioUnavailabilityReason.None)) + } + // copy address if (isAddressAvailable(cryptoCurrencyStatus.value.networkAddress)) { activeList.add(TokenActionsState.ActionState.CopyAddress(ScenarioUnavailabilityReason.None)) diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenActionsState.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenActionsState.kt index c6db8e28e2..2531bc3ee2 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenActionsState.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenActionsState.kt @@ -30,6 +30,8 @@ data class TokenActionsState( data class Send(override val unavailabilityReason: ScenarioUnavailabilityReason) : ActionState() + data class Analytics(override val unavailabilityReason: ScenarioUnavailabilityReason) : ActionState() + data class HideToken(override val unavailabilityReason: ScenarioUnavailabilityReason) : ActionState() } } \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/ManageTokensScreen.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/ManageTokensScreen.kt index d84cc432eb..3b4b3bb478 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/ManageTokensScreen.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/ManageTokensScreen.kt @@ -53,6 +53,8 @@ import com.tangem.core.ui.components.snackbar.TangemSnackbarHost import com.tangem.core.ui.event.EventEffect import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.haptic.TangemHapticEffect +import com.tangem.core.ui.res.LocalHapticManager import com.tangem.core.ui.res.LocalSnackbarHostState import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview @@ -369,6 +371,8 @@ private fun NetworksList( isEditable: Boolean, modifier: Modifier = Modifier, ) { + val hapticManager = LocalHapticManager.current + AnimatedVisibility( modifier = modifier, visible = networks is NetworksUM.Expanded, @@ -403,7 +407,14 @@ private fun NetworksList( if (isEditable) { TangemSwitch( checked = network.isSelected, - onCheckedChange = network.onSelectedStateChange, + onCheckedChange = { checked -> + if (checked) { + hapticManager.perform(TangemHapticEffect.View.ToggleOn) + } else { + hapticManager.perform(TangemHapticEffect.View.ToggleOff) + } + network.onSelectedStateChange(checked) + }, ) } }, diff --git a/features/markets/api/src/main/kotlin/com/tangem/features/markets/details/MarketsTokenDetailsComponent.kt b/features/markets/api/src/main/kotlin/com/tangem/features/markets/details/MarketsTokenDetailsComponent.kt index b7c38d44bb..3a4b5e4b57 100644 --- a/features/markets/api/src/main/kotlin/com/tangem/features/markets/details/MarketsTokenDetailsComponent.kt +++ b/features/markets/api/src/main/kotlin/com/tangem/features/markets/details/MarketsTokenDetailsComponent.kt @@ -19,6 +19,7 @@ interface MarketsTokenDetailsComponent : ComposableContentComponent { data class Params( val token: TokenMarketParams, val appCurrency: AppCurrency, + val showPortfolio: Boolean, ) @Composable diff --git a/features/markets/impl/build.gradle.kts b/features/markets/impl/build.gradle.kts index 8cccec2563..d356234b52 100644 --- a/features/markets/impl/build.gradle.kts +++ b/features/markets/impl/build.gradle.kts @@ -46,6 +46,7 @@ dependencies { implementation(deps.compose.ui.utils) implementation(deps.lifecycle.compose) implementation(deps.androidx.activity.compose) + implementation(deps.markdown.composeview) /* DI */ implementation(deps.hilt.android) diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/DefaultMarketsTokenDetailsComponent.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/DefaultMarketsTokenDetailsComponent.kt index 0d758ce282..22c2c2b970 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/DefaultMarketsTokenDetailsComponent.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/DefaultMarketsTokenDetailsComponent.kt @@ -33,17 +33,21 @@ internal class DefaultMarketsTokenDetailsComponent @AssistedInject constructor( private val model: MarketsTokenDetailsModel = getOrCreateModel(params) - private val portfolioComponent = portfolioComponentFactory.create( - context = child("my_portfolio"), - params = MarketsPortfolioComponent.Params(params.token), - ) + private val portfolioComponent: MarketsPortfolioComponent? = if (params.showPortfolio) { + portfolioComponentFactory.create( + context = child("my_portfolio"), + params = MarketsPortfolioComponent.Params(params.token), + ) + } else { + null + } init { componentScope.launch { model.networksState.collectLatest { when (it) { - is TokenNetworksState.NetworksAvailable -> portfolioComponent.setTokenNetworks(it.networks) - TokenNetworksState.NoNetworksAvailable -> portfolioComponent.setNoNetworksAvailable() + is TokenNetworksState.NetworksAvailable -> portfolioComponent?.setTokenNetworks(it.networks) + TokenNetworksState.NoNetworksAvailable -> portfolioComponent?.setNoNetworksAvailable() else -> {} } } @@ -79,10 +83,16 @@ internal class DefaultMarketsTokenDetailsComponent @AssistedInject constructor( backgroundColor = LocalMainBottomSheetColor.current.value, addTopBarStatusBarPadding = false, state = state, - onBackClick = ::navigateBack, + onBackClick = { + if (bsState == BottomSheetState.EXPANDED) { + navigateBack() + } + }, onHeaderSizeChange = onHeaderSizeChange, - portfolioBlock = { blockModifier -> - portfolioComponent.Content(blockModifier) + portfolioBlock = portfolioComponent?.let { component -> + { blockModifier -> + component.Content(blockModifier) + } }, ) } @@ -109,8 +119,10 @@ internal class DefaultMarketsTokenDetailsComponent @AssistedInject constructor( state = state, onBackClick = ::navigateBack, onHeaderSizeChange = {}, - portfolioBlock = { blockModifier -> - portfolioComponent.Content(blockModifier) + portfolioBlock = portfolioComponent?.let { component -> + { blockModifier -> + component.Content(blockModifier) + } }, ) } diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/MarketsTokenDetailsModel.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/MarketsTokenDetailsModel.kt index 83b17519eb..c1f7cd2393 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/MarketsTokenDetailsModel.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/MarketsTokenDetailsModel.kt @@ -4,7 +4,6 @@ import androidx.compose.runtime.Stable import arrow.core.getOrElse import com.tangem.common.ui.charts.state.MarketChartData import com.tangem.common.ui.charts.state.MarketChartDataProducer -import com.tangem.common.ui.charts.state.MarketChartLook import com.tangem.common.ui.charts.state.sorted import com.tangem.core.decompose.di.ComponentScoped import com.tangem.core.decompose.model.Model @@ -135,15 +134,16 @@ internal class MarketsTokenDetailsModel @Inject constructor( fiatCurrencySymbol = currentAppCurrency.value.symbol, ), dateTimeText = resourceReference(R.string.common_today), - priceChangePercentText = BigDecimalFormatter.formatPercent( - percent = params.token.tokenQuotes.h24Percent, - useAbsoluteValue = true, - ), + priceChangePercentText = params.token.tokenQuotes.h24Percent?.let { + BigDecimalFormatter.formatPercent( + percent = it, + useAbsoluteValue = true, + ) + }, priceChangeType = params.token.tokenQuotes.h24Percent.percentChangeType(), iconUrl = params.token.imageUrl, chartState = MarketsTokenDetailsUM.ChartState( dataProducer = chartDataProducer, - chartLook = MarketChartLook(), onLoadRetryClick = ::onLoadRetryClicked, status = MarketsTokenDetailsUM.ChartState.Status.LOADING, onMarkerPointSelected = ::onMarkerPointSelected, @@ -320,6 +320,7 @@ internal class MarketsTokenDetailsModel @Inject constructor( currentQuotes.value = newInfo.quotes val percent = newInfo.quotes.getPercentByInterval(interval = state.value.selectedInterval) + state.update { it.copy( priceText = newInfo.quotes.currentPrice.formatAsPrice(currentAppCurrency.value), diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/MarketsTokenDetailsContent.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/MarketsTokenDetailsContent.kt index 9bbdfb559c..295497c053 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/MarketsTokenDetailsContent.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/MarketsTokenDetailsContent.kt @@ -18,7 +18,6 @@ import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import com.tangem.common.ui.charts.state.MarketChartDataProducer -import com.tangem.common.ui.charts.state.MarketChartLook import com.tangem.core.ui.components.* import com.tangem.core.ui.components.appbar.TangemTopAppBar import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM @@ -54,7 +53,7 @@ internal fun MarketsTokenDetailsContent( addTopBarStatusBarPadding: Boolean, onBackClick: () -> Unit, onHeaderSizeChange: (Dp) -> Unit, - portfolioBlock: @Composable (Modifier) -> Unit, + portfolioBlock: @Composable ((Modifier) -> Unit)?, modifier: Modifier = Modifier, ) { Content( @@ -78,7 +77,7 @@ private fun Content( addTopBarStatusBarInsets: Boolean, onBackClick: () -> Unit, onHeaderSizeChange: (Dp) -> Unit, - portfolioBlock: @Composable (Modifier) -> Unit, + portfolioBlock: @Composable ((Modifier) -> Unit)?, modifier: Modifier = Modifier, ) { val density = LocalDensity.current @@ -129,9 +128,7 @@ private fun Content( ) } item { SpacerH32() } - item( - contentType = "chart", - ) { + item("chart") { MarketTokenDetailsChart( modifier = Modifier.fillMaxWidth(), backgroundColor = backgroundColor, @@ -167,11 +164,13 @@ private fun Header(state: MarketsTokenDetailsUM, modifier: Modifier = Modifier) style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.tertiary, ) - PriceChangeInPercent( - valueInPercent = state.priceChangePercentText, - type = state.priceChangeType, - textStyle = TangemTheme.typography.caption2, - ) + if (state.priceChangePercentText != null) { + PriceChangeInPercent( + valueInPercent = state.priceChangePercentText, + type = state.priceChangeType, + textStyle = TangemTheme.typography.caption2, + ) + } } } SpacerW4() @@ -290,7 +289,6 @@ private fun Preview() { priceChangeType = PriceChangeType.UP, chartState = MarketsTokenDetailsUM.ChartState( dataProducer = MarketChartDataProducer.build { }, - chartLook = MarketChartLook(), onLoadRetryClick = {}, status = MarketsTokenDetailsUM.ChartState.Status.LOADING, onMarkerPointSelected = { _, _ -> }, diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/components/InfoBottomSheet.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/components/InfoBottomSheet.kt index 0d44a41f91..d78f6b2d99 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/components/InfoBottomSheet.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/components/InfoBottomSheet.kt @@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.systemBars import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalDensity @@ -17,6 +16,7 @@ import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetTitle import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme import com.tangem.features.markets.details.impl.ui.state.InfoBottomSheetContent +import dev.jeziellago.compose.markdowntext.MarkdownText @Composable internal fun InfoBottomSheet(config: TangemBottomSheetConfig) { @@ -35,10 +35,14 @@ internal fun InfoBottomSheet(config: TangemBottomSheetConfig) { .verticalScroll(rememberScrollState()) .padding(horizontal = TangemTheme.dimens.spacing28), ) { - Text( - text = it.body.resolveReference(), - style = TangemTheme.typography.body2, - color = TangemTheme.colors.text.secondary, + MarkdownText( + markdown = it.body.resolveReference(), + disableLinkMovementMethod = true, + linkifyMask = 0, + syntaxHighlightColor = TangemTheme.colors.text.secondary, + style = TangemTheme.typography.body2.copy( + color = TangemTheme.colors.text.secondary, + ), ) SpacerH(bottomBarHeight) } diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/components/LinksBlock.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/components/LinksBlock.kt index cfbb51ec74..e08e4a3052 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/components/LinksBlock.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/components/LinksBlock.kt @@ -10,7 +10,7 @@ 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.fastForEach -import com.tangem.core.ui.components.SmallButtonShimmer +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 @@ -145,9 +145,8 @@ private fun SubBlockPlaceholder(modifier: Modifier = Modifier, lastBlock: Boolea horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12), ) { repeat(times = 3) { - SmallButtonShimmer( + ChipShimmer( modifier = Modifier.weight(1f), - withIcon = true, ) } } diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/components/PricePerformanceBlock.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/components/PricePerformanceBlock.kt index 057d1bbed6..4e947ff908 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/components/PricePerformanceBlock.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/components/PricePerformanceBlock.kt @@ -161,7 +161,7 @@ internal fun PricePerformanceBlockPlaceholder(modifier: Modifier = Modifier) { }, content = { Column( - modifier = modifier.padding(vertical = TangemTheme.dimens.spacing8), + modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8), verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12), ) { Row( diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/components/TokenMarketDetailsBody.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/components/TokenMarketDetailsBody.kt index 5d9e93fd4e..d7ae7737c6 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/components/TokenMarketDetailsBody.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/components/TokenMarketDetailsBody.kt @@ -13,7 +13,7 @@ import com.tangem.features.markets.tokenlist.impl.ui.components.UnableToLoadData internal fun LazyListScope.tokenMarketDetailsBody( state: MarketsTokenDetailsUM.Body, - portfolioBlock: @Composable (Modifier) -> Unit, + portfolioBlock: @Composable ((Modifier) -> Unit)?, ) { when (state) { MarketsTokenDetailsUM.Body.Loading -> { @@ -21,8 +21,10 @@ internal fun LazyListScope.tokenMarketDetailsBody( DescriptionPlaceholder(modifier = Modifier.blockPaddings()) } - item(key = "portfolio") { - portfolioBlock(Modifier.blockPaddings()) + if (portfolioBlock != null) { + item(key = "portfolio") { + portfolioBlock(Modifier.blockPaddings()) + } } loadingInfoBlocks() @@ -32,8 +34,10 @@ internal fun LazyListScope.tokenMarketDetailsBody( description(state.description) } - item(key = "portfolio") { - portfolioBlock(Modifier.blockPaddings()) + if (portfolioBlock != null) { + item(key = "portfolio") { + portfolioBlock(Modifier.blockPaddings()) + } } infoBlocksList(state.infoBlocks) diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/state/MarketsTokenDetailsUM.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/state/MarketsTokenDetailsUM.kt index 6023a1c8a2..cec823bccb 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/state/MarketsTokenDetailsUM.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/state/MarketsTokenDetailsUM.kt @@ -2,7 +2,6 @@ package com.tangem.features.markets.details.impl.ui.state import androidx.compose.runtime.Immutable import com.tangem.common.ui.charts.state.MarketChartDataProducer -import com.tangem.common.ui.charts.state.MarketChartLook import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.marketprice.PriceChangeType import com.tangem.core.ui.event.StateEvent @@ -15,7 +14,7 @@ internal data class MarketsTokenDetailsUM( val priceText: String, val iconUrl: String?, val dateTimeText: TextReference, - val priceChangePercentText: String, + val priceChangePercentText: String?, val priceChangeType: PriceChangeType, val selectedInterval: PriceChangeInterval, val markerSet: Boolean, @@ -29,7 +28,6 @@ internal data class MarketsTokenDetailsUM( data class ChartState( val status: Status, val dataProducer: MarketChartDataProducer, - val chartLook: MarketChartLook, val onLoadRetryClick: () -> Unit, val onMarkerPointSelected: (time: BigDecimal?, price: BigDecimal?) -> Unit, ) { diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/entry/impl/DefaultMarketsEntryComponent.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/entry/impl/DefaultMarketsEntryComponent.kt index 1419aa7349..7f96268b4c 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/entry/impl/DefaultMarketsEntryComponent.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/entry/impl/DefaultMarketsEntryComponent.kt @@ -70,6 +70,7 @@ internal class DefaultMarketsEntryComponent @AssistedInject constructor( params = MarketsTokenDetailsComponent.Params( token = token.toSerializableParam(), appCurrency = appCurrency, + showPortfolio = true, ), ), ) diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/TokenActionsHandler.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/TokenActionsHandler.kt index b77101acb9..c134371f8e 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/TokenActionsHandler.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/TokenActionsHandler.kt @@ -182,13 +182,12 @@ internal class TokenActionsHandler @AssistedInject constructor( is TokenActionsState.ActionState.HideToken -> null is TokenActionsState.ActionState.Receive -> TokenActionsBSContentUM.Action.Receive is TokenActionsState.ActionState.Sell -> TokenActionsBSContentUM.Action.Sell - is TokenActionsState.ActionState.Send -> { - TokenActionsBSContentUM.Action.Send.takeIf { - cryptoCurrencyData.status.value.amount.isNullOrZero().not() - } + is TokenActionsState.ActionState.Send -> TokenActionsBSContentUM.Action.Send.takeIf { + cryptoCurrencyData.status.value.amount.isNullOrZero().not() } is TokenActionsState.ActionState.Stake -> TokenActionsBSContentUM.Action.Stake is TokenActionsState.ActionState.Swap -> TokenActionsBSContentUM.Action.Exchange + is TokenActionsState.ActionState.Analytics -> null } .takeIf { this.unavailabilityReason == ScenarioUnavailabilityReason.None } diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/AddToPortfolioBottomSheet.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/AddToPortfolioBottomSheet.kt index 524c8bfd4f..b73ba99609 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/AddToPortfolioBottomSheet.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/AddToPortfolioBottomSheet.kt @@ -36,6 +36,8 @@ import com.tangem.core.ui.components.currency.icon.CoinIcon import com.tangem.core.ui.components.rows.ArrowRow import com.tangem.core.ui.components.rows.BlockchainRow import com.tangem.core.ui.extensions.resourceReference +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.features.markets.impl.R @@ -168,6 +170,8 @@ private fun ContinueButton( @Suppress("LongMethod") @Composable private fun NetworkSelection(state: SelectNetworkUM, modifier: Modifier = Modifier) { + val hapticManager = LocalHapticManager.current + InformationBlock( modifier = modifier, title = { @@ -229,7 +233,15 @@ private fun NetworkSelection(state: SelectNetworkUM, modifier: Modifier = Modifi } else { TangemTheme.colors.icon.inactive }, - onCheckedChange = { state.onNetworkSwitchClick(network, it) }, + onCheckedChange = { checked -> + if (checked) { + hapticManager.perform(TangemHapticEffect.View.ToggleOn) + } else { + hapticManager.perform(TangemHapticEffect.View.ToggleOff) + } + + state.onNetworkSwitchClick(network, checked) + }, enabled = network.isEnabled, ) }, diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/token/block/impl/model/TokenMarketBlockModel.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/token/block/impl/model/TokenMarketBlockModel.kt index bb584f1731..71c71a4d5e 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/token/block/impl/model/TokenMarketBlockModel.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/token/block/impl/model/TokenMarketBlockModel.kt @@ -143,6 +143,7 @@ internal class TokenMarketBlockModel @Inject constructor( AppRoute.MarketsTokenDetails( token = tokenParam, appCurrency = currentAppCurrency.value, + showPortfolio = false, ), ) } diff --git a/features/wallet/impl/build.gradle.kts b/features/wallet/impl/build.gradle.kts index 5aa98a53a1..db8ba49b05 100644 --- a/features/wallet/impl/build.gradle.kts +++ b/features/wallet/impl/build.gradle.kts @@ -79,6 +79,7 @@ dependencies { implementation(projects.domain.analytics) implementation(projects.domain.visa) implementation(projects.domain.staking.models) + implementation(projects.domain.markets.models) //TODO: Create api/impl modules for onboarding [REDACTED_JIRA] implementation(projects.features.onboarding) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/MultiWalletCurrencyActionsConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/MultiWalletCurrencyActionsConverter.kt index f4009e9c73..0ff004ae48 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/MultiWalletCurrencyActionsConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/MultiWalletCurrencyActionsConverter.kt @@ -95,6 +95,11 @@ internal class MultiWalletCurrencyActionsConverter( icon = R.drawable.ic_hide_24 action = { clickIntents.onHideTokensClick(cryptoCurrencyStatus) } } + is TokenActionsState.ActionState.Analytics -> { + title = resourceReference(R.string.common_analytics) + icon = R.drawable.ic_analytics_24 + action = { clickIntents.onAnalyticsClick(cryptoCurrencyStatus) } + } } return TokenActionButtonConfig( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCurrencyActionsClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCurrencyActionsClickIntents.kt index e66128d2c8..1a067b02f7 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCurrencyActionsClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCurrencyActionsClickIntents.kt @@ -20,6 +20,7 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.extenstions.unwrap import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.demo.IsDemoCardUseCase +import com.tangem.domain.markets.TokenMarketParams import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.tokens.* @@ -48,6 +49,7 @@ import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.take import kotlinx.coroutines.launch +import java.math.BigDecimal import javax.inject.Inject interface WalletCurrencyActionsClickIntents { @@ -77,6 +79,8 @@ interface WalletCurrencyActionsClickIntents { fun onPerformHideToken(cryptoCurrencyStatus: CryptoCurrencyStatus) fun onExploreClick() + + fun onAnalyticsClick(cryptoCurrencyStatus: CryptoCurrencyStatus) } @Suppress("LongParameterList", "LargeClass") @@ -378,6 +382,31 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor( showErrorIfDemoModeOrElse(action = ::openExplorer) } + override fun onAnalyticsClick(cryptoCurrencyStatus: CryptoCurrencyStatus) { + viewModelScope.launch { + val rawId = cryptoCurrencyStatus.currency.id.rawCurrencyId ?: return@launch + + appRouter.push( + AppRoute.MarketsTokenDetails( + TokenMarketParams( + id = rawId, + name = cryptoCurrencyStatus.currency.name, + symbol = cryptoCurrencyStatus.currency.symbol, + tokenQuotes = TokenMarketParams.Quotes( + currentPrice = cryptoCurrencyStatus.value.fiatRate ?: BigDecimal.ZERO, + h24Percent = cryptoCurrencyStatus.value.priceChange, + weekPercent = null, + monthPercent = null, + ), + imageUrl = cryptoCurrencyStatus.currency.iconUrl, + ), + appCurrency = getSelectedAppCurrencyUseCase.unwrap(), + showPortfolio = true, + ), + ) + } + } + override fun onStakeClick(cryptoCurrencyStatus: CryptoCurrencyStatus, yield: Yield?) { val userWallet = getSelectedWalletSyncUseCase.unwrap() ?: return stateHolder.update(CloseBottomSheetTransformer(userWalletId = userWallet.walletId)) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 4b1fb256b2..9fc95abab5 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -85,6 +85,7 @@ leakcanary = "2.13" decompose = "2.2.2" room = "2.6.1" markdown = "0.7.2" +markdownComposeView = "0.5.4" # endregion Other libraries # region Tangem @@ -270,4 +271,5 @@ room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" } room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" } room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" } markdown = { module = "org.jetbrains:markdown", version.ref = "markdown" } +markdown-composeview = { module = "com.github.jeziellago:compose-markdown", version.ref = "markdownComposeView" } # endregion Other