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 0751f831e7..b09329cb26 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 @@ -326,6 +326,7 @@ internal class ChildFactory @Inject constructor( amount = route.amount, tag = route.tag, destinationAddress = route.destinationAddress, + entryType = route.entryType.toSendEntryType(), ), componentFactory = sendComponentFactoryV2, ) @@ -687,4 +688,9 @@ internal class ChildFactory @Inject constructor( } } } +} + +private fun AppRoute.Send.EntryType.toSendEntryType(): SendComponent.EntryType = when (this) { + AppRoute.Send.EntryType.Manual -> SendComponent.EntryType.Manual + AppRoute.Send.EntryType.QR -> SendComponent.EntryType.QR } \ No newline at end of file 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 fda9bf9952..8f0ab46964 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 @@ -68,13 +68,20 @@ sealed class AppRoute(val path: String) : Route { val amount: String? = null, val tag: String? = null, val destinationAddress: String? = null, + val entryType: EntryType = EntryType.Manual, ) : AppRoute( path = "/send/${userWalletId.stringValue}/${currency.id.value}?" + "&$transactionId" + "&$amount" + "&$tag" + "&$destinationAddress", - ) + ) { + @Serializable + enum class EntryType { + Manual, + QR, + } + } @Serializable data class Details( diff --git a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/event/MainScreenAnalyticsEvent.kt b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/event/MainScreenAnalyticsEvent.kt index c05783e382..956ae894c9 100644 --- a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/event/MainScreenAnalyticsEvent.kt +++ b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/event/MainScreenAnalyticsEvent.kt @@ -52,6 +52,10 @@ sealed class MainScreenAnalyticsEvent( event = "Notice - Limits Info", ) + class ButtonQrScan : MainScreenAnalyticsEvent( + event = "Button - QR Scan", + ) + class ButtonExplore : MainScreenAnalyticsEvent( event = "Button - Explore", ) diff --git a/data/qr-scanning/src/main/java/com/tangem/data/qrscanning/parser/Bip321PaymentUriParser.kt b/data/qr-scanning/src/main/java/com/tangem/data/qrscanning/parser/Bip321PaymentUriParser.kt index 0d6ed638f3..2b8dc3085f 100644 --- a/data/qr-scanning/src/main/java/com/tangem/data/qrscanning/parser/Bip321PaymentUriParser.kt +++ b/data/qr-scanning/src/main/java/com/tangem/data/qrscanning/parser/Bip321PaymentUriParser.kt @@ -25,7 +25,10 @@ internal class Bip321PaymentUriParser( val matchingCurrencies = allCurrencies.filter { it.network.id in matchingNetworkIds } if (matchingCurrencies.isEmpty()) { return PaymentUriParser.ParseResult.RecognizedError( - ClassifiedQrContent.Error.UnsupportedNetwork, + ClassifiedQrContent.Error.UnsupportedNetwork( + raw = qrCode, + blockchain = matchingCoins.firstOrNull()?.network?.name, + ), ) } diff --git a/data/qr-scanning/src/main/java/com/tangem/data/qrscanning/parser/Eip681PaymentUriParser.kt b/data/qr-scanning/src/main/java/com/tangem/data/qrscanning/parser/Eip681PaymentUriParser.kt index 5a47f9b717..2d109a40c8 100644 --- a/data/qr-scanning/src/main/java/com/tangem/data/qrscanning/parser/Eip681PaymentUriParser.kt +++ b/data/qr-scanning/src/main/java/com/tangem/data/qrscanning/parser/Eip681PaymentUriParser.kt @@ -24,7 +24,10 @@ internal class Eip681PaymentUriParser( val matchingCoins = findMatchingCoins(parsed.chainId, coins) if (matchingCoins.isEmpty()) { return PaymentUriParser.ParseResult.RecognizedError( - ClassifiedQrContent.Error.UnsupportedNetwork, + ClassifiedQrContent.Error.UnsupportedNetwork( + raw = qrCode, + blockchain = parsed.chainId?.let { blockchainDataProvider.getBlockchainNameByChainId(it) }, + ), ) } @@ -42,7 +45,10 @@ internal class Eip681PaymentUriParser( PaymentUriParser.ParseResult.Success(result) } else { PaymentUriParser.ParseResult.RecognizedError( - ClassifiedQrContent.Error.UnsupportedNetwork, + ClassifiedQrContent.Error.UnsupportedNetwork( + raw = qrCode, + blockchain = matchingCoins.firstOrNull()?.network?.name, + ), ) } } diff --git a/data/qr-scanning/src/main/java/com/tangem/data/qrscanning/parser/QrContentClassifierParser.kt b/data/qr-scanning/src/main/java/com/tangem/data/qrscanning/parser/QrContentClassifierParser.kt index ef468d89b0..dc0ef5a3db 100644 --- a/data/qr-scanning/src/main/java/com/tangem/data/qrscanning/parser/QrContentClassifierParser.kt +++ b/data/qr-scanning/src/main/java/com/tangem/data/qrscanning/parser/QrContentClassifierParser.kt @@ -43,8 +43,12 @@ internal class QrContentClassifierParser( ) } - if (blockchainDataProvider.isSupportedAddress(qrCode)) { - return ClassifiedQrContent.Error.UnsupportedNetwork + val supportedBlockchain = blockchainDataProvider.findSupportedBlockchainName(qrCode) + if (supportedBlockchain != null) { + return ClassifiedQrContent.Error.UnsupportedNetwork( + raw = qrCode, + blockchain = supportedBlockchain, + ) } return ClassifiedQrContent.Error.Unrecognized(qrCode) @@ -76,7 +80,8 @@ internal class QrContentClassifierParser( fun getShareSchemes(network: Network): List fun validateAddress(network: Network, address: String): Boolean fun getChainId(network: Network): Long? - fun isSupportedAddress(address: String): Boolean + fun findSupportedBlockchainName(address: String): String? + fun getBlockchainNameByChainId(chainId: Long): String? } internal class DefaultBlockchainDataProvider : BlockchainDataProvider { @@ -92,12 +97,20 @@ internal class QrContentClassifierParser( return runCatching { network.toBlockchain().getChainId()?.toLong() }.getOrNull() } - override fun isSupportedAddress(address: String): Boolean { + override fun findSupportedBlockchainName(address: String): String? { return Blockchain.entries .filter { !it.isTestnet() } - .any { blockchain -> + .firstOrNull { blockchain -> runCatching { blockchain.validateAddress(address) }.getOrDefault(false) - } + }?.fullName + } + + override fun getBlockchainNameByChainId(chainId: Long): String? { + return Blockchain.entries + .filter { !it.isTestnet() } + .firstOrNull { blockchain -> + runCatching { blockchain.getChainId()?.toLong() == chainId }.getOrDefault(false) + }?.fullName } } diff --git a/data/qr-scanning/src/test/java/com/tangem/data/qrscanning/Eip681PaymentUriParserTest.kt b/data/qr-scanning/src/test/java/com/tangem/data/qrscanning/Eip681PaymentUriParserTest.kt index 7b0662377e..6fca241974 100644 --- a/data/qr-scanning/src/test/java/com/tangem/data/qrscanning/Eip681PaymentUriParserTest.kt +++ b/data/qr-scanning/src/test/java/com/tangem/data/qrscanning/Eip681PaymentUriParserTest.kt @@ -17,6 +17,7 @@ internal class Eip681PaymentUriParserTest { private val blockchainDataProvider = mockk { every { getShareSchemes(any()) } returns emptyList() every { getChainId(any()) } returns null + every { getBlockchainNameByChainId(any()) } returns null } private val parser = Eip681PaymentUriParser(blockchainDataProvider) diff --git a/data/qr-scanning/src/test/java/com/tangem/data/qrscanning/QrContentClassifierTest.kt b/data/qr-scanning/src/test/java/com/tangem/data/qrscanning/QrContentClassifierTest.kt index 86075b2a10..18f3572e4c 100644 --- a/data/qr-scanning/src/test/java/com/tangem/data/qrscanning/QrContentClassifierTest.kt +++ b/data/qr-scanning/src/test/java/com/tangem/data/qrscanning/QrContentClassifierTest.kt @@ -17,7 +17,8 @@ internal class QrContentClassifierTest { every { getShareSchemes(any()) } returns emptyList() every { validateAddress(any(), any()) } returns false every { getChainId(any()) } returns null - every { isSupportedAddress(any()) } returns false + every { findSupportedBlockchainName(any()) } returns null + every { getBlockchainNameByChainId(any()) } returns null } private val paymentUriParser = mockk { every { parse(any(), any(), any()) } returns PaymentUriParser.ParseResult.NotRecognized diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/DefaultWcPairUseCase.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/DefaultWcPairUseCase.kt index bf81785e59..78d58bd321 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/DefaultWcPairUseCase.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/pair/DefaultWcPairUseCase.kt @@ -41,13 +41,17 @@ internal class DefaultWcPairUseCase @AssistedInject constructor( @Suppress("LongMethod") override operator fun invoke(): Flow { - val (uri: String, source: WcPairRequest.Source) = pairRequest return flow { Timber.tag(WC_TAG).i("start pair flow $pairRequest") - analytics.send(WcAnalyticEvents.NewPairInitiated(source)) + analytics.send( + WcAnalyticEvents.NewPairInitiated( + source = pairRequest.source, + screen = pairRequest.screen, + ), + ) emit(WcPairState.Loading) - val pairResult = sdkDelegate.pair(uri) + val pairResult = sdkDelegate.pair(pairRequest.uri) .onLeft { Timber.tag(WC_TAG).e(it, "Failed to call pair $pairRequest") analytics.send( diff --git a/domain/qr-scanning/models/src/main/java/com/tangem/domain/qrscanning/models/ClassifiedQrContent.kt b/domain/qr-scanning/models/src/main/java/com/tangem/domain/qrscanning/models/ClassifiedQrContent.kt index da41ec31fa..1fefc8a555 100644 --- a/domain/qr-scanning/models/src/main/java/com/tangem/domain/qrscanning/models/ClassifiedQrContent.kt +++ b/domain/qr-scanning/models/src/main/java/com/tangem/domain/qrscanning/models/ClassifiedQrContent.kt @@ -3,28 +3,31 @@ package com.tangem.domain.qrscanning.models import com.tangem.domain.models.currency.CryptoCurrency import java.math.BigDecimal -sealed class ClassifiedQrContent { +sealed interface ClassifiedQrContent { - data class WalletConnect(val uri: String) : ClassifiedQrContent() + data class WalletConnect(val uri: String) : ClassifiedQrContent data class PaymentUri( val address: String, val amount: BigDecimal?, val memo: String?, val matchingCurrencies: List, - ) : ClassifiedQrContent() + ) : ClassifiedQrContent data class PlainAddress( val address: String, val matchingCurrencies: List, - ) : ClassifiedQrContent() + ) : ClassifiedQrContent - sealed class Error : ClassifiedQrContent() { + sealed interface Error : ClassifiedQrContent { /** QR code not recognized by any parser */ - data class Unrecognized(val raw: String) : Error() + data class Unrecognized(val raw: String) : Error /** Network or token recognized but not available in user's wallet */ - data object UnsupportedNetwork : Error() + data class UnsupportedNetwork( + val raw: String, + val blockchain: String?, + ) : Error } } \ No newline at end of file diff --git a/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcPairRequest.kt b/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcPairRequest.kt index 66ede87384..a1b54b760f 100644 --- a/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcPairRequest.kt +++ b/domain/wallet-connect/models/src/main/java/com/tangem/domain/walletconnect/model/WcPairRequest.kt @@ -8,6 +8,12 @@ data class WcPairRequest( val uri: String, val source: Source, val userWalletId: UserWalletId, + val screen: Screen? = null, ) { enum class Source { QR, DEEPLINK, CLIPBOARD, ETC } + + enum class Screen(val analyticsName: String) { + MAIN("Main Screen"), + WALLET_CONNECT("Wallet Connect Screen"), + } } \ No newline at end of file diff --git a/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/WcAnalyticEvents.kt b/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/WcAnalyticEvents.kt index 800271cacd..7097a4b00b 100644 --- a/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/WcAnalyticEvents.kt +++ b/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/WcAnalyticEvents.kt @@ -22,16 +22,23 @@ sealed class WcAnalyticEvents( class ScreenOpened : WcAnalyticEvents(event = "WC Screen Opened"), AppsFlyerIncludedEvent - class NewPairInitiated(source: WcPairRequest.Source) : WcAnalyticEvents( + class NewPairInitiated( + source: WcPairRequest.Source, + screen: WcPairRequest.Screen?, + ) : WcAnalyticEvents( event = "Session Initiated", - params = mapOf( - AnalyticsParam.SOURCE to when (source) { - WcPairRequest.Source.QR -> "QR" - WcPairRequest.Source.DEEPLINK -> "DeepLink" - WcPairRequest.Source.CLIPBOARD -> "Clipboard" - WcPairRequest.Source.ETC -> "etc" - }, - ), + params = buildMap { + put( + AnalyticsParam.SOURCE, + when (source) { + WcPairRequest.Source.QR -> "QR" + WcPairRequest.Source.DEEPLINK -> "DeepLink" + WcPairRequest.Source.CLIPBOARD -> "Clipboard" + WcPairRequest.Source.ETC -> "etc" + }, + ) + screen?.analyticsName?.let { put(SCREEN, it) } + }, ) class PairButtonConnect( @@ -289,6 +296,7 @@ sealed class WcAnalyticEvents( const val NETWORKS = "Networks" const val DOMAIN_VERIFICATION = "Domain Verification" + const val SCREEN = "Screen" const val WC_CATEGORY_NAME = "Wallet Connect" } } diff --git a/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/SendComponent.kt b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/SendComponent.kt index 88b5f8d9e2..1da68c6bc2 100644 --- a/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/SendComponent.kt +++ b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/SendComponent.kt @@ -14,9 +14,15 @@ interface SendComponent : ComposableContentComponent { val amount: String? = null, val tag: String? = null, val destinationAddress: String? = null, + val entryType: EntryType = EntryType.Manual, val callback: ModelCallback? = null, ) + enum class EntryType { + Manual, + QR, + } + interface Factory : ComponentFactory interface ModelCallback { diff --git a/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/analytics/CommonSendAnalyticEvents.kt b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/analytics/CommonSendAnalyticEvents.kt index 855c07c8e2..f31cb41c2a 100644 --- a/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/analytics/CommonSendAnalyticEvents.kt +++ b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/analytics/CommonSendAnalyticEvents.kt @@ -5,6 +5,7 @@ import com.tangem.core.analytics.models.AnalyticsParam.Key.BLOCKCHAIN import com.tangem.core.analytics.models.AnalyticsParam.Key.ERROR_CODE import com.tangem.core.analytics.models.AnalyticsParam.Key.SOURCE import com.tangem.core.analytics.models.AnalyticsParam.Key.TOKEN_PARAM +import com.tangem.core.analytics.models.AnalyticsParam.Key.TYPE import com.tangem.core.analytics.models.AppsFlyerIncludedEvent /** @@ -32,11 +33,13 @@ sealed class CommonSendAnalyticEvents( data class AmountScreenOpened( val categoryName: String, val source: CommonSendSource, + val type: SendEntryType = SendEntryType.Manual, ) : CommonSendAnalyticEvents( category = categoryName, event = "Amount Screen Opened", params = mapOf( SOURCE to source.analyticsName, + TYPE to type.analyticsName, ), ), AppsFlyerIncludedEvent @@ -95,6 +98,7 @@ sealed class CommonSendAnalyticEvents( val toDerivationIndex: Int?, val sendBlockchain: String, val sendToken: String, + val type: SendEntryType = SendEntryType.Manual, ) : CommonSendAnalyticEvents( category = categoryName, event = "Confirm Screen Opened", @@ -108,6 +112,7 @@ sealed class CommonSendAnalyticEvents( "$fromDerivationIndex, $toDerivationIndex", ) } + put(TYPE, type.analyticsName) }, ), AppsFlyerIncludedEvent @@ -221,6 +226,11 @@ sealed class CommonSendAnalyticEvents( Confirm, } + enum class SendEntryType(val analyticsName: String) { + QR("QR"), + Manual("Manually"), + } + enum class CommonSendSource(val analyticsName: String) { Send("Send"), Swap("Swap"), diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/networkselection/model/NetworkSelectionModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/networkselection/model/NetworkSelectionModel.kt index 1f937110c0..8267c7daea 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/networkselection/model/NetworkSelectionModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/networkselection/model/NetworkSelectionModel.kt @@ -3,6 +3,8 @@ package com.tangem.features.send.v2.networkselection.model import androidx.compose.runtime.Stable import com.tangem.common.getTotalCryptoAmount import com.tangem.common.getTotalFiatAmount +import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.features.send.v2.send.analytics.SendAnalyticEvents import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer @@ -44,6 +46,7 @@ import javax.inject.Inject internal class NetworkSelectionModel @Inject constructor( paramsContainer: ParamsContainer, override val dispatchers: CoroutineDispatcherProvider, + private val analyticsEventHandler: AnalyticsEventHandler, private val multiAccountStatusListSupplier: MultiAccountStatusListSupplier, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase, @@ -51,6 +54,10 @@ internal class NetworkSelectionModel @Inject constructor( private val params: NetworkSelectionComponent.Params = paramsContainer.require() + init { + analyticsEventHandler.send(SendAnalyticEvents.ChooseTokenScreenOpened()) + } + private val searchQuery = MutableStateFlow("") private val expandedWallets = MutableStateFlow( params.walletGroups.firstOrNull() @@ -227,7 +234,15 @@ internal class NetworkSelectionModel @Inject constructor( text = cryptoAmount.format { crypto(currency) }, isFlickering = isFlickering, ), - onItemClick = { params.onTokenSelected(context.userWalletId, currency) }, + onItemClick = { + analyticsEventHandler.send( + SendAnalyticEvents.TokenSelected( + token = currency.symbol, + blockchain = currency.network.name, + ), + ) + params.onTokenSelected(context.userWalletId, currency) + }, onItemLongClick = null, ) } diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt index 1cc1da5dce..1caf6d2e89 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt @@ -105,6 +105,7 @@ internal class DefaultSendComponent @AssistedInject constructor( sendToken = fromCurrency.symbol, fromDerivationIndex = fromDerivationIndex, toDerivationIndex = null, + type = model.consumeEntryType(), ), ) if (model.currentRoute.value.isEditMode) { @@ -116,6 +117,7 @@ internal class DefaultSendComponent @AssistedInject constructor( CommonSendAnalyticEvents.AmountScreenOpened( categoryName = model.analyticCategoryName, source = model.analyticsSendSource, + type = model.consumeEntryType(), ), ) activeComponent.updateState(model.uiState.value.amountUM) diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/analytics/SendAnalyticEvents.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/analytics/SendAnalyticEvents.kt index abf8b6eaa0..e288c415ef 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/analytics/SendAnalyticEvents.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/analytics/SendAnalyticEvents.kt @@ -50,6 +50,21 @@ internal sealed class SendAnalyticEvents( }, ), AppsFlyerIncludedEvent + class ChooseTokenScreenOpened : SendAnalyticEvents( + event = "Choose Token Screen Opened", + ) + + class TokenSelected( + val token: String, + val blockchain: String, + ) : SendAnalyticEvents( + event = "Token Selected", + params = mapOf( + TOKEN_PARAM to token, + BLOCKCHAIN to blockchain, + ), + ) + data class ConvertTokenButtonClicked( val token: String, val blockchain: String, diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt index b2cf810c08..d6bbf30296 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt @@ -105,6 +105,8 @@ internal class SendModel @Inject constructor( private val params: SendComponent.Params = paramsContainer.require() private val cryptoCurrency = params.currency + private var isEntryTypeConsumed = false + val analyticCategoryName = CommonSendAnalyticEvents.SEND_CATEGORY val analyticsSendSource = CommonSendAnalyticEvents.CommonSendSource.Send @@ -264,6 +266,15 @@ internal class SendModel @Inject constructor( showAlertError() } + fun consumeEntryType(): CommonSendAnalyticEvents.SendEntryType { + if (isEntryTypeConsumed) return CommonSendAnalyticEvents.SendEntryType.Manual + isEntryTypeConsumed = true + return when (params.entryType) { + SendComponent.EntryType.QR -> CommonSendAnalyticEvents.SendEntryType.QR + SendComponent.EntryType.Manual -> CommonSendAnalyticEvents.SendEntryType.Manual + } + } + private suspend fun prepareTransferTransaction(): Either { val predefinedValues = predefinedValues val cryptoCurrencyStatus = cryptoCurrencyStatusFlow.value diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/WalletComponent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/WalletComponent.kt index 3c717f349c..a62274f509 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/WalletComponent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/WalletComponent.kt @@ -202,6 +202,7 @@ internal class WalletComponent @AssistedInject constructor( address = dialogConfig.address, amount = dialogConfig.amount?.parseBigDecimal(currency.decimals), tag = dialogConfig.memo, + entryType = AppRoute.Send.EntryType.Manual, ) }, onDismiss = model.innerWalletRouter.dialogNavigation::dismiss, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt index 5340b32bc2..e64451ff14 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt @@ -4,6 +4,7 @@ import androidx.compose.runtime.Stable import arrow.core.getOrElse import com.arkivanov.decompose.router.slot.activate import com.arkivanov.decompose.router.slot.dismiss +import com.tangem.common.routing.AppRoute import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.core.analytics.models.AnalyticsParam @@ -767,6 +768,7 @@ internal class WalletModel @Inject constructor( userWalletId = stateHolder.getSelectedWalletId(), uri = target.uri, source = source, + screen = WcPairRequest.Screen.MAIN, ), ) } @@ -777,6 +779,7 @@ internal class WalletModel @Inject constructor( address = target.address, amount = target.amount?.parseBigDecimal(target.currency.decimals), tag = target.memo, + entryType = AppRoute.Send.EntryType.QR, ) } is QrSendTarget.Multiple -> { @@ -789,9 +792,17 @@ internal class WalletModel @Inject constructor( private fun handleQrError(error: ClassifiedQrContent.Error) { when (error) { is ClassifiedQrContent.Error.Unrecognized -> { + analyticsEventsHandler.send( + WalletScreenAnalyticsEvent.MainScreen.NoticeUnrecognizedQr(), + ) uiMessageSender.send(WalletAlertUM.qrCodeUnrecognized()) } is ClassifiedQrContent.Error.UnsupportedNetwork -> { + analyticsEventsHandler.send( + WalletScreenAnalyticsEvent.MainScreen.NoticeNoAvailableTokens( + blockchain = error.blockchain, + ), + ) uiMessageSender.send(WalletAlertUM.qrCodeUnsupportedNetwork()) } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletContentClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletContentClickIntents.kt index 5b9d50325d..bf79ed8458 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletContentClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletContentClickIntents.kt @@ -382,6 +382,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor( } override fun onScanQrClick() { + analyticsEventHandler.send(MainScreenAnalyticsEvent.ButtonQrScan()) router.openQrScanner() } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt index b0da023377..decb5b77df 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt @@ -186,6 +186,7 @@ internal class DefaultWalletRouter @Inject constructor( address: String, amount: String?, tag: String?, + entryType: AppRoute.Send.EntryType, ) { router.push( AppRoute.Send( @@ -194,6 +195,7 @@ internal class DefaultWalletRouter @Inject constructor( destinationAddress = address, amount = amount, tag = tag, + entryType = entryType, ), ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt index 52741298a7..56f3e54725 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt @@ -95,7 +95,14 @@ internal interface InnerWalletRouter { fun openQrScanner() /** Open send screen with prefilled destination */ - fun openSend(userWalletId: UserWalletId, currency: CryptoCurrency, address: String, amount: String?, tag: String?) + fun openSend( + userWalletId: UserWalletId, + currency: CryptoCurrency, + address: String, + amount: String?, + tag: String?, + entryType: AppRoute.Send.EntryType = AppRoute.Send.EntryType.Manual, + ) /** Open network selection bottom sheet for multiple QR matches */ fun openNetworkSelectionBottomSheet(target: QrSendTarget.Multiple) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt index 257ce14de3..71ae06f02a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt @@ -167,6 +167,17 @@ sealed class WalletScreenAnalyticsEvent { class NoticeSeedPhraseSupportButtonDeclined : MainScreen(event = "Button - Support Declined") + class NoticeUnrecognizedQr : MainScreen( + event = "Notice - Unrecognized QR", + ) + + class NoticeNoAvailableTokens(blockchain: String?) : MainScreen( + event = "Notice - No Available Tokens", + params = buildMap { + if (blockchain != null) put("Blockchain", blockchain) + }, + ) + // region Referral Promo class ReferralPromo : MainScreen(event = "Referral Banner") class ReferralPromoButtonParticipate : MainScreen(event = "Button - Referral Participate") diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcPairComponent.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcPairComponent.kt index 23ed261f7c..a160555493 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcPairComponent.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcPairComponent.kt @@ -15,7 +15,6 @@ import com.tangem.core.decompose.navigation.inner.InnerRouter import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetUM import com.tangem.core.ui.decompose.ComposableBottomSheetComponent import com.tangem.core.ui.decompose.ComposableContentComponent -import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.walletconnect.model.WcPairRequest import com.tangem.features.account.PortfolioSelectorComponent import com.tangem.features.walletconnect.connections.model.WcPairModel @@ -127,5 +126,7 @@ internal class WcPairComponent( } } - data class Params(val userWalletId: UserWalletId, val wcUrl: String, val source: WcPairRequest.Source) + data class Params( + val request: WcPairRequest, + ) } \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcConnectionsModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcConnectionsModel.kt index d82ef65634..cdccfa2ec0 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcConnectionsModel.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcConnectionsModel.kt @@ -86,6 +86,7 @@ internal class WcConnectionsModel @Inject constructor( userWalletId = params.userWalletId, uri = result.qrCode, source = source, + screen = WcPairRequest.Screen.WALLET_CONNECT, ), ) } diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcPairModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcPairModel.kt index a8bc0da03c..36238a65fd 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcPairModel.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcPairModel.kt @@ -29,7 +29,6 @@ import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.walletconnect.WcAnalyticEvents import com.tangem.domain.walletconnect.model.WcPairError import com.tangem.domain.walletconnect.model.WcPairError.Unknown -import com.tangem.domain.walletconnect.model.WcPairRequest import com.tangem.domain.walletconnect.model.WcSessionApprove import com.tangem.domain.walletconnect.model.WcSessionProposal import com.tangem.domain.walletconnect.model.sdkcopy.WcAppMetaData @@ -75,13 +74,7 @@ internal class WcPairModel @Inject constructor( ) : Model(), WcPairComponentCallback { private val params: WcPairComponent.Params = paramsContainer.require() - private val wcPairUseCase = wcPairUseCaseFactory.create( - WcPairRequest( - userWalletId = params.userWalletId, - uri = params.wcUrl, - source = params.source, - ), - ) + private val wcPairUseCase = wcPairUseCaseFactory.create(params.request) val stackNavigation = StackNavigation() val portfolioFetcher: PortfolioFetcher = portfolioFetcherFactory.create( @@ -109,7 +102,7 @@ internal class WcPairModel @Inject constructor( modelScope.launch { val portfolioBalance = portfolioFetcher.data.first().balances .firstNotNullOfOrNull { (walletId, balance) -> - if (params.userWalletId == walletId) balance else null + if (params.request.userWalletId == walletId) balance else null } if (portfolioBalance == null) { router.pop() diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/routing/DefaultWcRoutingComponent.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/routing/DefaultWcRoutingComponent.kt index 44524c20d9..7a3bbca637 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/routing/DefaultWcRoutingComponent.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/routing/DefaultWcRoutingComponent.kt @@ -89,9 +89,7 @@ internal class DefaultWcRoutingComponent @AssistedInject constructor( appComponentContext = childContext, portfolioSelectorComponentFactory = portfolioSelectorComponentFactory, params = WcPairComponent.Params( - userWalletId = config.request.userWalletId, - wcUrl = config.request.uri, - source = config.request.source, + request = config.request, ), ) is WcInnerRoute.UnsupportedMethodAlert -> AlertsComponent( diff --git a/gradle/tangem_dependencies.toml b/gradle/tangem_dependencies.toml index 324fed5b01..40a6e10dec 100644 --- a/gradle/tangem_dependencies.toml +++ b/gradle/tangem_dependencies.toml @@ -5,7 +5,7 @@ # https://github.com/tangem/tangem-sdk-android/ # https://github.com/tangem/vico -tangemBlockchainSdk = "develop-1454" +tangemBlockchainSdk = "develop-1455" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "develop-598" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^