diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 2ef3c24014..95ce7f4c75 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -232,6 +232,17 @@
android:scheme="tangem" />
+
+
+
+
+
+
+
+
+
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 afff976cd4..588ae2ef38 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
@@ -195,6 +195,9 @@ internal class ChildFactory @Inject constructor(
source = params.source,
)
},
+ preselectedSection = route.preselectedSection,
+ shouldOpenExchanges = route.shouldOpenExchanges,
+ exchangesCount = route.exchangesCount,
),
componentFactory = feedEntryComponentFactory,
)
@@ -458,7 +461,10 @@ internal class ChildFactory @Inject constructor(
is AppRoute.Markets -> {
createComponentChild(
context = context,
- params = FeedEntryRoute.MarketTokenList,
+ params = FeedEntryRoute.MarketTokenList(
+ preselectedOrder = route.preselectedOrder,
+ preselectedInterval = route.preselectedInterval,
+ ),
componentFactory = feedEntryComponentFactory,
)
}
diff --git a/app/src/main/java/com/tangem/tap/routing/utils/DeepLinkFactory.kt b/app/src/main/java/com/tangem/tap/routing/utils/DeepLinkFactory.kt
index fb1acbcd80..492d1d23ae 100644
--- a/app/src/main/java/com/tangem/tap/routing/utils/DeepLinkFactory.kt
+++ b/app/src/main/java/com/tangem/tap/routing/utils/DeepLinkFactory.kt
@@ -8,6 +8,7 @@ import com.tangem.data.card.sdk.CardSdkProvider
import com.tangem.feature.referral.api.deeplink.ReferralDeepLinkHandler
import com.tangem.features.feed.entry.deeplink.MarketsDeepLinkHandler
import com.tangem.features.feed.entry.deeplink.MarketsTokenDetailDeepLinkHandler
+import com.tangem.features.feed.entry.deeplink.MarketsTokenExchangesDeepLinkHandler
import com.tangem.features.feed.entry.deeplink.NewsDetailsDeepLinkHandler
import com.tangem.features.onramp.deeplink.BuyDeepLinkHandler
import com.tangem.features.onramp.deeplink.OnrampDeepLinkHandler
@@ -51,6 +52,7 @@ internal class DeepLinkFactory @Inject constructor(
private val swapDeepLink: SwapDeepLinkHandler.Factory,
private val promoDeepLink: PromoDeeplinkHandler.Factory,
private val onboardVisaDeepLink: OnboardVisaDeepLinkHandler.Factory,
+ private val marketsTokenExchangesDeepLink: MarketsTokenExchangesDeepLinkHandler.Factory,
private val newsDetailsDeepLink: NewsDetailsDeepLinkHandler.Factory,
) {
private val permittedAppRoute = MutableStateFlow(false)
@@ -148,8 +150,9 @@ internal class DeepLinkFactory @Inject constructor(
isFromOnNewIntent = isFromOnNewIntent,
)
DeepLinkRoute.Staking.host -> stakingDeepLink.create(coroutineScope, queryParams)
- DeepLinkRoute.Markets.host -> marketsDeepLink.create()
+ DeepLinkRoute.Markets.host -> marketsDeepLink.create(queryParams)
DeepLinkRoute.MarketTokenDetail.host -> marketsTokenDetailDeepLink.create(coroutineScope, queryParams)
+ DeepLinkRoute.TokenExchanges.host -> marketsTokenExchangesDeepLink.create(coroutineScope, queryParams)
DeepLinkRoute.Buy.host -> buyDeepLink.create()
DeepLinkRoute.Sell.host -> sellDeepLink.create()
DeepLinkRoute.Swap.host -> swapDeepLink.create()
diff --git a/app/src/test/kotlin/com/tangem/tap/routing/utils/DeepLinkFactoryTest.kt b/app/src/test/kotlin/com/tangem/tap/routing/utils/DeepLinkFactoryTest.kt
index 0cb5e4db8f..43ba7a2555 100644
--- a/app/src/test/kotlin/com/tangem/tap/routing/utils/DeepLinkFactoryTest.kt
+++ b/app/src/test/kotlin/com/tangem/tap/routing/utils/DeepLinkFactoryTest.kt
@@ -6,6 +6,7 @@ import com.tangem.data.card.sdk.CardSdkProvider
import com.tangem.feature.referral.api.deeplink.ReferralDeepLinkHandler
import com.tangem.features.feed.entry.deeplink.MarketsDeepLinkHandler
import com.tangem.features.feed.entry.deeplink.MarketsTokenDetailDeepLinkHandler
+import com.tangem.features.feed.entry.deeplink.MarketsTokenExchangesDeepLinkHandler
import com.tangem.features.feed.entry.deeplink.NewsDetailsDeepLinkHandler
import com.tangem.features.onramp.deeplink.BuyDeepLinkHandler
import com.tangem.features.onramp.deeplink.OnrampDeepLinkHandler
@@ -55,7 +56,7 @@ class DeepLinkFactoryTest {
every { create(any(), any()) } returns mockk()
}
private val marketsDeepLinkFactory = mockk(relaxed = true) {
- every { create() } returns mockk()
+ every { create(any()) } returns mockk()
}
private val marketsTokenDetailDeepLinkFactory = mockk(relaxed = true) {
every { create(any(), any()) } returns mockk()
@@ -86,6 +87,11 @@ class DeepLinkFactoryTest {
every { create(any(), any()) } returns mockk()
}
+ private val marketsTokenExchangesDeepLinkFactory =
+ mockk(relaxed = true) {
+ every { create(any(), any()) } returns mockk()
+ }
+
private val mockedUri = mockk(relaxed = true)
private val isFromOnNewIntent: Boolean = false
@@ -103,6 +109,7 @@ class DeepLinkFactoryTest {
stakingDeepLink = stakingDeepLinkFactory,
marketsDeepLink = marketsDeepLinkFactory,
marketsTokenDetailDeepLink = marketsTokenDetailDeepLinkFactory,
+ marketsTokenExchangesDeepLink = marketsTokenExchangesDeepLinkFactory,
buyDeepLink = buyDeepLinkFactory,
sellDeepLink = sellDeepLinkFactory,
swapDeepLink = swapDeepLinkFactory,
@@ -308,7 +315,7 @@ class DeepLinkFactoryTest {
every { mockedUri.host } returns "markets"
deepLinkFactory.handleDeeplink(mockedUri, testScope, isFromOnNewIntent)
advanceUntilIdle()
- verify { marketsDeepLinkFactory.create() }
+ verify { marketsDeepLinkFactory.create(any()) }
// Test Sell
every { mockedUri.host } returns "sell"
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 6fba5a36d5..e78861b261 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
@@ -10,6 +10,9 @@ import com.tangem.common.routing.entity.InitScreenLaunchMode
import com.tangem.core.decompose.navigation.Route
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.feedback.models.WalletMetaInfo
+import com.tangem.domain.markets.PreselectedMarketsInterval
+import com.tangem.domain.markets.PreselectedMarketsOrder
+import com.tangem.domain.markets.PreselectedTokenDetailsSection
import com.tangem.domain.markets.TokenMarketParams
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.AccountId
@@ -253,7 +256,10 @@ sealed class AppRoute(val path: String) : Route {
) : AppRoute(path = "/wallet_hardware_backup/${userWalletId.stringValue}")
@Serializable
- data object Markets : AppRoute(path = "/markets")
+ data class Markets(
+ val preselectedOrder: PreselectedMarketsOrder? = null,
+ val preselectedInterval: PreselectedMarketsInterval? = null,
+ ) : AppRoute(path = "/markets")
@Serializable
data class MarketsTokenDetails(
@@ -261,6 +267,9 @@ sealed class AppRoute(val path: String) : Route {
val appCurrency: AppCurrency,
val shouldShowPortfolio: Boolean,
val analyticsParams: AnalyticsParams? = null,
+ val preselectedSection: PreselectedTokenDetailsSection? = null,
+ val shouldOpenExchanges: Boolean = false,
+ val exchangesCount: Int? = null,
) : AppRoute(path = "/markets_token_details/${token.id}/$shouldShowPortfolio") {
@Serializable
diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/DeepLinkRoute.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/DeepLinkRoute.kt
index d57eb5e7f0..359839b059 100644
--- a/common/routing/src/main/kotlin/com/tangem/common/routing/DeepLinkRoute.kt
+++ b/common/routing/src/main/kotlin/com/tangem/common/routing/DeepLinkRoute.kt
@@ -60,6 +60,10 @@ sealed class DeepLinkRoute {
override val host: String = "promo"
}
+ data object TokenExchanges : DeepLinkRoute() {
+ override val host: String = "token_exchanges"
+ }
+
data object OnboardVisa : DeepLinkRoute() {
override val host: String = "onboard-visa"
}
diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/deeplink/DeeplinkConst.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/deeplink/DeeplinkConst.kt
index 630b68ded8..6fe8031620 100644
--- a/common/routing/src/main/kotlin/com/tangem/common/routing/deeplink/DeeplinkConst.kt
+++ b/common/routing/src/main/kotlin/com/tangem/common/routing/deeplink/DeeplinkConst.kt
@@ -15,4 +15,7 @@ object DeeplinkConst {
const val REF_KEY = "ref"
const val CAMPAIGN_KEY = "campaign"
const val NAME_KEY = "name"
+ const val ORDER_KEY = "order"
+ const val INTERVAL_KEY = "interval"
+ const val SECTION_KEY = "section"
}
\ No newline at end of file
diff --git a/domain/markets/models/src/main/kotlin/com/tangem/domain/markets/PreselectedMarketsInterval.kt b/domain/markets/models/src/main/kotlin/com/tangem/domain/markets/PreselectedMarketsInterval.kt
new file mode 100644
index 0000000000..d87f2342c1
--- /dev/null
+++ b/domain/markets/models/src/main/kotlin/com/tangem/domain/markets/PreselectedMarketsInterval.kt
@@ -0,0 +1,15 @@
+package com.tangem.domain.markets
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+enum class PreselectedMarketsInterval(val value: String) {
+ H24("24h"),
+ W1("1w"),
+ D30("30d"),
+ ;
+
+ companion object {
+ fun parse(value: String?): PreselectedMarketsInterval? = entries.firstOrNull { it.value == value }
+ }
+}
\ No newline at end of file
diff --git a/domain/markets/models/src/main/kotlin/com/tangem/domain/markets/PreselectedMarketsOrder.kt b/domain/markets/models/src/main/kotlin/com/tangem/domain/markets/PreselectedMarketsOrder.kt
new file mode 100644
index 0000000000..bc2cb04640
--- /dev/null
+++ b/domain/markets/models/src/main/kotlin/com/tangem/domain/markets/PreselectedMarketsOrder.kt
@@ -0,0 +1,17 @@
+package com.tangem.domain.markets
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+enum class PreselectedMarketsOrder(val value: String) {
+ Rating("rating"),
+ Trending("trending"),
+ Buyers("buyers"),
+ Gainers("gainers"),
+ Losers("losers"),
+ ;
+
+ companion object {
+ fun parse(value: String?): PreselectedMarketsOrder? = entries.firstOrNull { it.value == value }
+ }
+}
\ No newline at end of file
diff --git a/domain/markets/models/src/main/kotlin/com/tangem/domain/markets/PreselectedTokenDetailsSection.kt b/domain/markets/models/src/main/kotlin/com/tangem/domain/markets/PreselectedTokenDetailsSection.kt
new file mode 100644
index 0000000000..008a3b086e
--- /dev/null
+++ b/domain/markets/models/src/main/kotlin/com/tangem/domain/markets/PreselectedTokenDetailsSection.kt
@@ -0,0 +1,13 @@
+package com.tangem.domain.markets
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+enum class PreselectedTokenDetailsSection(val value: String) {
+ News("news"),
+ ;
+
+ companion object {
+ fun parse(value: String?): PreselectedTokenDetailsSection? = entries.firstOrNull { it.value == value }
+ }
+}
\ No newline at end of file
diff --git a/features/feed/api/src/main/kotlin/com/tangem/features/feed/entry/components/FeedEntryRoute.kt b/features/feed/api/src/main/kotlin/com/tangem/features/feed/entry/components/FeedEntryRoute.kt
index 7c9ffb7c0a..ab97474343 100644
--- a/features/feed/api/src/main/kotlin/com/tangem/features/feed/entry/components/FeedEntryRoute.kt
+++ b/features/feed/api/src/main/kotlin/com/tangem/features/feed/entry/components/FeedEntryRoute.kt
@@ -1,6 +1,9 @@
package com.tangem.features.feed.entry.components
import com.tangem.domain.appcurrency.model.AppCurrency
+import com.tangem.domain.markets.PreselectedMarketsInterval
+import com.tangem.domain.markets.PreselectedMarketsOrder
+import com.tangem.domain.markets.PreselectedTokenDetailsSection
import com.tangem.domain.markets.TokenMarketParams
import kotlinx.serialization.Serializable
@@ -13,6 +16,9 @@ sealed interface FeedEntryRoute {
val appCurrency: AppCurrency,
val shouldShowPortfolio: Boolean,
val analyticsParams: AnalyticsParams? = null,
+ val preselectedSection: PreselectedTokenDetailsSection? = null,
+ val shouldOpenExchanges: Boolean = false,
+ val exchangesCount: Int? = null,
) : FeedEntryRoute {
@Serializable
@@ -23,7 +29,10 @@ sealed interface FeedEntryRoute {
}
@Serializable
- data object MarketTokenList : FeedEntryRoute
+ data class MarketTokenList(
+ val preselectedOrder: PreselectedMarketsOrder? = null,
+ val preselectedInterval: PreselectedMarketsInterval? = null,
+ ) : FeedEntryRoute
@Serializable
data class NewsDetail(val articleId: Int, val preselectedArticlesId: List) : FeedEntryRoute
diff --git a/features/feed/api/src/main/kotlin/com/tangem/features/feed/entry/deeplink/MarketsDeepLinkHandler.kt b/features/feed/api/src/main/kotlin/com/tangem/features/feed/entry/deeplink/MarketsDeepLinkHandler.kt
index 5909a4876a..4cd535a841 100644
--- a/features/feed/api/src/main/kotlin/com/tangem/features/feed/entry/deeplink/MarketsDeepLinkHandler.kt
+++ b/features/feed/api/src/main/kotlin/com/tangem/features/feed/entry/deeplink/MarketsDeepLinkHandler.kt
@@ -3,6 +3,6 @@ package com.tangem.features.feed.entry.deeplink
interface MarketsDeepLinkHandler {
interface Factory {
- fun create(): MarketsDeepLinkHandler
+ fun create(params: Map): MarketsDeepLinkHandler
}
}
\ No newline at end of file
diff --git a/features/feed/api/src/main/kotlin/com/tangem/features/feed/entry/deeplink/MarketsTokenExchangesDeepLinkHandler.kt b/features/feed/api/src/main/kotlin/com/tangem/features/feed/entry/deeplink/MarketsTokenExchangesDeepLinkHandler.kt
new file mode 100644
index 0000000000..1ebdae4226
--- /dev/null
+++ b/features/feed/api/src/main/kotlin/com/tangem/features/feed/entry/deeplink/MarketsTokenExchangesDeepLinkHandler.kt
@@ -0,0 +1,10 @@
+package com.tangem.features.feed.entry.deeplink
+
+import kotlinx.coroutines.CoroutineScope
+
+interface MarketsTokenExchangesDeepLinkHandler {
+
+ interface Factory {
+ fun create(coroutineScope: CoroutineScope, params: Map): MarketsTokenExchangesDeepLinkHandler
+ }
+}
\ No newline at end of file
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/DefaultFeedEntryComponent.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/DefaultFeedEntryComponent.kt
index 48d74bc9f4..74271e53a0 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/DefaultFeedEntryComponent.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/DefaultFeedEntryComponent.kt
@@ -29,6 +29,9 @@ import com.tangem.features.feed.entry.components.FeedEntryComponent
import com.tangem.features.feed.entry.components.FeedEntryRoute
import com.tangem.features.feed.model.FeedEntryModel
import com.tangem.features.feed.model.feed.FeedModelClickIntents
+import com.tangem.domain.markets.PreselectedMarketsInterval
+import com.tangem.domain.markets.PreselectedMarketsOrder
+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.EntryContent
import dagger.assisted.Assisted
@@ -87,6 +90,7 @@ internal class DefaultFeedEntryComponent @AssistedInject constructor(
route = FeedEntryChildFactory.Child.TokenList(
params = DefaultMarketsTokenListComponent.Params(
preselectedSortType = sortBy ?: SortByTypeUM.Rating,
+ preselectedInterval = MarketsListUM.TrendInterval.H24,
shouldAlwaysShowSearchBar = sortBy == null,
),
),
@@ -231,11 +235,15 @@ internal class DefaultFeedEntryComponent @AssistedInject constructor(
paginationConfig = null,
)
},
+ preselectedSection = entryRoute.preselectedSection,
+ shouldOpenExchanges = entryRoute.shouldOpenExchanges,
+ exchangesCount = entryRoute.exchangesCount,
),
)
- FeedEntryRoute.MarketTokenList -> FeedEntryChildFactory.Child.TokenList(
+ is FeedEntryRoute.MarketTokenList -> FeedEntryChildFactory.Child.TokenList(
DefaultMarketsTokenListComponent.Params(
- preselectedSortType = SortByTypeUM.Rating,
+ preselectedSortType = mapOrderToSortType(entryRoute.preselectedOrder),
+ preselectedInterval = mapIntervalToTrendInterval(entryRoute.preselectedInterval),
shouldAlwaysShowSearchBar = false,
),
)
@@ -265,4 +273,24 @@ internal class DefaultFeedEntryComponent @AssistedInject constructor(
}
}
+private fun mapOrderToSortType(order: PreselectedMarketsOrder?): SortByTypeUM {
+ return when (order) {
+ PreselectedMarketsOrder.Rating -> SortByTypeUM.Rating
+ PreselectedMarketsOrder.Trending -> SortByTypeUM.Trending
+ PreselectedMarketsOrder.Buyers -> SortByTypeUM.ExperiencedBuyers
+ PreselectedMarketsOrder.Gainers -> SortByTypeUM.TopGainers
+ PreselectedMarketsOrder.Losers -> SortByTypeUM.TopLosers
+ null -> SortByTypeUM.Rating
+ }
+}
+
+private fun mapIntervalToTrendInterval(interval: PreselectedMarketsInterval?): MarketsListUM.TrendInterval {
+ return when (interval) {
+ PreselectedMarketsInterval.H24 -> MarketsListUM.TrendInterval.H24
+ PreselectedMarketsInterval.W1 -> MarketsListUM.TrendInterval.D7
+ PreselectedMarketsInterval.D30 -> MarketsListUM.TrendInterval.M1
+ null -> MarketsListUM.TrendInterval.H24
+ }
+}
+
internal interface FeedEntryClickIntents : FeedModelClickIntents
\ No newline at end of file
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/DefaultMarketsTokenDetailsComponent.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/DefaultMarketsTokenDetailsComponent.kt
index a9c1b09117..c4ac5551aa 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/DefaultMarketsTokenDetailsComponent.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/DefaultMarketsTokenDetailsComponent.kt
@@ -17,6 +17,7 @@ import androidx.compose.ui.res.vectorResource
import androidx.lifecycle.compose.LifecycleStartEffect
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.blockchainsdk.compatibility.getTokenIdIfL2Network
+import com.tangem.domain.markets.PreselectedTokenDetailsSection
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.context.child
@@ -228,6 +229,9 @@ internal class DefaultMarketsTokenDetailsComponent(
val analyticsParams: AnalyticsParams?,
val onBackClicked: () -> Unit,
val onArticleClick: (articleId: Int, preselectedArticlesId: List) -> Unit,
+ val preselectedSection: PreselectedTokenDetailsSection? = null,
+ val shouldOpenExchanges: Boolean = false,
+ val exchangesCount: Int? = null,
)
@Serializable
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/list/DefaultMarketsTokenListComponent.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/list/DefaultMarketsTokenListComponent.kt
index 2d69602f84..29e359843b 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/list/DefaultMarketsTokenListComponent.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/list/DefaultMarketsTokenListComponent.kt
@@ -29,6 +29,7 @@ import com.tangem.core.ui.res.TangemTheme
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.markets.TokenMarketParams
import com.tangem.features.feed.model.market.list.MarketsListModel
+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
@@ -127,6 +128,7 @@ internal class DefaultMarketsTokenListComponent(
@Serializable
data class Params(
val preselectedSortType: SortByTypeUM,
+ val preselectedInterval: MarketsListUM.TrendInterval,
val shouldAlwaysShowSearchBar: Boolean,
)
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/deeplink/DefaultMarketsDeepLinkHandler.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/deeplink/DefaultMarketsDeepLinkHandler.kt
index 2e81775f75..b2d72c2863 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/deeplink/DefaultMarketsDeepLinkHandler.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/deeplink/DefaultMarketsDeepLinkHandler.kt
@@ -2,20 +2,31 @@ package com.tangem.features.feed.deeplink
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
+import com.tangem.common.routing.deeplink.DeeplinkConst.INTERVAL_KEY
+import com.tangem.common.routing.deeplink.DeeplinkConst.ORDER_KEY
+import com.tangem.domain.markets.PreselectedMarketsInterval
+import com.tangem.domain.markets.PreselectedMarketsOrder
import com.tangem.features.feed.entry.deeplink.MarketsDeepLinkHandler
+import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
internal class DefaultMarketsDeepLinkHandler @AssistedInject constructor(
+ @Assisted private val queryParams: Map,
appRouter: AppRouter,
) : MarketsDeepLinkHandler {
init {
- appRouter.push(AppRoute.Markets)
+ appRouter.push(
+ AppRoute.Markets(
+ preselectedOrder = PreselectedMarketsOrder.parse(queryParams[ORDER_KEY]),
+ preselectedInterval = PreselectedMarketsInterval.parse(queryParams[INTERVAL_KEY]),
+ ),
+ )
}
@AssistedFactory
interface Factory : MarketsDeepLinkHandler.Factory {
- override fun create(): DefaultMarketsDeepLinkHandler
+ override fun create(params: Map): DefaultMarketsDeepLinkHandler
}
}
\ No newline at end of file
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/deeplink/DefaultMarketsTokenDetailDeepLinkHandler.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/deeplink/DefaultMarketsTokenDetailDeepLinkHandler.kt
index 89c501508c..89cc9b9877 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/deeplink/DefaultMarketsTokenDetailDeepLinkHandler.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/deeplink/DefaultMarketsTokenDetailDeepLinkHandler.kt
@@ -2,7 +2,9 @@ package com.tangem.features.feed.deeplink
import arrow.core.getOrElse
import com.tangem.common.routing.AppRoute
+import com.tangem.domain.markets.PreselectedTokenDetailsSection
import com.tangem.common.routing.AppRouter
+import com.tangem.common.routing.deeplink.DeeplinkConst.SECTION_KEY
import com.tangem.common.routing.deeplink.DeeplinkConst.TOKEN_ID_KEY
import com.tangem.data.common.currency.getTokenIconUrlFromDefaultHost
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
@@ -11,12 +13,12 @@ import com.tangem.domain.markets.GetTokenMarketInfoUseCase
import com.tangem.domain.markets.TokenMarketParams
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.features.feed.entry.deeplink.MarketsTokenDetailDeepLinkHandler
+import com.tangem.utils.logging.TangemLogger
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
-import com.tangem.utils.logging.TangemLogger
internal class DefaultMarketsTokenDetailDeepLinkHandler @AssistedInject constructor(
@Assisted private val scope: CoroutineScope,
@@ -32,8 +34,15 @@ internal class DefaultMarketsTokenDetailDeepLinkHandler @AssistedInject construc
private fun handleDeepLink() {
val tokenId = queryParams[TOKEN_ID_KEY]
+ val section = PreselectedTokenDetailsSection.parse(queryParams[SECTION_KEY])
- val rawTokenId = CryptoCurrency.RawID(tokenId.orEmpty())
+ if (tokenId.isNullOrEmpty()) {
+ TangemLogger.e("Markets token details deeplink does not contain token_id")
+ appRouter.push(AppRoute.Markets())
+ return
+ }
+
+ val rawTokenId = CryptoCurrency.RawID(tokenId)
scope.launch {
val appCurrency = getSelectedAppCurrencyUseCase.invokeSync().getOrElse {
@@ -65,6 +74,7 @@ internal class DefaultMarketsTokenDetailDeepLinkHandler @AssistedInject construc
appCurrency = appCurrency,
shouldShowPortfolio = true,
analyticsParams = null,
+ preselectedSection = section,
),
)
}
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/deeplink/DefaultMarketsTokenExchangesDeepLinkHandler.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/deeplink/DefaultMarketsTokenExchangesDeepLinkHandler.kt
new file mode 100644
index 0000000000..4fbd77c4ba
--- /dev/null
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/deeplink/DefaultMarketsTokenExchangesDeepLinkHandler.kt
@@ -0,0 +1,88 @@
+package com.tangem.features.feed.deeplink
+
+import arrow.core.getOrElse
+import com.tangem.common.routing.AppRoute
+import com.tangem.common.routing.AppRouter
+import com.tangem.common.routing.deeplink.DeeplinkConst.TOKEN_ID_KEY
+import com.tangem.data.common.currency.getTokenIconUrlFromDefaultHost
+import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
+import com.tangem.domain.appcurrency.model.AppCurrency
+import com.tangem.domain.markets.GetTokenMarketInfoUseCase
+import com.tangem.domain.markets.TokenMarketParams
+import com.tangem.domain.models.currency.CryptoCurrency
+import com.tangem.features.feed.entry.deeplink.MarketsTokenExchangesDeepLinkHandler
+import com.tangem.utils.logging.TangemLogger
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+internal class DefaultMarketsTokenExchangesDeepLinkHandler @AssistedInject constructor(
+ @Assisted private val scope: CoroutineScope,
+ @Assisted private val queryParams: Map,
+ private val appRouter: AppRouter,
+ private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
+ private val getTokenMarketInfoUseCase: GetTokenMarketInfoUseCase,
+) : MarketsTokenExchangesDeepLinkHandler {
+
+ init {
+ handleDeepLink()
+ }
+
+ private fun handleDeepLink() {
+ val tokenId = queryParams[TOKEN_ID_KEY]
+
+ if (tokenId.isNullOrEmpty()) {
+ TangemLogger.e("Markets token exchanges deeplink does not contain token_id")
+ appRouter.push(AppRoute.Markets())
+ return
+ }
+
+ val rawTokenId = CryptoCurrency.RawID(tokenId)
+
+ scope.launch {
+ val appCurrency = getSelectedAppCurrencyUseCase.invokeSync().getOrElse {
+ AppCurrency.Default
+ }
+ val tokenInfo = getTokenMarketInfoUseCase(
+ appCurrency = appCurrency,
+ tokenId = rawTokenId,
+ tokenSymbol = "",
+ ).getOrElse {
+ TangemLogger.e("Failed to get market token info for exchanges deeplink")
+ appRouter.push(AppRoute.Markets())
+ return@launch
+ }
+
+ appRouter.push(
+ AppRoute.MarketsTokenDetails(
+ token = TokenMarketParams(
+ id = rawTokenId,
+ name = tokenInfo.name,
+ symbol = tokenInfo.symbol,
+ tokenQuotes = TokenMarketParams.Quotes(
+ currentPrice = tokenInfo.quotes.currentPrice,
+ h24Percent = tokenInfo.quotes.h24ChangePercent,
+ weekPercent = tokenInfo.quotes.weekChangePercent,
+ monthPercent = tokenInfo.quotes.monthChangePercent,
+ ),
+ imageUrl = getTokenIconUrlFromDefaultHost(rawTokenId),
+ ),
+ appCurrency = appCurrency,
+ shouldShowPortfolio = true,
+ shouldOpenExchanges = true,
+ exchangesCount = tokenInfo.exchangesAmount,
+ ),
+ )
+ }
+ }
+
+ @AssistedFactory
+ interface Factory : MarketsTokenExchangesDeepLinkHandler.Factory {
+ override fun create(
+ coroutineScope: CoroutineScope,
+ queryParams: Map,
+ ): DefaultMarketsTokenExchangesDeepLinkHandler
+ }
+}
\ No newline at end of file
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/deeplink/di/MarketsDeepLinkModule.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/deeplink/di/MarketsDeepLinkModule.kt
index def0bf84fb..1e1e90cc24 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/deeplink/di/MarketsDeepLinkModule.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/deeplink/di/MarketsDeepLinkModule.kt
@@ -2,8 +2,10 @@ package com.tangem.features.feed.deeplink.di
import com.tangem.features.feed.deeplink.DefaultMarketsDeepLinkHandler
import com.tangem.features.feed.deeplink.DefaultMarketsTokenDetailDeepLinkHandler
+import com.tangem.features.feed.deeplink.DefaultMarketsTokenExchangesDeepLinkHandler
import com.tangem.features.feed.entry.deeplink.MarketsDeepLinkHandler
import com.tangem.features.feed.entry.deeplink.MarketsTokenDetailDeepLinkHandler
+import com.tangem.features.feed.entry.deeplink.MarketsTokenExchangesDeepLinkHandler
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
@@ -23,4 +25,10 @@ internal interface MarketsDeepLinkModule {
fun bindMarketsTokenDetailDeepLinkHandlerFactory(
impl: DefaultMarketsTokenDetailDeepLinkHandler.Factory,
): MarketsTokenDetailDeepLinkHandler.Factory
+
+ @Binds
+ @Singleton
+ fun bindMarketsTokenExchangesDeepLinkHandlerFactory(
+ impl: DefaultMarketsTokenExchangesDeepLinkHandler.Factory,
+ ): MarketsTokenExchangesDeepLinkHandler.Factory
}
\ No newline at end of file
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/details/MarketsTokenDetailsModel.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/details/MarketsTokenDetailsModel.kt
index 05f213308c..e412504e4d 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/details/MarketsTokenDetailsModel.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/details/MarketsTokenDetailsModel.kt
@@ -5,6 +5,7 @@ import arrow.core.Either
import arrow.core.getOrElse
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.common.TangemSiteShareUrlBuilder
+import com.tangem.domain.markets.PreselectedTokenDetailsSection
import com.tangem.common.ui.charts.state.MarketChartData
import com.tangem.common.ui.charts.state.MarketChartDataProducer
import com.tangem.common.ui.charts.state.sorted
@@ -21,6 +22,7 @@ import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.components.marketprice.PriceChangeType
import com.tangem.core.ui.event.consumedEvent
+import com.tangem.core.ui.event.triggeredEvent
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.format.bigdecimal.fiat
@@ -95,6 +97,7 @@ internal class MarketsTokenDetailsModel @Inject constructor(
private val quotesJob = JobHolder()
private var userCountry: UserCountry? = null
+ private var isScrollToSectionHandled = false
private val params = paramsContainer.require()
private val analyticsEventBuilder = MarketDetailsAnalyticsEvent.EventBuilder(token = params.token)
@@ -297,6 +300,15 @@ internal class MarketsTokenDetailsModel @Inject constructor(
initialLoad()
loadRelatedNews()
+
+ if (params.shouldOpenExchanges) {
+ modelScope.launch {
+ val exchangesCount = params.exchangesCount
+ ?: currentTokenInfo.value?.exchangesAmount
+ ?: 0
+ onListedOnClick(exchangesCount)
+ }
+ }
}
private fun initialLoad() {
@@ -519,6 +531,14 @@ internal class MarketsTokenDetailsModel @Inject constructor(
description = descriptionConverter.convert(newInfo),
infoBlocks = infoConverter.convert(newInfo),
),
+ scrollToSection = if (!isScrollToSectionHandled) {
+ mapSectionToKey(params.preselectedSection)?.let { key ->
+ isScrollToSectionHandled = true
+ triggeredEvent(data = key, onConsume = ::consumeScrollToSection)
+ } ?: consumedEvent()
+ } else {
+ marketsTokenDetailsUM.scrollToSection
+ },
)
}
@@ -755,6 +775,17 @@ internal class MarketsTokenDetailsModel @Inject constructor(
)
}
+ private fun mapSectionToKey(section: PreselectedTokenDetailsSection?): String? {
+ return when (section) {
+ PreselectedTokenDetailsSection.News -> MarketsTokenDetailsUM.RelatedNews.SECTION_KEY
+ null -> null
+ }
+ }
+
+ private fun consumeScrollToSection() {
+ state.update { it.copy(scrollToSection = consumedEvent()) }
+ }
+
private companion object {
const val QUOTES_UPDATE_INTERVAL_MILLIS = 60000L
const val RELATED_NEWS_LIMIT = 10
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/list/MarketsListModel.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/list/MarketsListModel.kt
index 11a933c2f1..79c0d6a2db 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/list/MarketsListModel.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/list/MarketsListModel.kt
@@ -67,6 +67,7 @@ internal class MarketsListModel @Inject constructor(
onShowTokensUnder100kClicked = { analyticsEventHandler.send(MarketsListAnalyticsEvent.ShowTokens()) },
shouldAlwaysShowSearchBar = Provider { modelParams.params.shouldAlwaysShowSearchBar },
preselectedSortType = Provider { modelParams.params.preselectedSortType },
+ preselectedInterval = Provider { modelParams.params.preselectedInterval },
onBackClick = modelParams.clickIntents.onBackClicked,
analyticsEventHandler = analyticsEventHandler,
onSearchBarClick = modelParams.clickIntents.onSearchClicked,
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/list/statemanager/MarketsListUMStateManager.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/list/statemanager/MarketsListUMStateManager.kt
index fe1b3b15a1..473c06b89b 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/list/statemanager/MarketsListUMStateManager.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/list/statemanager/MarketsListUMStateManager.kt
@@ -27,6 +27,7 @@ internal class MarketsListUMStateManager(
private val shouldAlwaysShowSearchBar: Provider,
private val currentVisibleIds: Provider>,
private val preselectedSortType: Provider,
+ private val preselectedInterval: Provider,
private val onLoadMoreUiItems: () -> Unit,
private val visibleItemsChanged: (itemsKeys: List) -> Unit,
private val onRetryButtonClicked: () -> Unit,
@@ -229,7 +230,7 @@ internal class MarketsListUMStateManager(
shouldAlwaysShowSearchBar = shouldAlwaysShowSearchBar(),
),
selectedSortBy = preselectedSortType(),
- selectedInterval = MarketsListUM.TrendInterval.H24,
+ selectedInterval = preselectedInterval(),
onIntervalClick = { selectedInterval = it },
onSortByButtonClick = { isSortByBottomSheetShown = true },
sortByBottomSheet = TangemBottomSheetConfig(
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/MarketsTokenDetailsContent.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/MarketsTokenDetailsContent.kt
index 195e45cf2f..79330be97f 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/MarketsTokenDetailsContent.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/MarketsTokenDetailsContent.kt
@@ -106,6 +106,13 @@ private fun Content(
lazyListState = lazyListState,
onShouldShowPriceSubtitleChange = state.onShouldShowPriceSubtitleChange,
)
+ EventEffect(state.scrollToSection) { targetKey ->
+ val targetIndex = lazyListState.layoutInfo.visibleItemsInfo
+ .firstOrNull { it.key == targetKey }?.index
+ if (targetIndex != null) {
+ lazyListState.animateScrollToItem(targetIndex)
+ }
+ }
var bottomSpacing by remember { mutableStateOf(0.dp) }
Box(
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/TokenMarketDetailsBody.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/TokenMarketDetailsBody.kt
index de92450ef8..b1d372de32 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/TokenMarketDetailsBody.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/TokenMarketDetailsBody.kt
@@ -76,6 +76,8 @@ private fun LazyListScope.tokenMarketDetailsBodyV1(
if (relatedNews.articles.isNotEmpty()) {
relatedNews(relatedNews)
+ } else {
+ sectionStub(RelatedNews.SECTION_KEY)
}
aboutCoinHeader()
@@ -91,6 +93,11 @@ private fun LazyListScope.tokenMarketDetailsBodyV1(
}
}
+// Empty item with a key so that deeplink scroll-to-section can target it before the real content is composed
+private fun LazyListScope.sectionStub(key: String) {
+ item(key) { }
+}
+
@Suppress("CanBeNonNullable")
private fun LazyListScope.tokenMarketDetailsBodyV2(state: MarketsTokenDetailsUM.Body, relatedNews: RelatedNews) {
when (state) {
@@ -244,6 +251,8 @@ internal fun LazyListScope.infoBlocksListV2(state: MarketsTokenDetailsUM.Informa
if (relatedNews.articles.isNotEmpty()) {
relatedNews(relatedNews)
+ } else {
+ sectionStub(RelatedNews.SECTION_KEY)
}
if (state.securityScore != null) {
@@ -324,7 +333,7 @@ private fun LazyListScope.loadingInfoBlocksV2() {
}
private fun LazyListScope.relatedNews(relatedNews: RelatedNews) {
- item("related-news") {
+ item(RelatedNews.SECTION_KEY) {
Column(
modifier = Modifier
.fillMaxWidth()
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/state/MarketsTokenDetailsUM.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/state/MarketsTokenDetailsUM.kt
index 2cd7078d51..cb53a7dd7e 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/state/MarketsTokenDetailsUM.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/state/MarketsTokenDetailsUM.kt
@@ -5,6 +5,7 @@ import com.tangem.common.ui.charts.state.MarketChartDataProducer
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.marketprice.PriceChangeType
import com.tangem.core.ui.event.StateEvent
+import com.tangem.core.ui.event.consumedEvent
import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.markets.PriceChangeInterval
import com.tangem.features.feed.ui.feed.components.articles.ArticleConfigUM
@@ -30,6 +31,7 @@ internal data class MarketsTokenDetailsUM(
val onShouldShowPriceSubtitleChange: (Boolean) -> Unit,
val relatedNews: RelatedNews,
val onShareClick: () -> Unit,
+ val scrollToSection: StateEvent = consumedEvent(),
) {
data class ChartState(
@@ -80,5 +82,9 @@ internal data class MarketsTokenDetailsUM(
val onArticledClicked: (id: Int) -> Unit,
val onFirstVisible: () -> Unit,
val onScroll: () -> Unit,
- )
+ ) {
+ companion object {
+ const val SECTION_KEY = "related-news"
+ }
+ }
}
\ No newline at end of file