Updated on 2026-08-14

This commit is contained in:
Tangem 2024-10-21 21:00:10 +03:00
commit f4568840a3
25 changed files with 205 additions and 192 deletions

@ -1 +1 @@
Subproject commit d52c8382e9e94b2b44ceecd7cb7bd897e091073c
Subproject commit cf01c91626bf58bdf6895c59a706b83c37a9e345

View file

@ -50,7 +50,6 @@ dependencies {
implementation(deps.okHttp.prettyLogging)
implementation(deps.retrofit)
implementation(deps.retrofit.moshi)
implementation(deps.reactive.network)
/** Time */
implementation(deps.jodatime)

View file

@ -64,6 +64,9 @@ enum class ExchangeStatus {
@Json(name = "tx-failed")
TxFailed,
@Json(name = "paused")
Paused,
@Json(name = "unknown")
Unknown,
}

View file

@ -0,0 +1,26 @@
package com.tangem.datasource.connection
import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import androidx.core.content.ContextCompat
internal class AndroidNetworkConnectionManager(
private val applicationContext: Context,
) : NetworkConnectionManager {
override val isOnline: Boolean
get() = isNetworkAvailable()
private fun isNetworkAvailable(): Boolean {
val connectivityManager = ContextCompat.getSystemService(applicationContext, ConnectivityManager::class.java)
// Network that is currently in use (several networks can be presented at the same time)
val defaultNetwork: Network = connectivityManager?.activeNetwork ?: return false
val networkCapabilities: NetworkCapabilities =
connectivityManager.getNetworkCapabilities(defaultNetwork) ?: return false
return networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
}
}

View file

@ -1,13 +1,8 @@
package com.tangem.datasource.connection
import kotlinx.coroutines.flow.StateFlow
/** Network connection manager */
interface NetworkConnectionManager {
/** Connection status */
val isOnline: Boolean
/** Connection status flow */
val isOnlineFlow: StateFlow<Boolean>
}

View file

@ -1,53 +0,0 @@
package com.tangem.datasource.connection
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler
import io.reactivex.BackpressureStrategy
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.reactive.asFlow
private const val PING_SERVER = "https://clients3.google.com/generate_204"
private const val PING_INTERVAL = 5_000
internal class RealInternetConnectionManager : NetworkConnectionManager {
private val scope =
CoroutineScope(
SupervisorJob() + Dispatchers.IO + FeatureCoroutineExceptionHandler.create("RealInternetConnectionManager"),
)
private val initialNetworkResult: Boolean by lazy {
ReactiveNetwork
.checkInternetConnectivity(
InternetObservingSettings.builder()
.host(PING_SERVER)
.port(443)
.build(),
)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.blockingGet()
}
override val isOnlineFlow: StateFlow<Boolean> = ReactiveNetwork
.observeInternetConnectivity(
InternetObservingSettings.builder()
.host(PING_SERVER)
.port(443)
.interval(PING_INTERVAL)
.build(),
)
.toFlowable(BackpressureStrategy.LATEST)
.asFlow()
.stateIn(scope, SharingStarted.Lazily, initialValue = initialNetworkResult)
override val isOnline: Boolean
get() = isOnlineFlow.value
}

View file

@ -1,10 +1,12 @@
package com.tangem.datasource.di
import android.content.Context
import com.tangem.datasource.connection.AndroidNetworkConnectionManager
import com.tangem.datasource.connection.NetworkConnectionManager
import com.tangem.datasource.connection.RealInternetConnectionManager
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@ -14,7 +16,7 @@ internal class NetworkConnectionModule {
@Provides
@Singleton
fun provideNetworkConnectionManager(): NetworkConnectionManager {
return RealInternetConnectionManager()
fun provideNetworkConnectionManager(@ApplicationContext applicationContext: Context): NetworkConnectionManager {
return AndroidNetworkConnectionManager(applicationContext = applicationContext)
}
}

View file

@ -99,6 +99,7 @@ internal class DefaultManageTokensRepository(
loadUserTokensFromRemote: Boolean,
): BatchFetchResult.Success<List<ManagedCryptoCurrency>> {
val supportedBlockchains = getSupportedBlockchains(userWallet)
val query = request.params.searchText.takeUnless(String?::isNullOrBlank)
val call = suspend {
tangemTechApi.getCoins(
@ -107,7 +108,7 @@ internal class DefaultManageTokensRepository(
transform = Blockchain::toNetworkId,
),
active = true,
searchText = request.params.searchText,
searchText = query,
offset = request.offset * request.limit,
limit = request.limit,
).getOrThrow()
@ -135,7 +136,7 @@ internal class DefaultManageTokensRepository(
val items = if (isFirstBatchFetching &&
tokensResponse != null &&
userWallet != null &&
request.params.searchText.isNullOrBlank()
query == null
) {
managedCryptoCurrencyFactory.createWithCustomTokens(
coinsResponse = updatedCoinsResponse,

View file

@ -93,7 +93,7 @@ internal class SendNotificationFactory constructor(
val currencyCheck = getCurrencyCheckUseCase(
userWalletId = userWalletId,
currencyStatus = cryptoCurrencyStatus,
amount = amountValue,
amount = sendingAmount,
fee = feeValue,
)
buildList {

View file

@ -128,4 +128,8 @@ internal class EthereumCustomFeeConverter(
const val GAS_DECIMALS = 0
const val FEE_AMOUNT = 0
}
}
internal fun MutableList<SendTextField.CustomFee>.setEmpty(index: Int) {
set(index, this[index].copy(value = ""))
}

View file

@ -12,6 +12,7 @@ import com.tangem.core.ui.utils.parseToBigDecimal
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.send.impl.R
import com.tangem.features.send.impl.presentation.state.fee.checkExceedBalance
import com.tangem.features.send.impl.presentation.state.fee.custom.EthereumCustomFeeConverter.Companion.ETHEREUM_GAS_UNIT
import com.tangem.features.send.impl.presentation.state.fee.custom.EthereumCustomFeeConverter.Companion.FEE_AMOUNT
import com.tangem.features.send.impl.presentation.state.fee.custom.EthereumCustomFeeConverter.Companion.GAS_DECIMALS
@ -85,15 +86,12 @@ internal class EthereumEIPCustomFeeConverter(
when (index) {
FEE_AMOUNT -> setOnAmountChange(value, index)
MAX_FEE -> setOnMaxFeeChange(value, index)
GAS_LIMIT -> setOnGasLimitChange(value, index)
else -> set(index, this[index].copy(value = value))
}
}.toImmutableList()
}
private fun MutableList<SendTextField.CustomFee>.setEmpty(index: Int) {
set(index, this[index].copy(value = ""))
}
private fun MutableList<SendTextField.CustomFee>.setOnAmountChange(value: String, index: Int) {
val gasLimit = this[GAS_LIMIT].value.parseToBigDecimal(this[GAS_LIMIT].decimals)
if (value.isBlank()) {
@ -148,6 +146,48 @@ internal class EthereumEIPCustomFeeConverter(
}
}
private fun MutableList<SendTextField.CustomFee>.setOnGasLimitChange(value: String, index: Int) {
if (value.isBlank()) {
setEmpty(FEE_AMOUNT)
setEmpty(GAS_LIMIT)
} else {
val newGasLimit = value.parseToBigDecimal(this[GAS_LIMIT].decimals)
val maxFee = this[MAX_FEE].value.parseToBigDecimal(this[MAX_FEE].decimals)
.movePointLeft(this[MAX_FEE].decimals) // from GWEI to ETH
val newFeeAmount = newGasLimit * maxFee
set(
index = FEE_AMOUNT,
element = this[FEE_AMOUNT].copy(
value = newFeeAmount.parseBigDecimal(this[FEE_AMOUNT].decimals),
label = getFiatReference(
rate = feeCryptoCurrencyStatusProvider()?.value?.fiatRate,
value = newFeeAmount,
appCurrency = appCurrencyProvider(),
),
),
)
val isNotExceedBalance = checkExceedBalance(
feeBalance = feeCryptoCurrencyStatusProvider()?.value?.amount,
feeAmount = newFeeAmount,
)
set(
index = index,
element = this[index].copy(
value = value,
keyboardOptions = KeyboardOptions(
imeAction = if (!isNotExceedBalance) ImeAction.None else ImeAction.Done,
keyboardType = KeyboardType.Number,
),
),
)
}
}
private companion object {
const val MAX_FEE = 1
const val PRIORITY_FEE = 2

View file

@ -12,6 +12,7 @@ import com.tangem.core.ui.utils.parseToBigDecimal
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.send.impl.R
import com.tangem.features.send.impl.presentation.state.fee.checkExceedBalance
import com.tangem.features.send.impl.presentation.state.fee.custom.EthereumCustomFeeConverter.Companion.ETHEREUM_GAS_UNIT
import com.tangem.features.send.impl.presentation.state.fee.custom.EthereumCustomFeeConverter.Companion.FEE_AMOUNT
import com.tangem.features.send.impl.presentation.state.fee.custom.EthereumCustomFeeConverter.Companion.GAS_DECIMALS
@ -76,15 +77,12 @@ internal class EthereumLegacyCustomFeeConverter(
when (index) {
FEE_AMOUNT -> setOnAmountChange(value, index)
GAS_PRICE -> setOnGasPriceChange(value, index)
GAS_LIMIT -> setOnGasLimitChange(value, index)
else -> set(index, this[index].copy(value = value))
}
}.toImmutableList()
}
private fun MutableList<SendTextField.CustomFee>.setEmpty(index: Int) {
set(index, this[index].copy(value = ""))
}
private fun MutableList<SendTextField.CustomFee>.setOnAmountChange(value: String, index: Int) {
val gasLimit = this[GAS_LIMIT].value.parseToBigDecimal(this[GAS_LIMIT].decimals)
if (value.isBlank()) {
@ -133,6 +131,47 @@ internal class EthereumLegacyCustomFeeConverter(
}
}
private fun MutableList<SendTextField.CustomFee>.setOnGasLimitChange(value: String, index: Int) {
if (value.isBlank()) {
setEmpty(FEE_AMOUNT)
setEmpty(GAS_LIMIT)
} else {
val newGasLimit = value.parseToBigDecimal(this[GAS_LIMIT].decimals)
val gasPrice = this[GAS_PRICE].value.parseToBigDecimal(this[GAS_PRICE].decimals)
.movePointLeft(this[GAS_PRICE].decimals) // from GWEI to ETH
val newFeeAmount = newGasLimit * gasPrice
set(
index = FEE_AMOUNT,
element = this[FEE_AMOUNT].copy(
value = newFeeAmount.parseBigDecimal(this[FEE_AMOUNT].decimals),
label = getFiatReference(
rate = feeCryptoCurrencyStatusProvider()?.value?.fiatRate,
value = newFeeAmount,
appCurrency = appCurrencyProvider(),
),
),
)
val isNotExceedBalance = checkExceedBalance(
feeBalance = feeCryptoCurrencyStatusProvider()?.value?.amount,
feeAmount = newFeeAmount,
)
set(
index = index,
element = this[index].copy(
value = value,
keyboardOptions = KeyboardOptions(
imeAction = if (!isNotExceedBalance) ImeAction.None else ImeAction.Done,
keyboardType = KeyboardType.Number,
),
),
)
}
}
private companion object {
const val GAS_PRICE = 1
const val GAS_LIMIT = 2

View file

@ -72,7 +72,7 @@ internal class YieldBalancesConverter(
val rewards = yieldBalance?.balance?.items
?.filter { it.type == BalanceType.REWARDS && !it.amount.isZero() }
val isActionable = rewards?.all { it.pendingActions.isNotEmpty() } == true
val isActionable = rewards?.any { it.pendingActions.isNotEmpty() } == true
val isRewardsClaimable = rewards?.isNotEmpty() == true
val isSolana = isSolana(cryptoCurrencyStatus.currency.network.id.value)

View file

@ -57,6 +57,7 @@ internal fun StakingClaimRewardsValidatorContent(
.roundedShapeItemDecoration(index, state.rewards.lastIndex, false)
.background(TangemTheme.colors.background.action)
.clickable(
enabled = item.pendingActions.isNotEmpty(),
onClick = {
clickIntents.onActiveStake(item)
},

View file

@ -169,14 +169,15 @@ internal class DefaultSwapRepository(
return withContext(coroutineDispatcher.io) {
either {
catch(
{
block = {
exchangeStatusConverter.convert(
tangemExpressApi
.getExchangeStatus(txId)
.getOrThrow(),
)
},
{
catch = {
Timber.e("getExchangeStatus error: $it")
raise(UnknownError(it.message))
},
)

View file

@ -155,7 +155,7 @@ internal object TokenDetailsPreviewData {
onStakeClicked = {},
)
val stakedBlock = StakingBlockUM.Staked(
val stakingBalanceBlock = StakingBlockUM.Staked(
cryptoValue = stringReference("5 SOL"),
fiatValue = stringReference("456.34 $"),
rewardValue = resourceReference(R.string.staking_details_no_rewards_to_claim, wrappedList("0.43 $")),
@ -351,6 +351,6 @@ internal object TokenDetailsPreviewData {
value = PagingData.from(txHistoryItems),
),
),
stakingBlocksState = stakedBlock,
stakingBlocksState = stakingBalanceBlock,
)
}

View file

@ -1,10 +1,10 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.state
import androidx.compose.runtime.Stable
import androidx.compose.runtime.Immutable
import com.tangem.core.ui.extensions.TextReference
import java.math.BigDecimal
@Stable
@Immutable
internal sealed interface StakingBlockUM {
data object TemporaryUnavailable : StakingBlockUM

View file

@ -25,6 +25,7 @@ import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList
import timber.log.Timber
import java.math.BigDecimal
import java.util.Locale
@ -115,7 +116,10 @@ internal class TokenDetailsSwapTransactionsStateConverter(
refundToken: CryptoCurrency?,
isRefundTerminalStatus: Boolean,
): SwapTransactionsState {
if (statusModel == null || tx.activeStatus == statusModel.status) return tx
if (statusModel == null || tx.activeStatus == statusModel.status) {
Timber.e("UpdateTxStatus isn't required. Current status isn't changed")
return tx
}
val hasFailed = tx.hasFailed || statusModel.status == ExchangeStatus.Failed
val notifications = getNotification(statusModel.status, statusModel.txExternalUrl, refundToken)
val showProviderLink = getShowProviderLink(notifications, statusModel)
@ -217,13 +221,15 @@ internal class TokenDetailsSwapTransactionsStateConverter(
isPaused = isPaused,
),
)
add(
sendStep(
isSending = isSending,
isSendingDone = isSendingDone,
isRefunded = isRefunded,
),
)
if (!isPaused) {
add(
sendStep(
isSending = isSending,
isSendingDone = isSendingDone,
isRefunded = isRefunded,
),
)
}
}
}
}.toPersistentList()

View file

@ -1,18 +1,12 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.staking
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
@ -23,7 +17,7 @@ import com.tangem.core.ui.extensions.orMaskWithStars
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData.stakedBlock
import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData.stakingBalanceBlock
import com.tangem.feature.tokendetails.presentation.tokendetails.state.StakingBlockUM
import com.tangem.features.tokendetails.impl.R
import com.tangem.utils.StringsSigns
@ -36,16 +30,7 @@ internal fun StakingBalanceBlock(
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = modifier
.fillMaxWidth()
.clip(TangemTheme.shapes.roundedCornersXMedium)
.background(TangemTheme.colors.background.primary)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = ripple(),
onClick = state.onStakeClicked,
)
.padding(TangemTheme.dimens.spacing12),
modifier = modifier.fillMaxWidth(),
) {
Column(
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4),
@ -108,6 +93,6 @@ private fun StakingBalanceBlock_Preview(
private class StakingBalanceBlockPreviewProvider : PreviewParameterProvider<StakingBlockUM.Staked> {
override val values: Sequence<StakingBlockUM.Staked>
get() = sequenceOf(stakedBlock)
get() = sequenceOf(stakingBalanceBlock)
}
// endregion

View file

@ -1,12 +1,10 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.staking
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.res.TangemTheme
@ -17,11 +15,7 @@ import com.tangem.features.tokendetails.impl.R
internal fun StakingTemporaryUnavailableBlock(modifier: Modifier = Modifier) {
Column(
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4),
modifier = modifier
.fillMaxWidth()
.clip(TangemTheme.shapes.roundedCornersXMedium)
.background(TangemTheme.colors.background.primary)
.padding(TangemTheme.dimens.spacing12),
modifier = modifier.fillMaxWidth(),
) {
Text(
text = stringResource(R.string.staking_native),

View file

@ -3,8 +3,11 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.
import android.content.res.Configuration
import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Text
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
@ -14,18 +17,15 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import com.tangem.core.ui.components.RectangleShimmer
import com.tangem.core.ui.components.SecondaryButton
import com.tangem.core.ui.components.SpacerW8
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.components.*
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.utils.getGreyScaleColorFilter
import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData.stakingBalanceBlock
import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData.stakingAvailableBlock
import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData.stakingLoadingBlock
import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData.stakingTemporaryUnavailableBlock
import com.tangem.feature.tokendetails.presentation.tokendetails.state.IconState
import com.tangem.feature.tokendetails.presentation.tokendetails.state.StakingBlockUM
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.CurrencyIcon
import com.tangem.features.tokendetails.impl.R
@ -39,26 +39,35 @@ import com.tangem.features.tokendetails.impl.R
*/
@Composable
internal fun TokenStakingBlock(state: StakingBlockUM, isBalanceHidden: Boolean, modifier: Modifier = Modifier) {
AnimatedContent(
targetState = state,
contentAlignment = Alignment.CenterStart,
label = "Staking block animation",
Column(
modifier = modifier
.clip(TangemTheme.shapes.roundedCornersXMedium)
.background(TangemTheme.colors.background.primary)
.clickable(
enabled = state is StakingBlockUM.Staked,
interactionSource = remember { MutableInteractionSource() },
indication = ripple(),
onClick = { (state as? StakingBlockUM.Staked)?.onStakeClicked?.invoke() },
)
.fillMaxWidth()
.padding(all = TangemTheme.dimens.spacing12),
) {
when (it) {
is StakingBlockUM.TemporaryUnavailable -> StakingTemporaryUnavailableBlock(modifier)
is StakingBlockUM.Loading -> StakingLoading(
iconState = it.iconState,
modifier = modifier,
)
is StakingBlockUM.Staked -> StakingBalanceBlock(
state = it,
isBalanceHidden = isBalanceHidden,
modifier = modifier,
)
is StakingBlockUM.StakeAvailable -> StakingAvailableContent(
state = it,
modifier = modifier,
)
AnimatedContent(
targetState = state,
contentAlignment = Alignment.CenterStart,
label = "Staking block animation",
) {
when (it) {
is StakingBlockUM.TemporaryUnavailable -> StakingTemporaryUnavailableBlock()
is StakingBlockUM.Loading -> StakingLoading()
is StakingBlockUM.Staked -> StakingBalanceBlock(
state = it,
isBalanceHidden = isBalanceHidden,
)
is StakingBlockUM.StakeAvailable -> StakingAvailableContent(
state = it,
)
}
}
}
}
@ -66,13 +75,7 @@ internal fun TokenStakingBlock(state: StakingBlockUM, isBalanceHidden: Boolean,
@Composable
private fun StakingAvailableContent(state: StakingBlockUM.StakeAvailable, modifier: Modifier = Modifier) {
Column(
modifier = modifier
.background(
color = TangemTheme.colors.background.primary,
shape = TangemTheme.shapes.roundedCornersXMedium,
)
.fillMaxWidth()
.padding(all = TangemTheme.dimens.spacing12),
modifier = modifier.fillMaxWidth(),
) {
Row {
val (alpha, colorFilter) = remember(state.iconState.isGrayscale) {
@ -115,52 +118,30 @@ private fun StakingAvailableContent(state: StakingBlockUM.StakeAvailable, modifi
}
@Composable
private fun StakingLoading(iconState: IconState, modifier: Modifier = Modifier) {
private fun StakingLoading(modifier: Modifier = Modifier) {
Column(
modifier = modifier
.background(
color = TangemTheme.colors.background.primary,
shape = TangemTheme.shapes.roundedCornersXMedium,
)
.fillMaxWidth()
.heightIn(min = TangemTheme.dimens.size72)
.padding(all = TangemTheme.dimens.spacing12),
modifier = modifier.fillMaxWidth(),
) {
Row {
val (alpha, colorFilter) = remember(iconState.isGrayscale) {
getGreyScaleColorFilter(iconState.isGrayscale)
}
CurrencyIcon(
modifier = Modifier
.size(TangemTheme.dimens.size20)
.clip(TangemTheme.shapes.roundedCorners8)
.align(Alignment.CenterVertically),
icon = iconState,
alpha = alpha,
colorFilter = colorFilter,
)
SpacerW8()
Column {
RectangleShimmer(
modifier = Modifier
.fillMaxWidth()
.height(TangemTheme.dimens.size20),
)
Spacer(modifier = Modifier.size(TangemTheme.dimens.size4))
Spacer(modifier = Modifier.size(TangemTheme.dimens.size8))
RectangleShimmer(
modifier = Modifier
.fillMaxWidth()
.height(TangemTheme.dimens.size20),
.height(TangemTheme.dimens.size36),
)
Spacer(modifier = Modifier.size(TangemTheme.dimens.size8))
}
}
SecondaryButton(
modifier = Modifier.fillMaxWidth(),
showProgress = true,
text = TextReference.EMPTY.resolveReference(),
onClick = { /* no-op */ },
RectangleShimmer(
modifier = Modifier
.fillMaxWidth()
.height(TangemTheme.dimens.size48),
)
}
}
@ -186,6 +167,7 @@ private class StakingBlockStateProvider : CollectionPreviewParameterProvider<Sta
stakingLoadingBlock,
stakingAvailableBlock,
stakingTemporaryUnavailableBlock,
stakingBalanceBlock,
),
)
// endregion Preview

View file

@ -88,7 +88,7 @@ internal enum class Wallet2CobrandImage(
Kaspa2(
cards2ResId = R.drawable.ill_kaspa2_card2_120_106,
cards3ResId = R.drawable.ill_kaspa2_card3_120_106,
batchIds = setOf("AF25"),
batchIds = setOf("AF25", "AF61", "AF72"),
),
KaspaReseller(

View file

@ -181,11 +181,7 @@ private fun WalletContent(
contentPadding = contentPadding,
horizontalAlignment = Alignment.CenterHorizontally,
) {
item(
// !!! Type of the key should be saveable via Bundle on Android !!!
key = state.wallets.map { it.walletCardState.id.stringValue },
contentType = state.wallets.map { it.walletCardState.id },
) {
item(key = "WalletsList" + state.selectedWalletIndex, contentType = "WalletsList") {
WalletsList(
lazyListState = walletsListState,
wallets = state.wallets.map(WalletState::walletCardState).toImmutableList(),

View file

@ -58,17 +58,11 @@ internal fun WalletsList(
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
flingBehavior = rememberWalletsFlingBehaviour(lazyListState = lazyListState, itemWidth = itemWidth),
) {
items(
items = wallets,
key = { it.id.stringValue },
contentType = { it.id.stringValue },
) { state ->
items(items = wallets, contentType = { it.id.stringValue }) { state ->
WalletCard(
state = state,
isBalanceHidden = isBalanceHidden,
modifier = Modifier
.animateItemPlacement()
.width(itemWidth),
modifier = Modifier.width(itemWidth),
)
}
}

View file

@ -69,7 +69,6 @@ zxingQrCode = "3.5.1"
mviCore = "1.3.1"
kotlinSerialization = "1.4.1"
arrow = "1.2.3"
reactiveNetwork = "3.0.8"
walletConnectCore = "1.18.0"
walletConnectWeb3 = "1.11.0"
prettyLogger = "2.2.0"
@ -89,7 +88,7 @@ markdownComposeView = "0.5.4"
# endregion Other libraries
# region Tangem
tangemBlockchainSdk = "release-app_5.17-828"
tangemBlockchainSdk = "release-app_5.17-830"
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
tangemCardSdk = "release-app_5.17-392"
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^
@ -252,7 +251,6 @@ mviCore-watcher = { module = "com.github.badoo.mvicore:mvicore-diff", version.re
kotlin-serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinSerialization" }
arrow-core = { module = "io.arrow-kt:arrow-core", version.ref = "arrow" }
arrow-fx = { module = "io.arrow-kt:arrow-fx-coroutines", version.ref = "arrow" }
reactive-network = { module = "com.github.pwittchen:reactivenetwork-rx2", version.ref = "reactiveNetwork" }
walletConnectCore = { module = "com.walletconnect:android-core", version.ref = "walletConnectCore" }
walletConnectWeb3 = { module = "com.walletconnect:web3wallet", version.ref = "walletConnectWeb3" }
prettyLogger = { module = "com.orhanobut:logger", version.ref = "prettyLogger" }