Updated on 2026-08-14
This commit is contained in:
parent
92034d6106
commit
625cf611f2
9 changed files with 151 additions and 7 deletions
|
|
@ -290,6 +290,17 @@
|
|||
android:host="tangem.com"
|
||||
android:path="/pay-app" />
|
||||
</intent-filter>
|
||||
<intent-filter android:autoVerify="true">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data
|
||||
android:scheme="https"
|
||||
android:host="tangem.com"
|
||||
android:pathPrefix="/news" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Disable android.startup completely. Used for Worker according doc -->
|
||||
|
|
|
|||
|
|
@ -727,6 +727,16 @@ internal class ChildFactory @Inject constructor(
|
|||
componentFactory = yieldSupplyActiveComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.NewsDetails -> {
|
||||
createComponentChild(
|
||||
context = context,
|
||||
params = FeedEntryRoute.NewsDetail(
|
||||
articleId = route.newsId,
|
||||
preselectedArticlesId = listOf(route.newsId),
|
||||
),
|
||||
componentFactory = feedEntryComponentFactory,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,8 @@ import com.tangem.common.routing.DeepLinkRoute
|
|||
import com.tangem.common.routing.DeepLinkScheme
|
||||
import com.tangem.data.card.sdk.CardSdkProvider
|
||||
import com.tangem.feature.referral.api.deeplink.ReferralDeepLinkHandler
|
||||
import com.tangem.features.feed.entry.deeplink.NewsDetailsDeepLinkHandler
|
||||
import com.tangem.features.feed.entry.featuretoggle.FeedFeatureToggle
|
||||
import com.tangem.features.markets.deeplink.MarketsDeepLinkHandler
|
||||
import com.tangem.features.markets.deeplink.MarketsTokenDetailDeepLinkHandler
|
||||
import com.tangem.features.onramp.deeplink.BuyDeepLinkHandler
|
||||
|
|
@ -50,6 +52,8 @@ internal class DeepLinkFactory @Inject constructor(
|
|||
private val swapDeepLink: SwapDeepLinkHandler.Factory,
|
||||
private val promoDeepLink: PromoDeeplinkHandler.Factory,
|
||||
private val onboardVisaDeepLink: OnboardVisaDeepLinkHandler.Factory,
|
||||
private val newsDetailsDeepLink: NewsDetailsDeepLinkHandler.Factory,
|
||||
private val feedFeatureToggle: FeedFeatureToggle,
|
||||
) {
|
||||
private val permittedAppRoute = MutableStateFlow(false)
|
||||
|
||||
|
|
@ -102,7 +106,7 @@ internal class DeepLinkFactory @Inject constructor(
|
|||
|
||||
private fun launchDeepLink(deeplinkUri: Uri, coroutineScope: CoroutineScope, isFromOnNewIntent: Boolean) {
|
||||
when (deeplinkUri.scheme) {
|
||||
DeepLinkScheme.Https.scheme -> handleHttpDeepLinks(deeplinkUri)
|
||||
DeepLinkScheme.Https.scheme -> handleHttpDeepLinks(deeplinkUri, coroutineScope)
|
||||
DeepLinkScheme.Tangem.scheme -> handleTangemDeepLinks(deeplinkUri, coroutineScope, isFromOnNewIntent)
|
||||
DeepLinkScheme.WalletConnect.scheme -> walletConnectDeepLink.create(deeplinkUri)
|
||||
else -> {
|
||||
|
|
@ -116,10 +120,18 @@ internal class DeepLinkFactory @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleHttpDeepLinks(deeplinkUri: Uri) {
|
||||
if (deeplinkUri.host == DeepLinkRoute.PayApp.host && deeplinkUri.path?.startsWith("/pay-app") == true) {
|
||||
onboardVisaDeepLink.create(deeplinkUri)
|
||||
return
|
||||
private fun handleHttpDeepLinks(deeplinkUri: Uri, coroutineScope: CoroutineScope) {
|
||||
if (deeplinkUri.host == DeepLinkRoute.PayApp.host) {
|
||||
when {
|
||||
deeplinkUri.path?.startsWith("/pay-app") == true -> {
|
||||
onboardVisaDeepLink.create(deeplinkUri)
|
||||
return
|
||||
}
|
||||
deeplinkUri.path?.startsWith("/news") == true && feedFeatureToggle.isFeedEnabled -> {
|
||||
newsDetailsDeepLink.create(coroutineScope, deeplinkUri)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import android.net.Uri
|
|||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.data.card.sdk.CardSdkProvider
|
||||
import com.tangem.feature.referral.api.deeplink.ReferralDeepLinkHandler
|
||||
import com.tangem.features.feed.entry.deeplink.NewsDetailsDeepLinkHandler
|
||||
import com.tangem.features.feed.entry.featuretoggle.FeedFeatureToggle
|
||||
import com.tangem.features.markets.deeplink.MarketsDeepLinkHandler
|
||||
import com.tangem.features.markets.deeplink.MarketsTokenDetailDeepLinkHandler
|
||||
import com.tangem.features.onramp.deeplink.BuyDeepLinkHandler
|
||||
|
|
@ -81,6 +83,12 @@ class DeepLinkFactoryTest {
|
|||
private val cardSdkProvider = mockk<CardSdkProvider>(relaxed = true) {
|
||||
every { sdk.uiVisibility() } returns MutableStateFlow(false)
|
||||
}
|
||||
|
||||
private val newsDeeplink = mockk<NewsDetailsDeepLinkHandler.Factory>(relaxed = true) {
|
||||
every { create(any(), any()) } returns mockk()
|
||||
}
|
||||
private val feedFeatureToggle = mockk<FeedFeatureToggle>()
|
||||
|
||||
private val mockedUri = mockk<Uri>(relaxed = true)
|
||||
private val isFromOnNewIntent: Boolean = false
|
||||
|
||||
|
|
@ -103,6 +111,8 @@ class DeepLinkFactoryTest {
|
|||
swapDeepLink = swapDeepLinkFactory,
|
||||
promoDeepLink = promoDeepLinkFactory,
|
||||
onboardVisaDeepLink = onboardVisaDeepLink,
|
||||
newsDetailsDeepLink = newsDeeplink,
|
||||
feedFeatureToggle = feedFeatureToggle,
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
|
|
|
|||
|
|
@ -468,4 +468,7 @@ sealed class AppRoute(val path: String) : Route {
|
|||
val cryptoCurrency: CryptoCurrency,
|
||||
val apy: String,
|
||||
) : AppRoute(path = "/yield_supply_active/${userWalletId.stringValue}/${cryptoCurrency.symbol}")
|
||||
|
||||
@Serializable
|
||||
data class NewsDetails(val newsId: Int) : AppRoute(path = "/news_details/$newsId")
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.features.feed.entry.deeplink
|
||||
|
||||
import android.net.Uri
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
interface NewsDetailsDeepLinkHandler {
|
||||
|
||||
interface Factory {
|
||||
fun create(coroutineScope: CoroutineScope, deeplinkUri: Uri): NewsDetailsDeepLinkHandler
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
package com.tangem.features.feed.deeplink
|
||||
|
||||
import android.net.Uri
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.features.feed.entry.deeplink.NewsDetailsDeepLinkHandler
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
internal class DefaultNewsDetailsDeepLinkHandler @AssistedInject constructor(
|
||||
@Assisted private val scope: CoroutineScope,
|
||||
@Assisted private val deeplinkUri: Uri,
|
||||
private val appRouter: AppRouter,
|
||||
) : NewsDetailsDeepLinkHandler {
|
||||
|
||||
init {
|
||||
handleDeepLink()
|
||||
}
|
||||
|
||||
private fun handleDeepLink() {
|
||||
scope.launch {
|
||||
val articleId = extractArticleIdFromUri(deeplinkUri)
|
||||
if (articleId == null) {
|
||||
Timber.e(
|
||||
"""
|
||||
Failed to extract article ID from deep link
|
||||
|- Received URI: $deeplinkUri
|
||||
""".trimIndent(),
|
||||
)
|
||||
return@launch
|
||||
}
|
||||
|
||||
appRouter.push(
|
||||
AppRoute.NewsDetails(newsId = articleId),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parsing URI with format https://tangem.com/news/{category}/{id}-{slug} to get id
|
||||
*/
|
||||
private fun extractArticleIdFromUri(uri: Uri): Int? {
|
||||
val path = uri.path ?: return null
|
||||
val pathSegments = path.split("/").filter { it.isNotBlank() }
|
||||
if (pathSegments.isEmpty() || pathSegments[0] != "news" || pathSegments.size < 2) {
|
||||
return null
|
||||
}
|
||||
val lastSegment = pathSegments.last()
|
||||
val idPart = lastSegment.substringBefore("-")
|
||||
return idPart.toIntOrNull()
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : NewsDetailsDeepLinkHandler.Factory {
|
||||
override fun create(coroutineScope: CoroutineScope, deeplinkUri: Uri): DefaultNewsDetailsDeepLinkHandler
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.features.feed.deeplink.di
|
||||
|
||||
import com.tangem.features.feed.deeplink.DefaultNewsDetailsDeepLinkHandler
|
||||
import com.tangem.features.feed.entry.deeplink.NewsDetailsDeepLinkHandler
|
||||
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 FeedDeepLinkModule {
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindNewsDetailsDeepLinkHandlerFactory(
|
||||
impl: DefaultNewsDetailsDeepLinkHandler.Factory,
|
||||
): NewsDetailsDeepLinkHandler.Factory
|
||||
}
|
||||
|
|
@ -27,6 +27,7 @@ import com.tangem.common.ui.news.ArticleHeader
|
|||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.SecondaryButtonIconStart
|
||||
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.components.buttons.common.TangemButtonSize
|
||||
import com.tangem.core.ui.components.pager.PagerIndicator
|
||||
|
|
@ -171,7 +172,10 @@ private fun ArticleDetail(article: ArticleUM, modifier: Modifier = Modifier, onL
|
|||
items = article.sources,
|
||||
key = SourceUM::id,
|
||||
) { source ->
|
||||
SourceItem(source = source)
|
||||
SourceItem(
|
||||
source = source,
|
||||
modifier = Modifier.fillParentMaxHeight(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -259,9 +263,11 @@ private fun SourceItem(source: SourceUM, modifier: Modifier = Modifier) {
|
|||
color = TangemTheme.colors.text.primary1,
|
||||
maxLines = 3,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.padding(bottom = 12.dp),
|
||||
)
|
||||
}
|
||||
|
||||
SpacerHMax()
|
||||
|
||||
Text(
|
||||
text = source.publishedAt.resolveReference(),
|
||||
style = TangemTheme.typography.caption2,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue