Updated on 2026-08-14

This commit is contained in:
Tangem 2025-10-02 23:41:01 +03:00
parent 3580350769
commit 8aba7b07d1
49 changed files with 172 additions and 134 deletions

View file

@ -15,6 +15,7 @@ data class EvmTransactionScanRequest(
)
@JsonClass(generateAdapter = true)
@Suppress("BooleanPropertyNaming")
data class EvmTransactionBulkScanRequest(
@Json(name = "chain") val chain: String,
@Json(name = "options") val options: List<String>,

View file

@ -39,8 +39,8 @@ internal class DevApiConfigsManager(
.onEach { savedEnvironments ->
val apiConfigs = configs.value
configs.value = apiConfigs.mapValues {
val (config, currentEnvironment) = it
configs.value = apiConfigs.mapValues { entry ->
val (config, currentEnvironment) = entry
savedEnvironments[config.id.name] ?: currentEnvironment
}

View file

@ -63,7 +63,7 @@ internal class MockApiConfigsManager(
super.addListener(listener)
configs
.map { it.entries.firstOrNull { it.key.id == listener.id } }
.map { map -> map.entries.firstOrNull { it.key.id == listener.id } }
.filterNotNull()
.onEach { (apiConfig, currentEnvironment) ->
listener.onChange(

View file

@ -5,6 +5,7 @@ import com.squareup.moshi.JsonClass
import java.math.BigDecimal
@JsonClass(generateAdapter = true)
@Suppress("BooleanPropertyNaming")
data class ConstructTransactionRequestBody(
@Json(name = "gasArgs")
val gasArgs: GasArgs? = null,

View file

@ -39,8 +39,8 @@ class AssetLoader @Inject constructor(
if (parsedConfig == null) Timber.e(IllegalStateException("Parsed config [$fileName] is null"))
parsedConfig
},
onFailure = {
Timber.e(it, "Failed to load config [$fileName] from assets")
onFailure = { throwable ->
Timber.e(throwable, "Failed to load config [$fileName] from assets")
null
},
)
@ -79,8 +79,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")
emptyMap()
},
)

View file

@ -74,7 +74,7 @@ internal class DefaultExpressServiceLoader @Inject constructor(
@Suppress("SuspendFunWithFlowReturnType")
private suspend fun getInitializationStatusInternal(userWalletId: UserWalletId): InitializationStatusFlow {
val initializationStatus = initializationStatuses.value.get(key = userWalletId)
val initializationStatus = initializationStatuses.value[userWalletId]
if (initializationStatus != null) return initializationStatus
val cached = expressAssetsStore.getSyncOrNull(userWalletId)

View file

@ -16,10 +16,10 @@ internal class DefaultAvailableAppCurrenciesStore(
override suspend fun store(response: CurrenciesResponse) {
val currencies = response.currencies
.map {
it.copy(
iconSmallUrl = response.imageHost?.plus(IMAGE_SMALL)?.format(it.id),
iconMediumUrl = response.imageHost?.plus(IMAGE_MEDIUM)?.format(it.id),
.map { currency ->
currency.copy(
iconSmallUrl = response.imageHost?.plus(IMAGE_SMALL)?.format(currency.id),
iconMediumUrl = response.imageHost?.plus(IMAGE_MEDIUM)?.format(currency.id),
)
}
.associateBy(CurrenciesResponse.Currency::code)

View file

@ -32,16 +32,16 @@ object NFTSdkAssetConverter : TwoWayConverter<Pair<Network, SdkNFTAsset>, NFTAss
label = rarity.label,
)
},
media = asset.media?.let {
media = asset.media?.let { media ->
NFTAsset.Media(
animationUrl = it.animationUrl,
imageUrl = it.imageUrl,
animationUrl = media.animationUrl,
imageUrl = media.imageUrl,
)
},
traits = asset.traits.map {
traits = asset.traits.map { trait ->
NFTAsset.Trait(
name = it.name,
value = it.value,
name = trait.name,
value = trait.value,
)
},
source = StatusSource.CACHE,

View file

@ -32,12 +32,12 @@ class NFTSdkCollectionConverter(
.filter {
it.id !is NFTAsset.Identifier.Unknown
}
.let {
if (it.isEmpty()) {
.let { items ->
if (items.isEmpty()) {
NFTCollection.Assets.Empty
} else {
NFTCollection.Assets.Value(
items = it,
items = items,
source = StatusSource.CACHE,
)
}

View file

@ -50,8 +50,8 @@ internal object PreferencesDataStore {
private fun createCorruptionHandler(): ReplaceFileCorruptionHandler<Preferences> {
return ReplaceFileCorruptionHandler(
produceNewData = {
Timber.w(it)
produceNewData = { corruptionException ->
Timber.w(corruptionException)
emptyPreferences()
},
)

View file

@ -14,9 +14,9 @@ inline fun <reified T> AppPreferencesStore.getObject(key: Preferences.Key<String
val adapter = moshi.adapter(T::class.java)
emitAll(
data.map { preferences ->
preferences[key]?.let {
preferences[key]?.let { value ->
try {
adapter.fromJson(it)
adapter.fromJson(value)
} catch (e: JsonDataException) {
null
}
@ -59,9 +59,9 @@ suspend inline fun <reified T> AppPreferencesStore.getObjectSyncOrNull(key: Pref
val adapter = moshi.adapter(T::class.java) // TODO: Support parameterized types
data.firstOrNull()
?.get(key)
?.let {
?.let { value ->
try {
adapter.fromJson(it)
adapter.fromJson(value)
} catch (e: JsonDataException) {
null
}
@ -76,9 +76,9 @@ suspend inline fun <reified T> AppPreferencesStore.getObjectSyncOrDefault(
val adapter = moshi.adapter(T::class.java)
data.firstOrNull()
?.get(key)
?.let {
?.let { value ->
try {
adapter.fromJson(it)
adapter.fromJson(value)
} catch (e: JsonDataException) {
default
}
@ -159,7 +159,7 @@ inline fun <reified V> AppPreferencesStore.getObjectMap(key: Preferences.Key<Str
val adapter = moshi.adapter<Map<String, V>>(type)
emitAll(
data.map { it[key]?.let(adapter::fromJson) ?: emptyMap() },
data.map { it[key]?.let(adapter::fromJson).orEmpty() },
)
}
}
@ -180,7 +180,7 @@ inline fun <reified T> AppPreferencesStore.getObjectSet(key: Preferences.Key<Str
val adapter = moshi.adapter<Set<T>>(Types.newParameterizedType(Set::class.java, T::class.java))
emitAll(
data.map {
it[key]?.let(adapter::fromJson) ?: emptySet()
it[key]?.let(adapter::fromJson).orEmpty()
},
)
}

View file

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

View file

@ -42,8 +42,8 @@ internal class DefaultExpressAssetsStore(
runtimeStore.store(userWalletId.stringValue, item)
}
launch {
persistenceStore.updateData {
it.toMutableMap().apply {
persistenceStore.updateData { assetsByWalletId ->
assetsByWalletId.toMutableMap().apply {
put(userWalletId.stringValue, item)
}
}

View file

@ -12,11 +12,11 @@ internal object PendingActionConverter : Converter<BalanceDTO.PendingAction, Pen
passthrough = value.passthrough,
args = with(value.args) {
PendingAction.PendingActionArgs(
amount = this?.amount?.let {
amount = this?.amount?.let { amount ->
PendingAction.PendingActionArgs.Amount(
required = it.required,
minimum = it.minimum,
maximum = it.maximum,
required = amount.required,
minimum = amount.minimum,
maximum = amount.maximum,
)
},
duration = this?.duration?.let { duration ->
@ -28,10 +28,10 @@ internal object PendingActionConverter : Converter<BalanceDTO.PendingAction, Pen
},
validatorAddress = this?.validatorAddress?.required,
validatorAddresses = this?.validatorAddresses?.required,
tronResource = this?.tronResource?.let {
tronResource = this?.tronResource?.let { tronResource ->
PendingAction.PendingActionArgs.TronResource(
required = it.required,
options = it.options,
required = tronResource.required,
options = tronResource.options,
)
},
signatureVerification = this?.signatureVerification?.required,

View file

@ -28,14 +28,14 @@ internal class DefaultWalletManagersStore(
): WalletManager? {
val walletManagers = getSyncOrNull(userWalletId)
return walletManagers?.singleOrNull {
it.wallet.blockchain == blockchain &&
it.wallet.publicKey.derivationPath?.rawPath == derivationPath
return walletManagers?.singleOrNull { walletManager ->
walletManager.wallet.blockchain == blockchain &&
walletManager.wallet.publicKey.derivationPath?.rawPath == derivationPath
}
}
override suspend fun getAllSync(userWalletId: UserWalletId): List<WalletManager> {
return getSyncOrNull(userWalletId) ?: emptyList()
return getSyncOrNull(userWalletId).orEmpty()
}
override suspend fun store(userWalletId: UserWalletId, walletManager: WalletManager) {

View file

@ -84,12 +84,12 @@ fun ResizableText(
reduceFactor: Double = 0.9,
) {
var fontSize by remember { mutableStateOf(style.fontSize) }
var readyToDraw by remember { mutableStateOf(value = false) }
var isReadyToDraw by remember { mutableStateOf(value = false) }
Text(
modifier = modifier
.drawWithContent {
if (readyToDraw) drawContent()
if (isReadyToDraw) drawContent()
}
.wrapContentHeight(),
text = text,
@ -106,7 +106,7 @@ fun ResizableText(
if (minFontSize != TextUnit.Unspecified && reducedFontSize <= minFontSize) {
fontSize = minFontSize
readyToDraw = true
isReadyToDraw = true
} else {
fontSize = reducedFontSize
}
@ -115,7 +115,7 @@ fun ResizableText(
if (result.hasVisualOverflow) {
reduceFontSize()
} else {
readyToDraw = true
isReadyToDraw = true
}
},
)

View file

@ -174,7 +174,7 @@ private fun TangemTextField(
null
},
)
iconRes?.let { iconRes ->
iconRes?.let { resId ->
IconButton(
modifier = Modifier.size(32.dp),
onClick = onClear,
@ -182,7 +182,7 @@ private fun TangemTextField(
) {
Icon(
modifier = Modifier.size(24.dp),
painter = painterResource(id = iconRes),
painter = painterResource(id = resId),
tint = colors.trailingIconColor(enabled = enabled, isError = isError).value,
contentDescription = "Clear input",
)

View file

@ -55,11 +55,11 @@ fun RoundedActionButton(
ActionBaseButton(
config = config,
shape = RoundedCornerShape(size = TangemTheme.dimens.radius24),
content = { modifier ->
content = { contentModifier ->
ActionButtonContent(
config = config,
text = { color -> Text(text = config.text, textColor = color) },
modifier = modifier.padding(
modifier = contentModifier.padding(
start = TangemTheme.dimens.spacing16,
end = TangemTheme.dimens.spacing24,
),
@ -138,9 +138,13 @@ fun ActionBaseButton(
onClick = config.onClick,
onLongClick = {
val toastReference = config.onLongClick?.invoke()
toastReference?.let { toastReference ->
if (toastReference != null) {
Toast
.makeText(context, toastReference.resolveReference(context.resources), Toast.LENGTH_SHORT)
.makeText(
context,
toastReference.resolveReference(context.resources),
Toast.LENGTH_SHORT,
)
.show()
}
},

View file

@ -9,6 +9,7 @@ package com.tangem.core.ui.components.containers.pullToRefresh
data class PullToRefreshConfig(val isRefreshing: Boolean, val onRefresh: (ShowRefreshState) -> Unit) {
@JvmInline
@Suppress("BooleanPropertyNaming")
value class ShowRefreshState(
val value: Boolean = true,
)

View file

@ -228,7 +228,12 @@ internal data class DropdownMenuPositionProvider(
onPositionCalculated(
anchorBounds,
IntRect(x, y, x + popupContentSize.width, y + popupContentSize.height),
IntRect(
left = x,
top = y,
right = x + popupContentSize.width,
bottom = y + popupContentSize.height,
),
)
return IntOffset(x, y)
}

View file

@ -75,7 +75,12 @@ fun AmountTextField(
val decimalFormat = rememberDecimalFormat()
BoxWithConstraints(modifier = modifier) {
val fontSize = if (isAutoResize) {
resizeFont(visualTransformation, value, textStyle, reduceFactor)
resizeFont(
visualTransformation = visualTransformation,
value = value,
textStyle = textStyle,
reduceFactor = reduceFactor,
)
} else {
textStyle.fontSize
}

View file

@ -108,9 +108,9 @@ private fun CellDecoration(
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(12.dp),
) {
repeat(length) {
val char = if (it < value.length) {
if (isPasswordVisual) PASSWORD_VISUAL_CHAR.toString() else value[it].toString()
repeat(length) { i ->
val char = if (i < value.length) {
if (isPasswordVisual) PASSWORD_VISUAL_CHAR.toString() else value[i].toString()
} else {
""
}

View file

@ -55,7 +55,12 @@ class AmountVisualTransformation(
val groupingSymbol = decimalFormat.decimalFormatSymbols.groupingSeparator
return TransformedText(
text = formattedText,
offsetMapping = OffsetMappingImpl(text.text, formattedText, symbol, groupingSymbol),
offsetMapping = OffsetMappingImpl(
text = text.text,
formattedText = formattedText,
currencySymbol = symbol,
groupingSymbol = groupingSymbol,
),
)
}
@ -93,7 +98,7 @@ class AmountVisualTransformation(
private val text: String,
private val formattedText: AnnotatedString,
private val currencySymbol: String?,
private val gropingSymbol: Char,
private val groupingSymbol: Char,
) : OffsetMapping {
override fun originalToTransformed(offset: Int): Int {
var noneDigitCount = 0
@ -101,7 +106,7 @@ class AmountVisualTransformation(
val symbolOffset = currencySymbol?.let { formattedText.indexOf(it) } ?: -1
while (i < offset + noneDigitCount) {
val char = formattedText.getOrNull(i++)
if (char == gropingSymbol) noneDigitCount++
if (char == groupingSymbol) noneDigitCount++
if (symbolOffset == 0 && char?.isWhitespace() == true) noneDigitCount++
}
var transformedOffset = if (symbolOffset == 0 && currencySymbol != null) {
@ -118,7 +123,7 @@ class AmountVisualTransformation(
}
override fun transformedToOriginal(offset: Int): Int {
val noneDigitCount = formattedText.take(offset).count { it == gropingSymbol }
val noneDigitCount = formattedText.take(offset).count { it == groupingSymbol }
return (offset - noneDigitCount).coerceIn(0, text.length)
}
}

View file

@ -141,11 +141,11 @@ fun TangemLinearProgressIndicator(
drawLinearIndicatorBackground(backgroundColor, strokeWidth, strokeCap)
if (firstLineHead - firstLineTail > 0) {
drawLinearIndicator(
firstLineHead,
firstLineTail,
color,
strokeWidth,
strokeCap,
startFraction = firstLineHead,
endFraction = firstLineTail,
color = color,
strokeWidth = strokeWidth,
strokeCap = strokeCap,
)
}
if ((secondLineHead - secondLineTail) > 0) {
@ -204,7 +204,12 @@ private fun DrawScope.drawLinearIndicator(
// if there isn't enough space to draw the stroke caps, fall back to StrokeCap.Butt
if (strokeCap == StrokeCap.Butt || height > width) {
// Progress line
drawLine(color, Offset(barStart, yOffset), Offset(barEnd, yOffset), strokeWidth)
drawLine(
color = color,
start = Offset(barStart, yOffset),
end = Offset(barEnd, yOffset),
strokeWidth = strokeWidth,
)
} else {
// need to adjust barStart and barEnd for the stroke caps
val strokeCapOffset = strokeWidth / 2
@ -253,7 +258,7 @@ private const val SECOND_LINE_TAIL_DELAY = 1267
private val FIRST_LINE_HEAD_EASING = CubicBezierEasing(a = 0.2f, b = 0f, c = 0.8f, d = 1f)
private val FIRST_LINE_TAIL_EASING = CubicBezierEasing(a = 0.4f, b = 0f, c = 1f, d = 1f)
private val SECOND_LINE_HEAD_EASING = CubicBezierEasing(a = 0f, b = 0f, c = 0.65f, d = 1f)
private val SECOND_LINE_TAIL_EASING = CubicBezierEasing(a = 0.1f, 0f, 0.45f, d = 1f)
private val SECOND_LINE_TAIL_EASING = CubicBezierEasing(a = 0.1f, b = 0f, c = 0.45f, d = 1f)
// region Preview
@Preview(showBackground = true, widthDp = 360)

View file

@ -99,11 +99,11 @@ fun StoriesProgressBar(
.clip(RoundedCornerShape(2.dp))
.background(TangemColorPalette.White)
.fillMaxHeight()
.let {
.let { modifier ->
when (index) {
currentStep -> it.fillMaxWidth(progress.value)
in 0..currentStep -> it.fillMaxWidth(fraction = 1f)
else -> it
currentStep -> modifier.fillMaxWidth(progress.value)
in 0..currentStep -> modifier.fillMaxWidth(fraction = 1f)
else -> modifier
}
},
)