Updated on 2026-08-14

This commit is contained in:
Tangem 2025-03-13 19:37:45 +03:00
commit 0cf428b94a
706 changed files with 12779 additions and 5939 deletions

View file

@ -1,6 +0,0 @@
package com.tangem.datasource.api.common.visa
interface TangemVisaAuthProvider {
suspend fun getAuthHeader(cardId: String): String
}

View file

@ -2,6 +2,7 @@ package com.tangem.datasource.api.express.models.response
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import org.joda.time.DateTime
@JsonClass(generateAdapter = true)
data class ExchangeStatusResponse(
@ -26,6 +27,12 @@ data class ExchangeStatusResponse(
@Json(name = "refundContractAddress")
val refundContractAddress: String? = null,
@Json(name = "createdAt")
val createdAt: DateTime? = null,
@Json(name = "averageDuration")
val averageDuration: Int? = null,
)
@JsonClass(generateAdapter = false)

View file

@ -5,9 +5,7 @@ import com.tangem.datasource.api.utils.ReadTimeout
import com.tangem.datasource.api.visa.models.request.ActivationByCardWalletRequest
import com.tangem.datasource.api.visa.models.request.ActivationByCustomerWalletRequest
import com.tangem.datasource.api.visa.models.request.SetPinCodeRequest
import com.tangem.datasource.api.visa.models.response.CardActivationRemoteStateResponse
import com.tangem.datasource.api.visa.models.response.CardWalletDataToSignResponse
import com.tangem.datasource.api.visa.models.response.CustomerWalletDataToSignResponse
import com.tangem.datasource.api.visa.models.response.*
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.Header
@ -71,4 +69,19 @@ interface TangemVisaApi {
@Header("Authorization") authHeader: String,
@Body body: SetPinCodeRequest,
): ApiResponse<Unit>
@GET("customer/info")
suspend fun getCustomerInfo(
@Header("Authorization") authHeader: String,
@Query("card_id") cardId: String,
): ApiResponse<VisaCustomerInfo>
@GET("product_instance/transactions")
suspend fun getTxHistory(
@Header("Authorization") authHeader: String,
@Query("customer_id") customerId: String,
@Query("product_instance_id") productInstanceId: String,
@Query("limit") limit: Int,
@Query("offset") offset: Int,
): ApiResponse<VisaTxHistoryResponse>
}

View file

@ -1,5 +1,6 @@
package com.tangem.datasource.api.visa
import com.tangem.datasource.api.common.response.ApiResponse
import com.tangem.datasource.api.visa.models.response.GenerateNonceResponse
import com.tangem.datasource.api.visa.models.response.JWTResponse
import retrofit2.http.Field
@ -27,5 +28,5 @@ interface TangemVisaAuthApi {
): JWTResponse
@POST("auth/refresh_token")
suspend fun refreshAccessToken(@Field("refresh_token") refreshToken: String): JWTResponse
suspend fun refreshAccessToken(@Field("refresh_token") refreshToken: String): ApiResponse<JWTResponse>
}

View file

@ -0,0 +1,15 @@
package com.tangem.datasource.api.visa.models.response
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class VisaCustomerInfo(
@Json(name = "payment_accounts") val paymentAccounts: List<PaymentAccount>,
) {
data class PaymentAccount(
@Json(name = "id") val id: String,
@Json(name = "customer_wallet_address") val customerWalletAddress: String,
@Json(name = "payment_account_address") val paymentAccountAddress: String,
)
}

View file

