Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-20 15:31:57 +03:00
commit 47dc1c38df
762 changed files with 23960 additions and 10924 deletions

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

@ -41,7 +41,8 @@ interface TangemExpressApi {
@Query("fromNetwork") fromNetwork: String,
@Query("toContractAddress") toContractAddress: String,
@Query("toNetwork") toNetwork: String,
@Query("fromAmount") fromAmount: String,
@Query("fromAmount") fromAmount: String?,
@Query("toAmount") toAmount: String? = null,
@Query("fromDecimals") fromDecimals: Int,
@Query("toDecimals") toDecimals: Int,
@Query("providerId") providerId: String,
@ -57,7 +58,8 @@ interface TangemExpressApi {
@Query("toContractAddress") toContractAddress: String,
@Query("fromAddress") fromAddress: String,
@Query("toNetwork") toNetwork: String,
@Query("fromAmount") fromAmount: String,
@Query("fromAmount") fromAmount: String?,
@Query("toAmount") toAmount: String? = null,
@Query("fromDecimals") fromDecimals: Int,
@Query("toDecimals") toDecimals: Int,
@Query("providerId") providerId: String,

View file

@ -37,8 +37,8 @@ interface TangemPayApi {
@POST("v1/deeplink/validate")
suspend fun validateDeeplink(@Body body: DeeplinkValidityRequest): ApiResponse<DeeplinkValidityResponse>
@GET("v1/customer/eligibility")
suspend fun checkCustomerEligibility(): ApiResponse<CustomerEligibilityResponse>
@GET("v1/eligibility/channels")
suspend fun getEligibilityChannels(): ApiResponse<TangemPayEligibilityChannels>
@GET("v1/order/{order_id}")
suspend fun getOrder(

View file

@ -4,11 +4,11 @@ import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class CustomerEligibilityResponse(
@Json(name = "result") val result: Result?,
data class TangemPayEligibilityChannels(
@Json(name = "result") val result: Result,
) {
@JsonClass(generateAdapter = true)
data class Result(
@Json(name = "is_tangem_pay_available") val isTangemPayAvailable: Boolean,
@Json(name = "channels") val channels: List<String>,
)
}

View file

@ -5,6 +5,9 @@ import com.tangem.datasource.api.promotion.models.PromoBannerResponse
import com.tangem.datasource.api.promotion.models.PromoBannerV2Response
import com.tangem.datasource.api.promotion.models.StoryContentResponse
import com.tangem.datasource.api.tangemTech.models.*
import com.tangem.datasource.api.tangemTech.models.promobanners.PromoBannerDisplaysResponse
import com.tangem.datasource.api.tangemTech.models.promobanners.DismissPromoBannerRequest
import com.tangem.datasource.api.tangemTech.models.promobanners.DismissPromoBannerResponse
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
import com.tangem.datasource.api.tangemTech.models.account.GetWalletArchivedAccountsResponse
import com.tangem.datasource.api.tangemTech.models.account.SaveWalletAccountsResponse
@ -25,6 +28,7 @@ interface TangemTechApi {
suspend fun getCoins(
@Header("Cache-Control") cacheControl: String = "max-age=600",
@Query("contractAddress") contractAddress: String? = null,
@Query("contractAddresses") contractAddresses: String? = null,
@Query("exchangeable") exchangeable: Boolean? = null,
@Query("networkIds") networkIds: String? = null,
@Query("networkId") networkId: String? = null,
@ -42,15 +46,6 @@ interface TangemTechApi {
@GET("v1/geo")
suspend fun getUserCountryCode(): GeoResponse
@GET("v1/user-tokens/{user-id}")
suspend fun getUserTokens(@Path(value = "user-id") userId: String): ApiResponse<UserTokensResponse>
@PUT("v1/user-tokens/{user-id}")
suspend fun saveUserTokens(
@Path(value = "user-id") userId: String,
@Body userTokens: UserTokensResponse,
): ApiResponse<Unit>
@PUT("/v1/wallets/{walletId}/tokens")
suspend fun saveTokens(
@Path(value = "walletId") userId: String,
@ -135,14 +130,8 @@ interface TangemTechApi {
@PATCH("v1/user-wallets/wallets/{wallet_id}")
suspend fun updateWallet(@Path("wallet_id") walletId: String, @Body body: WalletBody): ApiResponse<Unit>
@POST("v1/user-wallets/wallets/create-and-connect-by-appuid/{application_id}")
suspend fun associateApplicationIdWithWallets(
@Path("application_id") applicationId: String,
@Body body: List<WalletIdBody>,
): ApiResponse<Unit>
@PUT("/v1/user-wallets/applications/{application_id}/wallets")
suspend fun associateApplicationIdWithWalletsV2(
suspend fun associateApplicationIdWithWallets(
@Path("application_id") applicationId: String,
@Body body: AssociateApplicationIdWithWalletsBody,
): ApiResponse<Unit>
@ -197,6 +186,21 @@ interface TangemTechApi {
): ApiResponse<PromoBannerV2Response>
// endregion
// region promo banners
@GET("v1/banner/displays")
suspend fun getPromoBannerDisplays(
@Query("walletId") walletId: String,
@Query("placeholder") placeholder: String,
@Query("locale") locale: String,
): ApiResponse<PromoBannerDisplaysResponse>
@PATCH("v1/displays/{displayId}")
suspend fun dismissPromoBannerDisplay(
@Path("displayId") displayId: String,
@Body body: DismissPromoBannerRequest,
): ApiResponse<DismissPromoBannerResponse>
// endregion
/**
* Stores transaction hash in cache to prevent duplicate push
* notifications for yield operations (deposit, withdraw, send).

View file

@ -0,0 +1,10 @@
package com.tangem.datasource.api.tangemTech.models.promobanners
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class DismissPromoBannerRequest(
@Json(name = "walletId") val walletId: String,
@Json(name = "isDismissed") val isDismissed: Boolean,
)

View file

@ -0,0 +1,12 @@
package com.tangem.datasource.api.tangemTech.models.promobanners
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class DismissPromoBannerResponse(
@Json(name = "displayId") val displayId: String,
@Json(name = "walletId") val walletId: String,
@Json(name = "isDismissed") val isDismissed: Boolean,
@Json(name = "dismissedAt") val dismissedAt: String?,
)

View file

@ -0,0 +1,24 @@
package com.tangem.datasource.api.tangemTech.models.promobanners
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class PromoBannerDisplaysResponse(
@Json(name = "items") val items: List<PromoBannerDisplayDTO>,
)
@Suppress("BooleanPropertyNaming")
@JsonClass(generateAdapter = true)
data class PromoBannerDisplayDTO(
@Json(name = "id") val id: String,
@Json(name = "placeholder") val placeholder: String,
@Json(name = "priority") val priority: String,
@Json(name = "title") val title: String,
@Json(name = "subtitle") val subtitle: String,
@Json(name = "iconUrl") val iconUrl: String?,
@Json(name = "deeplink") val deeplink: String?,
@Json(name = "buttonEnabled") val buttonEnabled: Boolean,
@Json(name = "buttonText") val buttonText: String?,
@Json(name = "dismissable") val dismissable: Boolean,
)

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,21 +0,0 @@
package com.tangem.datasource.di.local
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.token.DefaultUserTokensResponseStore
import com.tangem.datasource.local.token.UserTokensResponseStore
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal object LocalTokenModule {
@Provides
@Singleton
fun provideUserTokensResponseStore(appPreferencesStore: AppPreferencesStore): UserTokensResponseStore {
return DefaultUserTokensResponseStore(appPreferencesStore = appPreferencesStore)
}
}

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

@ -136,6 +136,8 @@ object PreferencesKeys {
val HAS_HAD_FIRST_TOP_UP_KEY by lazy { stringPreferencesKey(name = "hasHadFirstTopUp") }
val PENDING_DISCOVERY_SYNC_KEY by lazy { stringPreferencesKey(name = "pendingDiscoverySync") }
// region Notifications
val NOTIFICATIONS_APPLICATION_ID_KEY by lazy { stringPreferencesKey(name = "notificationsApplicationId") }
@ -161,7 +163,7 @@ object PreferencesKeys {
val TANGEM_PAY_ACTIVE_WITHDRAW_ORDERS_KEY by lazy {
stringPreferencesKey(name = "tangemPayActiveWithdrawOrdersKey")
}
val TANGEM_PAY_ELIGIBILITY_KEY by lazy { booleanPreferencesKey(name = "tangemPayEligibility") }
val TANGEM_PAY_ELIGIBILITY_KEY by lazy { stringSetPreferencesKey(name = "tangemPayEligibilityList") }
fun getShouldShowNotificationKey(key: String) = booleanPreferencesKey("showShowNotificationUM_$key")
// endregion

View file

@ -1,51 +0,0 @@
package com.tangem.datasource.local.token
import androidx.datastore.preferences.core.stringPreferencesKey
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.utils.getObject
import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull
import com.tangem.datasource.local.preferences.utils.storeObject
import com.tangem.domain.models.wallet.UserWalletId
import kotlinx.coroutines.flow.Flow
/**
* Default implementation of [UserTokensResponseStore]
*
* @property appPreferencesStore app preferences store
*
[REDACTED_AUTHOR]
*/
internal class DefaultUserTokensResponseStore(
private val appPreferencesStore: AppPreferencesStore,
) : UserTokensResponseStore {
override fun get(userWalletId: UserWalletId): Flow<UserTokensResponse?> {
return appPreferencesStore.getObject<UserTokensResponse>(
key = createPreferencesKey(userWalletId = userWalletId.stringValue),
)
}
override suspend fun getSyncOrNull(userWalletId: UserWalletId): UserTokensResponse? {
return appPreferencesStore.getObjectSyncOrNull<UserTokensResponse>(
key = createPreferencesKey(userWalletId = userWalletId.stringValue),
)
}
override suspend fun store(userWalletId: UserWalletId, response: UserTokensResponse) {
appPreferencesStore.storeObject(
key = createPreferencesKey(userWalletId = userWalletId.stringValue),
value = response,
)
}
override suspend fun clear(userWalletId: UserWalletId) {
appPreferencesStore.updateData { preferences ->
val key = createPreferencesKey(userWalletId = userWalletId.stringValue)
preferences.toMutablePreferences().apply { remove(key) }
}
}
private fun createPreferencesKey(userWalletId: String) = stringPreferencesKey(name = "user_tokens_$userWalletId")
}

View file

@ -1,22 +0,0 @@
package com.tangem.datasource.local.token
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
import com.tangem.domain.models.wallet.UserWalletId
import kotlinx.coroutines.flow.Flow
/**
* Store of [UserTokensResponse]
*
[REDACTED_AUTHOR]
*/
interface UserTokensResponseStore {
fun get(userWalletId: UserWalletId): Flow<UserTokensResponse?>
/** Get [UserTokensResponse] synchronously by [userWalletId] or null */
suspend fun getSyncOrNull(userWalletId: UserWalletId): UserTokensResponse?
suspend fun store(userWalletId: UserWalletId, response: UserTokensResponse)
suspend fun clear(userWalletId: UserWalletId)
}

View file

@ -52,8 +52,8 @@ interface TangemPayStorage {
suspend fun getHideMainOnboardingBanner(userWalletId: UserWalletId): Boolean
suspend fun storeHideOnboardingBanner(userWalletId: UserWalletId, hide: Boolean)
suspend fun storeTangemPayEligibility(eligibility: Boolean)
suspend fun getTangemPayEligibility(): Boolean
suspend fun storeTangemPayEligibility(eligibility: Set<String>)
suspend fun getTangemPayEligibility(): Set<String>
suspend fun clearAll(userWalletId: UserWalletId, customerWalletAddress: String)
}