Updated on 2026-08-14

This commit is contained in:
Tangem 2025-02-17 14:50:26 +04:00
parent b6a8a4937b
commit 22cbd1b422
10 changed files with 64 additions and 69 deletions

View file

@ -7,7 +7,10 @@ import com.squareup.moshi.Moshi
import com.tangem.datasource.local.datastore.RuntimeDataStore
import com.tangem.datasource.local.network.DefaultNetworksStatusesStore
import com.tangem.datasource.local.network.NetworksStatusesStore
import com.tangem.datasource.local.network.utils.NetworkStatusesSerializer
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.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
@ -30,7 +33,11 @@ internal object NetworksStatusesStoreModule {
return DefaultNetworksStatusesStore(
runtimeDataStore = RuntimeDataStore(),
persistenceDataStore = DataStoreFactory.create(
serializer = NetworkStatusesSerializer(moshi),
serializer = MoshiDataStoreSerializer(
moshi = moshi,
types = mapWithStringKeyTypes(valueTypes = setTypes<NetworkStatusDM>()),
defaultValue = emptyMap(),
),
produceFile = { context.dataStoreFile(fileName = "networks_statuses") },
scope = CoroutineScope(context = dispatchers.io + SupervisorJob()),
),

View file

@ -2,7 +2,7 @@ package com.tangem.datasource.local.network
import androidx.datastore.core.DataStore
import com.tangem.datasource.local.datastore.RuntimeDataStore
import com.tangem.datasource.local.network.entity.NetworkStatusesDM
import com.tangem.datasource.local.network.entity.NetworkStatusesByWalletId
import com.tangem.datasource.local.network.utils.toDataModel
import com.tangem.datasource.local.network.utils.toDomainModel
import com.tangem.domain.tokens.model.Network
@ -16,7 +16,7 @@ import kotlinx.coroutines.sync.withLock
internal class DefaultNetworksStatusesStore(
private val runtimeDataStore: RuntimeDataStore<Set<NetworkStatus>>,
private val persistenceDataStore: DataStore<NetworkStatusesDM>,
private val persistenceDataStore: DataStore<NetworkStatusesByWalletId>,
) : NetworksStatusesStore {
private val mutex = Mutex()
@ -33,7 +33,7 @@ internal class DefaultNetworksStatusesStore(
.firstOrNull { it.id == status.networkId }
?: return@mapNotNullTo null
status.toDomainModel(network)
status.toDomainModel(network = network, isCached = true)
}
.orEmpty()
@ -110,7 +110,7 @@ internal class DefaultNetworksStatusesStore(
persistenceDataStore.updateData { storedStatuses ->
val userWalletStatuses = storedStatuses[userWalletId.stringValue] ?: emptySet()
val updatedStatuses = userWalletStatuses.addOrReplace(status.toDataModel(network)) {
val updatedStatuses = userWalletStatuses.addOrReplace(item = status.toDataModel(network)) {
it.networkId == network.id
}

View file

@ -1,27 +1,34 @@
package com.tangem.datasource.local.network.entity
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import com.tangem.domain.tokens.model.Network
import java.math.BigDecimal
internal typealias NetworkStatusesDM = Map<String, Set<NetworkStatusDM>>
internal typealias NetworkStatusesByWalletId = Map<String, Set<NetworkStatusDM>>
@JsonClass(generateAdapter = true)
internal data class NetworkStatusDM(
val networkId: Network.ID,
val selectedAddress: String,
val availableAddresses: Set<Address>,
val amounts: Map<String, BigDecimal>,
@Json(name = "network_id") val networkId: Network.ID,
@Json(name = "selected_address") val selectedAddress: String,
@Json(name = "available_addresses") val availableAddresses: Set<Address>,
@Json(name = "amounts") val amounts: Map<String, BigDecimal>,
) {
@JsonClass(generateAdapter = true)
data class Address(
val value: String,
val type: Type,
@Json(name = "value") val value: String,
@Json(name = "type") val type: Type,
) {
@JsonClass(generateAdapter = false)
enum class Type {
Primary, Secondary,
@Json(name = "primary")
Primary,
@Json(name = "secondary")
Secondary,
}
}
}

View file

@ -32,7 +32,7 @@ internal fun NetworkStatus.Verified.toDataModel(network: Network): NetworkStatus
)
}
internal fun NetworkStatusDM.toDomainModel(network: Network): NetworkStatus {
internal fun NetworkStatusDM.toDomainModel(network: Network, isCached: Boolean): NetworkStatus {
return NetworkStatus(
network = network,
value = NetworkStatus.Verified(
@ -40,6 +40,7 @@ internal fun NetworkStatusDM.toDomainModel(network: Network): NetworkStatus {
amounts = mapToDomainAmounts(amounts),
pendingTransactions = mapOf(),
),
isCached = isCached,
)
}

View file

@ -1,36 +0,0 @@
package com.tangem.datasource.local.network.utils
import androidx.datastore.core.Serializer
import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
import com.tangem.datasource.local.network.entity.NetworkStatusDM
import com.tangem.datasource.local.network.entity.NetworkStatusesDM
import java.io.InputStream
import java.io.OutputStream
internal class NetworkStatusesSerializer(moshi: Moshi) : Serializer<NetworkStatusesDM> {
private val adapter by lazy {
val type = Types.newParameterizedType(
Map::class.java,
String::class.java,
Types.newParameterizedType(Set::class.java, NetworkStatusDM::class.java),
)
moshi.adapter<NetworkStatusesDM>(type)
}
override val defaultValue: NetworkStatusesDM = emptyMap()
override suspend fun readFrom(input: InputStream): NetworkStatusesDM {
return input.bufferedReader().use { reader ->
adapter.fromJson(reader.readText()) ?: defaultValue
}
}
override suspend fun writeTo(t: NetworkStatusesDM, output: OutputStream) {
output.bufferedWriter().use { writer ->
writer.write(adapter.toJson(t))
}
}
}

View file

@ -3,6 +3,10 @@ package com.tangem.datasource.utils
import com.squareup.moshi.Types
import java.lang.reflect.ParameterizedType
fun mapWithStringKeyTypes(valueTypes: ParameterizedType): ParameterizedType {
return Types.newParameterizedType(Map::class.java, String::class.java, valueTypes)
}
inline fun <reified T> mapWithStringKeyTypes(): ParameterizedType {
return Types.newParameterizedType(Map::class.java, String::class.java, T::class.java)
}

View file

@ -120,8 +120,17 @@ internal class DefaultNetworksRepository(
refresh: Boolean,
) = coroutineScope {
if (refresh) {
val statusesToRefresh = networks.map { NetworkStatus(it, NetworkStatus.Refreshing) }
networksStatusesStore.storeAll(userWalletId, statusesToRefresh)
val statusesToRefresh = networksStatusesStore.getSyncOrNull(userWalletId)?.mapNotNull {
if (it.network in networks) {
it.copy(value = NetworkStatus.Refreshing)
} else {
null
}
}
if (statusesToRefresh != null) {
networksStatusesStore.storeAll(key = userWalletId, values = statusesToRefresh)
}
}
val currencies = getCurrencies(userWalletId, networks)

View file

@ -36,6 +36,7 @@ internal class NetworkStatusFactory {
),
)
},
isCached = false,
)
}

View file

@ -8,10 +8,12 @@ import java.math.BigDecimal
*
* @property network The network for which the status is provided.
* @property value The specific status value, represented as a sealed class to encapsulate the various possible states of the network.
* @property isCached flag that determines whether the status is a cache
*/
data class NetworkStatus(
val network: Network,
val value: Value,
val isCached: Boolean,
) {
/**

View file

@ -8,7 +8,6 @@ import com.tangem.domain.tokens.model.NetworkAddress
import com.tangem.domain.tokens.model.NetworkStatus
import java.math.BigDecimal
@Suppress("MemberVisibilityCanBePrivate")
internal object MockNetworks {
val amountToCreateAccount: BigDecimal = BigDecimal.TEN
@ -52,17 +51,26 @@ internal object MockNetworks {
transactionExtrasType = Network.TransactionExtrasType.NONE,
)
val networkStatus1 = NetworkStatus(
val verifiedNetworksStatuses: NonEmptySet<NetworkStatus>
get() = nonEmptySetOf(
verifiedNetworkStatus1,
verifiedNetworkStatus2,
verifiedNetworkStatus3,
)
private val networkStatus1 = NetworkStatus(
network = network1,
value = NetworkStatus.Unreachable(address = null),
isCached = false,
)
val networkStatus2 = NetworkStatus(
private val networkStatus2 = NetworkStatus(
network = network2,
value = NetworkStatus.MissedDerivation,
isCached = false,
)
val networkStatus3 = NetworkStatus(
private val networkStatus3 = NetworkStatus(
network = network3,
value = NetworkStatus.NoAccount(
amountToCreateAccount = amountToCreateAccount,
@ -71,11 +79,10 @@ internal object MockNetworks {
),
errorMessage = "",
),
isCached = false,
)
val errorNetworksStatuses = nonEmptySetOf(networkStatus1, networkStatus2, networkStatus3)
val verifiedNetworkStatus1: NetworkStatus
private val verifiedNetworkStatus1: NetworkStatus
get() = networkStatus1.copy(
value = NetworkStatus.Verified(
amounts = mapOf(
@ -90,7 +97,7 @@ internal object MockNetworks {
),
)
val verifiedNetworkStatus2: NetworkStatus
private val verifiedNetworkStatus2: NetworkStatus
get() = networkStatus2.copy(
value = NetworkStatus.Verified(
amounts = mapOf(
@ -105,7 +112,7 @@ internal object MockNetworks {
),
)
val verifiedNetworkStatus3: NetworkStatus
private val verifiedNetworkStatus3: NetworkStatus
get() = networkStatus3.copy(
value = NetworkStatus.Verified(
amounts = mapOf(
@ -120,11 +127,4 @@ internal object MockNetworks {
),
),
)
val verifiedNetworksStatuses: NonEmptySet<NetworkStatus>
get() = nonEmptySetOf(
verifiedNetworkStatus1,
verifiedNetworkStatus2,
verifiedNetworkStatus3,
)
}