@ -0,0 +1,88 @@
package com.tangem.datasource.api.visa.models.response
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import org.joda.time.DateTime
import java.math.BigDecimal
@JsonClass(generateAdapter = true)
data class VisaTxHistoryResponse(
@Json(name = "card_wallet_address")
val cardWalletAddress: String,
@Json(name = "transactions")
val transactions: List<Transaction>,
) {
@JsonClass(generateAdapter = true)
data class Transaction(
@Json(name = "auth_code")
val authCode: String?,
@Json(name = "billing_amount")
val billingAmount: BigDecimal,
@Json(name = "billing_currency_code")
val billingCurrencyCode: Int,
@Json(name = "blockchain_amount")
val blockchainAmount: BigDecimal,
@Json(name = "blockchain_coin_name")
val blockchainCoinName: String,
@Json(name = "blockchain_fee")
val blockchainFee: BigDecimal,
// @Json(name = "local_dt")
// val localDate: DateTime?,
@Json(name = "merchant_category_code")
val merchantCategoryCode: String?,
@Json(name = "merchant_city")
val merchantCity: String?,
@Json(name = "merchant_country_code")
val merchantCountryCode: String?,
@Json(name = "merchant_name")
val merchantName: String?,
@Json(name = "requests")
val requests: List<Request>,
@Json(name = "rrn")
val rrn: String?,
@Json(name = "transaction_amount")
val transactionAmount: BigDecimal,
@Json(name = "transaction_currency_code")
val transactionCurrencyCode: Int,
@Json(name = "transaction_dt")
val transactionDt: DateTime,
@Json(name = "transaction_id")
val transactionId: Long,
@Json(name = "transaction_status")
val transactionStatus: String,
@Json(name = "transaction_type")
val transactionType: String,
) {
@JsonClass(generateAdapter = true)
data class Request(
@Json(name = "billing_amount")
val billingAmount: BigDecimal,
@Json(name = "billing_currency_code")
val billingCurrencyCode: Int,
@Json(name = "blockchain_amount")
val blockchainAmount: BigDecimal,
@Json(name = "blockchain_fee")
val blockchainFee: BigDecimal,
@Json(name = "error_code")
val errorCode: Int,
@Json(name = "request_dt")
val requestDt: DateTime,
@Json(name = "request_status")
val requestStatus: String,
@Json(name = "request_type")
val requestType: String,
@Json(name = "transaction_amount")
val transactionAmount: BigDecimal,
@Json(name = "transaction_currency_code")
val transactionCurrencyCode: Int,
@Json(name = "transaction_request_id")
val transactionRequestId: Long,
@Json(name = "tx_hash")
val txHash: String?,
@Json(name = "tx_status")
val txStatus: String?,
)
}
}

View file

@ -3,6 +3,8 @@ package com.tangem.datasource.di
import com.squareup.moshi.Moshi
import com.squareup.moshi.adapters.PolymorphicJsonAdapterFactory
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import com.tangem.blockchain.nft.models.NFTAsset
import com.tangem.blockchain.nft.models.NFTCollection
import com.tangem.common.json.MoshiJsonConverter
import com.tangem.datasource.api.common.adapter.BigDecimalAdapter
import com.tangem.datasource.api.common.adapter.DateTimeAdapter
@ -45,6 +47,18 @@ class MoshiModule {
.withSubtype(NetworkStatusDM.Verified::class.java, "amounts")
.withSubtype(NetworkStatusDM.NoAccount::class.java, "amount_to_create_account"),
)
.add(
PolymorphicJsonAdapterFactory.of(NFTCollection.Identifier::class.java, "bc")
.withSubtype(NFTCollection.Identifier.EVM::class.java, "evm")
.withSubtype(NFTCollection.Identifier.TON::class.java, "ton")
.withDefaultValue(NFTCollection.Identifier.Unknown),
)
.add(
PolymorphicJsonAdapterFactory.of(NFTAsset.Identifier::class.java, "bc")
.withSubtype(NFTAsset.Identifier.EVM::class.java, "evm")
.withSubtype(NFTAsset.Identifier.TON::class.java, "ton")
.withDefaultValue(NFTAsset.Identifier.Unknown),
)
.addLast(KotlinJsonAdapterFactory())
.addStakeKitEnumFallbackAdapters()
.build()

View file

@ -36,6 +36,8 @@ class EnvironmentConfigModel(
@Json(name = "bittensorOnfinalityKey") val bittensorOnfinalityKey: String?,
@Json(name = "koinosProApiKey") val koinosProApiKey: String?,
@Json(name = "alephiumTangemApiKey") val alephiumTangemApiKey: String?,
@Json(name = "moralisApiKey") val moralisApiKey: String?,
@Json(name = "nftScanApiKey") val nftScanApiKey: String?,
)
@JsonClass(generateAdapter = true)

View file

