Updated on 2026-08-14

This commit is contained in:
Tangem 2026-05-21 17:59:40 +05:00
commit a22e77ebe8
669 changed files with 27695 additions and 7905 deletions

View file

@ -1,24 +0,0 @@
package com.tangem.datasource.api.promotion.models
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class PromoBannerResponse(
@Json(name = "name") val name: String,
@Json(name = "all") val bannerState: BannerState?,
) {
@JsonClass(generateAdapter = true)
data class BannerState(
@Json(name = "timeline") val timeline: Timeline,
@Json(name = "status") val status: String,
@Json(name = "link") val link: String?,
)
@JsonClass(generateAdapter = true)
data class Timeline(
@Json(name = "start") val start: String,
@Json(name = "end") val end: String,
)
}

View file

@ -1,10 +0,0 @@
package com.tangem.datasource.api.promotion.models
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class PromoBannerV2Response(
@Json(name = "promotions")
val promotions: List<PromoBannerResponse>,
)

View file

@ -150,6 +150,8 @@ data class YieldDTO(
data class PeriodDTO(
@Json(name = "days")
val days: Int?,
@Json(name = "seconds")
val seconds: Int?,
)
@JsonClass(generateAdapter = true)

View file

@ -1,4 +1,4 @@
package com.tangem.datasource.api.promotion.models
package com.tangem.datasource.api.stories.models
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass

View file

@ -1,9 +1,7 @@
package com.tangem.datasource.api.tangemTech
import com.tangem.datasource.api.common.response.ApiResponse
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.stories.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
@ -52,6 +50,17 @@ interface TangemTechApi {
@Body userTokens: UserTokensResponse,
): ApiResponse<Unit>
@GET("/v1/wallets/{wallet_id}/notification-preferences")
suspend fun getPushNotificationPreferences(
@Path("wallet_id") walletId: String,
): ApiResponse<PushNotificationPreferencesResponse>
@PUT("/v1/wallets/{wallet_id}/notification-preferences")
suspend fun updatePushNotificationPreferences(
@Path("wallet_id") walletId: String,
@Body body: PushNotificationPreferencesBody,
): ApiResponse<Unit>
// region Referral
/** Returns referral status by [walletId] */
@GET("v1/referral/{walletId}")
@ -172,20 +181,6 @@ interface TangemTechApi {
): ApiResponse<GetWalletArchivedAccountsResponse>
// endregion
// region promo banners
@GET("/v1/promotion")
suspend fun getPromoBanner(
@Query("programName") name: String,
@Header("Cache-Control") cacheControl: String = "max-age=600",
): ApiResponse<PromoBannerResponse>
@GET("/v2/promotion")
suspend fun getPromoBannersV2(
@Query("walletId") walletId: String,
@Header("Cache-Control") cacheControl: String = "max-age=600",
): ApiResponse<PromoBannerV2Response>
// endregion
// region promo banners
@GET("v1/banner/displays")
suspend fun getPromoBannerDisplays(

View file

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

View file

@ -0,0 +1,14 @@
package com.tangem.datasource.api.tangemTech.models
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class PushNotificationPreferencesBody(
@Json(name = "transactionAlerts")
val areTransactionAlertsEnabled: Boolean,
@Json(name = "offersUpdates")
val areOffersUpdatesEnabled: Boolean,
@Json(name = "priceAlerts")
val arePriceAlertsEnabled: Boolean,
)

View file

@ -0,0 +1,11 @@
package com.tangem.datasource.api.tangemTech.models
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class PushNotificationPreferencesResponse(
@Json(name = "transactionAlerts") val transactionAlerts: PushNotificationPreferenceState,
@Json(name = "offersUpdates") val offersUpdates: PushNotificationPreferenceState,
@Json(name = "priceAlerts") val priceAlerts: PushNotificationPreferenceState,
)

View file

@ -11,10 +11,8 @@ import com.tangem.datasource.local.onramp.paymentmethods.DefaultOnrampPaymentMet
import com.tangem.datasource.local.onramp.paymentmethods.OnrampPaymentMethodsStore
import com.tangem.datasource.local.onramp.quotes.DefaultOnrampQuotesStore
import com.tangem.datasource.local.onramp.quotes.OnrampQuotesStore
import com.tangem.datasource.local.onramp.sepa.DefaultOnrampCurrentCountryByIPStore
import com.tangem.datasource.local.onramp.sepa.DefaultOnrampSepaAvailabilityStore
import com.tangem.datasource.local.onramp.sepa.OnrampCurrentCountryByIPStore
import com.tangem.datasource.local.onramp.sepa.OnrampSepaAvailabilityStore
import com.tangem.datasource.local.onramp.country.DefaultOnrampCurrentCountryByIPStore
import com.tangem.datasource.local.onramp.country.OnrampCurrentCountryByIPStore
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -55,12 +53,6 @@ internal object OnrampStoreModule {
return DefaultOnrampCurrenciesStore(dataStore = RuntimeDataStore())
}
@Provides
@Singleton
fun provideOnrampSepaAvailableStore(): OnrampSepaAvailabilityStore {
return DefaultOnrampSepaAvailabilityStore(dataStore = RuntimeDataStore())
}
@Provides
@Singleton
fun provideOnrampCurrentCountryByIPStore(): OnrampCurrentCountryByIPStore {

View file

@ -1,30 +0,0 @@
package com.tangem.datasource.di
import com.tangem.datasource.local.datastore.RuntimeDataStore
import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.datasource.local.promo.DefaultPromoBannerStore
import com.tangem.datasource.local.promo.DefaultPromoStoriesStore
import com.tangem.datasource.local.promo.PromoBannerStore
import com.tangem.datasource.local.promo.PromoStoriesStore
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object PromoStoreModule {
@Provides
@Singleton
fun providePromoStoriesStore(): PromoStoriesStore {
return DefaultPromoStoriesStore(dataStore = RuntimeDataStore())
}
@Provides
@Singleton
fun providePromoBannerStore(): PromoBannerStore {
return DefaultPromoBannerStore(dataStore = RuntimeSharedStore())
}
}

View file

@ -0,0 +1,21 @@
package com.tangem.datasource.di
import com.tangem.datasource.local.datastore.RuntimeDataStore
import com.tangem.datasource.local.stories.DefaultStoriesStore
import com.tangem.datasource.local.stories.StoriesStore
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object StoriesStoreModule {
@Provides
@Singleton
fun provideStoriesStore(): StoriesStore {
return DefaultStoriesStore(dataStore = RuntimeDataStore())
}
}

View file

@ -13,4 +13,19 @@ interface AppsFlyerStore {
suspend fun storeIfAbsent(value: AppsFlyerConversionData)
suspend fun storeUIDIfAbsent(value: String)
suspend fun getDeeplink(source: AppsFlyerDeeplinkSource): String?
suspend fun storeDeeplink(source: AppsFlyerDeeplinkSource, deeplink: String)
suspend fun clearDeeplink(source: AppsFlyerDeeplinkSource)
}
enum class AppsFlyerDeeplinkSource {
TangemPayHotWalletOnboarding,
;
fun toStoreKey() = when (this) {
TangemPayHotWalletOnboarding -> "tangem_pay_hot_wallet_onboarding"
}
}

View file

@ -56,8 +56,22 @@ internal class DefaultAppsFlyerStore(
}
}
private companion object {
override suspend fun getDeeplink(source: AppsFlyerDeeplinkSource): String? =
appPreferencesStore.getSyncOrNull(stringPreferencesKey(source.toStoreKey()))
override suspend fun storeDeeplink(source: AppsFlyerDeeplinkSource, deeplink: String) {
appPreferencesStore.editData { preferences ->
preferences[stringPreferencesKey(source.toStoreKey())] = deeplink
}
}
override suspend fun clearDeeplink(source: AppsFlyerDeeplinkSource) {
appPreferencesStore.editData { preferences ->
preferences.remove(stringPreferencesKey(source.toStoreKey()))
}
}
private companion object {
val UID_KEY = stringPreferencesKey("APPS_FLYER_UID")
val CONVERSION_DATA_KEY = stringPreferencesKey("APPS_FLYER_CONVERSION_DATA")
}

View file

@ -1,4 +1,4 @@
package com.tangem.datasource.local.onramp.sepa
package com.tangem.datasource.local.onramp.country
import com.tangem.datasource.local.datastore.core.StringKeyDataStore
import com.tangem.datasource.local.datastore.core.StringKeyDataStoreDecorator

View file

@ -1,4 +1,4 @@
package com.tangem.datasource.local.onramp.sepa
package com.tangem.datasource.local.onramp.country
import com.tangem.domain.onramp.model.OnrampCountry

View file

@ -1,20 +0,0 @@
package com.tangem.datasource.local.onramp.sepa
import com.tangem.datasource.local.datastore.core.StringKeyDataStore
import com.tangem.datasource.local.datastore.core.StringKeyDataStoreDecorator
internal class DefaultOnrampSepaAvailabilityStore(
val dataStore: StringKeyDataStore<Boolean>,
) : OnrampSepaAvailabilityStore, StringKeyDataStoreDecorator<OnrampSepaAvailabilityStoreKey, Boolean>(
wrappedDataStore = dataStore,
) {
override fun provideStringKey(key: OnrampSepaAvailabilityStoreKey) = with(key) {
buildString {
append(userWallet.walletId.toString())
append("_")
append(country.code)
append("_")
append(cryptoCurrency.id.value)
}
}
}

View file

@ -1,10 +0,0 @@
package com.tangem.datasource.local.onramp.sepa
import kotlinx.coroutines.flow.Flow
interface OnrampSepaAvailabilityStore {
suspend fun getSyncOrNull(key: OnrampSepaAvailabilityStoreKey): Boolean?
fun get(key: OnrampSepaAvailabilityStoreKey): Flow<Boolean>
suspend fun store(key: OnrampSepaAvailabilityStoreKey, value: Boolean)
suspend fun clear()
}

View file

@ -1,11 +0,0 @@
package com.tangem.datasource.local.onramp.sepa
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.onramp.model.OnrampCountry
data class OnrampSepaAvailabilityStoreKey(
val userWallet: UserWallet,
val country: OnrampCountry,
val cryptoCurrency: CryptoCurrency,
)

View file

@ -11,9 +11,6 @@ import androidx.datastore.preferences.core.emptyPreferences
import androidx.datastore.preferences.preferencesDataStoreFile
import com.tangem.datasource.local.preferences.PreferencesDataStore.INSTANCE
import com.tangem.datasource.local.preferences.PreferencesKeys.APP_LOGS_KEY
import com.tangem.datasource.local.preferences.PreferencesKeys.IS_TOKEN_SWAP_PROMO_OKX_SHOW_KEY
import com.tangem.datasource.local.preferences.PreferencesKeys.IS_WALLET_SWAP_PROMO_OKX_SHOW_KEY
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 com.tangem.datasource.local.preferences.utils.SwapCurrencyIdMigration
@ -83,9 +80,6 @@ internal object PreferencesDataStore {
),
SwapCurrencyIdMigration(),
CleanupKeyMigration(key = APP_LOGS_KEY),
CleanupKeyMigration(key = IS_WALLET_SWAP_PROMO_OKX_SHOW_KEY),
CleanupKeyMigration(key = IS_TOKEN_SWAP_PROMO_OKX_SHOW_KEY),
CleanupKeyMigration(key = SHOULD_SHOW_RING_PROMO_KEY),
)
}
}

