Updated on 2026-08-14

This commit is contained in:
Tangem 2026-04-17 20:08:45 +04:00
parent 8e6eca1b4c
commit e20956359c
9 changed files with 169 additions and 28 deletions

View file

@ -7,6 +7,7 @@ import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.common.routing.AppRouter import com.tangem.common.routing.AppRouter
import com.tangem.core.abtests.manager.ABTestsManager import com.tangem.core.abtests.manager.ABTestsManager
import com.tangem.core.analytics.filter.OneTimeEventFilter import com.tangem.core.analytics.filter.OneTimeEventFilter
import com.tangem.core.analytics.paramsinterceptor.SendTransactionSignerInfoInterceptor
import com.tangem.core.analytics.utils.TrackingContextProxy import com.tangem.core.analytics.utils.TrackingContextProxy
import com.tangem.core.configtoggle.blockchain.ExcludedBlockchainsManager import com.tangem.core.configtoggle.blockchain.ExcludedBlockchainsManager
import com.tangem.core.configtoggle.feature.FeatureTogglesManager import com.tangem.core.configtoggle.feature.FeatureTogglesManager
@ -151,4 +152,6 @@ interface ApplicationEntryPoint {
fun getAppsFlyerClientFactory(): AppsFlyerClient.Factory fun getAppsFlyerClientFactory(): AppsFlyerClient.Factory
fun getScanFailsRequester(): ScanFailsRequester fun getScanFailsRequester(): ScanFailsRequester
fun getSendTransactionSignerInfoInterceptor(): SendTransactionSignerInfoInterceptor
} }

View file

@ -18,13 +18,8 @@ import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.common.routing.AppRouter import com.tangem.common.routing.AppRouter
import com.tangem.core.abtests.manager.ABTestsManager import com.tangem.core.abtests.manager.ABTestsManager
import com.tangem.core.analytics.Analytics import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.api.ParamsInterceptor
import com.tangem.core.analytics.filter.AppsFlyerEventFilter import com.tangem.core.analytics.filter.AppsFlyerEventFilter
import com.tangem.core.analytics.filter.OneTimeEventFilter import com.tangem.core.analytics.filter.OneTimeEventFilter
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.Basic
import com.tangem.core.analytics.models.Basic.TransactionSent.WalletForm
import com.tangem.core.configtoggle.blockchain.ExcludedBlockchainsManager import com.tangem.core.configtoggle.blockchain.ExcludedBlockchainsManager
import com.tangem.core.configtoggle.feature.FeatureTogglesManager import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.core.decompose.ui.UiMessageSender import com.tangem.core.decompose.ui.UiMessageSender
@ -238,6 +233,9 @@ open class TangemApplication : Application(), ImageLoaderFactory, Configuration.
private val scanFailsRequester private val scanFailsRequester
get() = entryPoint.getScanFailsRequester() get() = entryPoint.getScanFailsRequester()
private val sendTransactionSignerInfoInterceptor
get() = entryPoint.getSendTransactionSignerInfoInterceptor()
// endregion // endregion
private val appScope = MainScope() private val appScope = MainScope()
@ -424,23 +422,7 @@ open class TangemApplication : Application(), ImageLoaderFactory, Configuration.
jsonConverter = MoshiConverter.sdkMoshiConverter, jsonConverter = MoshiConverter.sdkMoshiConverter,
) )
Analytics.addParamsInterceptor( Analytics.addParamsInterceptor(interceptor = sendTransactionSignerInfoInterceptor)
interceptor = object : ParamsInterceptor {
override fun id(): String = "SendTransactionSignerInfoInterceptor"
override fun canBeAppliedTo(event: AnalyticsEvent): Boolean = event is Basic.TransactionSent
override fun intercept(params: MutableMap<String, String>) {
val isLastSignWithRing = store.state.globalState.isLastSignWithRing
params[AnalyticsParam.WALLET_FORM] = if (isLastSignWithRing) {
WalletForm.Ring.name
} else {
WalletForm.Card.name
}
}
},
)
factory.build(Analytics, buildData) factory.build(Analytics, buildData)
} }

View file

@ -3,13 +3,15 @@ package com.tangem.tap.common.libs.blockchainsdk
import com.tangem.Message import com.tangem.Message
import com.tangem.TangemSdk import com.tangem.TangemSdk
import com.tangem.blockchain.common.TransactionSigner import com.tangem.blockchain.common.TransactionSigner
import com.tangem.core.analytics.models.Basic.TransactionSent.WalletForm
import com.tangem.core.analytics.store.LastSignedWalletFormStore
import com.tangem.data.card.TransactionSignerFactory import com.tangem.data.card.TransactionSignerFactory
import com.tangem.domain.card.models.TwinKey import com.tangem.domain.card.models.TwinKey
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.TangemSigner import com.tangem.tap.domain.TangemSigner
import com.tangem.tap.store
internal class DefaultTransactionSignerFactory : TransactionSignerFactory { internal class DefaultTransactionSignerFactory(
private val lastSignedWalletFormStore: LastSignedWalletFormStore,
) : TransactionSignerFactory {
override fun createTransactionSigner(cardId: String?, sdk: TangemSdk, twinKey: TwinKey?): TransactionSigner { override fun createTransactionSigner(cardId: String?, sdk: TangemSdk, twinKey: TwinKey?): TransactionSigner {
return TangemSigner( return TangemSigner(
@ -18,7 +20,9 @@ internal class DefaultTransactionSignerFactory : TransactionSignerFactory {
initialMessage = Message(), initialMessage = Message(),
twinKey = twinKey, twinKey = twinKey,
) { signResponse -> ) { signResponse ->
store.dispatch(action = GlobalAction.IsSignWithRing(signResponse.isRing)) lastSignedWalletFormStore.update(
if (signResponse.isRing) WalletForm.Ring else WalletForm.Card,
)
} }
} }
} }

View file

@ -1,5 +1,6 @@
package com.tangem.tap.di.libs.blockchainsdk package com.tangem.tap.di.libs.blockchainsdk
import com.tangem.core.analytics.store.LastSignedWalletFormStore
import com.tangem.data.card.TransactionSignerFactory import com.tangem.data.card.TransactionSignerFactory
import com.tangem.tap.common.libs.blockchainsdk.DefaultTransactionSignerFactory import com.tangem.tap.common.libs.blockchainsdk.DefaultTransactionSignerFactory
import dagger.Module import dagger.Module
@ -17,7 +18,9 @@ internal class TransactionSignerFactoryModule {
@Provides @Provides
@Singleton @Singleton
fun provideTransactionSignerFactory(): TransactionSignerFactory { fun provideTransactionSignerFactory(
return DefaultTransactionSignerFactory() lastSignedWalletFormStore: LastSignedWalletFormStore,
): TransactionSignerFactory {
return DefaultTransactionSignerFactory(lastSignedWalletFormStore)
} }
} }

View file

@ -4,6 +4,10 @@ plugins {
id("configuration") id("configuration")
} }
tasks.withType<Test>().configureEach {
useJUnitPlatform()
}
dependencies { dependencies {
/** DI */ /** DI */
@ -25,4 +29,8 @@ dependencies {
/** For calculating user id hash */ /** For calculating user id hash */
implementation(tangemDeps.card.core) implementation(tangemDeps.card.core)
/** Tests */
testImplementation(projects.test.core)
testRuntimeOnly(deps.test.junit5.engine)
} }

View file

@ -0,0 +1,16 @@
package com.tangem.core.analytics.di
import com.tangem.core.analytics.paramsinterceptor.SendTransactionSignerInfoInterceptor
import com.tangem.core.analytics.store.LastSignedWalletFormStore
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
@Module
@InstallIn(SingletonComponent::class)
internal interface LastSignedWalletFormStoreModule {
@Binds
fun bindLastSignedWalletFormStore(impl: SendTransactionSignerInfoInterceptor): LastSignedWalletFormStore
}

View file

@ -0,0 +1,34 @@
package com.tangem.core.analytics.paramsinterceptor
import com.tangem.core.analytics.api.ParamsInterceptor
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.Basic
import com.tangem.core.analytics.store.LastSignedWalletFormStore
import kotlinx.coroutines.flow.MutableStateFlow
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class SendTransactionSignerInfoInterceptor @Inject constructor() :
ParamsInterceptor,
LastSignedWalletFormStore {
private val walletForm = MutableStateFlow(Basic.TransactionSent.WalletForm.Card)
override fun update(form: Basic.TransactionSent.WalletForm) {
walletForm.value = form
}
override fun id(): String = ID
override fun canBeAppliedTo(event: AnalyticsEvent): Boolean = event is Basic.TransactionSent
override fun intercept(params: MutableMap<String, String>) {
params[AnalyticsParam.WALLET_FORM] = walletForm.value.name
}
private companion object {
const val ID = "SendTransactionSignerInfoInterceptor"
}
}

View file

@ -0,0 +1,8 @@
package com.tangem.core.analytics.store
import com.tangem.core.analytics.models.Basic
interface LastSignedWalletFormStore {
fun update(form: Basic.TransactionSent.WalletForm)
}

View file

@ -0,0 +1,83 @@
package com.tangem.core.analytics.paramsinterceptor
import com.google.common.truth.Truth
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.Basic
import io.mockk.mockk
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
internal class SendTransactionSignerInfoInterceptorTest {
private lateinit var interceptor: SendTransactionSignerInfoInterceptor
@BeforeEach
fun setUp() {
interceptor = SendTransactionSignerInfoInterceptor()
}
@Test
fun `id returns stable identifier`() {
Truth.assertThat(interceptor.id()).isEqualTo("SendTransactionSignerInfoInterceptor")
}
@Test
fun `canBeAppliedTo returns true for TransactionSent event`() {
val event = mockk<Basic.TransactionSent>()
Truth.assertThat(interceptor.canBeAppliedTo(event)).isTrue()
}
@Test
fun `canBeAppliedTo returns false for any other event`() {
val event = mockk<AnalyticsEvent>()
Truth.assertThat(interceptor.canBeAppliedTo(event)).isFalse()
}
@Test
fun `intercept writes Card by default`() {
val params = mutableMapOf<String, String>()
interceptor.intercept(params)
Truth.assertThat(params[AnalyticsParam.WALLET_FORM])
.isEqualTo(Basic.TransactionSent.WalletForm.Card.name)
}
@Test
fun `intercept writes last updated wallet form`() {
interceptor.update(Basic.TransactionSent.WalletForm.Ring)
val params = mutableMapOf<String, String>()
interceptor.intercept(params)
Truth.assertThat(params[AnalyticsParam.WALLET_FORM])
.isEqualTo(Basic.TransactionSent.WalletForm.Ring.name)
}
@Test
fun `update overrides previous wallet form`() {
interceptor.update(Basic.TransactionSent.WalletForm.Ring)
interceptor.update(Basic.TransactionSent.WalletForm.Card)
val params = mutableMapOf<String, String>()
interceptor.intercept(params)
Truth.assertThat(params[AnalyticsParam.WALLET_FORM])
.isEqualTo(Basic.TransactionSent.WalletForm.Card.name)
}
@Test
fun `intercept preserves other params`() {
val params = mutableMapOf("Source" to "Send")
interceptor.intercept(params)
Truth.assertThat(params).containsEntry("Source", "Send")
Truth.assertThat(params).containsKey(AnalyticsParam.WALLET_FORM)
}
}