Updated on 2026-08-14

This commit is contained in:
Tangem 2022-06-02 10:23:44 +03:00
parent 973bf9a6e9
commit 0ea16afef0
12 changed files with 108 additions and 66 deletions

View file

@ -1,16 +1,11 @@
package com.tangem.tap.features.wallet.models
import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.features.wallet.redux.ProgressState
import java.math.BigDecimal
data class TotalBalance(
val state: State,
val state: ProgressState,
val fiatAmount: BigDecimal,
val fiatCurrency: FiatCurrency,
) {
enum class State {
Loading,
SomeTokensFailed,
Success,
}
}
)

View file

@ -1,7 +1,11 @@
package com.tangem.tap.features.wallet.redux
import android.content.Context
import com.tangem.blockchain.common.*
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import com.tangem.blockchain.common.Wallet
import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.common.address.AddressType
import com.tangem.common.card.Card
import com.tangem.tap.common.entities.FiatCurrency
@ -11,8 +15,8 @@ import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.wallet.R
import org.rekotlin.Action
import java.math.BigDecimal
import org.rekotlin.Action
sealed class WalletAction : Action {
@ -21,6 +25,7 @@ sealed class WalletAction : Action {
data class SetIfTestnetCard(val isTestnet: Boolean) : WalletAction()
object LoadData : WalletAction() {
object Refresh : WalletAction()
data class Failure(val error: TapError) : WalletAction()
}

View file

@ -33,7 +33,7 @@ import com.tangem.tap.features.wallet.models.hasPendingTransactions
import com.tangem.tap.features.wallet.models.hasSendableAmounts
import com.tangem.tap.features.wallet.models.isSendableAmount
import com.tangem.tap.features.wallet.redux.reducers.calculateTotalFiatAmount
import com.tangem.tap.features.wallet.redux.reducers.findTotalBalanceState
import com.tangem.tap.features.wallet.redux.reducers.findProgressState
import com.tangem.tap.features.wallet.ui.BalanceStatus
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
@ -203,6 +203,7 @@ data class WalletState(
fun updateWalletStore(walletStore: WalletStore?): WalletState {
return copy(wallets = replaceWalletInWallets(walletStore))
.updateTotalBalance()
.updateProgressState()
}
private fun updateWalletStores(walletStores: List<WalletStore>): WalletState {
@ -220,6 +221,7 @@ data class WalletState(
}
return copy(wallets = updatedWallets + walletStoresMutable)
.updateTotalBalance()
.updateProgressState()
}
fun removeWallet(walletData: WalletData?): WalletState {
@ -282,9 +284,9 @@ data class WalletState(
}
private fun updateTotalBalance(): WalletState {
return if (wallets.isNotEmpty()) {
return if (this.wallets.isNotEmpty()) {
val globalState = store.state.globalState
val walletsData = wallets
val walletsData = this.wallets
.flatMap(WalletStore::walletsData)
.filterNot { wallet ->
wallet.currency.isCustomCurrency(
@ -297,7 +299,7 @@ data class WalletState(
this.copy(
totalBalance = TotalBalance(
state = walletsData.findTotalBalanceState(),
state = walletsData.findProgressState(),
fiatAmount = walletsData.calculateTotalFiatAmount(),
fiatCurrency = globalState.appCurrency
)
@ -306,6 +308,19 @@ data class WalletState(
totalBalance = null
)
}
private fun updateProgressState(): WalletState {
return if (this.wallets.isNotEmpty()) {
val walletsData = this.wallets
.flatMap(WalletStore::walletsData)
val newProgressState = walletsData.findProgressState()
this.copy(
state = walletsData.findProgressState(),
error = this.error.takeIf { newProgressState == ProgressState.Error }
)
} else this
}
}
sealed interface WalletDialog : StateDialog {
@ -318,7 +333,7 @@ sealed interface WalletDialog : StateDialog {
) : WalletDialog
}
enum class ProgressState : WidgetState { Loading, Done, Error }
enum class ProgressState : WidgetState { Loading, Refreshing, Done, Error }
enum class ErrorType { NoInternetConnection }

View file

@ -15,7 +15,12 @@ import com.tangem.domain.common.extensions.withMainContext
import com.tangem.operations.attestation.Attestation
import com.tangem.operations.attestation.OnlineCardVerifier
import com.tangem.tap.common.analytics.Analytics
import com.tangem.tap.common.extensions.*
import com.tangem.tap.common.extensions.copyToClipboard
import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.onCardScanned
import com.tangem.tap.common.extensions.shareText
import com.tangem.tap.common.extensions.stripZeroPlainString
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.navigation.AppScreen
@ -28,12 +33,17 @@ import com.tangem.tap.features.send.redux.PrepareSendScreen
import com.tangem.tap.features.wallet.models.PendingTransactionType
import com.tangem.tap.features.wallet.models.getPendingTransactions
import com.tangem.tap.features.wallet.models.getSendableAmounts
import com.tangem.tap.features.wallet.redux.*
import com.tangem.tap.features.wallet.redux.Currency
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletData
import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.redux.WalletStore
import com.tangem.tap.network.NetworkStateChanged
import com.tangem.tap.preferencesStorage
import com.tangem.tap.scope
import com.tangem.tap.store
import com.tangem.tap.tangemSdkManager
import java.math.BigDecimal
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.launch
@ -41,7 +51,6 @@ import org.rekotlin.Action
import org.rekotlin.DispatchFunction
import org.rekotlin.Middleware
import timber.log.Timber
import java.math.BigDecimal
class WalletMiddleware {
private val tradeCryptoMiddleware = TradeCryptoMiddleware()
@ -198,7 +207,8 @@ class WalletMiddleware {
store.dispatchOnMain(WalletAction.Warnings.CheckIfNeeded)
}
}
is WalletAction.LoadData -> {
is WalletAction.LoadData,
is WalletAction.LoadData.Refresh -> {
scope.launch {
val scanNoteResponse = globalState.scanResponse ?: return@launch
if (walletState.walletsData.isNotEmpty()) {

View file

@ -11,7 +11,6 @@ import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.features.wallet.models.filterByToken
import com.tangem.tap.features.wallet.models.getPendingTransactions
import com.tangem.tap.features.wallet.models.removeUnknownTransactions
import com.tangem.tap.features.wallet.models.toPendingTransactions
import com.tangem.tap.features.wallet.redux.Currency
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletData
@ -41,8 +40,8 @@ class MultiWalletReducer {
}
val walletData = WalletData(
currencyData = BalanceWidgetData(
BalanceStatus.Loading,
blockchain.blockchain.fullName,
status = BalanceStatus.Loading,
currency = blockchain.blockchain.fullName,
currencySymbol = blockchain.blockchain.currency,
token = cardToken
),
@ -77,8 +76,8 @@ class MultiWalletReducer {
val walletData = WalletData(
currencyData = BalanceWidgetData(
BalanceStatus.Loading,
action.blockchain.blockchain.fullName,
status = BalanceStatus.Loading,
currency = action.blockchain.blockchain.fullName,
currencySymbol = action.blockchain.blockchain.currency,
),
walletAddresses = createAddressList(wallet),
@ -184,7 +183,7 @@ fun Token.toWallet(state: WalletState, blockchain: BlockchainNetwork): WalletDat
return WalletData(
currencyData = BalanceWidgetData(
BalanceStatus.Loading,
status = BalanceStatus.Loading,
currency = this.name,
currencySymbol = this.symbol
),

View file

@ -12,7 +12,6 @@ import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.features.wallet.models.filterByToken
import com.tangem.tap.features.wallet.models.getPendingTransactions
import com.tangem.tap.features.wallet.models.removeUnknownTransactions
import com.tangem.tap.features.wallet.models.toPendingTransactions
import com.tangem.tap.features.wallet.redux.Currency
import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.features.wallet.redux.TradeCryptoState
@ -61,7 +60,8 @@ class OnWalletLoadedReducer {
val fiatAmount = walletData.fiatRate?.let { coinAmountValue?.toFiatValue(it) }
val newWalletData = walletData.copy(
currencyData = walletData.currencyData.copy(
status = balanceStatus, currency = wallet.blockchain.fullName,
status = balanceStatus,
currency = wallet.blockchain.fullName,
currencySymbol = wallet.blockchain.currency,
blockchainAmount = coinAmountValue,
amount = coinAmountValue,
@ -110,17 +110,8 @@ class OnWalletLoadedReducer {
val newWallets = tokens + newWalletData
val wallets = walletState.replaceSomeWallets((newWallets))
val state = if (wallets.any { it.currencyData.status == BalanceStatus.Loading }) {
ProgressState.Loading
} else {
ProgressState.Done
}
return walletState
.updateWalletsData(wallets)
.copy(
state = state,
error = null
)
}
private fun onSingleWalletLoaded(wallet: Wallet, walletState: WalletState): WalletState {

View file

@ -1,14 +1,14 @@
package com.tangem.tap.features.wallet.redux.reducers
import com.tangem.tap.features.wallet.models.TotalBalance
import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.features.wallet.redux.WalletData
import com.tangem.tap.features.wallet.ui.BalanceStatus
import java.math.BigDecimal
fun List<WalletData>.findTotalBalanceState(): TotalBalance.State {
fun List<WalletData>.findProgressState(): ProgressState {
return this
.mapToTotalBalanceState()
.reduce(TotalBalance.State::or)
.mapToProgressState()
.reduce(ProgressState::or)
}
fun List<WalletData>.calculateTotalFiatAmount(): BigDecimal {
@ -17,34 +17,43 @@ fun List<WalletData>.calculateTotalFiatAmount(): BigDecimal {
.reduce(BigDecimal::plus)
}
private fun List<WalletData>.mapToTotalBalanceState(): List<TotalBalance.State> {
private fun List<WalletData>.mapToProgressState(): List<ProgressState> {
return this.map {
when (it.currencyData.status) {
BalanceStatus.Refreshing -> ProgressState.Refreshing
BalanceStatus.VerifiedOnline,
BalanceStatus.SameCurrencyTransactionInProgress,
BalanceStatus.TransactionInProgress,
BalanceStatus.NoAccount -> TotalBalance.State.Success
BalanceStatus.NoAccount -> ProgressState.Done
BalanceStatus.Unreachable,
BalanceStatus.EmptyCard,
BalanceStatus.UnknownBlockchain -> TotalBalance.State.SomeTokensFailed
BalanceStatus.UnknownBlockchain -> ProgressState.Error
BalanceStatus.Loading,
null -> TotalBalance.State.Loading
null -> ProgressState.Loading
}
}
}
private infix fun TotalBalance.State.or(newState: TotalBalance.State): TotalBalance.State {
private infix fun ProgressState.or(newState: ProgressState): ProgressState {
return when (this) {
TotalBalance.State.Loading -> when (newState) {
TotalBalance.State.Loading,
TotalBalance.State.SomeTokensFailed,
TotalBalance.State.Success -> this
ProgressState.Loading -> when (newState) {
ProgressState.Loading,
ProgressState.Refreshing,
ProgressState.Error,
ProgressState.Done -> this
}
TotalBalance.State.Success,
TotalBalance.State.SomeTokensFailed -> when (newState) {
TotalBalance.State.Loading,
TotalBalance.State.SomeTokensFailed -> newState
TotalBalance.State.Success -> this
ProgressState.Done,
ProgressState.Error -> when (newState) {
ProgressState.Loading,
ProgressState.Refreshing,
ProgressState.Error -> newState
ProgressState.Done -> this
}
ProgressState.Refreshing -> when (newState) {
ProgressState.Loading -> this
ProgressState.Refreshing,
ProgressState.Error,
ProgressState.Done -> newState
}
}
}

View file

@ -25,7 +25,6 @@ import com.tangem.tap.features.wallet.redux.TradeCryptoState
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletAddresses
import com.tangem.tap.features.wallet.redux.WalletData
import com.tangem.tap.features.wallet.redux.WalletDialog
import com.tangem.tap.features.wallet.redux.WalletMainButton
import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.redux.WalletStore
@ -135,14 +134,25 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
error = null,
)
}
is WalletAction.LoadData.Refresh -> {
newState = newState.copy(
state = ProgressState.Refreshing,
error = null,
)
}
is WalletAction.LoadWallet -> {
val balanceStatus = if (newState.state == ProgressState.Refreshing) {
BalanceStatus.Refreshing
} else {
BalanceStatus.Loading
}
if (action.blockchain == null) {
val wallets = newState.wallets.map {
it.copy(
walletsData = it.walletsData.map { walletData ->
walletData.copy(
currencyData = walletData.currencyData.copy(
status = BalanceStatus.Loading,
status = balanceStatus,
currency = walletData.currencyData.currency,
currencySymbol = walletData.currencyData.currencySymbol,
),
@ -169,7 +179,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
.map { wallet ->
wallet.copy(
currencyData = wallet.currencyData.copy(
status = BalanceStatus.Loading,
status = balanceStatus,
currency = wallet.currencyData.currency,
currencySymbol = wallet.currencyData.currencySymbol,
),

View file

@ -13,6 +13,7 @@ enum class BalanceStatus {
SameCurrencyTransactionInProgress,
Unreachable,
Loading,
Refreshing,
NoAccount,
EmptyCard,
UnknownBlockchain

View file

@ -135,15 +135,14 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
showWarningsIfPresent(state.mainWarningsList)
binding.srlWallet.isRefreshing = state.state == ProgressState.Refreshing
binding.srlWallet.setOnRefreshListener {
if (state.state != ProgressState.Loading) {
store.dispatch(WalletAction.LoadData)
if (state.state != ProgressState.Loading ||
state.state != ProgressState.Refreshing
) {
store.dispatch(WalletAction.LoadData.Refresh)
}
}
if (state.state != ProgressState.Loading) {
binding.srlWallet.isRefreshing = false
}
}
private fun showWarningsIfPresent(warnings: List<WarningMessage>) {

View file

@ -64,6 +64,9 @@ class WalletAdapter
fun bind(wallet: WalletData) = with(binding) {
val status = wallet.currencyData.status
// Skip changes when on refreshing status
if (status == BalanceStatus.Refreshing) return@with
val isCustomCurrency = wallet.currency.isCustomCurrency(
derivationStyle = store.state.globalState
.scanResponse
@ -83,6 +86,7 @@ class WalletAdapter
BalanceStatus.EmptyCard,
BalanceStatus.UnknownBlockchain,
BalanceStatus.Loading,
BalanceStatus.Refreshing,
null -> null
}

View file

@ -14,6 +14,7 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.currenciesRepository
import com.tangem.tap.features.tokens.redux.TokensAction
import com.tangem.tap.features.wallet.models.TotalBalance
import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.ui.BalanceStatus
@ -125,16 +126,19 @@ class MultiWalletView : WalletView {
) = with(binding.lCardTotalBalance) {
root.isVisible = totalBalance != null
if (totalBalance != null) {
if (totalBalance.state == TotalBalance.State.Loading) {
// Skip changes when on refreshing state
if (totalBalance.state == ProgressState.Refreshing) return@with
if (totalBalance.state == ProgressState.Loading) {
veilBalance.veil()
} else {
veilBalance.unVeil()
}
tvProcessing.animateVisibility(
show = totalBalance.state == TotalBalance.State.SomeTokensFailed
show = totalBalance.state == ProgressState.Error
)
tvBalance.text = if (totalBalance.state == TotalBalance.State.SomeTokensFailed) ""
tvBalance.text = if (totalBalance.state == ProgressState.Error) ""
else totalBalance.fiatAmount.formatAmountAsSpannedString(
currencySymbol = totalBalance.fiatCurrency.symbol
)