@ -0,0 +1,49 @@
package com.tangem.datasource.local.nft
import androidx.datastore.core.DataStore
import com.tangem.blockchain.nft.models.NFTAsset
import com.tangem.blockchain.nft.models.NFTCollection
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map
internal class DefaultNFTPersistenceStore(
private val collectionsPersistenceStore: DataStore<List<NFTCollection>>,
private val pricesPersistenceStore: DataStore<Map<NFTAsset.Identifier, NFTAsset.SalePrice>>,
) : NFTPersistenceStore {
override fun getCollections(): Flow<List<NFTCollection>> = collectionsPersistenceStore.data
override suspend fun getCollectionsSync(): List<NFTCollection>? = collectionsPersistenceStore
.data
.firstOrNull()
override fun getAsset(collectionId: NFTCollection.Identifier, assetId: NFTAsset.Identifier): Flow<NFTAsset?> =
collectionsPersistenceStore.data
.map { collection ->
collection
.firstOrNull { it.identifier == collectionId }
?.getAsset(assetId)
}
override fun getSalePrice(assetId: NFTAsset.Identifier): Flow<NFTAsset.SalePrice?> = pricesPersistenceStore.data
.map { it[assetId] }
override suspend fun getSalePricesSync(): Map<NFTAsset.Identifier, NFTAsset.SalePrice>? = pricesPersistenceStore
.data
.firstOrNull()
override suspend fun saveCollections(collections: List<NFTCollection>) {
collectionsPersistenceStore.updateData {
collections
}
}
override suspend fun saveSalePrice(assetId: NFTAsset.Identifier, salePrice: NFTAsset.SalePrice) {
pricesPersistenceStore.updateData {
it.toMutableMap().apply { this[assetId] = salePrice }
}
}
private fun NFTCollection.getAsset(assetId: NFTAsset.Identifier) = assets.firstOrNull { it.identifier == assetId }
}

View file

@ -0,0 +1,106 @@
package com.tangem.datasource.local.nft
import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.domain.models.StatusSource
import com.tangem.domain.nft.models.NFTAsset
import com.tangem.domain.nft.models.NFTCollection
import com.tangem.domain.nft.models.NFTCollections
import com.tangem.domain.nft.models.NFTSalePrice
import com.tangem.domain.tokens.model.Network
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
internal class DefaultNFTRuntimeStore(
private val network: Network,
private val collectionsRuntimeStore: RuntimeSharedStore<NFTCollections>,
private val pricesRuntimeStore: RuntimeSharedStore<Map<NFTAsset.Identifier, NFTSalePrice>>,
) : NFTRuntimeStore {
override suspend fun initialize(collections: NFTCollections, prices: Map<NFTAsset.Identifier, NFTSalePrice>) {
collectionsRuntimeStore.store(collections)
pricesRuntimeStore.store(prices)
}
override fun getCollections(): Flow<NFTCollections> = collectionsRuntimeStore
.get()
.combine(pricesRuntimeStore.get()) { collectionsData, prices ->
collectionsData.mergeWithPrices(prices)
}
override suspend fun getCollectionsSync(): NFTCollections {
val collectionsData = collectionsRuntimeStore.getSyncOrNull()
val prices = pricesRuntimeStore.getSyncOrNull()
return collectionsData
?.mergeWithPrices(prices.orEmpty())
?: NFTCollections(
network = network,
content = NFTCollections.Content.Collections(
collections = null,
source = StatusSource.CACHE,
),
)
}
override fun getAsset(collectionId: NFTCollection.Identifier, assetId: NFTAsset.Identifier): Flow<NFTAsset> =
collectionsRuntimeStore
.get()
.combine(getSalePrice(assetId)) { collectionsData, price ->
collectionsData
.getCollection(collectionId)
?.getAsset(assetId)
?.mergeWithPrice(price)
?: NFTAsset.Error(assetId)
}
override fun getSalePrice(assetId: NFTAsset.Identifier): Flow<NFTSalePrice> = pricesRuntimeStore
.get()
.map { it[assetId] ?: NFTSalePrice.Empty(assetId) }
override suspend fun saveCollections(collections: NFTCollections) {
collectionsRuntimeStore.store(collections)
}
override suspend fun saveSalePrice(salePrice: NFTSalePrice) {
pricesRuntimeStore.update(emptyMap()) {
it.plus(salePrice.assetId to salePrice)
}
}
private fun NFTCollections.getCollection(collectionId: NFTCollection.Identifier): NFTCollection? =
(content as? NFTCollections.Content.Collections)
?.collections
?.firstOrNull { it.id == collectionId }
private fun NFTCollection.getAsset(assetId: NFTAsset.Identifier): NFTAsset? =
assets.firstOrNull { it.id == assetId }
private fun NFTCollections.mergeWithPrices(prices: Map<NFTAsset.Identifier, NFTSalePrice>): NFTCollections =
when (val content = this.content) {
is NFTCollections.Content.Collections -> {
this.copy(
content = content.mergeWithPrices(prices),
)
}
is NFTCollections.Content.Error -> this
}
private fun NFTCollections.Content.Collections.mergeWithPrices(prices: Map<NFTAsset.Identifier, NFTSalePrice>) =
copy(
collections = this.collections?.map { data ->
data.copy(
assets = data.assets.map { asset ->
asset.mergeWithPrice(prices[asset.id] ?: NFTSalePrice.Empty(asset.id))
},
)
},
source = this.source,
)
private fun NFTAsset.mergeWithPrice(price: NFTSalePrice): NFTAsset = when (this) {
is NFTAsset.Error -> this
is NFTAsset.Value -> copy(
salePrice = price,
)
}
}

