diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 06a868e0a4..ac76057c04 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -34,6 +34,7 @@ @@ -208,6 +208,39 @@ android:host="token_chart" android:scheme="tangem" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 f33be6d875..02afc7ecfe 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 @@ -4,12 +4,12 @@ import android.net.Uri import com.tangem.common.routing.AppRoute 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.markets.deeplink.MarketsDeepLinkHandler import com.tangem.features.markets.deeplink.MarketsTokenDetailDeepLinkHandler -import com.tangem.features.onramp.deeplink.BuyDeepLinkHandler -import com.tangem.features.onramp.deeplink.OnrampDeepLinkHandler -import com.tangem.features.send.v2.api.deeplink.SellDeepLinkHandler +import com.tangem.features.onramp.deeplink.* +import com.tangem.features.send.v2.api.deeplink.SellRedirectDeepLinkHandler import com.tangem.features.staking.api.deeplink.StakingDeepLinkHandler import com.tangem.features.tokendetails.deeplink.TokenDetailsDeepLinkHandler import com.tangem.features.wallet.deeplink.WalletDeepLinkHandler @@ -21,6 +21,7 @@ import dagger.hilt.android.scopes.ActivityScoped import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.transformLatest import timber.log.Timber @@ -29,9 +30,10 @@ import javax.inject.Inject @Suppress("LongParameterList") @ActivityScoped internal class DeepLinkFactory @Inject constructor( + private val cardSdkProvider: CardSdkProvider, private val onrampDeepLink: OnrampDeepLinkHandler.Factory, - private val sellDeepLink: SellDeepLinkHandler.Factory, - private val buyDeepLink: BuyDeepLinkHandler.Factory, + private val sellRedirectDeepLink: SellRedirectDeepLinkHandler.Factory, + private val buyRedirectDeepLink: BuyRedirectDeepLinkHandler.Factory, private val referralDeepLink: ReferralDeepLinkHandler.Factory, private val walletConnectDeepLink: WalletConnectDeepLinkHandler.Factory, private val walletDeepLink: WalletDeepLinkHandler.Factory, @@ -39,6 +41,9 @@ internal class DeepLinkFactory @Inject constructor( private val stakingDeepLink: StakingDeepLinkHandler.Factory, private val marketsDeepLink: MarketsDeepLinkHandler.Factory, private val marketsTokenDetailDeepLink: MarketsTokenDetailDeepLinkHandler.Factory, + private val buyDeepLink: BuyDeepLinkHandler.Factory, + private val sellDeepLink: SellDeepLinkHandler.Factory, + private val swapDeepLink: SwapDeepLinkHandler.Factory, ) { private val permittedAppRoute = MutableStateFlow(false) @@ -55,15 +60,19 @@ internal class DeepLinkFactory @Inject constructor( |- Received URI: $deeplinkUri """.trimIndent(), ) - permittedAppRoute - .transformLatest { isPermitted -> - if (isPermitted) { - lastDeepLink?.let { - launchDeepLink(it, coroutineScope, isFromOnNewIntent) - } - lastDeepLink = null + combine( + permittedAppRoute, + cardSdkProvider.sdk.uiVisibility(), + ) { isRoutePermitted, isCardSdkVisible -> + isRoutePermitted to isCardSdkVisible + }.transformLatest, Unit> { (isRoutePermitted, isCardSdkVisible) -> + if (isRoutePermitted && !isCardSdkVisible) { + lastDeepLink?.let { + launchDeepLink(it, coroutineScope, isFromOnNewIntent) } + lastDeepLink = null } + } .launchIn(coroutineScope) .saveIn(deepLinkHandlerJobHolder) } @@ -103,8 +112,8 @@ internal class DeepLinkFactory @Inject constructor( val queryParams = getQueryParams(deeplinkUri) when (deeplinkUri.host) { DeepLinkRoute.Onramp.host -> onrampDeepLink.create(coroutineScope, queryParams) - DeepLinkRoute.Sell.host -> sellDeepLink.create(coroutineScope, queryParams) - DeepLinkRoute.Buy.host -> buyDeepLink.create(coroutineScope) + DeepLinkRoute.SellRedirect.host -> sellRedirectDeepLink.create(coroutineScope, queryParams) + DeepLinkRoute.BuyRedirect.host -> buyRedirectDeepLink.create(coroutineScope) DeepLinkRoute.Referral.host -> referralDeepLink.create() DeepLinkRoute.Wallet.host -> walletDeepLink.create() DeepLinkRoute.TokenDetails.host -> tokenDetailsDeepLink.create( @@ -115,6 +124,9 @@ internal class DeepLinkFactory @Inject constructor( DeepLinkRoute.Staking.host -> stakingDeepLink.create(coroutineScope, queryParams) DeepLinkRoute.Markets.host -> marketsDeepLink.create() DeepLinkRoute.MarketTokenDetail.host -> marketsTokenDetailDeepLink.create(coroutineScope, queryParams) + DeepLinkRoute.Buy.host -> buyDeepLink.create() + DeepLinkRoute.Sell.host -> sellDeepLink.create() + DeepLinkRoute.Swap.host -> swapDeepLink.create() else -> { Timber.i( """ 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 8078f58b05..018f3b76b8 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 @@ -2,12 +2,12 @@ package com.tangem.tap.routing.utils 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.markets.deeplink.MarketsDeepLinkHandler import com.tangem.features.markets.deeplink.MarketsTokenDetailDeepLinkHandler -import com.tangem.features.onramp.deeplink.BuyDeepLinkHandler -import com.tangem.features.onramp.deeplink.OnrampDeepLinkHandler -import com.tangem.features.send.v2.api.deeplink.SellDeepLinkHandler +import com.tangem.features.onramp.deeplink.* +import com.tangem.features.send.v2.api.deeplink.SellRedirectDeepLinkHandler import com.tangem.features.staking.api.deeplink.StakingDeepLinkHandler import com.tangem.features.tokendetails.deeplink.TokenDetailsDeepLinkHandler import com.tangem.features.wallet.deeplink.WalletDeepLinkHandler @@ -18,6 +18,7 @@ import io.mockk.verify import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.* import org.junit.After import org.junit.Before @@ -30,10 +31,10 @@ class DeepLinkFactoryTest { private val onrampDeepLinkFactory = mockk(relaxed = true) { every { create(any(), any()) } returns mockk() } - private val sellDeepLinkFactory = mockk(relaxed = true) { + private val sellRedirectDeepLinkFactory = mockk(relaxed = true) { every { create(any(), any()) } returns mockk() } - private val buyDeepLinkFactory = mockk(relaxed = true) { + private val buyRedirectDeepLinkFactory = mockk(relaxed = true) { every { create(any()) } returns mockk() } private val referralDeepLinkFactory = mockk(relaxed = true) { @@ -57,7 +58,19 @@ class DeepLinkFactoryTest { private val marketsTokenDetailDeepLinkFactory = mockk(relaxed = true) { every { create(any(), any()) } returns mockk() } + private val sellDeepLinkFactory = mockk(relaxed = true) { + every { create() } returns mockk() + } + private val buyDeepLinkFactory = mockk(relaxed = true) { + every { create() } returns mockk() + } + private val swapDeepLinkFactory = mockk(relaxed = true) { + every { create() } returns mockk() + } + private val cardSdkProvider = mockk(relaxed = true) { + every { sdk.uiVisibility() } returns MutableStateFlow(false) + } private val mockedUri = mockk(relaxed = true) private val isFromOnNewIntent: Boolean = false @@ -65,16 +78,20 @@ class DeepLinkFactoryTest { private lateinit var testScope: TestScope private val deepLinkFactory = DeepLinkFactory( - onrampDeepLinkFactory, - sellDeepLinkFactory, - buyDeepLinkFactory, - referralDeepLinkFactory, - walletConnectDeepLinkFactory, - walletDeepLinkFactory, - tokenDetailsDeepLinkFactory, - stakingDeepLinkFactory, - marketsDeepLinkFactory, - marketsTokenDetailDeepLinkFactory, + cardSdkProvider = cardSdkProvider, + onrampDeepLink = onrampDeepLinkFactory, + sellRedirectDeepLink = sellRedirectDeepLinkFactory, + buyRedirectDeepLink = buyRedirectDeepLinkFactory, + referralDeepLink = referralDeepLinkFactory, + walletConnectDeepLink = walletConnectDeepLinkFactory, + walletDeepLink = walletDeepLinkFactory, + tokenDetailsDeepLink = tokenDetailsDeepLinkFactory, + stakingDeepLink = stakingDeepLinkFactory, + marketsDeepLink = marketsDeepLinkFactory, + marketsTokenDetailDeepLink = marketsTokenDetailDeepLinkFactory, + buyDeepLink = buyDeepLinkFactory, + sellDeepLink = sellDeepLinkFactory, + swapDeepLink = swapDeepLinkFactory, ) @OptIn(ExperimentalCoroutinesApi::class) @@ -140,6 +157,24 @@ class DeepLinkFactoryTest { verify(inverse = true) { onrampDeepLinkFactory.create(any(), any()) } } + @Test + fun `handleDeeplink does not launch when card scan visible`() = runTest { + every { mockedUri.scheme } returns "tangem" + every { mockedUri.host } returns "onramp" + every { mockedUri.query } returns "param=value" + every { mockedUri.queryParameterNames } returns setOf("param") + every { mockedUri.getQueryParameter("param") } returns "value" + every { cardSdkProvider.sdk.uiVisibility() } returns MutableStateFlow(true) + + deepLinkFactory.handleDeeplink(mockedUri, testScope, isFromOnNewIntent) + deepLinkFactory.checkRoutingReadiness(AppRoute.Wallet) + + advanceUntilIdle() + + // Verify no handler was called + verify(inverse = true) { onrampDeepLinkFactory.create(any(), any()) } + } + @Test fun `launchDeepLink handles tangem scheme correctly`() = runTest { every { mockedUri.scheme } returns "tangem" @@ -184,8 +219,8 @@ class DeepLinkFactoryTest { verify(inverse = true) { onrampDeepLinkFactory.create(any(), any()) - sellDeepLinkFactory.create(any(), any()) - buyDeepLinkFactory.create(any()) + sellRedirectDeepLinkFactory.create(any(), any()) + buyRedirectDeepLinkFactory.create(any()) referralDeepLinkFactory.create() walletConnectDeepLinkFactory.create(any()) walletDeepLinkFactory.create() @@ -208,11 +243,11 @@ class DeepLinkFactoryTest { advanceUntilIdle() verify { onrampDeepLinkFactory.create(eq(testScope), eq(mapOf("param" to "value"))) } - // Test Sell + // Test Sell Redirect every { mockedUri.host } returns "redirect_sell" deepLinkFactory.handleDeeplink(mockedUri, testScope, isFromOnNewIntent) advanceUntilIdle() - verify { sellDeepLinkFactory.create(eq(testScope), eq(mapOf("param" to "value"))) } + verify { sellRedirectDeepLinkFactory.create(eq(testScope), eq(mapOf("param" to "value"))) } // Test Token Details every { mockedUri.host } returns "token" @@ -242,11 +277,11 @@ class DeepLinkFactoryTest { every { mockedUri.queryParameterNames } returns emptySet() every { mockedUri.getQueryParameter(any()) } returns "" - // Test Buy + // Test Buy Redirect every { mockedUri.host } returns "redirect" deepLinkFactory.handleDeeplink(mockedUri, testScope, isFromOnNewIntent) advanceUntilIdle() - verify { buyDeepLinkFactory.create(eq(testScope)) } + verify { buyRedirectDeepLinkFactory.create(eq(testScope)) } // Test Referral every { mockedUri.host } returns "referral" @@ -265,6 +300,24 @@ class DeepLinkFactoryTest { deepLinkFactory.handleDeeplink(mockedUri, testScope, isFromOnNewIntent) advanceUntilIdle() verify { marketsDeepLinkFactory.create() } + + // Test Sell + every { mockedUri.host } returns "sell" + deepLinkFactory.handleDeeplink(mockedUri, testScope, isFromOnNewIntent) + advanceUntilIdle() + verify { sellDeepLinkFactory.create() } + + // Test Swap + every { mockedUri.host } returns "swap" + deepLinkFactory.handleDeeplink(mockedUri, testScope, isFromOnNewIntent) + advanceUntilIdle() + verify { sellDeepLinkFactory.create() } + + // Test Buy + every { mockedUri.host } returns "buy" + deepLinkFactory.handleDeeplink(mockedUri, testScope, isFromOnNewIntent) + advanceUntilIdle() + verify { buyDeepLinkFactory.create() } } @Test @@ -279,12 +332,15 @@ class DeepLinkFactoryTest { verify(inverse = true) { onrampDeepLinkFactory.create(any(), any()) - sellDeepLinkFactory.create(any(), any()) - buyDeepLinkFactory.create(any()) + sellRedirectDeepLinkFactory.create(any(), any()) + buyRedirectDeepLinkFactory.create(any()) referralDeepLinkFactory.create() walletConnectDeepLinkFactory.create(any()) walletDeepLinkFactory.create() tokenDetailsDeepLinkFactory.create(any(), any(), any()) + buyDeepLinkFactory.create() + sellDeepLinkFactory.create() + swapDeepLinkFactory.create() } } diff --git a/core/deep-links/src/main/kotlin/com/tangem/core/deeplink/DeeplinkConst.kt b/core/deep-links/src/main/kotlin/com/tangem/core/deeplink/DeeplinkConst.kt index cf598f7b4f..e0ffec3703 100644 --- a/core/deep-links/src/main/kotlin/com/tangem/core/deeplink/DeeplinkConst.kt +++ b/core/deep-links/src/main/kotlin/com/tangem/core/deeplink/DeeplinkConst.kt @@ -1,11 +1,12 @@ package com.tangem.core.deeplink object DeeplinkConst { - @Deprecated("Used only for push notifications mapping, use `WALLET_ID_KEY` only") - const val PAYLOAD_WALLET_ID_KEY = "user_wallet_id" const val TANGEM_SCHEME = "tangem" - const val WALLET_ID_KEY = "walletId" + const val WALLET_ID_KEY = "user_wallet_id" const val NETWORK_ID_KEY = "network_id" const val TYPE_KEY = "type" const val TOKEN_ID_KEY = "token_id" + const val DERIVATION_PATH_KEY = "derivation_path" + const val TRANSACTION_ID_KEY = "transaction_id" + const val NAME_KEY = "name" } \ No newline at end of file diff --git a/core/deep-links/src/main/kotlin/com/tangem/core/deeplink/converter/PayloadToDeeplinkConverter.kt b/core/deep-links/src/main/kotlin/com/tangem/core/deeplink/converter/PayloadToDeeplinkConverter.kt index 8470ffbb71..f6ce9ad32e 100644 --- a/core/deep-links/src/main/kotlin/com/tangem/core/deeplink/converter/PayloadToDeeplinkConverter.kt +++ b/core/deep-links/src/main/kotlin/com/tangem/core/deeplink/converter/PayloadToDeeplinkConverter.kt @@ -3,9 +3,11 @@ package com.tangem.core.deeplink.converter import com.tangem.common.routing.DeepLinkRoute import com.tangem.common.routing.DeepLinkScheme import com.tangem.core.deeplink.DEEPLINK_KEY +import com.tangem.core.deeplink.DeeplinkConst.DERIVATION_PATH_KEY +import com.tangem.core.deeplink.DeeplinkConst.NAME_KEY import com.tangem.core.deeplink.DeeplinkConst.NETWORK_ID_KEY -import com.tangem.core.deeplink.DeeplinkConst.PAYLOAD_WALLET_ID_KEY import com.tangem.core.deeplink.DeeplinkConst.TOKEN_ID_KEY +import com.tangem.core.deeplink.DeeplinkConst.TRANSACTION_ID_KEY import com.tangem.core.deeplink.DeeplinkConst.TYPE_KEY import com.tangem.core.deeplink.DeeplinkConst.WALLET_ID_KEY import com.tangem.utils.converter.Converter @@ -25,21 +27,29 @@ object PayloadToDeeplinkConverter : Converter, String?> { val type = payload[TYPE_KEY] ?: return null val networkId = payload[NETWORK_ID_KEY] ?: return null val tokenId = payload[TOKEN_ID_KEY] ?: return null - val walletId = payload[PAYLOAD_WALLET_ID_KEY] ?: return null + val walletId = payload[WALLET_ID_KEY] ?: return null + val derivationPath = payload[DERIVATION_PATH_KEY] ?: return null + val transactionId = payload[TRANSACTION_ID_KEY] + val name = payload[NAME_KEY] - return DeepLinkBuilder().setScheme(DeepLinkScheme.Tangem.scheme) - .setAction(DeepLinkRoute.TokenDetails.host) - .addQueryParam(NETWORK_ID_KEY, networkId) - .addQueryParam(TOKEN_ID_KEY, tokenId) - .addQueryParam(TYPE_KEY, type) - .addQueryParam(WALLET_ID_KEY, walletId) - .build() + return DeepLinkBuilder().setScheme(DeepLinkScheme.Tangem.scheme).apply { + setAction(DeepLinkRoute.TokenDetails.host) + addQueryParam(NETWORK_ID_KEY, networkId) + addQueryParam(TOKEN_ID_KEY, tokenId) + addQueryParam(TYPE_KEY, type) + addQueryParam(WALLET_ID_KEY, walletId) + addQueryParam(DERIVATION_PATH_KEY, derivationPath) + + transactionId?.let { addQueryParam(TRANSACTION_ID_KEY, it) } + name?.let { addQueryParam(NAME_KEY, it) } + }.build() } private fun isTangemPushNotificationPayload(payload: Map): Boolean { return payload.containsKey(TYPE_KEY) && payload.containsKey(NETWORK_ID_KEY) && payload.containsKey(TOKEN_ID_KEY) && - payload.containsKey(PAYLOAD_WALLET_ID_KEY) + payload.containsKey(WALLET_ID_KEY) && + payload.containsKey(DERIVATION_PATH_KEY) } } \ No newline at end of file diff --git a/core/deep-links/src/test/kotlin/com/tangem/core/deeplink/converter/PayloadToDeeplinkConverterTest.kt b/core/deep-links/src/test/kotlin/com/tangem/core/deeplink/converter/PayloadToDeeplinkConverterTest.kt index 697ff01d29..f3887c86e9 100644 --- a/core/deep-links/src/test/kotlin/com/tangem/core/deeplink/converter/PayloadToDeeplinkConverterTest.kt +++ b/core/deep-links/src/test/kotlin/com/tangem/core/deeplink/converter/PayloadToDeeplinkConverterTest.kt @@ -2,10 +2,11 @@ package com.tangem.core.deeplink.converter import com.google.common.truth.Truth.assertThat import com.tangem.core.deeplink.DEEPLINK_KEY +import com.tangem.core.deeplink.DeeplinkConst.DERIVATION_PATH_KEY import com.tangem.core.deeplink.DeeplinkConst.NETWORK_ID_KEY -import com.tangem.core.deeplink.DeeplinkConst.PAYLOAD_WALLET_ID_KEY import com.tangem.core.deeplink.DeeplinkConst.TOKEN_ID_KEY import com.tangem.core.deeplink.DeeplinkConst.TYPE_KEY +import com.tangem.core.deeplink.DeeplinkConst.WALLET_ID_KEY import org.junit.Test internal class PayloadToDeeplinkConverterTest { @@ -14,7 +15,8 @@ internal class PayloadToDeeplinkConverterTest { fun `GIVEN payload with deeplink key WHEN convert THEN should return deeplink value`() { // GIVEN val payload = mapOf( - DEEPLINK_KEY to "tangem://token-details?networkId=ethereum&tokenId=0x123&type=token&walletId=wallet123", + DEEPLINK_KEY to "tangem://token-details?networkId=ethereum&tokenId=0x123&type=token&user_wallet_id=wallet123" + + "&derivation_path=m'0'0'0", ) // WHEN @@ -22,7 +24,7 @@ internal class PayloadToDeeplinkConverterTest { // THEN assertThat(result).isEqualTo( - "tangem://token-details?networkId=ethereum&tokenId=0x123&type=token&walletId=wallet123", + "tangem://token-details?networkId=ethereum&tokenId=0x123&type=token&user_wallet_id=wallet123&derivation_path=m'0'0'0", ) } @@ -33,7 +35,8 @@ internal class PayloadToDeeplinkConverterTest { TYPE_KEY to "token", NETWORK_ID_KEY to "ethereum", TOKEN_ID_KEY to "0x123", - PAYLOAD_WALLET_ID_KEY to "wallet123", + WALLET_ID_KEY to "wallet123", + DERIVATION_PATH_KEY to "m'0'0'0", ) // WHEN @@ -41,7 +44,7 @@ internal class PayloadToDeeplinkConverterTest { // THEN assertThat(result).isEqualTo( - "tangem://token?network_id=ethereum&token_id=0x123&type=token&walletId=wallet123", + "tangem://token?network_id=ethereum&token_id=0x123&type=token&user_wallet_id=wallet123&derivation_path=m'0'0'0", ) } @@ -51,7 +54,8 @@ internal class PayloadToDeeplinkConverterTest { val payload = mapOf( NETWORK_ID_KEY to "ethereum", TOKEN_ID_KEY to "0x123", - PAYLOAD_WALLET_ID_KEY to "wallet123", + WALLET_ID_KEY to "wallet123", + DERIVATION_PATH_KEY to "m'0'0'0", ) // WHEN @@ -67,7 +71,8 @@ internal class PayloadToDeeplinkConverterTest { val payload = mapOf( TYPE_KEY to "token", TOKEN_ID_KEY to "0x123", - PAYLOAD_WALLET_ID_KEY to "wallet123", + WALLET_ID_KEY to "wallet123", + DERIVATION_PATH_KEY to "m'0'0'0", ) // WHEN @@ -83,7 +88,8 @@ internal class PayloadToDeeplinkConverterTest { val payload = mapOf( TYPE_KEY to "token", NETWORK_ID_KEY to "ethereum", - PAYLOAD_WALLET_ID_KEY to "wallet123", + WALLET_ID_KEY to "wallet123", + DERIVATION_PATH_KEY to "m'0'0'0", ) // WHEN diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockStakingRepository.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockStakingRepository.kt index c53411a627..303da59cda 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockStakingRepository.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockStakingRepository.kt @@ -48,6 +48,11 @@ class MockStakingRepository : StakingRepository { cryptoCurrency: CryptoCurrency, ): Flow = flowOf(StakingAvailability.Unavailable) + override suspend fun getStakingAvailabilitySync( + userWalletId: UserWalletId, + cryptoCurrency: CryptoCurrency, + ): StakingAvailability = StakingAvailability.Unavailable + override suspend fun getActions( userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, diff --git a/features/markets/impl/build.gradle.kts b/features/markets/impl/build.gradle.kts index 499ba281a4..2ce867d718 100644 --- a/features/markets/impl/build.gradle.kts +++ b/features/markets/impl/build.gradle.kts @@ -15,7 +15,6 @@ dependencies { /* Project - API */ api(projects.features.markets.api) api(projects.features.onramp.api) - implementation(projects.core.navigation) /* Data */ implementation(projects.data.common) @@ -38,6 +37,7 @@ dependencies { implementation(projects.domain.wallets) implementation(projects.domain.wallets.models) implementation(projects.domain.settings) + implementation(projects.domain.notifications.models) // FIXME [REDACTED_TASK_KEY] // Remove the "Buy" and "Sell" actions from the redux middleware. @@ -73,6 +73,8 @@ dependencies { implementation(projects.core.configToggles) implementation(projects.core.analytics) implementation(projects.core.analytics.models) + implementation(projects.core.navigation) + implementation(projects.core.deepLinks) /* Common */ implementation(projects.common.ui) diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/deeplink/DefaultMarketsTokenDetailDeepLinkHandler.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/deeplink/DefaultMarketsTokenDetailDeepLinkHandler.kt index 5007ac70ab..f9d5ca176b 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/deeplink/DefaultMarketsTokenDetailDeepLinkHandler.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/deeplink/DefaultMarketsTokenDetailDeepLinkHandler.kt @@ -3,6 +3,7 @@ package com.tangem.features.markets.deeplink import arrow.core.getOrElse import com.tangem.common.routing.AppRoute import com.tangem.common.routing.AppRouter +import com.tangem.core.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 @@ -17,14 +18,18 @@ import kotlinx.coroutines.launch import timber.log.Timber internal class DefaultMarketsTokenDetailDeepLinkHandler @AssistedInject constructor( - @Assisted scope: CoroutineScope, - @Assisted queryParams: Map, - appRouter: AppRouter, - getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, - getTokenMarketInfoUseCase: GetTokenMarketInfoUseCase, + @Assisted private val scope: CoroutineScope, + @Assisted private val queryParams: Map, + private val appRouter: AppRouter, + private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, + private val getTokenMarketInfoUseCase: GetTokenMarketInfoUseCase, ) : MarketsTokenDetailDeepLinkHandler { init { + handleDeepLink() + } + + private fun handleDeepLink() { val tokenId = queryParams[TOKEN_ID_KEY] val rawTokenId = CryptoCurrency.RawID(tokenId.orEmpty()) @@ -36,7 +41,7 @@ internal class DefaultMarketsTokenDetailDeepLinkHandler @AssistedInject construc val tokenInfo = getTokenMarketInfoUseCase( appCurrency = appCurrency, tokenId = rawTokenId, - tokenSymbol = TOKEN_SYMBOL_KEY, + tokenSymbol = "", // used for analytics ).getOrElse { Timber.e("Failed to get market token info") return@launch @@ -71,9 +76,4 @@ internal class DefaultMarketsTokenDetailDeepLinkHandler @AssistedInject construc queryParams: Map, ): DefaultMarketsTokenDetailDeepLinkHandler } - - private companion object { - const val TOKEN_ID_KEY = "token_id" - const val TOKEN_SYMBOL_KEY = "token_symbol" - } } \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/deeplink/DefaultBuyDeepLinkHandler.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/deeplink/DefaultBuyDeepLinkHandler.kt index 32029a1244..5633cf6424 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/deeplink/DefaultBuyDeepLinkHandler.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/deeplink/DefaultBuyDeepLinkHandler.kt @@ -1,25 +1,15 @@ package com.tangem.features.onramp.deeplink -import arrow.core.getOrElse -import com.tangem.core.analytics.api.AnalyticsEventHandler -import com.tangem.domain.tokens.GetCryptoCurrencyUseCase -import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent -import com.tangem.domain.wallets.models.isMultiCurrency +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.AppRouter import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase -import com.tangem.features.onramp.OnrampFeatureToggles -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 DefaultBuyDeepLinkHandler @AssistedInject constructor( - @Assisted scope: CoroutineScope, - onrampFeatureToggles: OnrampFeatureToggles, + router: AppRouter, getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, - getCryptoCurrencyUseCase: GetCryptoCurrencyUseCase, - analyticsEventHandler: AnalyticsEventHandler, ) : BuyDeepLinkHandler { init { @@ -29,21 +19,13 @@ internal class DefaultBuyDeepLinkHandler @AssistedInject constructor( Timber.e("Error on getting user wallet: $it") }, ifRight = { userWallet -> - if (!onrampFeatureToggles.isFeatureEnabled && !userWallet.isMultiCurrency) { - scope.launch { - val cryptoCurrency = getCryptoCurrencyUseCase(userWallet.walletId).getOrElse { - Timber.e("Error on getting cryptoCurrency: $it") - return@launch - } - analyticsEventHandler.send(TokenScreenAnalyticsEvent.Bought(cryptoCurrency.symbol)) - } - } + router.push(AppRoute.BuyCrypto(userWallet.walletId)) }, ) } @AssistedFactory interface Factory : BuyDeepLinkHandler.Factory { - override fun create(coroutineScope: CoroutineScope): DefaultBuyDeepLinkHandler + override fun create(): DefaultBuyDeepLinkHandler } } \ No newline at end of file diff --git a/features/referral/impl/build.gradle.kts b/features/referral/impl/build.gradle.kts index 89e4f4f57c..34f1402a0b 100644 --- a/features/referral/impl/build.gradle.kts +++ b/features/referral/impl/build.gradle.kts @@ -22,6 +22,7 @@ dependencies { implementation(projects.core.utils) implementation(projects.core.ui) implementation(projects.core.decompose) + implementation(projects.core.deepLinks) implementation(projects.libs.crypto) implementation(projects.common.routing) @@ -41,6 +42,7 @@ dependencies { implementation(projects.domain.wallets) implementation(projects.domain.legacy) implementation(projects.domain.wallets.models) + implementation(projects.domain.notifications.models) implementation(projects.features.referral.domain) /** Other libraries */ diff --git a/features/staking/impl/build.gradle.kts b/features/staking/impl/build.gradle.kts index 05decae6d5..ebfa0a6ffd 100644 --- a/features/staking/impl/build.gradle.kts +++ b/features/staking/impl/build.gradle.kts @@ -47,6 +47,7 @@ dependencies { implementation(projects.core.analytics) implementation(projects.core.analytics.models) implementation(projects.core.decompose) + implementation(projects.core.deepLinks) /** Domain */ @@ -66,6 +67,7 @@ dependencies { implementation(projects.domain.txhistory) implementation(projects.domain.feedback) implementation(projects.domain.feedback.models) + implementation(projects.domain.notifications.models) /** Common */ implementation(projects.common.ui) diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/testpush/viewmodel/transformers/markettokens/TestPushMarketTokenClickBottomSheetTransformer.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/testpush/viewmodel/transformers/markettokens/TestPushMarketTokenClickBottomSheetTransformer.kt index 08f232befb..58fcfdbf4e 100644 --- a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/testpush/viewmodel/transformers/markettokens/TestPushMarketTokenClickBottomSheetTransformer.kt +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/testpush/viewmodel/transformers/markettokens/TestPushMarketTokenClickBottomSheetTransformer.kt @@ -19,8 +19,7 @@ internal class TestPushMarketTokenClickBottomSheetTransformer( append(DeepLinkRoute.MarketTokenDetail.host) append("?token_id=") append(tokenMarket.id) - append("&token_symbol=") - append(tokenMarket.symbol) + append("&type=promo") }, ), ), diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/deeplink/DefaultTokenDetailsDeepLinkHandler.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/deeplink/DefaultTokenDetailsDeepLinkHandler.kt index 41456e71f5..379b813c06 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/deeplink/DefaultTokenDetailsDeepLinkHandler.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/deeplink/DefaultTokenDetailsDeepLinkHandler.kt @@ -39,56 +39,57 @@ internal class DefaultTokenDetailsDeepLinkHandler @AssistedInject constructor( } private fun handleDeepLink() { - // It is okay here, we are navigating from outside, and there is no other way to getting UserWallet - val userWalletId = queryParams[WALLET_ID_KEY]?.let(::UserWalletId) - ?: getSelectedWalletSyncUseCase().getOrNull()?.walletId - - if (userWalletId == null) { - Timber.e("Error on getting user wallet") - return - } - val networkId = queryParams[NETWORK_ID_KEY] val tokenId = queryParams[TOKEN_ID_KEY] val type = NotificationType.getType(queryParams[TYPE_KEY]) - if (type != NotificationType.Unknown) { - scope.launch { - val cryptoCurrency = getCryptoCurrenciesUseCase(userWalletId = userWalletId).getOrElse { - Timber.e("Error on getting crypto currency list") - return@launch - }.firstOrNull { - it.network.backendId == networkId && - it.id.rawCurrencyId?.value == tokenId - } + // It is okay here, we are navigating from outside, and there is no other way to getting UserWallet + val selectedUserWalletId = getSelectedWalletSyncUseCase().getOrNull()?.walletId + val walletId = queryParams[WALLET_ID_KEY]?.let(::UserWalletId) ?: selectedUserWalletId - if (cryptoCurrency == null) { - Timber.e( - """ + // If selected user wallet is different than from deeplink - ignore deeplink + // If selected user wallet is null - ignore deeplink + if (walletId != selectedUserWalletId || selectedUserWalletId == null) { + Timber.e("Error on getting user wallet") + return + } + + scope.launch { + val cryptoCurrency = getCryptoCurrenciesUseCase(userWalletId = selectedUserWalletId).getOrElse { + Timber.e("Error on getting crypto currency list") + return@launch + }.firstOrNull { + val isNetwork = it.network.backendId.equals(networkId, ignoreCase = true) + val isCurrency = it.id.rawCurrencyId?.value?.equals(tokenId, ignoreCase = true) == true + isNetwork && isCurrency + } + + if (cryptoCurrency == null) { + Timber.e( + """ Could not get crypto currency for |- $NETWORK_ID_KEY: $networkId |- $TOKEN_ID_KEY: $tokenId - """.trimIndent(), - ) - return@launch - } - - analyticsEventHandler.send(PushNotificationAnalyticEvents.NotificationOpened(type.name)) - - appRouter.push( - AppRoute.CurrencyDetails( - userWalletId = userWalletId, - currency = cryptoCurrency, - ), + """.trimIndent(), ) + return@launch + } - if (isFromOnNewIntent) { - fetchCurrencyStatusUseCase.invoke( - userWalletId = userWalletId, - id = cryptoCurrency.id, - refresh = true, - ) - } + analyticsEventHandler.send(PushNotificationAnalyticEvents.NotificationOpened(type.name)) + + appRouter.push( + AppRoute.CurrencyDetails( + userWalletId = selectedUserWalletId, + currency = cryptoCurrency, + ), + ) + + if (isFromOnNewIntent) { + fetchCurrencyStatusUseCase.invoke( + userWalletId = selectedUserWalletId, + id = cryptoCurrency.id, + refresh = true, + ) } } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/deeplink/DefaultWalletDeepLinkHandler.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/deeplink/DefaultWalletDeepLinkHandler.kt index 508333060f..35d56c1bec 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/deeplink/DefaultWalletDeepLinkHandler.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/deeplink/DefaultWalletDeepLinkHandler.kt @@ -1,10 +1,18 @@ package com.tangem.feature.wallet.deeplink +import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.AppRouter import com.tangem.features.wallet.deeplink.WalletDeepLinkHandler import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -internal class DefaultWalletDeepLinkHandler @AssistedInject constructor() : WalletDeepLinkHandler { +internal class DefaultWalletDeepLinkHandler @AssistedInject constructor( + router: AppRouter, +) : WalletDeepLinkHandler { + + init { + router.popTo(AppRoute.Wallet) + } @AssistedFactory interface Factory : WalletDeepLinkHandler.Factory {