View file

@ -59,6 +59,8 @@ object PreferencesKeys {
val LAST_SWAPPED_CRYPTOCURRENCY_ID_KEY by lazy { stringPreferencesKey(name = "lastSwappedCryptoCurrency") }
val SWAP_UI_MODE_KEY by lazy { stringPreferencesKey(name = "swapUiMode") }
val WAS_TWINS_ONBOARDING_SHOWN by lazy { booleanPreferencesKey(name = "twinsOnboardingShown") }
val IS_TANGEM_TOS_ACCEPTED_KEY by lazy { booleanPreferencesKey(name = "tangem_tos_accepted") }
@ -85,23 +87,10 @@ object PreferencesKeys {
val UNSUBMITTED_TRANSACTIONS_KEY by lazy { stringPreferencesKey(name = "unsubmittedTransactions") }
@Deprecated("Remove after CleanupKeyMigration")
val IS_WALLET_SWAP_PROMO_OKX_SHOW_KEY by lazy {
booleanPreferencesKey(name = "isWalletSwapPromoOkxShown")
}
@Deprecated("Remove after CleanupKeyMigration")
val IS_TOKEN_SWAP_PROMO_OKX_SHOW_KEY by lazy {
booleanPreferencesKey(name = "isTokenSwapPromoOkxShown")
}
val apiConfigsEnvironmentKey by lazy { stringPreferencesKey(name = "apiConfigsEnvironment") }
val ADDED_WALLETS_WITH_RING_KEY by lazy { stringSetPreferencesKey(name = "addedWalletsWithRing") }
@Deprecated("Remove after CleanupKeyMigration")
val SHOULD_SHOW_RING_PROMO_KEY by lazy { booleanPreferencesKey(name = "shouldShowRingPromo") }
val ONRAMP_DEFAULT_COUNTRY by lazy { stringPreferencesKey(name = "onrampDefaultCountry") }
val ONRAMP_TRANSACTIONS_STATUSES_KEY by lazy { stringPreferencesKey(name = "onrampTransactionsStatuses") }
@ -170,8 +159,6 @@ object PreferencesKeys {
// region Promo
fun getShouldShowStoriesKey(storyId: String) = booleanPreferencesKey("shouldShowStories_$storyId")
fun getShouldShowPromoKey(promoId: String) = booleanPreferencesKey("shouldShowPromo_$promoId")
// endregion
// region Permission

View file

@ -1,19 +0,0 @@
package com.tangem.datasource.local.promo
import com.tangem.datasource.api.promotion.models.PromoBannerResponse
import com.tangem.datasource.local.datastore.RuntimeSharedStore
internal class DefaultPromoBannerStore(
private val dataStore: RuntimeSharedStore<Map<String, PromoBannerResponse>>,
) : PromoBannerStore {
override suspend fun getSyncOrNull(promoId: String): PromoBannerResponse? {
return dataStore.getSyncOrNull()?.get(promoId)
}
override suspend fun store(promoId: String, promoBanner: PromoBannerResponse) {
dataStore.update(emptyMap()) { current ->
current + (promoId to promoBanner)
}
}
}

View file

@ -1,10 +0,0 @@
package com.tangem.datasource.local.promo
import com.tangem.datasource.api.promotion.models.PromoBannerResponse
interface PromoBannerStore {
suspend fun getSyncOrNull(promoId: String): PromoBannerResponse?
suspend fun store(promoId: String, promoBanner: PromoBannerResponse)
}

View file

@ -1,12 +1,12 @@
package com.tangem.datasource.local.promo
package com.tangem.datasource.local.stories
import com.tangem.datasource.api.promotion.models.StoryContentResponse
import com.tangem.datasource.api.stories.models.StoryContentResponse
import com.tangem.datasource.local.datastore.core.StringKeyDataStore
import kotlinx.coroutines.flow.Flow
internal class DefaultPromoStoriesStore(
internal class DefaultStoriesStore(
private val dataStore: StringKeyDataStore<StoryContentResponse>,
) : PromoStoriesStore {
) : StoriesStore {
override suspend fun getSyncOrNull(storyId: String): StoryContentResponse? {
return dataStore.getSyncOrNull(storyId)
}

View file

@ -1,9 +1,9 @@
package com.tangem.datasource.local.promo
package com.tangem.datasource.local.stories
import com.tangem.datasource.api.promotion.models.StoryContentResponse
import com.tangem.datasource.api.stories.models.StoryContentResponse
import kotlinx.coroutines.flow.Flow
interface PromoStoriesStore {
interface StoriesStore {
suspend fun getSyncOrNull(storyId: String): StoryContentResponse?
fun get(storyId: String): Flow<StoryContentResponse?>

View file

@ -59,6 +59,7 @@ sealed interface PaymentAccountStatusValueDM {
data class DeactivatedAccount(
@Json(name = "deactivated_account") val marker: Boolean = true,
@Json(name = "fiat_balance") val fiatBalance: FiatBalanceDM,
@Json(name = "crypto_balance") val cryptoBalance: CryptoBalanceDM,
) : PaymentAccountStatusValueDM
@JsonClass(generateAdapter = true)