View file

@ -0,0 +1,21 @@
package com.tangem.datasource.local.nft
import com.tangem.blockchain.nft.models.NFTAsset
import com.tangem.blockchain.nft.models.NFTCollection
import kotlinx.coroutines.flow.Flow
interface NFTPersistenceStore {
fun getCollections(): Flow<List<NFTCollection>>
suspend fun getCollectionsSync(): List<NFTCollection>?
fun getAsset(collectionId: NFTCollection.Identifier, assetId: NFTAsset.Identifier): Flow<NFTAsset?>
fun getSalePrice(assetId: NFTAsset.Identifier): Flow<NFTAsset.SalePrice?>
suspend fun getSalePricesSync(): Map<NFTAsset.Identifier, NFTAsset.SalePrice>?
suspend fun saveCollections(collections: List<NFTCollection>)
suspend fun saveSalePrice(assetId: NFTAsset.Identifier, salePrice: NFTAsset.SalePrice)
}

View file

@ -0,0 +1,65 @@
package com.tangem.datasource.local.nft
import android.content.Context
import androidx.datastore.core.DataStoreFactory
import androidx.datastore.dataStoreFile
import com.squareup.moshi.Moshi
import com.tangem.blockchain.nft.models.NFTAsset
import com.tangem.blockchain.nft.models.NFTCollection
import com.tangem.datasource.di.NetworkMoshi
import com.tangem.datasource.utils.MoshiDataStoreSerializer
import com.tangem.datasource.utils.listTypes
import com.tangem.datasource.utils.mapWithCustomKeyTypes
import com.tangem.domain.tokens.model.Network
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class NFTPersistenceStoreFactory @Inject constructor(
@NetworkMoshi private val moshi: Moshi,
@ApplicationContext private val context: Context,
private val dispatchers: CoroutineDispatcherProvider,
) {
fun provide(network: Network): NFTPersistenceStore {
val networkStringIdentifier = listOfNotNull(
network.id.value,
network.derivationPath.value,
).joinToString("_") {
// remove all non-alphanumeric characters
it
.toCharArray()
.filter(Char::isLetterOrDigit)
.joinToString("")
.lowercase()
}
return DefaultNFTPersistenceStore(
collectionsPersistenceStore = DataStoreFactory.create(
serializer = MoshiDataStoreSerializer(
moshi = moshi,
types = listTypes<NFTCollection>(),
defaultValue = emptyList(),
),
produceFile = {
context.dataStoreFile(fileName = "nft_${networkStringIdentifier}_collections")
},
scope = CoroutineScope(context = dispatchers.io + SupervisorJob()),
),
pricesPersistenceStore = DataStoreFactory.create(
serializer = MoshiDataStoreSerializer(
moshi = moshi,
types = mapWithCustomKeyTypes<NFTAsset.Identifier, NFTAsset.SalePrice>(),
defaultValue = emptyMap(),
),
produceFile = {
context.dataStoreFile(fileName = "nft_${networkStringIdentifier}_prices")
},
scope = CoroutineScope(context = dispatchers.io + SupervisorJob()),
),
)
}
}

View file

@ -0,0 +1,24 @@
package com.tangem.datasource.local.nft
import com.tangem.domain.nft.models.NFTAsset
import com.tangem.domain.nft.models.NFTCollection
import com.tangem.domain.nft.models.NFTCollections
import com.tangem.domain.nft.models.NFTSalePrice
import kotlinx.coroutines.flow.Flow
interface NFTRuntimeStore {
suspend fun initialize(collections: NFTCollections, prices: Map<NFTAsset.Identifier, NFTSalePrice>)
fun getCollections(): Flow<NFTCollections>
suspend fun getCollectionsSync(): NFTCollections
fun getAsset(collectionId: NFTCollection.Identifier, assetId: NFTAsset.Identifier): Flow<NFTAsset?>
fun getSalePrice(assetId: NFTAsset.Identifier): Flow<NFTSalePrice?>
suspend fun saveCollections(collections: NFTCollections)
suspend fun saveSalePrice(salePrice: NFTSalePrice)
}

View file

@ -0,0 +1,16 @@
package com.tangem.datasource.local.nft
import com.tangem.datasource.local.datastore.RuntimeSharedStore
import com.tangem.domain.tokens.model.Network
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class NFTRuntimeStoreFactory @Inject constructor() {
fun provide(network: Network): NFTRuntimeStore = DefaultNFTRuntimeStore(
network = network,
collectionsRuntimeStore = RuntimeSharedStore(),
pricesRuntimeStore = RuntimeSharedStore(),
)
}

View file

@ -0,0 +1,55 @@
package com.tangem.datasource.local.nft.converter
import com.tangem.domain.models.StatusSource
import com.tangem.domain.nft.models.NFTAsset
import com.tangem.domain.nft.models.NFTSalePrice
import com.tangem.domain.tokens.model.Network
import com.tangem.utils.converter.Converter
import com.tangem.blockchain.nft.models.NFTAsset as SdkNFTAsset
class NFTSdkAssetConverter(
private val nftSdkAssetIdentifierConverter: NFTSdkAssetIdentifierConverter,
private val nftSdkCollectionIdentifierConverter: NFTSdkCollectionIdentifierConverter,
) : Converter<Pair<Network, SdkNFTAsset>, NFTAsset> {
override fun convert(value: Pair<Network, SdkNFTAsset>): NFTAsset {
val (network, asset) = value
val assetId = nftSdkAssetIdentifierConverter.convert(asset.identifier)
val collectionId = nftSdkCollectionIdentifierConverter.convert(asset.collectionIdentifier)
return NFTAsset.Value(
id = assetId,
collectionId = collectionId,
network = network,
contractType = asset.contractType,
owner = asset.owner,
name = asset.name,
description = asset.description,
salePrice = asset.salePrice?.let {
NFTSalePrice.Value(
assetId = assetId,
value = it.value,
symbol = it.symbol,
source = StatusSource.CACHE,
)
} ?: NFTSalePrice.Empty(assetId = assetId),
rarity = asset.rarity?.let {
NFTAsset.Value.Rarity(
rank = it.rank,
label = it.label,
)
},
media = asset.media?.let {
NFTAsset.Value.Media(
url = it.url,
mimetype = it.mimetype,
)
},
traits = asset.traits.map {
NFTAsset.Value.Trait(
name = it.name,
value = it.value,
)
},
source = StatusSource.CACHE,
)
}
}

View file

@ -0,0 +1,29 @@
package com.tangem.datasource.local.nft.converter
import com.tangem.domain.nft.models.NFTAsset
import com.tangem.utils.converter.TwoWayConverter
import com.tangem.blockchain.nft.models.NFTAsset as SdkNFTAsset
object NFTSdkAssetIdentifierConverter : TwoWayConverter<SdkNFTAsset.Identifier, NFTAsset.Identifier> {
override fun convert(value: SdkNFTAsset.Identifier): NFTAsset.Identifier = when (value) {
is SdkNFTAsset.Identifier.EVM -> NFTAsset.Identifier.EVM(
tokenId = value.tokenId,
tokenAddress = value.tokenAddress,
)
is SdkNFTAsset.Identifier.TON -> NFTAsset.Identifier.TON(
tokenAddress = value.tokenAddress,
)
is SdkNFTAsset.Identifier.Unknown -> NFTAsset.Identifier.Unknown
}
override fun convertBack(value: NFTAsset.Identifier): SdkNFTAsset.Identifier = when (value) {
is NFTAsset.Identifier.EVM -> SdkNFTAsset.Identifier.EVM(
tokenId = value.tokenId,
tokenAddress = value.tokenAddress,
)
is NFTAsset.Identifier.TON -> SdkNFTAsset.Identifier.TON(
tokenAddress = value.tokenAddress,
)
is NFTAsset.Identifier.Unknown -> SdkNFTAsset.Identifier.Unknown
}
}

