Updated on 2026-08-14
This commit is contained in:
commit
f4568840a3
25 changed files with 205 additions and 192 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit d52c8382e9e94b2b44ceecd7cb7bd897e091073c
|
||||
Subproject commit cf01c91626bf58bdf6895c59a706b83c37a9e345
|
||||
|
|
@ -50,7 +50,6 @@ dependencies {
|
|||
implementation(deps.okHttp.prettyLogging)
|
||||
implementation(deps.retrofit)
|
||||
implementation(deps.retrofit.moshi)
|
||||
implementation(deps.reactive.network)
|
||||
|
||||
/** Time */
|
||||
implementation(deps.jodatime)
|
||||
|
|
|
|||
|
|
@ -64,6 +64,9 @@ enum class ExchangeStatus {
|
|||
@Json(name = "tx-failed")
|
||||
TxFailed,
|
||||
|
||||
@Json(name = "paused")
|
||||
Paused,
|
||||
|
||||
@Json(name = "unknown")
|
||||
Unknown,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ internal class SendNotificationFactory constructor(
|
|||
val currencyCheck = getCurrencyCheckUseCase(
|
||||
userWalletId = userWalletId,
|
||||
currencyStatus = cryptoCurrencyStatus,
|
||||
amount = amountValue,
|
||||
amount = sendingAmount,
|
||||
fee = feeValue,
|
||||
)
|
||||
buildList {
|
||||
|
|
|
|||
|
|
@ -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 = ""))
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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" }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue