Updated on 2026-08-14

This commit is contained in:
Tangem 2025-09-30 16:48:26 +03:00
parent acf110dbcc
commit 6910f74fd9
242 changed files with 866 additions and 732 deletions

View file

@ -12,7 +12,7 @@ import kotlinx.coroutines.flow.StateFlow
interface ApiConfigsManager {
/** Flag that determines whether the manager is initialized */
val isInitialized: StateFlow<Boolean>
val initializedState: StateFlow<Boolean>
/** Initialize resources */
fun initialize()

View file

@ -28,11 +28,11 @@ internal class DevApiConfigsManager(
override val configs: StateFlow<Map<ApiConfig, ApiEnvironment>>
field = MutableStateFlow(value = getInitialConfigs())
override val isInitialized: StateFlow<Boolean>
override val initializedState: StateFlow<Boolean>
field = MutableStateFlow(value = false)
override fun initialize() {
isInitialized.value = false
initializedState.value = false
appPreferencesStore.getObjectMap<ApiEnvironment>(PreferencesKeys.apiConfigsEnvironmentKey)
.distinctUntilChanged()
@ -45,8 +45,8 @@ internal class DevApiConfigsManager(
savedEnvironments[config.id.name] ?: currentEnvironment
}
if (!isInitialized.value) {
isInitialized.value = true
if (!initializedState.value) {
initializedState.value = true
}
notifyListeners(apiConfigs = apiConfigs, savedEnvironments = savedEnvironments)

View file

@ -24,7 +24,7 @@ internal class MockApiConfigsManager(
override val configs: StateFlow<Map<ApiConfig, ApiEnvironment>>
field = MutableStateFlow(value = getInitialConfigs())
override val isInitialized: StateFlow<Boolean> = MutableStateFlow(value = true)
override val initializedState: StateFlow<Boolean> = MutableStateFlow(value = true)
private val coroutineScope = CoroutineScope(SupervisorJob() + dispatchers.default)

View file

@ -15,7 +15,7 @@ internal class ProdApiConfigsManager(
private val apiConfigs: ApiConfigs,
) : ApiConfigsManager {
override val isInitialized: StateFlow<Boolean> = MutableStateFlow(value = true)
override val initializedState: StateFlow<Boolean> = MutableStateFlow(value = true)
override fun initialize() = Unit

View file

@ -72,14 +72,14 @@ sealed class ApiResponseError : Exception() {
/** Represents a network error, typically when there's no connectivity. */
@Suppress("UnusedPrivateMember")
data object NetworkException : ApiResponseError() {
private fun readResolve(): Any = NetworkException
class NetworkException : ApiResponseError() {
private fun readResolve(): Any = NetworkException()
}
/** Represents a timeout error, typically when the server takes too long to respond. */
@Suppress("UnusedPrivateMember")
data object TimeoutException : ApiResponseError() {
private fun readResolve(): Any = TimeoutException
class TimeoutException : ApiResponseError() {
private fun readResolve(): Any = TimeoutException()
}
/**
@ -87,5 +87,5 @@ sealed class ApiResponseError : Exception() {
*
* @property cause The exception that caused this error.
*/
data class UnknownException(override val cause: Throwable) : ApiResponseError()
class UnknownException(override val cause: Throwable) : ApiResponseError()
}

View file

@ -58,10 +58,10 @@ internal fun Throwable.toApiError(): ApiResponseError = when (this) {
is ConnectException,
is UnknownHostException,
is SSLHandshakeException,
-> ApiResponseError.NetworkException
-> ApiResponseError.NetworkException()
is TimeoutException,
is TimeoutCancellationException,
is SocketTimeoutException,
-> ApiResponseError.TimeoutException
-> ApiResponseError.TimeoutException()
else -> ApiResponseError.UnknownException(cause = this)
}

View file

@ -4,6 +4,7 @@ import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
@Suppress("BooleanPropertyNaming")
data class Asset(
@Json(name = "contractAddress")
val contractAddress: String,

View file

@ -56,6 +56,7 @@ data class TokenMarketInfoResponse(
)
@JsonClass(generateAdapter = true)
@Suppress("BooleanPropertyNaming")
data class Network(
@Json(name = "network_id")
val networkId: String,

View file

@ -4,6 +4,7 @@ import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
@Suppress("BooleanPropertyNaming")
data class OnrampCountryDTO(
@Json(name = "name")
val name: String,

View file

@ -26,6 +26,7 @@ data class TangemPayTxHistoryResponse(
)
@JsonClass(generateAdapter = true)
@Suppress("BooleanPropertyNaming")
data class Spend(
@Json(name = "amount") val amount: BigDecimal,
@Json(name = "currency") val currency: String,

View file

@ -36,6 +36,7 @@ data class ActionRequestBody(
)
@JsonClass(generateAdapter = true)
@Suppress("BooleanPropertyNaming")
data class ActionRequestBodyArgs(
@Json(name = "amount")
val amount: String,

View file

@ -5,6 +5,7 @@ import com.squareup.moshi.JsonClass
import java.math.BigDecimal
@JsonClass(generateAdapter = true)
@Suppress("BooleanPropertyNaming")
data class AddressArgumentDTO(
@Json(name = "required")
val required: Boolean,

View file

@ -1,3 +1,5 @@
@file:Suppress("BooleanPropertyNaming")
package com.tangem.datasource.api.stakekit.models.response.model
import com.squareup.moshi.Json

View file

@ -1,3 +1,5 @@
@file:Suppress("BooleanPropertyNaming")
package com.tangem.datasource.api.stakekit.models.response.model
import com.squareup.moshi.Json

View file

@ -1,3 +1,5 @@
@file:Suppress("BooleanPropertyNaming")
package com.tangem.datasource.api.tangemTech.models
import com.squareup.moshi.Json
@ -12,12 +14,13 @@ data class CoinsResponse(
) {
@JsonClass(generateAdapter = true)
@Suppress("BooleanPropertyNaming")
data class Coin(
@Json(name = "id") val id: String,
@Json(name = "name") val name: String,
@Json(name = "symbol") val symbol: String,
@Json(name = "active") val active: Boolean,
@Json(name = "networks") val networks: List<Network> = listOf(),
@Json(name = "networks") val networks: List<Network> = emptyList(),
) {
@JsonClass(generateAdapter = true)

View file

@ -4,6 +4,7 @@ import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
@Suppress("BooleanPropertyNaming")
data class CreateUserNetworkAccountResponse(
@Json(name = "status") val status: Boolean,
@Json(name = "data") val data: AccountCreated,

View file

@ -5,6 +5,7 @@ import com.squareup.moshi.JsonClass
import com.tangem.common.extensions.calculateHashCode
@JsonClass(generateAdapter = true)
@Suppress("BooleanPropertyNaming")
data class UserTokensResponse(
@Json(name = "version") val version: Int = 0,
@Json(name = "group") val group: GroupType,

View file

@ -4,6 +4,7 @@ import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
@Suppress("BooleanPropertyNaming")
data class WalletBody(
@Json(name = "notifyStatus") val notifyStatus: Boolean? = null,
@Json(name = "name") val name: String? = null,

View file

@ -4,6 +4,7 @@ import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
@Suppress("BooleanPropertyNaming")
data class WalletResponse(
@Json(name = "notifyStatus") val notifyStatus: Boolean,
@Json(name = "id") val id: String,

View file

@ -59,8 +59,8 @@ class AssetLoader @Inject constructor(
if (parsedConfig == null) Timber.e(IllegalStateException("Parsed config [$fileName] is null"))
parsedConfig.orEmpty()
},
onFailure = {
Timber.e(it, "Failed to load config [$fileName] from assets")
onFailure = { throwable ->
Timber.e(throwable, "Failed to load config [$fileName] from assets")
emptyList()
},
)

View file

@ -192,6 +192,7 @@ internal class RetrofitApiBuilder @Inject constructor(
}
}
@Suppress("UseEmptyCounterpart")
private companion object {
val excludedApiForLogging: Set<ApiConfig.ID> = setOf(

View file

@ -72,6 +72,7 @@ internal class DefaultExpressServiceLoader @Inject constructor(
return flow { getInitializationStatusInternal(userWalletId).collect { emit(it) } }
}
@Suppress("SuspendFunWithFlowReturnType")
private suspend fun getInitializationStatusInternal(userWalletId: UserWalletId): InitializationStatusFlow {
val initializationStatus = initializationStatuses.value.get(key = userWalletId)
if (initializationStatus != null) return initializationStatus
@ -79,8 +80,8 @@ internal class DefaultExpressServiceLoader @Inject constructor(
val cached = expressAssetsStore.getSyncOrNull(userWalletId)
val default: InitializationStatusFlow = MutableStateFlow(value = cached?.lceContent() ?: lceLoading())
initializationStatuses.update {
it.toMutableMap().apply {
initializationStatuses.update { statuses ->
statuses.toMutableMap().apply {
put(key = userWalletId, value = default)
}
}

View file

@ -26,10 +26,10 @@ object NFTSdkAssetConverter : TwoWayConverter<Pair<Network, SdkNFTAsset>, NFTAss
amount = asset.amount,
decimals = asset.decimals,
salePrice = salePrice,
rarity = asset.rarity?.let {
rarity = asset.rarity?.let { rarity ->
NFTAsset.Rarity(
rank = it.rank,
label = it.label,
rank = rarity.rank,
label = rarity.label,
)
},
media = asset.media?.let {
@ -65,22 +65,22 @@ object NFTSdkAssetConverter : TwoWayConverter<Pair<Network, SdkNFTAsset>, NFTAss
amount = value.amount,
decimals = value.decimals,
salePrice = salePrice,
rarity = value.rarity?.let {
rarity = value.rarity?.let { rarity ->
SdkNFTAsset.Rarity(
rank = it.rank,
label = it.label,
rank = rarity.rank,
label = rarity.label,
)
},
media = value.media?.let {
media = value.media?.let { media ->
SdkNFTAsset.Media(
animationUrl = it.animationUrl,
imageUrl = it.imageUrl,
animationUrl = media.animationUrl,
imageUrl = media.imageUrl,
)
},
traits = value.traits.map {
traits = value.traits.map { trait ->
SdkNFTAsset.Trait(
name = it.name,
value = it.value,
name = trait.name,
value = trait.value,
)
},
)

View file

@ -37,9 +37,9 @@ inline fun <reified T> AppPreferencesStore.getObject(key: Preferences.Key<String
return flow {
val adapter = moshi.adapter(T::class.java)
emitAll(
data.map {
data.map { prefs ->
try {
it[key]?.let(adapter::fromJson) ?: default
prefs[key]?.let(adapter::fromJson) ?: default
} catch (e: JsonDataException) {
default
}

View file

@ -12,10 +12,10 @@ internal class DefaultSwapBestRateAnimationStore(
* If true, reset flag to false
*/
override suspend fun getSyncOrNull(): Boolean {
val value = dataStore.getSyncOrNull() ?: true
if (value) {
val shouldShowBestRateAnimation = dataStore.getSyncOrNull() ?: true
if (shouldShowBestRateAnimation) {
dataStore.store(false)
}
return value
return shouldShowBestRateAnimation
}
}

View file

@ -8,7 +8,7 @@ internal class DefaultTokenReceiveWarningActionStore(
) : TokenReceiveWarningActionStore {
override suspend fun getSync(): Set<String> {
return persistenceStore.data.firstOrNull() ?: emptySet()
return persistenceStore.data.firstOrNull().orEmpty()
}
override suspend fun store(symbol: String) {

View file

@ -19,11 +19,11 @@ internal object PendingActionConverter : Converter<BalanceDTO.PendingAction, Pen
maximum = it.maximum,
)
},
duration = this?.duration?.let {
duration = this?.duration?.let { duration ->
PendingAction.PendingActionArgs.Duration(
required = it.required,
minimum = it.minimum,
maximum = it.maximum,
required = duration.required,
minimum = duration.minimum,
maximum = duration.maximum,
)
},
validatorAddress = this?.validatorAddress?.required,

View file

@ -20,9 +20,9 @@ internal fun OkHttpClient.Builder.addHeaders(
Interceptor { chain ->
val request = chain.request().newBuilder().apply {
runBlocking {
requestHeaders.forEach {
val value = it.value.invoke()
if (value.isNotBlank()) addHeader(name = it.key, value = value)
requestHeaders.forEach { header ->
val value = header.value.invoke()
if (value.isNotBlank()) addHeader(name = header.key, value = value)
}
}
}.build()

View file

@ -101,7 +101,7 @@ class NetworkLogsSaveInterceptor(
private fun logResponseMessage(response: Response, startNs: Long) {
val responseHeaders = response.headers
val responseBody = response.body!!
val responseBody = requireNotNull(response.body)
val contentLength = responseBody.contentLength()
val message = if (!response.promisesBody()) {

View file

@ -46,8 +46,8 @@ sealed class RequestHeader(vararg pairs: Pair<String, ProviderSuspend<String>>)
fun String.checkHeaderValueOrEmpty(): String {
for (i in this.indices) {
val c = this[i]
val charCondition = c == '\t' || c in '\u0020'..'\u007e'
if (!charCondition) {
val isChar = c == '\t' || c in '\u0020'..'\u007e'
if (!isChar) {
return ""
}
}