View file

@ -0,0 +1,32 @@
package com.tangem.datasource.local.nft.converter
import com.tangem.domain.nft.models.NFTAsset
import com.tangem.domain.nft.models.NFTCollection
import com.tangem.domain.tokens.model.Network
import com.tangem.utils.converter.Converter
import com.tangem.blockchain.nft.models.NFTCollection as SdkNFTCollection
class NFTSdkCollectionConverter(
private val nftSdkCollectionIdentifierConverter: NFTSdkCollectionIdentifierConverter,
private val nftSdkAssetConverter: NFTSdkAssetConverter,
) : Converter<Pair<Network, SdkNFTCollection>, NFTCollection> {
override fun convert(value: Pair<Network, SdkNFTCollection>): NFTCollection {
val (network, collection) = value
val collectionId = nftSdkCollectionIdentifierConverter.convert(collection.identifier)
return NFTCollection(
id = collectionId,
network = network,
name = collection.name,
description = collection.description,
logoUrl = collection.logoUrl,
count = collection.count,
assets = collection.assets
.map { asset ->
nftSdkAssetConverter.convert(network to asset)
}
.filter {
it.id !is NFTAsset.Identifier.Unknown
},
)
}
}

View file

@ -0,0 +1,27 @@
package com.tangem.datasource.local.nft.converter
import com.tangem.domain.nft.models.NFTCollection
import com.tangem.utils.converter.TwoWayConverter
import com.tangem.blockchain.nft.models.NFTCollection as SdkNFTCollection
object NFTSdkCollectionIdentifierConverter : TwoWayConverter<SdkNFTCollection.Identifier, NFTCollection.Identifier> {
override fun convert(value: SdkNFTCollection.Identifier): NFTCollection.Identifier = when (value) {
is SdkNFTCollection.Identifier.EVM -> NFTCollection.Identifier.EVM(
tokenAddress = value.tokenAddress,
)
is SdkNFTCollection.Identifier.TON -> NFTCollection.Identifier.TON(
contractAddress = value.contractAddress,
)
is SdkNFTCollection.Identifier.Unknown -> NFTCollection.Identifier.Unknown
}
override fun convertBack(value: NFTCollection.Identifier): SdkNFTCollection.Identifier = when (value) {
is NFTCollection.Identifier.EVM -> SdkNFTCollection.Identifier.EVM(
tokenAddress = value.tokenAddress,
)
is NFTCollection.Identifier.TON -> SdkNFTCollection.Identifier.TON(
contractAddress = value.contractAddress,
)
is NFTCollection.Identifier.Unknown -> SdkNFTCollection.Identifier.Unknown
}
}

View file

@ -121,6 +121,8 @@ object PreferencesKeys {
val SEED_FIRST_NOTIFICATION_SHOW_TIME by lazy { longPreferencesKey("seedFirstNotificationTime") }
val WALLETS_NFT_ENABLED_STATES_KEY by lazy { stringPreferencesKey(name = "walletsNftEnabledStates") }
fun getShouldShowStoriesKey(storyId: String) = booleanPreferencesKey("shouldShowStories_$storyId")
// region Permission

View file

@ -11,6 +11,10 @@ inline fun <reified T> mapWithStringKeyTypes(): ParameterizedType {
return Types.newParameterizedType(Map::class.java, String::class.java, T::class.java)
}
inline fun <reified K, reified T> mapWithCustomKeyTypes(): ParameterizedType {
return Types.newParameterizedType(Map::class.java, K::class.java, T::class.java)
}
inline fun <reified T> listTypes(): ParameterizedType {
return Types.newParameterizedType(List::class.java, T::class.java)
}