Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-12 17:55:33 +03:00
parent bc8bb4da19
commit 07f94d3a95
86 changed files with 317 additions and 387 deletions

View file

@ -2,11 +2,9 @@ package com.tangem.tap.common.analytics.appsflyer
import com.appsflyer.deeplink.DeepLink
import com.tangem.datasource.local.appsflyer.AppsFlyerStore
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.wallets.models.AppsFlyerConversionData
import com.tangem.feature.referral.domain.SetShouldShowMobileWalletPromoUseCase
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
@ -20,10 +18,9 @@ import kotlin.contracts.contract
class AppsFlyerReferralParamsHandler @Inject constructor(
private val appsFlyerStore: AppsFlyerStore,
private val setShouldShowMobileWalletPromoUseCase: SetShouldShowMobileWalletPromoUseCase,
dispatchers: CoroutineDispatcherProvider,
private val coroutineScope: AppCoroutineScope,
) {
private val coroutineScope = CoroutineScope(dispatchers.io + SupervisorJob())
private val mutex = Mutex()
fun handle(deepLink: DeepLink) {

View file

@ -6,16 +6,14 @@ import com.appsflyer.attribution.AppsFlyerRequestListener
import com.tangem.core.analytics.api.EventLogger
import com.tangem.core.analytics.api.UserIdHolder
import com.tangem.datasource.local.appsflyer.AppsFlyerStore
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.tap.common.analytics.appsflyer.AppsFlyerDeepLinkListener
import com.tangem.tap.common.analytics.appsflyer.TangemAFConversionListener
import com.tangem.tap.common.analytics.handlers.firebase.UnderscoreAnalyticsEventConverter
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import timber.log.Timber
@ -26,15 +24,13 @@ class AppsFlyerClient @AssistedInject constructor(
@ApplicationContext private val context: Context,
appsFlyerDeepLinkListener: AppsFlyerDeepLinkListener,
tangemAFConversionListener: TangemAFConversionListener,
dispatchers: CoroutineDispatcherProvider,
private val appsFlyerStore: AppsFlyerStore,
private val coroutineScope: AppCoroutineScope,
) : AppsFlyerAnalyticsClient {
private val appsFlyerLib: AppsFlyerLib = AppsFlyerLib.getInstance()
private val eventConverter = UnderscoreAnalyticsEventConverter()
private val coroutineScope = CoroutineScope(SupervisorJob() + dispatchers.default)
init {
with(appsFlyerLib) {
setAppId(context.packageName)

View file

@ -0,0 +1,44 @@
package com.tangem.tap.core
import co.touchlab.kermit.Logger
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.SupervisorJob
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
internal class DefaultAppCoroutineScope @Inject constructor(
dispatchers: CoroutineDispatcherProvider,
private val analyticsExceptionHandler: AnalyticsExceptionHandler,
) : AppCoroutineScope {
private val tag = "AppCoroutineScope"
override val coroutineContext: CoroutineContext = SupervisorJob() +
// keep IO dispatcher to avoid blocking Default with IO operations
dispatchers.io +
CoroutineName(tag) +
CoroutineExceptionHandler { context, throwable ->
val coroutineName = context[CoroutineName]?.name.orEmpty()
logError(throwable, coroutineName)
}
private fun logError(throwable: Throwable, coroutineName: String) {
Logger.withTag(tag).e(
messageString = "CoroutineName $coroutineName",
throwable = throwable,
)
val event = ExceptionAnalyticsEvent(
exception = throwable,
params = mapOf(
"source" to tag,
"coroutineName" to coroutineName,
),
)
analyticsExceptionHandler.sendException(event)
}
}

View file

@ -4,6 +4,7 @@ import com.tangem.datasource.api.moonpay.MoonPayApi
import com.tangem.datasource.local.config.environment.EnvironmentConfig
import com.tangem.domain.card.ScanCardUseCase
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.exchange.RampStateManager
import com.tangem.domain.express.ExpressServiceFetcher
import com.tangem.domain.tokens.repository.CurrenciesRepository
@ -21,8 +22,6 @@ import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import javax.inject.Singleton
@Module
@ -62,8 +61,8 @@ internal object ActivityModule {
@Provides
@Singleton
@DelayedWork
fun provideActivityDelayedWorkCoroutineScope(): CoroutineScope {
return CoroutineScope(SupervisorJob() + Dispatchers.IO)
fun provideActivityDelayedWorkCoroutineScope(appScope: AppCoroutineScope): CoroutineScope {
return appScope
}
@Provides

View file

@ -8,6 +8,7 @@ import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.navigation.finisher.AppFinisher
import com.tangem.domain.card.BuildConfig
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
import com.tangem.sdk.api.TangemSdkManager
@ -17,7 +18,6 @@ import com.tangem.tap.domain.tasks.product.BlockchainToDeriveFinder
import com.tangem.tap.domain.tasks.visa.TangemPayGenerateAddressAndSignChallengeTask
import com.tangem.tap.domain.tasks.visa.VisaCardActivationTask
import com.tangem.tap.domain.visa.VisaCardScanHandler
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -44,7 +44,7 @@ internal class TangemSdkManagerModule {
analyticsExceptionHandler: AnalyticsExceptionHandler,
blockchainToDeriveFinder: BlockchainToDeriveFinder,
analyticsEventHandler: AnalyticsEventHandler,
dispatchers: CoroutineDispatcherProvider,
appScope: AppCoroutineScope,
): TangemSdkManager {
return if (BuildConfig.MOCK_DATA_SOURCE) {
MockTangemSdkManager(resources = context.resources)
@ -62,7 +62,7 @@ internal class TangemSdkManagerModule {
analyticsExceptionHandler = analyticsExceptionHandler,
blockchainToDeriveFinder = blockchainToDeriveFinder,
analyticsEventHandler = analyticsEventHandler,
dispatchers = dispatchers,
coroutineScope = appScope,
)
}
}

View file

@ -5,10 +5,13 @@ import com.tangem.core.navigation.finisher.AppFinisher
import com.tangem.core.navigation.settings.SettingsManager
import com.tangem.core.navigation.share.ShareManager
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.tap.common.finisher.AndroidAppFinisher
import com.tangem.tap.common.settings.IntentSettingsManager
import com.tangem.tap.common.share.IntentShareManager
import com.tangem.tap.common.url.CustomTabsUrlOpener
import com.tangem.tap.core.DefaultAppCoroutineScope
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -18,21 +21,28 @@ import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal object UtilsModule {
internal interface UtilsModule {
@Provides
@Singleton
fun provideShareManager(): ShareManager = IntentShareManager()
@Binds
fun provideAppScope(defaultAppScope: DefaultAppCoroutineScope): AppCoroutineScope
@Provides
@Singleton
fun provideUrlOpener(): UrlOpener = CustomTabsUrlOpener()
companion object {
@Provides
@Singleton
fun provideAppFinisher(@ApplicationContext context: Context): AppFinisher = AndroidAppFinisher(context)
@Provides
@Singleton
fun provideShareManager(): ShareManager = IntentShareManager()
@Provides
@Singleton
fun provideSettingsManager(@ApplicationContext context: Context): SettingsManager = IntentSettingsManager(context)
@Provides
@Singleton
fun provideUrlOpener(): UrlOpener = CustomTabsUrlOpener()
@Provides
@Singleton
fun provideAppFinisher(@ApplicationContext context: Context): AppFinisher = AndroidAppFinisher(context)
@Provides
@Singleton
fun provideSettingsManager(@ApplicationContext context: Context): SettingsManager =
IntentSettingsManager(context)
}
}

View file

@ -4,6 +4,7 @@ import com.tangem.data.wallets.hot.TangemHotWalletSigner
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.account.supplier.SingleAccountListSupplier
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.demo.models.DemoConfig
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
import com.tangem.domain.networks.single.SingleNetworkStatusSupplier
@ -18,13 +19,10 @@ import com.tangem.domain.transaction.WalletAddressServiceRepository
import com.tangem.domain.transaction.usecase.*
import com.tangem.domain.transaction.usecase.gasless.*
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import javax.inject.Singleton
@Suppress("TooManyFunctions", "LargeClass")
@ -55,8 +53,8 @@ internal object TransactionDomainModule {
walletManagersFacade: WalletManagersFacade,
singleNetworkStatusFetcher: SingleNetworkStatusFetcher,
tangemHotWalletSignerFactory: TangemHotWalletSigner.Factory,
dispatchers: CoroutineDispatcherProvider,
pushNotificationsRepository: PushNotificationsRepository,
appScope: AppCoroutineScope,
): SendTransactionUseCase {
return SendTransactionUseCase(
demoConfig = DemoConfig,
@ -64,7 +62,7 @@ internal object TransactionDomainModule {
transactionRepository = transactionRepository,
walletManagersFacade = walletManagersFacade,
singleNetworkStatusFetcher = singleNetworkStatusFetcher,
parallelUpdatingScope = CoroutineScope(SupervisorJob() + dispatchers.io),
parallelUpdatingScope = appScope,
getHotWalletSigner = tangemHotWalletSignerFactory::create,
pushNotificationsRepository = pushNotificationsRepository,
)

View file

@ -1,6 +1,7 @@
package com.tangem.tap.di.domain
import com.tangem.domain.blockaid.BlockAidGasEstimate
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
import com.tangem.domain.quotes.QuotesRepository
import com.tangem.domain.tokens.repository.CurrenciesRepository
@ -15,8 +16,6 @@ import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import javax.inject.Singleton
@Suppress("TooManyFunctions")
@ -253,12 +252,12 @@ internal object YieldSupplyDomainModule {
fun provideYieldSupplyPendingProcessorUseCase(
yieldSupplyRepository: YieldSupplyRepository,
singleNetworkStatusFetcher: SingleNetworkStatusFetcher,
dispatcherProvider: CoroutineDispatcherProvider,
appScope: AppCoroutineScope,
): YieldSupplyPendingTracker {
return YieldSupplyPendingTracker(
yieldSupplyRepository = yieldSupplyRepository,
singleNetworkStatusFetcher = singleNetworkStatusFetcher,
coroutineScope = CoroutineScope(SupervisorJob() + dispatcherProvider.io),
coroutineScope = appScope,
)
}
}

View file

@ -34,6 +34,7 @@ import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.models.scan.CardDTO
@ -65,7 +66,6 @@ import com.tangem.tap.domain.twins.CreateFirstTwinWalletTask
import com.tangem.tap.domain.twins.CreateSecondTwinWalletTask
import com.tangem.tap.domain.twins.FinalizeTwinTask
import com.tangem.tap.domain.visa.VisaCardScanHandler
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.wallet.R
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
@ -86,7 +86,7 @@ internal class DefaultTangemSdkManager(
private val analyticsExceptionHandler: AnalyticsExceptionHandler,
private val blockchainToDeriveFinder: BlockchainToDeriveFinder,
private val analyticsEventHandler: AnalyticsEventHandler,
dispatchers: CoroutineDispatcherProvider,
private val coroutineScope: AppCoroutineScope,
) : TangemSdkManager {
private val awaitInitializationMutex = Mutex()
@ -115,8 +115,6 @@ internal class DefaultTangemSdkManager(
override val userCodeRequestPolicy: UserCodeRequestPolicy
get() = tangemSdk.config.userCodeRequestPolicy
private val coroutineScope = CoroutineScope(SupervisorJob() + dispatchers.io)
override suspend fun checkNeedEnrollBiometrics(awaitInitialization: Boolean): Boolean {
return try {
needEnrollBiometrics

View file

@ -5,8 +5,8 @@ import com.tangem.datasource.local.appsflyer.AppsFlyerStore
import com.tangem.domain.wallets.models.AppsFlyerConversionData
import com.tangem.feature.referral.domain.SetShouldShowMobileWalletPromoUseCase
import com.tangem.test.core.ProvideTestModels
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import arrow.core.right
import com.tangem.common.test.TestAppCoroutineScope
import io.mockk.clearMocks
import io.mockk.coEvery
import io.mockk.coVerify
@ -30,7 +30,7 @@ class AppsFlyerReferralParamsHandlerTest {
}
private val handler = AppsFlyerReferralParamsHandler(
appsFlyerStore = appsFlyerStore,
dispatchers = TestingCoroutineDispatcherProvider(),
coroutineScope = TestAppCoroutineScope(),
setShouldShowMobileWalletPromoUseCase = setShouldShowMobileWalletPromoUseCase,
)

View file

@ -0,0 +1,11 @@
package com.tangem.common.test
import com.tangem.utils.coroutines.AppCoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.test.TestScope
import kotlin.coroutines.CoroutineContext
class TestAppCoroutineScope(override val coroutineContext: CoroutineContext = Dispatchers.Unconfined) : AppCoroutineScope {
constructor(testScope: TestScope) : this(testScope.coroutineContext)
}

View file

@ -6,13 +6,11 @@ import com.tangem.core.abtests.manager.ABTestsManager
import com.tangem.core.abtests.manager.impl.AmplitudeABTestsManager
import com.tangem.core.abtests.manager.impl.StubABTestsManager
import com.tangem.datasource.local.config.environment.EnvironmentConfig
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.AppCoroutineScope
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import javax.inject.Singleton
@Module
@ -24,7 +22,7 @@ internal object ABTestsManagerModule {
fun provideABTestsManager(
application: Application,
environmentConfig: EnvironmentConfig,
dispatchers: CoroutineDispatcherProvider,
appScope: AppCoroutineScope,
): ABTestsManager {
return if (BuildConfig.AB_TESTS_ENABLED) {
StubABTestsManager()
@ -32,7 +30,7 @@ internal object ABTestsManagerModule {
AmplitudeABTestsManager(
application = application,
apiKey = environmentConfig.amplitudeApiKey,
scope = CoroutineScope(context = dispatchers.io + SupervisorJob()),
scope = appScope,
)
}
}

View file

@ -7,14 +7,14 @@ import com.amplitude.experiment.ExperimentConfig
import com.amplitude.experiment.ExperimentUser
import com.tangem.core.abtests.manager.ABTestsManager
import com.tangem.core.analytics.models.AnalyticsParam
import kotlinx.coroutines.CoroutineScope
import com.tangem.utils.coroutines.AppCoroutineScope
import kotlinx.coroutines.launch
import timber.log.Timber
internal class AmplitudeABTestsManager(
val application: Application,
val apiKey: String,
val scope: CoroutineScope,
val scope: AppCoroutineScope,
) : ABTestsManager {
private lateinit var client: ExperimentClient

View file

@ -7,9 +7,7 @@ import com.tangem.datasource.api.common.config.ApiEnvironmentConfig
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.getObjectMap
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import com.tangem.utils.coroutines.AppCoroutineScope
import kotlinx.coroutines.flow.*
/**
@ -22,7 +20,7 @@ import kotlinx.coroutines.flow.*
internal class DevApiConfigsManager(
private val apiConfigs: ApiConfigs,
private val appPreferencesStore: AppPreferencesStore,
private val dispatchers: CoroutineDispatcherProvider,
private val appScope: AppCoroutineScope,
) : MutableApiConfigsManager() {
override val configs: StateFlow<Map<ApiConfig, ApiEnvironment>>
@ -51,7 +49,7 @@ internal class DevApiConfigsManager(
notifyListeners(apiConfigs = apiConfigs, savedEnvironments = savedEnvironments)
}
.launchIn(CoroutineScope(SupervisorJob() + dispatchers.default))
.launchIn(appScope)
}
override fun getEnvironmentConfig(id: ApiConfig.ID): ApiEnvironmentConfig {

View file

@ -4,10 +4,22 @@ import com.tangem.datasource.api.common.config.ApiConfig
import com.tangem.datasource.api.common.config.ApiConfigs
import com.tangem.datasource.api.common.config.ApiEnvironment
import com.tangem.datasource.api.common.config.ApiEnvironmentConfig
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import com.tangem.utils.coroutines.AppCoroutineScope
import kotlinx.coroutines.flow.*
import kotlin.Boolean
import kotlin.String
import kotlin.Unit
import kotlin.collections.Map
import kotlin.collections.any
import kotlin.collections.associateWith
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.collections.first
import kotlin.collections.firstOrNull
import kotlin.collections.mapValues
import kotlin.collections.plus
import kotlin.error
import kotlin.to
/**
* Implementation of [ApiConfigsManager] in MOCK environment
@ -18,7 +30,7 @@ import kotlinx.coroutines.flow.*
*/
internal class MockApiConfigsManager(
private val apiConfigs: ApiConfigs,
dispatchers: CoroutineDispatcherProvider,
private val coroutineScope: AppCoroutineScope,
) : MutableApiConfigsManager() {
override val configs: StateFlow<Map<ApiConfig, ApiEnvironment>>
@ -26,8 +38,6 @@ internal class MockApiConfigsManager(
override val initializedState: StateFlow<Boolean> = MutableStateFlow(value = true)
private val coroutineScope = CoroutineScope(SupervisorJob() + dispatchers.default)
override fun initialize() = Unit
override fun getEnvironmentConfig(id: ApiConfig.ID): ApiEnvironmentConfig {

View file

@ -3,6 +3,7 @@ package com.tangem.datasource.di
import android.content.Context
import com.squareup.moshi.Moshi
import com.tangem.datasource.local.preferences.*
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
@ -20,10 +21,11 @@ internal object AppPreferencesStoreModule {
fun provideAppPreferencesStore(
@ApplicationContext appContext: Context,
dispatchers: CoroutineDispatcherProvider,
appScope: AppCoroutineScope,
@SdkMoshi moshi: Moshi,
): AppPreferencesStore {
return AppPreferencesStore(
preferencesDataStore = PreferencesDataStore.getInstance(context = appContext, dispatcher = dispatchers.io),
preferencesDataStore = PreferencesDataStore.getInstance(context = appContext, appScope = appScope),
moshi = moshi,
dispatchers = dispatchers,
)

View file

@ -11,14 +11,12 @@ import com.tangem.datasource.local.token.ExpressAssetsStore
import com.tangem.datasource.utils.MoshiDataStoreSerializer
import com.tangem.datasource.utils.listTypes
import com.tangem.datasource.utils.mapWithStringKeyTypes
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.AppCoroutineScope
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import javax.inject.Singleton
@Module
@ -30,7 +28,7 @@ internal object ExpressAssetsStoreModule {
fun provideExpressAssetsStore(
@NetworkMoshi moshi: Moshi,
@ApplicationContext context: Context,
dispatchers: CoroutineDispatcherProvider,
appScope: AppCoroutineScope,
): ExpressAssetsStore {
return DefaultExpressAssetsStore(
persistenceStore = DataStoreFactory.create(
@ -40,7 +38,7 @@ internal object ExpressAssetsStoreModule {
defaultValue = emptyMap(),
),
produceFile = { context.dataStoreFile("express_assets") },
scope = CoroutineScope(context = dispatchers.io + SupervisorJob()),
scope = appScope,
),
runtimeStore = RuntimeDataStore(),
)

View file

@ -25,7 +25,7 @@ import com.tangem.datasource.api.visa.VisaApi
import com.tangem.datasource.di.utils.RetrofitApiBuilder
import com.tangem.datasource.di.utils.RetrofitApiBuilder.Timeouts
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.AppCoroutineScope
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -47,11 +47,11 @@ internal object NetworkModule {
fun provideApiConfigManager(
apiConfigs: ApiConfigs,
appPreferencesStore: AppPreferencesStore,
dispatchers: CoroutineDispatcherProvider,
appScope: AppCoroutineScope,
): ApiConfigsManager {
return when {
BuildConfig.BUILD_TYPE == MOCKED_BUILD_TYPE -> MockApiConfigsManager(apiConfigs, dispatchers)
BuildConfig.TESTER_MENU_ENABLED -> DevApiConfigsManager(apiConfigs, appPreferencesStore, dispatchers)
BuildConfig.BUILD_TYPE == MOCKED_BUILD_TYPE -> MockApiConfigsManager(apiConfigs, appScope)
BuildConfig.TESTER_MENU_ENABLED -> DevApiConfigsManager(apiConfigs, appPreferencesStore, appScope)
else -> ProdApiConfigsManager(apiConfigs)
}
}

View file

@ -20,14 +20,12 @@ import com.tangem.datasource.utils.MoshiDataStoreSerializer
import com.tangem.datasource.utils.listTypes
import com.tangem.datasource.utils.mapWithStringKeyTypes
import com.tangem.datasource.utils.setTypes
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.AppCoroutineScope
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import javax.inject.Singleton
@Module
@ -39,7 +37,7 @@ internal object StakingStoreModule {
fun provideStakingTokensStore(
@NetworkMoshi moshi: Moshi,
@ApplicationContext context: Context,
dispatchers: CoroutineDispatcherProvider,
appScope: AppCoroutineScope,
): StakingYieldsStore {
return DefaultStakingYieldsStore(
dataStore = DataStoreFactory.create(
@ -49,7 +47,7 @@ internal object StakingStoreModule {
defaultValue = emptyList(),
),
produceFile = { context.dataStoreFile(fileName = "yields_cache") },
scope = CoroutineScope(context = dispatchers.io + SupervisorJob()),
scope = appScope,
),
)
}
@ -59,7 +57,7 @@ internal object StakingStoreModule {
fun provideYieldsBalancesPersistenceStore(
@NetworkMoshi moshi: Moshi,
@ApplicationContext context: Context,
dispatchers: CoroutineDispatcherProvider,
appScope: AppCoroutineScope,
): DataStore<Map<String, Set<YieldBalanceWrapperDTO>>> {
return DataStoreFactory.create(
serializer = MoshiDataStoreSerializer(
@ -68,7 +66,7 @@ internal object StakingStoreModule {
defaultValue = emptyMap(),
),
produceFile = { context.dataStoreFile(fileName = "yield_balances") },
scope = CoroutineScope(context = dispatchers.io + SupervisorJob()),
scope = appScope,
)
}
@ -83,7 +81,7 @@ internal object StakingStoreModule {
fun provideP2PEthPoolBalancesPersistenceStore(
@NetworkMoshi moshi: Moshi,
@ApplicationContext context: Context,
dispatchers: CoroutineDispatcherProvider,
appScope: AppCoroutineScope,
): DataStore<Map<String, Set<P2PEthPoolAccountResponse>>> {
return DataStoreFactory.create(
serializer = MoshiDataStoreSerializer(
@ -92,7 +90,7 @@ internal object StakingStoreModule {
defaultValue = emptyMap(),
),
produceFile = { context.dataStoreFile(fileName = "p2p_eth_pool_balances") },
scope = CoroutineScope(context = dispatchers.io + SupervisorJob()),
scope = appScope,
)
}
@ -101,7 +99,7 @@ internal object StakingStoreModule {
fun provideP2PEthPoolVaultsStore(
@NetworkMoshi moshi: Moshi,
@ApplicationContext context: Context,
dispatchers: CoroutineDispatcherProvider,
appScope: AppCoroutineScope,
): P2PEthPoolVaultsStore {
return DefaultP2PEthPoolVaultsStore(
dataStore = DataStoreFactory.create(
@ -111,7 +109,7 @@ internal object StakingStoreModule {
defaultValue = emptyList(),
),
produceFile = { context.dataStoreFile(fileName = "p2p_eth_pool_vaults") },
scope = CoroutineScope(context = dispatchers.io + SupervisorJob()),
scope = appScope,
),
)
}

View file

@ -8,14 +8,12 @@ import com.tangem.datasource.local.token.DefaultTokenReceiveWarningActionStore
import com.tangem.datasource.local.token.TokenReceiveWarningActionStore
import com.tangem.datasource.utils.MoshiDataStoreSerializer
import com.tangem.datasource.utils.setTypes
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.AppCoroutineScope
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import javax.inject.Singleton
@Module
@ -27,7 +25,7 @@ object TokenReceiveWarningModule {
fun provideTokenReceiveWarningStore(
@NetworkMoshi moshi: Moshi,
@ApplicationContext context: Context,
dispatchers: CoroutineDispatcherProvider,
appScope: AppCoroutineScope,
): TokenReceiveWarningActionStore {
return DefaultTokenReceiveWarningActionStore(
persistenceStore = DataStoreFactory.create(
@ -37,7 +35,7 @@ object TokenReceiveWarningModule {
defaultValue = emptySet(),
),
produceFile = { context.dataStoreFile(fileName = "token_receive_warnings_viewed") },
scope = CoroutineScope(context = dispatchers.io + SupervisorJob()),
scope = appScope,
),
)
}

View file

@ -8,16 +8,14 @@ import com.tangem.datasource.local.walletconnect.DefaultWalletConnectStore
import com.tangem.datasource.local.walletconnect.WalletConnectStore
import com.tangem.datasource.utils.MoshiDataStoreSerializer
import com.tangem.datasource.utils.setTypes
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.walletconnect.model.WcPendingApprovalSessionDTO
import com.tangem.domain.walletconnect.model.WcSessionDTO
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import javax.inject.Singleton
@Module
@ -29,9 +27,8 @@ object WalletConnectModule {
fun provideWalletConnectStore(
@NetworkMoshi moshi: Moshi,
@ApplicationContext context: Context,
dispatchers: CoroutineDispatcherProvider,
scope: AppCoroutineScope,
): WalletConnectStore {
val scope = CoroutineScope(context = dispatchers.io + SupervisorJob())
return DefaultWalletConnectStore(
persistenceStore = DataStoreFactory.create(
serializer = MoshiDataStoreSerializer(

View file

@ -9,14 +9,12 @@ import com.tangem.datasource.local.yieldsupply.DefaultYieldMarketsStore
import com.tangem.datasource.local.yieldsupply.YieldMarketsStore
import com.tangem.datasource.utils.MoshiDataStoreSerializer
import com.tangem.datasource.utils.listTypes
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.AppCoroutineScope
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import javax.inject.Singleton
@Module
@ -28,7 +26,7 @@ object YieldSupplyModule {
fun provideYieldMarketsStore(
@NetworkMoshi moshi: Moshi,
@ApplicationContext context: Context,
dispatchers: CoroutineDispatcherProvider,
appScope: AppCoroutineScope,
): YieldMarketsStore {
return DefaultYieldMarketsStore(
persistenceStore = DataStoreFactory.create(
@ -38,7 +36,7 @@ object YieldSupplyModule {
defaultValue = emptyList(),
),
produceFile = { context.dataStoreFile(fileName = "yield_markets_cache") },
scope = CoroutineScope(context = dispatchers.io + SupervisorJob()),
scope = appScope,
),
)
}

View file

@ -1,6 +1,7 @@
package com.tangem.datasource.local.logs
import android.content.Context
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.*
@ -27,13 +28,9 @@ import javax.inject.Singleton
class AppLogsStore @Inject constructor(
@ApplicationContext private val applicationContext: Context,
private val dispatchers: CoroutineDispatcherProvider,
private val scope: AppCoroutineScope,
) {
private val scope = CoroutineScope(
context = SupervisorJob() + dispatchers.io +
CoroutineExceptionHandler { _, error -> Timber.e("AppLogsStore.scope is failed $error") },
)
private val mutex = Mutex()
private val zipMutex = Mutex()

View file

@ -10,12 +10,10 @@ import com.tangem.datasource.di.NetworkMoshi
import com.tangem.datasource.local.nft.custom.NFTPriceId
import com.tangem.datasource.utils.MoshiDataStoreSerializer
import com.tangem.datasource.utils.listTypes
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import java.lang.reflect.ParameterizedType
import javax.inject.Inject
import javax.inject.Singleton
@ -24,7 +22,7 @@ import javax.inject.Singleton
class NFTPersistenceStoreFactory @Inject constructor(
@NetworkMoshi private val moshi: Moshi,
@ApplicationContext private val context: Context,
private val dispatchers: CoroutineDispatcherProvider,
private val appScope: AppCoroutineScope,
) {
fun provide(userWalletId: UserWalletId, network: Network): NFTPersistenceStore {
@ -61,7 +59,7 @@ class NFTPersistenceStoreFactory @Inject constructor(
defaultValue = defaultValue,
),
produceFile = { context.dataStoreFile(fileName = fileName) },
scope = CoroutineScope(context = dispatchers.io + SupervisorJob()),
scope = appScope,
)
private fun Network.ID.formatted(): String = rawId.value

View file

@ -16,10 +16,8 @@ import com.tangem.datasource.local.preferences.PreferencesKeys.IS_WALLET_SWAP_PR
import com.tangem.datasource.local.preferences.PreferencesKeys.SHOULD_SHOW_RING_PROMO_KEY
import com.tangem.datasource.local.preferences.utils.CleanupKeyMigration
import com.tangem.datasource.local.preferences.utils.SharedPreferencesKeyMigration
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import com.tangem.utils.coroutines.AppCoroutineScope
import timber.log.Timber
import kotlin.coroutines.CoroutineContext
/**
* Application preferences data store 'DataStore<Preferences>'.
@ -35,15 +33,15 @@ internal object PreferencesDataStore {
private var INSTANCE: DataStore<Preferences>? = null
fun getInstance(context: Context, dispatcher: CoroutineContext): DataStore<Preferences> {
return INSTANCE ?: create(context, dispatcher).also { INSTANCE = it }
fun getInstance(context: Context, appScope: AppCoroutineScope): DataStore<Preferences> {
return INSTANCE ?: create(context, appScope).also { INSTANCE = it }
}
private fun create(context: Context, dispatcher: CoroutineContext): DataStore<Preferences> {
private fun create(context: Context, appScope: AppCoroutineScope): DataStore<Preferences> {
return PreferenceDataStoreFactory.create(
corruptionHandler = createCorruptionHandler(),
migrations = createMigrations(context = context),
scope = CoroutineScope(context = dispatcher + SupervisorJob()),
scope = appScope,
produceFile = { context.preferencesDataStoreFile(name = PREFERENCES_FILE_NAME) },
)
}

View file

@ -0,0 +1,5 @@
package com.tangem.utils.coroutines
import kotlinx.coroutines.CoroutineScope
interface AppCoroutineScope : CoroutineScope

View file

@ -1,7 +0,0 @@
package com.tangem.utils.coroutines
import javax.inject.Qualifier
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class DelayedWork

View file

@ -1,23 +0,0 @@
package com.tangem.utils.di
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.DelayedWork
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object DelayedWorkCoroutineModule {
@Provides
@Singleton
@DelayedWork
fun provideDelayedWorkCoroutineScope(coroutineDispatcherProvider: CoroutineDispatcherProvider): CoroutineScope {
return CoroutineScope(SupervisorJob() + coroutineDispatcherProvider.io)
}
}

View file

@ -26,14 +26,13 @@ import com.tangem.datasource.utils.setTypes
import com.tangem.domain.account.repository.AccountsCRUDRepository
import com.tangem.domain.account.repository.AccountsExpandedRepository
import com.tangem.domain.account.tokens.MainAccountTokensMigration
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import javax.inject.Singleton
@Module
@ -69,7 +68,7 @@ internal object AccountDataModule {
fun provideAccountsExpandedRepository(
@NetworkMoshi moshi: Moshi,
@ApplicationContext context: Context,
dispatchers: CoroutineDispatcherProvider,
appScope: AppCoroutineScope,
): AccountsExpandedRepository {
val store = DataStoreFactory.create<Map<String, Set<AccountsExpandedDTO>>>(
serializer = MoshiDataStoreSerializer(
@ -78,7 +77,7 @@ internal object AccountDataModule {
defaultValue = emptyMap(),
),
produceFile = { context.dataStoreFile(fileName = "account_expanded_store") },
scope = CoroutineScope(context = dispatchers.io + SupervisorJob()),
scope = appScope,
)
return DefaultAccountsExpandedRepository(
@ -107,14 +106,14 @@ internal object AccountDataModule {
userTokensSaver: UserTokensSaver,
accountTokenMigrationStore: AccountTokenMigrationStore,
eTagsStore: ETagsStore,
dispatchers: CoroutineDispatcherProvider,
appScope: AppCoroutineScope,
): DefaultMainAccountTokensMigration {
return DefaultMainAccountTokensMigration(
accountsResponseStoreFactory = accountsResponseStoreFactory,
accountTokenMigrationStore = accountTokenMigrationStore,
userTokensSaver = userTokensSaver,
eTagsStore = eTagsStore,
dispatchers = dispatchers,
coroutineScope = appScope,
)
}
}

View file

@ -10,11 +10,9 @@ import com.squareup.moshi.adapter
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
import com.tangem.datasource.di.NetworkMoshi
import com.tangem.datasource.utils.MoshiDataStoreSerializer
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
import javax.inject.Singleton
@ -35,7 +33,7 @@ typealias AccountsResponseStore = DataStore<GetWalletAccountsResponse?>
internal class AccountsResponseStoreFactory @Inject constructor(
@ApplicationContext private val context: Context,
@NetworkMoshi private val moshi: Moshi,
private val dispatchers: CoroutineDispatcherProvider,
private val appScope: AppCoroutineScope,
) {
@OptIn(ExperimentalStdlibApi::class)
@ -53,7 +51,7 @@ internal class AccountsResponseStoreFactory @Inject constructor(
DataStoreFactory.create(
serializer = MoshiDataStoreSerializer(defaultValue = null, adapter = adapter),
produceFile = { context.dataStoreFile(fileName = "wallet_accounts_${userWalletId.stringValue}") },
scope = CoroutineScope(context = dispatchers.io + SupervisorJob()),
scope = appScope,
)
}
}

View file

@ -20,12 +20,10 @@ import com.tangem.datasource.api.tangemTech.models.account.toUserTokensResponse
import com.tangem.datasource.local.accounts.AccountTokenMigrationStore
import com.tangem.datasource.utils.getSyncOrNull
import com.tangem.domain.account.tokens.MainAccountTokensMigration
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.models.account.DerivationIndex
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.lib.crypto.derivation.AccountNodeRecognizer
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import timber.log.Timber
@ -43,11 +41,9 @@ internal class DefaultMainAccountTokensMigration(
private val accountTokenMigrationStore: AccountTokenMigrationStore,
private val userTokensSaver: UserTokensSaver,
private val eTagsStore: ETagsStore,
dispatchers: CoroutineDispatcherProvider,
private val coroutineScope: AppCoroutineScope,
) : MainAccountTokensMigration {
private val coroutineScope = CoroutineScope(dispatchers.default + SupervisorJob())
internal suspend fun migrate(userWalletId: UserWalletId): Either<Throwable, GetWalletAccountsResponse> = either {
val store = accountsResponseStoreFactory.create(userWalletId)
val response = store.getSyncOrNull()

View file

@ -3,8 +3,8 @@ package com.tangem.data.account.store
import android.content.Context
import com.google.common.truth.Truth
import com.squareup.moshi.Moshi
import com.tangem.common.test.TestAppCoroutineScope
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.clearMocks
import io.mockk.mockk
import org.junit.jupiter.api.AfterEach
@ -19,7 +19,7 @@ class AccountsResponseStoreFactoryTest {
private val factory: AccountsResponseStoreFactory = AccountsResponseStoreFactory(
context = context,
moshi = moshi,
dispatchers = TestingCoroutineDispatcherProvider(),
appScope = TestAppCoroutineScope(),
)
@AfterEach

View file

@ -1,6 +1,7 @@
package com.tangem.data.account.token
import arrow.core.right
import com.tangem.common.test.TestAppCoroutineScope
import com.tangem.data.account.converter.createGetWalletAccountsResponse
import com.tangem.data.account.converter.createWalletAccountDTO
import com.tangem.data.account.store.AccountsResponseStore
@ -18,7 +19,6 @@ import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.test.core.assertEither
import com.tangem.test.core.assertEitherLeft
import com.tangem.test.core.assertEitherRight
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runTest
@ -43,7 +43,7 @@ class DefaultMainAccountTokensMigrationTest {
accountTokenMigrationStore = accountTokenMigrationStore,
userTokensSaver = userTokensSaver,
eTagsStore = eTagsStore,
dispatchers = TestingCoroutineDispatcherProvider(),
coroutineScope = TestAppCoroutineScope(),
)
private val userWalletId = UserWalletId("011")

View file

@ -13,6 +13,7 @@ import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.local.appsflyer.AppsFlyerStore
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.demo.models.DemoConfig
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.repository.WalletsRepository
@ -22,8 +23,6 @@ import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import javax.inject.Singleton
@Module
@ -69,6 +68,7 @@ internal object DataCommonModule {
dispatchers: CoroutineDispatcherProvider,
addressesEnricher: UserTokensResponseAddressesEnricher,
walletServerBinder: WalletServerBinder,
appScope: AppCoroutineScope,
): UserTokensSaver {
return UserTokensSaver(
tangemTechApi = tangemTechApi,
@ -76,7 +76,7 @@ internal object DataCommonModule {
dispatchers = dispatchers,
addressesEnricher = addressesEnricher,
pushTokensRetryerPool = RetryerPool(
coroutineScope = CoroutineScope(SupervisorJob() + dispatchers.default),
coroutineScope = appScope,
),
walletServerBinder = walletServerBinder,
)

View file

@ -151,7 +151,7 @@ internal class DefaultQuotesFetcherTest {
quotes = setOf(
QuoteMetadata(
cryptoCurrencyId = "ethereum",
timestamp = DateTime.now().millis - 10_000,
timestamp = DateTime.now().millis - 20_000,
value = cachedQuote,
),
),
@ -194,7 +194,7 @@ internal class DefaultQuotesFetcherTest {
quotes = setOf(
QuoteMetadata(
cryptoCurrencyId = "ethereum",
timestamp = DateTime.now().millis - 10_000,
timestamp = DateTime.now().millis - 20_000,
value = cachedQuote,
),
QuoteMetadata(
@ -385,7 +385,7 @@ internal class DefaultQuotesFetcherTest {
quotes = setOf(
QuoteMetadata(
cryptoCurrencyId = "solana",
timestamp = DateTime.now().millis - 10_000,
timestamp = DateTime.now().millis - 20_000,
value = MockQuoteResponseFactory.createSinglePrice(BigDecimal.ZERO),
),
),

View file

@ -15,6 +15,7 @@ import com.tangem.datasource.local.network.entity.NetworkStatusDM
import com.tangem.datasource.utils.MoshiDataStoreSerializer
import com.tangem.datasource.utils.mapWithStringKeyTypes
import com.tangem.datasource.utils.setTypes
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.networks.repository.NetworksRepository
import com.tangem.domain.networks.utils.NetworksCleaner
import com.tangem.domain.walletmanager.WalletManagersFacade
@ -24,8 +25,6 @@ import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import javax.inject.Singleton
@Module
@ -37,7 +36,7 @@ internal object NetworkDataModule {
fun provideNetworksStatusesStore(
@NetworkMoshi moshi: Moshi,
@ApplicationContext context: Context,
dispatchers: CoroutineDispatcherProvider,
appScope: AppCoroutineScope,
): NetworksStatusesStore {
return DefaultNetworksStatusesStore(
context = context,
@ -49,9 +48,9 @@ internal object NetworkDataModule {
defaultValue = emptyMap(),
),
produceFile = { context.dataStoreFile(fileName = "networks_statuses_2") },
scope = CoroutineScope(context = dispatchers.io + SupervisorJob()),
scope = appScope,
),
dispatchers = dispatchers,
scope = appScope,
)
}

View file

@ -7,11 +7,11 @@ import com.tangem.data.networks.converters.SimpleNetworkStatusConverter
import com.tangem.data.networks.models.SimpleNetworkStatus
import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.datasource.local.network.entity.NetworkStatusDM
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.network.NetworkStatus
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.extensions.addOrReplace
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
@ -33,11 +33,9 @@ internal class DefaultNetworksStatusesStore(
context: Context,
private val runtimeStore: RuntimeSharedStore<WalletIdWithSimpleStatus>,
private val persistenceDataStore: DataStore<WalletIdWithStatusDM>,
dispatchers: CoroutineDispatcherProvider,
private val scope: AppCoroutineScope,
) : NetworksStatusesStore {
private val scope = CoroutineScope(context = SupervisorJob() + dispatchers.io)
init {
scope.launch {
try {

View file

@ -2,6 +2,7 @@ package com.tangem.data.networks.store
import com.google.common.truth.Truth
import com.tangem.blockchain.common.Blockchain
import com.tangem.common.test.TestAppCoroutineScope
import com.tangem.common.test.datastore.MockStateDataStore
import com.tangem.common.test.domain.network.MockNetworkStatusFactory
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
@ -10,7 +11,6 @@ import com.tangem.data.networks.toSimple
import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.test.core.getEmittedValues
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.mockk
import kotlinx.coroutines.test.runTest
import org.junit.Test
@ -27,7 +27,7 @@ internal class GetTest {
context = mockk(),
runtimeStore = runtimeStore,
persistenceDataStore = persistenceStore,
dispatchers = TestingCoroutineDispatcherProvider(),
scope = TestAppCoroutineScope(),
)
@Test

View file

@ -2,6 +2,7 @@ package com.tangem.data.networks.store
import androidx.datastore.core.DataStore
import com.google.common.truth.Truth
import com.tangem.common.test.TestAppCoroutineScope
import com.tangem.common.test.datastore.MockStateDataStore
import com.tangem.common.test.domain.network.MockNetworkStatusFactory
import com.tangem.data.networks.models.SimpleNetworkStatus
@ -10,7 +11,6 @@ import com.tangem.data.networks.toSimple
import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.flow.emptyFlow
@ -33,7 +33,7 @@ internal class InitializationTest {
context = mockk(),
runtimeStore = runtimeStore,
persistenceDataStore = persistenceStore,
dispatchers = TestingCoroutineDispatcherProvider(),
scope = TestAppCoroutineScope(),
)
Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(null)
@ -48,7 +48,7 @@ internal class InitializationTest {
context = mockk(),
runtimeStore = runtimeStore,
persistenceDataStore = persistenceStore,
dispatchers = TestingCoroutineDispatcherProvider(),
scope = TestAppCoroutineScope(),
)
Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(emptyMap<String, Set<SimpleNetworkStatus>>())
@ -71,7 +71,7 @@ internal class InitializationTest {
context = mockk(),
runtimeStore = runtimeStore,
persistenceDataStore = persistenceStore,
dispatchers = TestingCoroutineDispatcherProvider(),
scope = TestAppCoroutineScope(),
)
val expectedStatus = status.toSimple().copy(value = status.value.copySealed(source = StatusSource.CACHE))

View file

@ -1,6 +1,7 @@
package com.tangem.data.networks.store
import com.google.common.truth.Truth
import com.tangem.common.test.TestAppCoroutineScope
import com.tangem.common.test.datastore.MockStateDataStore
import com.tangem.common.test.domain.network.MockNetworkStatusFactory
import com.tangem.data.networks.models.SimpleNetworkStatus
@ -10,7 +11,6 @@ import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.network.NetworkStatus
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.mockk
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.test.runTest
@ -31,7 +31,7 @@ internal class ParameterizedStoreStatusTest(private val model: Model) {
context = mockk(),
runtimeStore = runtimeStore,
persistenceDataStore = persistenceStore,
dispatchers = TestingCoroutineDispatcherProvider(),
scope = TestAppCoroutineScope(),
)
@Test

View file

@ -1,6 +1,7 @@
package com.tangem.data.networks.store
import com.google.common.truth.Truth
import com.tangem.common.test.TestAppCoroutineScope
import com.tangem.common.test.datastore.MockStateDataStore
import com.tangem.common.test.domain.network.MockNetworkStatusFactory
import com.tangem.data.networks.models.SimpleNetworkStatus
@ -10,7 +11,6 @@ import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.network.NetworkStatus
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.mockk
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.test.runTest
@ -31,7 +31,7 @@ internal class ParameterizedStoreSuccessTest(private val model: Model) {
context = mockk(),
runtimeStore = runtimeStore,
persistenceDataStore = persistenceStore,
dispatchers = TestingCoroutineDispatcherProvider(),
scope = TestAppCoroutineScope(),
)
@Test

View file

@ -1,6 +1,7 @@
package com.tangem.data.networks.store
import com.google.common.truth.Truth
import com.tangem.common.test.TestAppCoroutineScope
import com.tangem.common.test.datastore.MockStateDataStore
import com.tangem.common.test.domain.network.MockNetworkStatusFactory
import com.tangem.data.networks.models.SimpleNetworkStatus
@ -9,7 +10,6 @@ import com.tangem.data.networks.toSimple
import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.domain.models.network.NetworkStatus
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.mockk
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.test.runTest
@ -30,7 +30,7 @@ internal class ParameterizedStoreTest(private val model: Model) {
context = mockk(),
runtimeStore = runtimeStore,
persistenceDataStore = persistenceStore,
dispatchers = TestingCoroutineDispatcherProvider(),
scope = TestAppCoroutineScope(),
)
@Test

View file

@ -1,6 +1,7 @@
package com.tangem.data.networks.store
import com.google.common.truth.Truth
import com.tangem.common.test.TestAppCoroutineScope
import com.tangem.common.test.datastore.MockStateDataStore
import com.tangem.common.test.domain.network.MockNetworkStatusFactory
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
@ -10,7 +11,6 @@ import com.tangem.datasource.local.network.entity.NetworkStatusDM
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.network.NetworkStatus
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.mockk
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.test.runTest
@ -28,7 +28,7 @@ internal class SetSourceAsCacheTest {
context = mockk(),
runtimeStore = runtimeStore,
persistenceDataStore = persistenceStore,
dispatchers = TestingCoroutineDispatcherProvider(),
scope = TestAppCoroutineScope(),
)
@Test

View file

@ -1,6 +1,7 @@
package com.tangem.data.networks.store
import com.google.common.truth.Truth
import com.tangem.common.test.TestAppCoroutineScope
import com.tangem.common.test.datastore.MockStateDataStore
import com.tangem.common.test.domain.network.MockNetworkStatusFactory
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
@ -10,7 +11,6 @@ import com.tangem.datasource.local.network.entity.NetworkStatusDM
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.network.NetworkStatus
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.mockk
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.test.runTest
@ -28,7 +28,7 @@ internal class SetSourceAsOnlyCacheTest {
context = mockk(),
runtimeStore = runtimeStore,
persistenceDataStore = persistenceStore,
dispatchers = TestingCoroutineDispatcherProvider(),
scope = TestAppCoroutineScope(),
)
@Test

View file

@ -1,6 +1,7 @@
package com.tangem.data.networks.store
import com.google.common.truth.Truth
import com.tangem.common.test.TestAppCoroutineScope
import com.tangem.common.test.datastore.MockStateDataStore
import com.tangem.common.test.domain.network.MockNetworkStatusFactory
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
@ -10,7 +11,6 @@ import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.datasource.local.network.entity.NetworkStatusDM
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.mockk
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.test.runTest
@ -28,7 +28,7 @@ internal class StoreStatusTest {
context = mockk(),
runtimeStore = runtimeStore,
persistenceDataStore = persistenceStore,
dispatchers = TestingCoroutineDispatcherProvider(),
scope = TestAppCoroutineScope(),
)
@Test

View file

@ -1,6 +1,7 @@
package com.tangem.data.networks.store
import com.google.common.truth.Truth
import com.tangem.common.test.TestAppCoroutineScope
import com.tangem.common.test.datastore.MockStateDataStore
import com.tangem.common.test.domain.network.MockNetworkStatusFactory
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
@ -9,7 +10,6 @@ import com.tangem.data.networks.toSimple
import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.mockk
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.test.runTest
@ -27,7 +27,7 @@ internal class StoreSuccessTest {
context = mockk(),
runtimeStore = runtimeStore,
persistenceDataStore = persistenceStore,
dispatchers = TestingCoroutineDispatcherProvider(),
scope = TestAppCoroutineScope(),
)
@Test

View file

@ -1,6 +1,7 @@
package com.tangem.data.networks.store
import com.google.common.truth.Truth
import com.tangem.common.test.TestAppCoroutineScope
import com.tangem.common.test.datastore.MockStateDataStore
import com.tangem.common.test.domain.network.MockNetworkStatusFactory
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
@ -9,7 +10,6 @@ import com.tangem.data.networks.toSimple
import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.mockk
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.test.runTest
@ -27,7 +27,7 @@ internal class StoreTest {
context = mockk(),
runtimeStore = runtimeStore,
persistenceDataStore = persistenceStore,
dispatchers = TestingCoroutineDispatcherProvider(),
scope = TestAppCoroutineScope(),
)
@Test

View file

@ -1,6 +1,7 @@
package com.tangem.data.networks.store
import com.google.common.truth.Truth
import com.tangem.common.test.TestAppCoroutineScope
import com.tangem.common.test.datastore.MockStateDataStore
import com.tangem.common.test.domain.network.MockNetworkStatusFactory
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
@ -13,7 +14,6 @@ import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.network.NetworkAddress
import com.tangem.domain.models.network.NetworkStatus
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.mockk
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.test.runTest
@ -31,7 +31,7 @@ internal class UpdateStatusSourceTest {
context = mockk(),
runtimeStore = runtimeStore,
persistenceDataStore = persistenceStore,
dispatchers = TestingCoroutineDispatcherProvider(),
scope = TestAppCoroutineScope(),
)
@Test

View file

@ -22,6 +22,7 @@ import com.tangem.domain.card.common.extensions.canHandleToken
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.common.wallets.getSyncStrict
import com.tangem.domain.common.wallets.loadAndGet
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.onramp.model.HotCryptoCurrency
@ -30,10 +31,9 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.JobHolder
import com.tangem.utils.coroutines.runCatching
import com.tangem.utils.coroutines.saveIn
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.plus
import timber.log.Timber
/**
@ -59,9 +59,10 @@ internal class DefaultHotCryptoRepository(
private val walletAccountsFetcher: WalletAccountsFetcher,
private val dispatchers: CoroutineDispatcherProvider,
private val analyticsEventHandler: AnalyticsEventHandler,
appScope: AppCoroutineScope,
) : HotCryptoRepository {
private val coroutineScope = CoroutineScope(dispatchers.main + SupervisorJob())
private val coroutineScope = appScope + dispatchers.main
private val hotCryptoJobHolder = JobHolder()

View file

@ -28,6 +28,7 @@ import com.tangem.datasource.local.onramp.sepa.OnrampCurrentCountryByIPStore
import com.tangem.datasource.local.onramp.sepa.OnrampSepaAvailabilityStore
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.onramp.repositories.*
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
@ -109,6 +110,7 @@ internal object OnrampDataModule {
dispatchers: CoroutineDispatcherProvider,
analyticsEventHandler: AnalyticsEventHandler,
walletAccountsFetcher: WalletAccountsFetcher,
appScope: AppCoroutineScope,
): HotCryptoRepository {
return DefaultHotCryptoRepository(
excludedBlockchains = excludedBlockchains,
@ -119,6 +121,7 @@ internal object OnrampDataModule {
dispatchers = dispatchers,
analyticsEventHandler = analyticsEventHandler,
walletAccountsFetcher = walletAccountsFetcher,
appScope = appScope,
)
}

View file

@ -14,17 +14,15 @@ import com.tangem.datasource.di.NetworkMoshi
import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.datasource.utils.MoshiDataStoreSerializer
import com.tangem.datasource.utils.mapWithStringKeyTypes
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.quotes.QuotesRepository
import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher
import com.tangem.domain.quotes.multi.MultiQuoteUpdater
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import javax.inject.Singleton
@Module
@ -36,7 +34,7 @@ internal object QuotesDataModule {
fun provideQuotesStoreV2(
@NetworkMoshi moshi: Moshi,
@ApplicationContext context: Context,
dispatchers: CoroutineDispatcherProvider,
appScope: AppCoroutineScope,
): QuotesStatusesStore {
return DefaultQuotesStatusesStore(
runtimeStore = RuntimeSharedStore(),
@ -47,9 +45,9 @@ internal object QuotesDataModule {
defaultValue = emptyMap(),
),
produceFile = { context.dataStoreFile(fileName = "quotes") },
scope = CoroutineScope(context = dispatchers.io + SupervisorJob()),
scope = appScope,
),
dispatchers = dispatchers,
scope = appScope,
)
}
@ -65,13 +63,13 @@ internal object QuotesDataModule {
appCurrencyResponseStore: AppCurrencyResponseStore,
quotesStatusesStore: QuotesStatusesStore,
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
dispatchers: CoroutineDispatcherProvider,
coroutineScope: AppCoroutineScope,
): MultiQuoteUpdater {
return DefaultMultiQuoteUpdater(
appCurrencyResponseStore = appCurrencyResponseStore,
quotesStatusesStore = quotesStatusesStore,
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
dispatchers = dispatchers,
coroutineScope = coroutineScope,
)
}
}

View file

@ -4,16 +4,14 @@ import androidx.annotation.VisibleForTesting
import arrow.core.left
import com.tangem.data.quotes.store.QuotesStatusesStore
import com.tangem.datasource.appcurrency.AppCurrencyResponseStore
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.core.utils.EitherFlow
import com.tangem.domain.models.quote.QuoteStatus
import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher
import com.tangem.domain.quotes.multi.MultiQuoteUpdater
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.JobHolder
import com.tangem.utils.coroutines.saveIn
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import timber.log.Timber
@ -32,10 +30,9 @@ internal class DefaultMultiQuoteUpdater(
private val appCurrencyResponseStore: AppCurrencyResponseStore,
private val quotesStatusesStore: QuotesStatusesStore,
private val multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
dispatchers: CoroutineDispatcherProvider,
private val coroutineScope: AppCoroutineScope,
) : MultiQuoteUpdater {
private val coroutineScope = CoroutineScope(SupervisorJob() + dispatchers.default)
private val updaterHolder = JobHolder()
override fun subscribe() {

View file

@ -4,13 +4,11 @@ import androidx.datastore.core.DataStore
import com.tangem.data.quotes.converter.QuoteStatusConverter
import com.tangem.datasource.api.tangemTech.models.QuotesResponse
import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.quote.QuoteStatus
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.extensions.addOrReplace
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.firstOrNull
@ -29,11 +27,9 @@ internal typealias CurrencyIdWithQuote = Map<String, QuotesResponse.Quote>
internal class DefaultQuotesStatusesStore(
private val runtimeStore: RuntimeSharedStore<Set<QuoteStatus>>,
private val persistenceDataStore: DataStore<CurrencyIdWithQuote>,
dispatchers: CoroutineDispatcherProvider,
private val scope: AppCoroutineScope,
) : QuotesStatusesStore {
private val scope = CoroutineScope(context = SupervisorJob() + dispatchers.io)
init {
scope.launch {
val cachedStatuses = persistenceDataStore.data.firstOrNull()

View file

@ -2,12 +2,12 @@ package com.tangem.data.quotes.multi
import arrow.core.right
import com.google.common.truth.Truth
import com.tangem.common.test.TestAppCoroutineScope
import com.tangem.data.quotes.store.QuotesStatusesStore
import com.tangem.datasource.api.tangemTech.models.CurrenciesResponse
import com.tangem.datasource.appcurrency.AppCurrencyResponseStore
import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher
import com.tangem.test.core.getEmittedValues
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.test.runTest
@ -26,7 +26,7 @@ internal class DefaultMultiQuoteStatusUpdaterTest {
appCurrencyResponseStore = appCurrencyResponseStore,
quotesStatusesStore = quotesStore,
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
dispatchers = TestingCoroutineDispatcherProvider(),
coroutineScope = TestAppCoroutineScope(),
)
@Test

View file

@ -1,6 +1,7 @@
package com.tangem.data.quotes.store
import com.google.common.truth.Truth
import com.tangem.common.test.TestAppCoroutineScope
import com.tangem.common.test.data.quote.MockQuoteResponseFactory
import com.tangem.common.test.data.quote.toDomain
import com.tangem.common.test.datastore.MockStateDataStore
@ -9,7 +10,6 @@ import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.quote.QuoteStatus
import com.tangem.test.core.ProvideTestModels
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
@ -39,7 +39,7 @@ internal class QuotesStatusesStoreExtTest {
store = DefaultQuotesStatusesStore(
runtimeStore = runtimeStore,
persistenceDataStore = persistenceStore,
dispatchers = TestingCoroutineDispatcherProvider(),
scope = TestAppCoroutineScope(),
)
}

View file

@ -2,6 +2,7 @@ package com.tangem.data.quotes.store
import androidx.datastore.core.DataStore
import com.google.common.truth.Truth
import com.tangem.common.test.TestAppCoroutineScope
import com.tangem.common.test.data.quote.MockQuoteResponseFactory
import com.tangem.common.test.data.quote.toDomain
import com.tangem.common.test.datastore.MockStateDataStore
@ -11,7 +12,6 @@ import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.quote.QuoteStatus
import com.tangem.test.core.ProvideTestModels
import com.tangem.test.core.getEmittedValues
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.flow.emptyFlow
@ -53,7 +53,7 @@ internal class QuotesStatusesStoreTest {
store = DefaultQuotesStatusesStore(
runtimeStore = runtimeStore,
persistenceDataStore = persistenceStore,
dispatchers = TestingCoroutineDispatcherProvider(),
scope = TestAppCoroutineScope(),
)
}
@ -73,7 +73,7 @@ internal class QuotesStatusesStoreTest {
DefaultQuotesStatusesStore(
runtimeStore = runtimeStore,
persistenceDataStore = persistenceStore,
dispatchers = TestingCoroutineDispatcherProvider(),
scope = TestAppCoroutineScope(),
)
val actual = runtimeStore.getSyncOrNull()
@ -93,7 +93,7 @@ internal class QuotesStatusesStoreTest {
DefaultQuotesStatusesStore(
runtimeStore = runtimeStore,
persistenceDataStore = persistenceStore,
dispatchers = TestingCoroutineDispatcherProvider(),
scope = TestAppCoroutineScope(),
)
val actual = runtimeStore.getSyncOrNull()
@ -120,7 +120,7 @@ internal class QuotesStatusesStoreTest {
DefaultQuotesStatusesStore(
runtimeStore = runtimeStore,
persistenceDataStore = persistenceStore,
dispatchers = TestingCoroutineDispatcherProvider(),
scope = TestAppCoroutineScope(),
)
val actual = runtimeStore.getSyncOrNull()

View file

@ -8,11 +8,11 @@ import com.tangem.data.staking.store.StakeKitBalancesStore
import com.tangem.datasource.api.ethpool.models.response.P2PEthPoolAccountResponse
import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO
import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.staking.multi.MultiStakingBalanceProducer
import com.tangem.domain.staking.multi.MultiStakingBalanceSupplier
import com.tangem.domain.staking.single.SingleStakingBalanceProducer
import com.tangem.domain.staking.single.SingleStakingBalanceSupplier
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -27,12 +27,12 @@ internal object StakingBalanceSupplierModule {
@Singleton
fun provideStakingBalancesStore(
persistenceStore: DataStore<Map<String, Set<YieldBalanceWrapperDTO>>>,
dispatchers: CoroutineDispatcherProvider,
scope: AppCoroutineScope,
): StakeKitBalancesStore {
return DefaultStakeKitBalancesStore(
runtimeStore = RuntimeSharedStore(),
persistenceStore = persistenceStore,
dispatchers = dispatchers,
scope = scope,
)
}
@ -40,12 +40,12 @@ internal object StakingBalanceSupplierModule {
@Singleton
fun provideP2PEthPoolBalancesStore(
persistenceStore: DataStore<Map<String, Set<P2PEthPoolAccountResponse>>>,
dispatchers: CoroutineDispatcherProvider,
scope: AppCoroutineScope,
): P2PEthPoolBalancesStore {
return DefaultP2PEthPoolBalancesStore(
runtimeStore = RuntimeSharedStore(),
persistenceStore = persistenceStore,
dispatchers = dispatchers,
scope = scope,
)
}

View file

@ -4,14 +4,12 @@ import androidx.datastore.core.DataStore
import com.tangem.data.staking.converters.ethpool.P2PEthPoolStakingBalanceConverter
import com.tangem.datasource.api.ethpool.models.response.P2PEthPoolAccountResponse
import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.staking.StakingID
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.extensions.addOrReplace
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.firstOrNull
@ -33,11 +31,9 @@ internal typealias WalletIdWithP2PEthPoolResponses = Map<String, Set<P2PEthPoolA
internal class DefaultP2PEthPoolBalancesStore(
private val runtimeStore: RuntimeSharedStore<WalletIdWithP2PStakingBalances>,
private val persistenceStore: DataStore<WalletIdWithP2PEthPoolResponses>,
dispatchers: CoroutineDispatcherProvider,
private val scope: AppCoroutineScope,
) : P2PEthPoolBalancesStore {
private val scope = CoroutineScope(context = SupervisorJob() + dispatchers.io)
init {
scope.launch {
val cachedData = persistenceStore.data.firstOrNull() ?: return@launch

View file

@ -4,14 +4,12 @@ import androidx.datastore.core.DataStore
import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO
import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.datasource.local.token.converter.StakingBalanceConverter
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.staking.StakingID
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.extensions.addOrReplace
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.firstOrNull
@ -33,11 +31,9 @@ internal typealias WalletIdWithStakingBalances = Map<UserWalletId, Set<StakingBa
internal class DefaultStakeKitBalancesStore(
private val runtimeStore: RuntimeSharedStore<WalletIdWithStakingBalances>,
private val persistenceStore: DataStore<WalletIdWithWrappers>,
dispatchers: CoroutineDispatcherProvider,
private val scope: AppCoroutineScope,
) : StakeKitBalancesStore {
private val scope = CoroutineScope(context = SupervisorJob() + dispatchers.io)
init {
scope.launch {
val cachedStatuses = persistenceStore.data.firstOrNull() ?: return@launch

View file

@ -1,6 +1,7 @@
package com.tangem.data.staking.store
import com.google.common.truth.Truth
import com.tangem.common.test.TestAppCoroutineScope
import com.tangem.common.test.data.staking.MockYieldBalanceWrapperDTOFactory
import com.tangem.common.test.datastore.MockStateDataStore
import com.tangem.data.staking.toDomain
@ -8,7 +9,6 @@ import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.test.core.getEmittedValues
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import kotlinx.coroutines.test.runTest
import org.junit.Test
@ -23,7 +23,7 @@ internal class StakingBalancesStoreGetMethodTest {
private val store = DefaultStakeKitBalancesStore(
runtimeStore = runtimeStore,
persistenceStore = persistenceStore,
dispatchers = TestingCoroutineDispatcherProvider(),
scope = TestAppCoroutineScope(),
)
@Test

View file

@ -2,13 +2,13 @@ package com.tangem.data.staking.store
import androidx.datastore.core.DataStore
import com.google.common.truth.Truth
import com.tangem.common.test.TestAppCoroutineScope
import com.tangem.common.test.data.staking.MockYieldBalanceWrapperDTOFactory
import com.tangem.common.test.datastore.MockStateDataStore
import com.tangem.data.staking.toDomain
import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.flow.emptyFlow
@ -30,7 +30,7 @@ internal class StakingBalancesStoreInitializationTest {
DefaultStakeKitBalancesStore(
runtimeStore = runtimeStore,
persistenceStore = persistenceStore,
dispatchers = TestingCoroutineDispatcherProvider(),
scope = TestAppCoroutineScope(),
)
Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(null)
@ -44,7 +44,7 @@ internal class StakingBalancesStoreInitializationTest {
DefaultStakeKitBalancesStore(
runtimeStore = runtimeStore,
persistenceStore = persistenceStore,
dispatchers = TestingCoroutineDispatcherProvider(),
scope = TestAppCoroutineScope(),
)
Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(emptyMap<String, Set<StakingBalance>>())
@ -66,7 +66,7 @@ internal class StakingBalancesStoreInitializationTest {
DefaultStakeKitBalancesStore(
runtimeStore = runtimeStore,
persistenceStore = persistenceStore,
dispatchers = TestingCoroutineDispatcherProvider(),
scope = TestAppCoroutineScope(),
)
val expected = mapOf(userWalletId to setOf(wrapper.toDomain()))

View file

@ -1,6 +1,7 @@
package com.tangem.data.staking.store
import com.google.common.truth.Truth
import com.tangem.common.test.TestAppCoroutineScope
import com.tangem.common.test.data.staking.MockYieldBalanceWrapperDTOFactory
import com.tangem.common.test.datastore.MockStateDataStore
import com.tangem.data.staking.toDomain
@ -10,7 +11,6 @@ import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.staking.StakingID
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.test.runTest
import org.junit.Test
@ -26,7 +26,7 @@ internal class StakingBalancesStoreUpdateMethodsTest {
private val store = DefaultStakeKitBalancesStore(
runtimeStore = runtimeStore,
persistenceStore = persistenceStore,
dispatchers = TestingCoroutineDispatcherProvider(),
scope = TestAppCoroutineScope(),
)
@Test

View file

@ -2,6 +2,7 @@ package com.tangem.data.pay
import com.tangem.common.card.FirmwareVersion
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.isLocked
@ -9,7 +10,6 @@ import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.domain.pay.TangemPayEligibilityManager
import com.tangem.domain.pay.repository.OnboardingRepository
import com.tangem.hot.sdk.model.HotWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.sync.Mutex
@ -17,15 +17,14 @@ import kotlinx.coroutines.sync.withLock
import javax.inject.Inject
internal class DefaultTangemPayEligibilityManager @Inject constructor(
dispatchers: CoroutineDispatcherProvider,
private val userWalletsListRepository: UserWalletsListRepository,
private val coroutineScope: AppCoroutineScope,
private val onboardingRepository: OnboardingRepository,
) : TangemPayEligibilityManager {
private var cachedEligibleWallets: List<UserWalletData>? = null
private var eligibleWalletsDeferred: Deferred<List<UserWalletData>>? = null
private val loadMutex = Mutex()
private val coroutineScope = CoroutineScope(SupervisorJob() + dispatchers.default)
init {
resetDataWhenWalletsUpdate()

View file

@ -18,6 +18,7 @@ import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.datasource.local.visa.entity.PaymentAccountStatusDM
import com.tangem.datasource.utils.MoshiDataStoreSerializer
import com.tangem.datasource.utils.mapWithStringKeyTypes
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.pay.TangemPayCryptoCurrencyFactory
import com.tangem.domain.pay.TangemPayEligibilityManager
import com.tangem.domain.pay.flow.PaymentAccountStatusFetcher
@ -38,8 +39,6 @@ import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import javax.inject.Singleton
@Module
@ -112,6 +111,7 @@ internal interface TangemPayDataModule {
@NetworkMoshi moshi: Moshi,
@ApplicationContext context: Context,
dispatchers: CoroutineDispatcherProvider,
scope: AppCoroutineScope,
): PaymentAccountStatusesStore {
return PaymentAccountStatusesStore(
runtimeStore = RuntimeSharedStore(),
@ -122,9 +122,9 @@ internal interface TangemPayDataModule {
defaultValue = emptyMap(),
),
produceFile = { context.dataStoreFile(fileName = "payment_account_statuses") },
scope = CoroutineScope(context = dispatchers.io + SupervisorJob()),
scope = scope,
),
dispatchers = dispatchers,
scope = scope,
)
}

View file

@ -19,6 +19,7 @@ import com.tangem.datasource.api.pay.models.response.FreezeUnfreezeCardResponse
import com.tangem.datasource.api.pay.models.response.OrderResponse.Result.Status
import com.tangem.datasource.local.visa.TangemPayCardFrozenStateStore
import com.tangem.datasource.local.visa.TangemPayStorage
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.model.SetPinResult
import com.tangem.domain.pay.model.TangemPayCardBalance
@ -45,9 +46,9 @@ internal class DefaultTangemPayCardDetailsRepository @Inject constructor(
private val storage: TangemPayStorage,
private val cardFrozenStateStore: TangemPayCardFrozenStateStore,
private val errorConverter: TangemPayErrorConverter,
private val pollingScope: AppCoroutineScope,
) : TangemPayCardDetailsRepository {
private val pollingScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
private val pollingJobs = mutableMapOf<String, Job>()
private val storePollingMutex = Mutex()

View file

@ -9,6 +9,7 @@ import com.tangem.datasource.api.pay.models.request.WithdrawDataRequest
import com.tangem.datasource.api.pay.models.request.WithdrawRequest
import com.tangem.datasource.api.pay.models.response.WithdrawResponse
import com.tangem.datasource.local.visa.TangemPayStorage
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.pay.TangemPayWithdrawExchangeState
@ -46,9 +47,9 @@ internal class DefaultTangemPayWithdrawRepository @Inject constructor(
private val tangemPayStorage: TangemPayStorage,
private val swapRepository: SwapRepository,
private val orderRepository: CustomerOrderRepository,
private val withdrawPollingScope: AppCoroutineScope,
) : TangemPayWithdrawRepository {
private val withdrawPollingScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
private val withdrawPollingJobs = mutableMapOf<String, Job>()
private val withdrawPollingMutex = Mutex()

View file

@ -4,11 +4,9 @@ import androidx.datastore.core.DataStore
import com.tangem.data.pay.converter.PaymentAccountStatusDMConverter
import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.datasource.local.visa.entity.PaymentAccountStatusDM
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.PaymentAccountStatus
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.firstOrNull
@ -28,11 +26,9 @@ internal typealias WalletIdWithPaymentStatusDM = Map<String, PaymentAccountStatu
internal class PaymentAccountStatusesStore(
private val runtimeStore: RuntimeSharedStore<WalletIdWithPaymentStatus>,
private val persistenceDataStore: DataStore<WalletIdWithPaymentStatusDM>,
dispatchers: CoroutineDispatcherProvider,
private val scope: AppCoroutineScope,
) {
private val scope = CoroutineScope(context = SupervisorJob() + dispatchers.io)
init {
scope.launch {
try {

View file

@ -17,7 +17,6 @@ import com.tangem.data.walletconnect.respond.WcRespondService
import com.tangem.data.walletconnect.sessions.DefaultWcSessionsManager
import com.tangem.data.walletconnect.utils.WcNamespaceConverter
import com.tangem.data.walletconnect.utils.WcNetworksConverter
import com.tangem.data.walletconnect.utils.WcScope
import com.tangem.datasource.di.SdkMoshi
import com.tangem.datasource.local.walletconnect.WalletConnectStore
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
@ -34,6 +33,7 @@ import com.tangem.domain.walletconnect.usecase.initialize.WcInitializeUseCase
import com.tangem.domain.walletconnect.usecase.pair.WcPairUseCase
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
@ -80,8 +80,8 @@ internal object WalletConnectDataModule {
@Provides
@Singleton
fun sdkDelegate(wcScope: WcScope, store: WalletConnectStore): WcPairSdkDelegate = WcPairSdkDelegate(
scope = wcScope,
fun sdkDelegate(appScope: AppCoroutineScope, store: WalletConnectStore): WcPairSdkDelegate = WcPairSdkDelegate(
scope = appScope,
store = store,
)
@ -93,7 +93,7 @@ internal object WalletConnectDataModule {
getWallets: GetWalletsUseCase,
wcNetworksConverter: WcNetworksConverter,
analytics: AnalyticsEventHandler,
wcScope: WcScope,
appScope: AppCoroutineScope,
): DefaultWcSessionsManager {
return DefaultWcSessionsManager(
store = store,
@ -101,7 +101,7 @@ internal object WalletConnectDataModule {
getWallets = getWallets,
wcNetworksConverter = wcNetworksConverter,
analytics = analytics,
scope = wcScope,
scope = appScope,
)
}
@ -109,10 +109,6 @@ internal object WalletConnectDataModule {
@Singleton
fun wcSessionsManager(default: DefaultWcSessionsManager): WcSessionsManager = default
@Provides
@Singleton
fun wcScope(dispatchers: CoroutineDispatcherProvider): WcScope = WcScope(dispatchers)
@Provides
@Singleton
fun wcRequestService(default: DefaultWcRequestService): WcRequestService = default

View file

@ -6,13 +6,13 @@ import arrow.core.right
import com.reown.walletkit.client.Wallet
import com.reown.walletkit.client.WalletKit
import com.tangem.data.walletconnect.utils.WC_TAG
import com.tangem.data.walletconnect.utils.WcScope
import com.tangem.data.walletconnect.utils.WcSdkObserver
import com.tangem.datasource.local.walletconnect.WalletConnectStore
import com.tangem.data.walletconnect.utils.getDappOriginUrl
import com.tangem.domain.walletconnect.model.WcPairError
import com.tangem.domain.walletconnect.model.WcPairError.ApprovalFailed
import com.tangem.domain.walletconnect.model.WcPendingApprovalSessionDTO
import com.tangem.utils.coroutines.AppCoroutineScope
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.awaitClose
@ -22,7 +22,7 @@ import kotlin.coroutines.resume
import kotlin.time.Duration.Companion.seconds
internal class WcPairSdkDelegate(
private val scope: WcScope,
private val scope: AppCoroutineScope,
private val store: WalletConnectStore,
) : WcSdkObserver {

View file

@ -15,6 +15,7 @@ import com.tangem.domain.walletconnect.model.WcSession
import com.tangem.domain.walletconnect.model.WcSessionDTO
import com.tangem.domain.walletconnect.repository.WcSessionsManager
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.*
@ -32,7 +33,7 @@ internal class DefaultWcSessionsManager(
private val dispatchers: CoroutineDispatcherProvider,
private val wcNetworksConverter: WcNetworksConverter,
private val analytics: AnalyticsEventHandler,
private val scope: WcScope,
private val scope: AppCoroutineScope,
) : WcSessionsManager, WcSdkObserver {
private val onSessionDelete = Channel<Wallet.Model.SessionDelete>(capacity = Channel.BUFFERED)

View file

@ -1,13 +0,0 @@
package com.tangem.data.walletconnect.utils
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlin.coroutines.CoroutineContext
internal class WcScope(
dispatchers: CoroutineDispatcherProvider,
) : CoroutineScope {
override val coroutineContext: CoroutineContext = SupervisorJob() + dispatchers.io
}

View file

@ -2,6 +2,7 @@ package com.tangem.data.wallets.hot
import com.tangem.common.core.TangemSdkError
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.settings.repositories.LegacySettingsRepository
@ -11,10 +12,7 @@ import com.tangem.domain.wallets.repository.WalletsRepository
import com.tangem.hot.sdk.TangemHotSdk
import com.tangem.hot.sdk.exception.WrongPasswordException
import com.tangem.hot.sdk.model.*
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.runSuspendCatching
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
@ -27,11 +25,9 @@ class DefaultHotWalletAccessor @Inject constructor(
private val hotWalletPasswordRequester: HotWalletPasswordRequester,
private val walletsRepository: WalletsRepository,
private val legacySettingsRepository: LegacySettingsRepository,
dispatchers: CoroutineDispatcherProvider,
private val scope: AppCoroutineScope,
) : HotWalletAccessor {
private val scope = CoroutineScope(context = SupervisorJob() + dispatchers.io)
private val contextualUnlockHotWallet: ConcurrentHashMap<HotWalletId, UnlockHotWallet?> = ConcurrentHashMap()
override suspend fun signHashes(hotWalletId: HotWalletId, dataToSign: List<DataToSign>): List<SignedData> =

View file

@ -36,6 +36,7 @@ dependencies {
implementation(deps.kotlin.datetime)
implementation(deps.kotlin.serialization)
implementation(deps.timber)
implementation(deps.kermit)
implementation(tangemDeps.blockchain)

View file

@ -1,7 +1,6 @@
package com.tangem.domain.account.status.di
import com.tangem.domain.account.status.producer.*
import com.tangem.domain.core.flow.FlowProducerScope
import com.tangem.domain.core.flow.FlowProducerTools
import dagger.Binds
import dagger.Module
@ -28,8 +27,4 @@ internal interface AccountStatusListProducerFactoryModule {
@Binds
@Singleton
fun bindDefaultFlowProducerTools(impl: DefaultFlowProducerTools): FlowProducerTools
@Binds
@Singleton
fun bindFlowProducerScope(impl: DefaultFlowProducerAppScope): FlowProducerScope
}

View file

@ -8,6 +8,7 @@ import com.tangem.domain.account.status.utils.CryptoCurrencyBalanceFetcher
import com.tangem.domain.account.status.utils.CryptoCurrencyMetadataCleaner
import com.tangem.domain.account.supplier.SingleAccountListSupplier
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.express.ExpressServiceFetcher
import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher
import com.tangem.domain.networks.multi.MultiNetworkStatusSupplier
@ -26,8 +27,6 @@ import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import javax.inject.Singleton
@Module
@ -104,6 +103,7 @@ internal object AccountStatusUseCaseModule {
cryptoCurrencyMetadataCleaner: CryptoCurrencyMetadataCleaner,
expressServiceFetcher: ExpressServiceFetcher,
dispatchers: CoroutineDispatcherProvider,
appScope: AppCoroutineScope,
): ManageCryptoCurrenciesUseCase {
return ManageCryptoCurrenciesUseCase(
singleAccountStatusListSupplier = singleAccountStatusListSupplier,
@ -114,7 +114,7 @@ internal object AccountStatusUseCaseModule {
cryptoCurrencyBalanceFetcher = cryptoCurrencyBalanceFetcher,
cryptoCurrencyMetadataCleaner = cryptoCurrencyMetadataCleaner,
expressServiceFetcher = expressServiceFetcher,
parallelUpdatingScope = CoroutineScope(SupervisorJob() + dispatchers.default),
parallelUpdatingScope = appScope,
dispatchers = dispatchers,
)
}
@ -126,14 +126,14 @@ internal object AccountStatusUseCaseModule {
multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
multiStakingBalanceFetcher: MultiStakingBalanceFetcher,
stakingIdFactory: StakingIdFactory,
dispatchers: CoroutineDispatcherProvider,
appScope: AppCoroutineScope,
): CryptoCurrencyBalanceFetcher {
return CryptoCurrencyBalanceFetcher(
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
multiQuoteStatusFetcher = multiQuoteStatusFetcher,
multiStakingBalanceFetcher = multiStakingBalanceFetcher,
stakingIdFactory = stakingIdFactory,
parallelUpdatingScope = CoroutineScope(SupervisorJob() + dispatchers.default),
parallelUpdatingScope = appScope,
)
}

View file

@ -1,54 +1,19 @@
package com.tangem.domain.account.status.producer
import co.touchlab.kermit.Logger
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
import com.tangem.domain.core.flow.FlowProducer
import com.tangem.domain.core.flow.FlowProducerScope
import com.tangem.domain.core.flow.FlowProducerTools
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import timber.log.Timber
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
class DefaultFlowProducerAppScope @Inject constructor(
dispatchers: CoroutineDispatcherProvider,
private val analyticsExceptionHandler: AnalyticsExceptionHandler,
) : FlowProducerScope {
private val tag = "FlowProducerScope"
override val coroutineContext: CoroutineContext = SupervisorJob() +
dispatchers.default +
CoroutineName(tag) +
CoroutineExceptionHandler { context, throwable ->
@Suppress("NullableToStringCall")
val coroutineName = context[CoroutineName]?.name.toString()
logError(throwable, coroutineName)
}
private fun logError(throwable: Throwable, coroutineName: String) {
Timber.tag("FlowProducerExceptionHandler").e(
throwable,
"CoroutineName $coroutineName",
)
val event = ExceptionAnalyticsEvent(
exception = throwable,
params = mapOf(
"source" to tag,
"coroutineName" to coroutineName,
),
)
analyticsExceptionHandler.sendException(event)
}
}
class DefaultFlowProducerTools @Inject constructor(
private val scope: FlowProducerScope,
private val scope: AppCoroutineScope,
private val dispatchers: CoroutineDispatcherProvider,
private val analyticsExceptionHandler: AnalyticsExceptionHandler,
) : FlowProducerTools {
@ -72,6 +37,7 @@ class DefaultFlowProducerTools @Inject constructor(
}
return upstream
.flowOn(dispatchers.default)
.distinctUntilChanged()
.shareIn(
scope = scope,
@ -89,8 +55,8 @@ class DefaultFlowProducerTools @Inject constructor(
private fun logError(cause: Throwable, flowProducerName: String, attempt: Long) {
val tag = "FlowProducerRetryWhen"
Timber.tag(tag)
.e(cause, "flowProducerName $flowProducerName attempt $attempt")
Logger.withTag(tag)
.e("flowProducerName $flowProducerName attempt $attempt", cause)
val event = ExceptionAnalyticsEvent(
exception = cause,

View file

@ -8,6 +8,7 @@ import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.account.status.utils.CryptoCurrencyBalanceFetcher
import com.tangem.domain.account.status.utils.CryptoCurrencyMetadataCleaner
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.core.utils.eitherOn
import com.tangem.domain.express.ExpressServiceFetcher
import com.tangem.domain.express.models.ExpressAsset
@ -51,7 +52,7 @@ class ManageCryptoCurrenciesUseCase(
private val cryptoCurrencyBalanceFetcher: CryptoCurrencyBalanceFetcher,
private val cryptoCurrencyMetadataCleaner: CryptoCurrencyMetadataCleaner,
private val expressServiceFetcher: ExpressServiceFetcher,
private val parallelUpdatingScope: CoroutineScope,
private val parallelUpdatingScope: AppCoroutineScope,
private val dispatchers: CoroutineDispatcherProvider,
) {

View file

@ -1,7 +1,6 @@
package com.tangem.domain.core.flow
import arrow.core.Option
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharedFlow
@ -37,8 +36,6 @@ interface FlowProducer<Data> {
}
}
interface FlowProducerScope : CoroutineScope
interface FlowProducerTools {
fun <T> shareInProducer(flow: Flow<T>, flowProducer: FlowProducer<T>, withRetryWhen: Boolean = true): SharedFlow<T>

View file

@ -20,6 +20,7 @@ import com.tangem.domain.card.common.TapWorkarounds.isStart2Coin
import com.tangem.domain.card.common.TapWorkarounds.isTangemTwins
import com.tangem.domain.card.models.TwinKey
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.demo.DemoTransactionSender
import com.tangem.domain.demo.models.DemoConfig
import com.tangem.domain.models.network.Network
@ -31,7 +32,6 @@ import com.tangem.domain.transaction.error.SendTransactionError
import com.tangem.domain.transaction.error.parseWrappedError
import com.tangem.domain.transaction.models.EventTransactionTypeDto
import com.tangem.domain.walletmanager.WalletManagersFacade
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@ -43,7 +43,7 @@ class SendTransactionUseCase(
private val transactionRepository: TransactionRepository,
private val walletManagersFacade: WalletManagersFacade,
private val singleNetworkStatusFetcher: SingleNetworkStatusFetcher,
private val parallelUpdatingScope: CoroutineScope,
private val parallelUpdatingScope: AppCoroutineScope,
private val getHotWalletSigner: (UserWallet.Hot) -> TransactionSigner,
private val pushNotificationsRepository: PushNotificationsRepository,
) {

View file

@ -4,7 +4,7 @@ import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
import com.tangem.domain.yield.supply.YieldSupplyRepository
import kotlinx.coroutines.CoroutineScope
import com.tangem.utils.coroutines.AppCoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
@ -26,7 +26,7 @@ import java.util.concurrent.ConcurrentHashMap
class YieldSupplyPendingTracker(
private val yieldSupplyRepository: YieldSupplyRepository,
private val singleNetworkStatusFetcher: SingleNetworkStatusFetcher,
private val coroutineScope: CoroutineScope,
private val coroutineScope: AppCoroutineScope,
) {
private data class TrackedKey(

View file

@ -2,6 +2,7 @@ package com.tangem.domain.yield.supply.usecase
import arrow.core.Either
import com.google.common.truth.Truth.assertThat
import com.tangem.common.test.TestAppCoroutineScope
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWalletId
@ -36,7 +37,7 @@ class YieldSupplyPendingTrackerTest {
useCase = YieldSupplyPendingTracker(
yieldSupplyRepository = yieldSupplyRepository,
singleNetworkStatusFetcher = singleNetworkStatusFetcher,
coroutineScope = testScope,
coroutineScope = TestAppCoroutineScope(testScope),
)
}

View file

@ -1,17 +1,20 @@
package com.tangem.features.send.v2.common
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.nft.RefreshAllNFTUseCase
import com.tangem.domain.tokens.FetchPendingTransactionsUseCase
import com.tangem.domain.tokens.UpdateDelayedNetworkStatusUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.features.txhistory.entity.TxHistoryContentUpdateEmitter
import com.tangem.utils.coroutines.DelayedWork
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.*
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@Suppress("LongParameterList")
internal class SendBalanceUpdater @AssistedInject constructor(
@ -20,7 +23,7 @@ internal class SendBalanceUpdater @AssistedInject constructor(
private val getTxHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
private val txHistoryContentUpdateEmitter: TxHistoryContentUpdateEmitter,
private val refreshAllNFTUseCase: RefreshAllNFTUseCase,
@DelayedWork private val coroutineScope: CoroutineScope,
private val coroutineScope: AppCoroutineScope,
@Assisted private val userWallet: UserWallet,
@Assisted private val cryptoCurrency: CryptoCurrency,
) {

View file

@ -40,6 +40,7 @@ import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
@ -101,7 +102,6 @@ import com.tangem.utils.extensions.orZero
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
@ -151,7 +151,7 @@ internal class StakingModel @Inject constructor(
private val paramsInterceptorHolder: ParamsInterceptorHolder,
private val shareManager: ShareManager,
private val urlOpener: UrlOpener,
@DelayedWork private val coroutineScope: CoroutineScope,
private val coroutineScope: AppCoroutineScope,
private val innerRouter: InnerStakingRouter,
private val messageSender: UiMessageSender,
private val giveApprovalFeatureToggles: GiveApprovalFeatureToggles,

View file

@ -1,5 +1,6 @@
package com.tangem.features.staking.impl.presentation.state.helpers
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.staking.FetchActionsUseCase
@ -10,7 +11,6 @@ import com.tangem.domain.tokens.FetchCurrencyStatusUseCase
import com.tangem.domain.tokens.FetchPendingTransactionsUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
import com.tangem.features.txhistory.entity.TxHistoryContentUpdateEmitter
import com.tangem.utils.coroutines.DelayedWork
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@ -24,7 +24,7 @@ internal class StakingBalanceUpdater @AssistedInject constructor(
private val txHistoryContentUpdateEmitter: TxHistoryContentUpdateEmitter,
private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase,
private val fetchStakingYieldBalanceUseCase: FetchStakingYieldBalanceUseCase,
@DelayedWork private val coroutineScope: CoroutineScope,
private val coroutineScope: AppCoroutineScope,
@Assisted private val userWallet: UserWallet,
@Assisted private val cryptoCurrencyStatus: CryptoCurrencyStatus,
@Assisted private val integration: StakingIntegration,

View file

@ -2,12 +2,10 @@ package com.tangem.feature.walletsettings.utils
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.domain.account.usecase.ApplyAccountListSortingUseCase
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.*
import timber.log.Timber
import javax.inject.Inject
@ -27,15 +25,13 @@ import kotlin.time.Duration.Companion.seconds
internal class AccountListSortingSaver @Inject constructor(
private val applyAccountListSortingUseCase: ApplyAccountListSortingUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
dispatchers: CoroutineDispatcherProvider,
private val coroutineScope: AppCoroutineScope,
) {
/** Flow to hold the account IDs for sorting, with an initial null value. */
val accountsOrderFlow: StateFlow<List<AccountId>?>
private field = MutableStateFlow<List<AccountId>?>(value = null)
private val coroutineScope = CoroutineScope(SupervisorJob() + dispatchers.io)
init {
accountsOrderFlow
.filterNotNull()

View file

@ -21,6 +21,7 @@ import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.di.NetworkMoshi
import com.tangem.datasource.local.config.environment.EnvironmentConfig
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.libs.blockchain_sdk.BuildConfig
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
@ -28,8 +29,6 @@ import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import javax.inject.Singleton
@Module
@ -75,12 +74,12 @@ internal object BlockchainSDKFactoryModule {
fun provideChangedBlockchainProvidersResponseDataStore(
@NetworkMoshi moshi: Moshi,
@ApplicationContext context: Context,
dispatchers: CoroutineDispatcherProvider,
appScope: AppCoroutineScope,
): DataStore<BlockchainProvidersResponse> {
return DataStoreFactory.create(
serializer = BlockchainProvidersResponseSerializer(moshi),
produceFile = { context.dataStoreFile("changed_providers") },
scope = CoroutineScope(dispatchers.io + SupervisorJob()),
scope = appScope,
)
}