Updated on 2026-08-14

This commit is contained in:
Tangem 2023-02-28 19:41:48 +03:00
parent 8b81f309c8
commit d28e324cd0
22 changed files with 151 additions and 1473 deletions

View file

@ -327,9 +327,10 @@ class DetailsMiddleware {
scanResponse: ScanResponse?,
enableAccessCodesSaving: Boolean,
): CompletionResult<Unit> {
val userWallet = scanResponse?.let { UserWalletBuilder(it).build() }
val userWallet = userWalletsListManager.selectedUserWalletSync
?: scanResponse?.let { UserWalletBuilder(it).build() }
?: return CompletionResult.Failure(
TangemSdkError.ExceptionError(IllegalStateException("scanResponse is null")),
error = TangemSdkError.ExceptionError(IllegalStateException("scanResponse is null")),
)
updateUserWalletsListManager(enableUserWalletsSaving = true)

View file

@ -68,6 +68,8 @@ object OnboardingHelper {
when {
// When should save user wallets, then save card without navigate to save wallet screen
preferencesStorage.shouldSaveUserWallets -> scope.launch {
proceedWithScanResponse(scanResponse, backupCardsIds)
store.dispatchOnMain(
SaveWalletAction.ProvideBackupInfo(
scanResponse = scanResponse,
@ -81,7 +83,7 @@ object OnboardingHelper {
// then open save wallet screen
tangemSdkManager.canUseBiometry &&
preferencesStorage.shouldShowSaveUserWalletScreen -> scope.launch {
proceedWithScanResponse(scanResponse)
proceedWithScanResponse(scanResponse, backupCardsIds)
delay(timeMillis = 1_200)
@ -96,7 +98,7 @@ object OnboardingHelper {
}
// If device has no biometry and save wallet screen has been shown, then go through old scenario
else -> scope.launch {
proceedWithScanResponse(scanResponse)
proceedWithScanResponse(scanResponse, backupCardsIds)
}
}
@ -107,13 +109,16 @@ object OnboardingHelper {
Analytics.removeContext()
}
private suspend fun proceedWithScanResponse(scanResponse: ScanResponse) {
val userWallet = UserWalletBuilder(scanResponse).build().guard {
Timber.e("User wallet not created")
return
}
private suspend fun proceedWithScanResponse(scanResponse: ScanResponse, backupCardsIds: List<String>?) {
val userWallet = UserWalletBuilder(scanResponse)
.backupCardsIds(backupCardsIds?.toSet())
.build()
.guard {
Timber.e("User wallet not created")
return
}
userWalletsListManager.save(userWallet)
userWalletsListManager.save(userWallet, canOverride = true)
.doOnFailure { error ->
Timber.e(error, "Unable to save user wallet")
}

View file

@ -11,10 +11,11 @@ import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.features.onboarding.OnboardingHelper
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.models.toCurrencies
import com.tangem.tap.scope
import com.tangem.tap.store
import com.tangem.tap.tangemSdkManager
import com.tangem.tap.userTokensRepository
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.rekotlin.Action
@ -112,9 +113,12 @@ private fun handleOtherCardsAction(action: Action) {
)
}
store.dispatch(
WalletAction.MultiWallet.SaveCurrencies(blockchainNetworks, updatedResponse.card),
)
scope.launch {
userTokensRepository.saveUserTokens(
card = result.data.card,
tokens = blockchainNetworks.toCurrencies(),
)
}
delay(DELAY_SDK_DIALOG_CLOSE)
store.dispatch(OnboardingOtherCardsAction.SetStepOfScreen(OnboardingOtherCardsStep.Done))

View file

@ -26,13 +26,14 @@ import com.tangem.tap.features.onboarding.OnboardingDialog
import com.tangem.tap.features.onboarding.OnboardingHelper
import com.tangem.tap.features.onboarding.products.wallet.saltPay.SaltPayActivationManagerFactory
import com.tangem.tap.features.onboarding.products.wallet.saltPay.redux.OnboardingSaltPayAction
import com.tangem.tap.features.wallet.models.toCurrencies
import com.tangem.tap.features.wallet.redux.Artwork
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.preferencesStorage
import com.tangem.tap.scope
import com.tangem.tap.store
import com.tangem.tap.tangemSdk
import com.tangem.tap.tangemSdkManager
import com.tangem.tap.userTokensRepository
import kotlinx.coroutines.launch
import org.rekotlin.Action
import org.rekotlin.DispatchFunction
@ -135,12 +136,12 @@ private fun handleWalletAction(action: Action, state: () -> AppState?, dispatch:
BlockchainNetwork(blockchain, result.data.card)
}
store.dispatch(
WalletAction.MultiWallet.SaveCurrencies(
blockchainNetworks = blockchainNetworks,
scope.launch {
userTokensRepository.saveUserTokens(
card = result.data.card,
),
)
tokens = blockchainNetworks.toCurrencies(),
)
}
startCardActivation(updatedResponse)
store.dispatch(OnboardingWalletAction.ResumeBackup)
}

View file

@ -19,6 +19,7 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.model.builders.UserWalletBuilder
import com.tangem.tap.domain.userWalletList.UserWalletsListManager
import com.tangem.tap.domain.userWalletList.di.provideBiometricImplementation
import com.tangem.tap.domain.userWalletList.isLockable
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.foregroundActivityObserver
import com.tangem.tap.preferencesStorage
@ -97,7 +98,8 @@ internal class SaveWalletMiddleware {
val userWallet = userWalletsListManager.selectedUserWalletSync
?: UserWalletBuilder(scanResponse)
.backupCardsIds(state.backupInfo?.backupCardsIds)
.build() ?: return@launch
.build()
?: return@launch
provideBiometricUserWalletsListManager()
@ -110,13 +112,11 @@ internal class SaveWalletMiddleware {
}
.doOnSuccess {
preferencesStorage.shouldSaveUserWallets = true
// Enable saving access codes only if this is the first time user save the wallet
preferencesStorage.shouldSaveAccessCodes = isFirstSavedWallet ||
preferencesStorage.shouldSaveAccessCodes
store.dispatchWithMain(SaveWalletAction.Save.Success)
store.dispatchWithMain(NavigationAction.PopBackTo(AppScreen.Wallet))
store.dispatchWithMain(WalletAction.UpdateCanSaveUserWallets(canSaveUserWallets = true))
}
@ -124,6 +124,8 @@ internal class SaveWalletMiddleware {
}
private suspend fun provideBiometricUserWalletsListManager() {
if (store.state.globalState.userWalletsListManager?.isLockable == true) return
val context = foregroundActivityObserver.foregroundActivity?.applicationContext.guard {
val error = IllegalStateException("No activities in foreground")
Timber.e(error)

View file

@ -12,6 +12,7 @@ import com.tangem.blockchain.common.TransactionSender
import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.guard
import com.tangem.common.services.Result
import com.tangem.core.analytics.Analytics
import com.tangem.domain.common.CardDTO
@ -333,17 +334,16 @@ private fun updateWarnings(dispatch: (Action) -> Unit) {
}
private suspend fun updateWallet(walletManager: WalletManager) {
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync
if (selectedUserWallet != null) {
val wallet = walletManager.wallet
walletCurrenciesManager.update(
userWallet = selectedUserWallet,
currency = Currency.Blockchain(
blockchain = wallet.blockchain,
derivationPath = wallet.publicKey.derivationPath?.rawPath,
),
)
} else {
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard {
Timber.e("Unable to update wallet, no user wallet selected")
return
}
val wallet = walletManager.wallet
walletCurrenciesManager.update(
userWallet = selectedUserWallet,
currency = Currency.Blockchain(
blockchain = wallet.blockchain,
derivationPath = wallet.publicKey.derivationPath?.rawPath,
),
)
}

View file

@ -5,6 +5,7 @@ import com.tangem.blockchain.common.DerivationStyle
import com.tangem.common.CompletionResult
import com.tangem.common.card.EllipticCurve
import com.tangem.common.extensions.ByteArrayKey
import com.tangem.common.extensions.guard
import com.tangem.common.extensions.toMapKey
import com.tangem.common.flatMap
import com.tangem.common.hdWallet.DerivationPath
@ -273,35 +274,33 @@ class TokensMiddleware {
)
private fun submitAdd(scanResponse: ScanResponse, currencyList: List<Currency>) {
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync
if (selectedUserWallet != null) {
scope.launch {
userWalletsListManager.update(
userWalletId = selectedUserWallet.walletId,
update = { userWallet ->
userWallet.copy(scanResponse = scanResponse)
},
)
.flatMap { updatedUserWallet ->
walletCurrenciesManager.addCurrencies(
userWallet = updatedUserWallet,
currenciesToAdd = currencyList,
)
}
}
} else {
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard {
Timber.e("Unable to add currencies, no user wallet selected")
return
}
scope.launch {
userWalletsListManager.update(
userWalletId = selectedUserWallet.walletId,
update = { userWallet ->
userWallet.copy(scanResponse = scanResponse)
},
)
.flatMap { updatedUserWallet ->
walletCurrenciesManager.addCurrencies(
userWallet = updatedUserWallet,
currenciesToAdd = currencyList,
)
}
}
}
private suspend fun removeCurrenciesIfNeeded(currencies: List<Currency>) {
if (currencies.isEmpty()) return
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync
if (selectedUserWallet != null) {
walletCurrenciesManager.removeCurrencies(selectedUserWallet, currencies)
} else {
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard {
Timber.e("Unable to remove currencies, no user wallet selected")
return
}
walletCurrenciesManager.removeCurrencies(selectedUserWallet, currencies)
}
private fun isNeedToDerive(scanResponse: ScanResponse, currency: Currency): Boolean {

View file

@ -2,12 +2,8 @@ package com.tangem.tap.features.wallet.redux
import android.content.Context
import com.tangem.blockchain.common.Amount
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.core.analytics.AnalyticsEvent
import com.tangem.domain.common.CardDTO
import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.common.redux.NotificationAction
import com.tangem.tap.domain.TapError
@ -20,7 +16,6 @@ import com.tangem.tap.features.wallet.models.TotalBalance
import com.tangem.tap.features.wallet.redux.models.WalletDialog
import com.tangem.wallet.R
import org.rekotlin.Action
import java.math.BigDecimal
sealed class WalletAction : Action {
@ -28,81 +23,20 @@ sealed class WalletAction : Action {
data class UpdateCanSaveUserWallets(val canSaveUserWallets: Boolean) : WalletAction()
data class ResetState(val newCard: CardDTO) : WalletAction()
data class SetIfTestnetCard(val isTestnet: Boolean) : WalletAction()
object LoadData : WalletAction() {
object Refresh : WalletAction()
object Success : WalletAction()
data class Failure(val error: TapError?) : WalletAction()
}
data class LoadWallet(
val blockchain: BlockchainNetwork? = null,
val walletManager: WalletManager? = null,
) : WalletAction() {
data class Success(val wallet: Wallet, val blockchain: BlockchainNetwork) : WalletAction()
data class NoAccount(
val wallet: Wallet,
val blockchain: BlockchainNetwork,
val amountToCreateAccount: String,
) : WalletAction()
data class Failure(val wallet: Wallet, val errorMessage: String? = null) : WalletAction()
}
data class SetArtworkId(val artworkId: String?) : WalletAction()
sealed class UserTokens : WalletAction() {
object Loading : UserTokens()
object Loaded : UserTokens()
}
sealed class MultiWallet : WalletAction() {
data class SetIsMultiwalletAllowed(val isMultiwalletAllowed: Boolean) : MultiWallet()
data class AddBlockchains(
val blockchains: List<BlockchainNetwork>,
val walletManagers: List<WalletManager>,
) : MultiWallet()
data class AddTokens(
val tokens: List<Token>,
val blockchain: BlockchainNetwork,
) : MultiWallet()
data class AddBlockchain(
val blockchain: BlockchainNetwork,
val walletManager: WalletManager?,
val save: Boolean,
) : MultiWallet()
data class AddToken(
val token: Token,
val blockchain: BlockchainNetwork,
val save: Boolean,
) : MultiWallet()
data class SaveCurrencies(
val blockchainNetworks: List<BlockchainNetwork>,
val card: CardDTO? = null,
) : MultiWallet()
data class TokenLoaded(
val amount: Amount,
val token: Token,
val blockchain: BlockchainNetwork,
) : MultiWallet()
data class SelectWallet(val currency: Currency?) : MultiWallet()
data class SetSingleWalletCurrency(val currency: Currency?) : MultiWallet()
data class TryToRemoveWallet(val currency: Currency) : MultiWallet()
data class RemoveWallet(val currency: Currency) : MultiWallet()
data class RemoveWallets(val currencies: List<Currency>) : MultiWallet()
data class ShowWalletBackupWarning(val show: Boolean) : MultiWallet()
object BackupWallet : MultiWallet()
object ScheduleCheckForMissingDerivation : MultiWallet()
data class AddMissingDerivations(val blockchains: List<BlockchainNetwork>) : MultiWallet()
@ -129,24 +63,6 @@ sealed class WalletAction : Action {
class CheckRemainingSignatures(val remainingSignatures: Int?) : Warnings()
}
data class LoadFiatRate(
val wallet: Wallet? = null,
val coinsList: List<Currency>? = null,
) : WalletAction() {
data class Success(
val fiatRates: Map<Currency, BigDecimal?>,
) : WalletAction()
object Failure : WalletAction()
}
class LoadCardInfo(val card: CardDTO) : WalletAction()
data class LoadArtwork(val card: CardDTO, val artworkId: String?) : WalletAction() {
data class Success(val artwork: Artwork) : WalletAction()
object Failure : WalletAction()
}
data class Scan(val onScanSuccessEvent: AnalyticsEvent?) : WalletAction()
data class Send(val amount: Amount? = null) : WalletAction()
@ -183,7 +99,6 @@ sealed class WalletAction : Action {
data class ExploreAddress(val exploreUrl: String, val context: Context) : WalletAction()
object CreateWallet : WalletAction()
object EmptyWallet : WalletAction()
object ChangeWallet : WalletAction()
object ShowSaveWalletIfNeeded : WalletAction()
@ -206,14 +121,6 @@ sealed class WalletAction : Action {
data class ChangeSelectedAddress(val type: AddressType) : WalletAction()
data class SetWalletRent(
val wallet: Wallet,
val minRent: String,
val rentExempt: String,
) : WalletAction()
data class RemoveWalletRent(val wallet: Wallet) : WalletAction()
sealed class AppCurrencyAction : WalletAction() {
object ChooseAppCurrency : AppCurrencyAction()
data class SelectAppCurrency(val fiatCurrency: FiatCurrency) : AppCurrencyAction()

View file

@ -2,8 +2,6 @@ package com.tangem.tap.features.wallet.redux
import android.graphics.Bitmap
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.tap.common.entities.Button
@ -11,16 +9,13 @@ import com.tangem.tap.common.extensions.toQrCode
import com.tangem.tap.common.redux.global.CryptoCurrencyName
import com.tangem.tap.common.toggleWidget.WidgetState
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
import com.tangem.tap.domain.getFirstToken
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.models.TotalBalance
import com.tangem.tap.features.wallet.redux.reducers.calculateTotalFiatAmount
import com.tangem.tap.features.wallet.redux.reducers.findProgressState
import com.tangem.tap.features.wallet.ui.BalanceStatus
import com.tangem.tap.store
import com.tangem.tap.userWalletsListManager
import org.rekotlin.StateType
import kotlin.properties.ReadOnlyProperty
@ -88,12 +83,6 @@ data class WalletState(
val primaryWalletData: WalletData?
get() = primaryWalletStore?.walletsData?.firstOrNull()
val primaryBlockchain: Blockchain?
get() = primaryWalletManager?.wallet?.blockchain
val primaryToken: Token?
get() = primaryWalletManager?.wallet?.getFirstToken()
val primaryTokenData: WalletData?
get() = primaryWalletStore?.walletsData?.toMutableList()
?.apply { remove(primaryWalletData) }
@ -103,9 +92,6 @@ data class WalletState(
primaryWalletData?.currencyData?.status != BalanceStatus.EmptyCard &&
primaryWalletData?.currencyData?.status != BalanceStatus.UnknownBlockchain
val hasSavedWallets: Boolean
get() = userWalletsListManager.hasUserWallets
fun getWalletManager(currency: Currency?): WalletManager? {
if (currency?.blockchain == null) return null
return getWalletStore(currency)?.walletManager
@ -132,14 +118,7 @@ data class WalletState(
}
}
fun getWalletStore(wallet: Wallet?): WalletStore? {
if (wallet == null) return null
val currency =
Currency.Blockchain(wallet.blockchain, wallet.publicKey.derivationPath?.rawPath)
return getWalletStore(currency)
}
fun getWalletStore(blockchainNetwork: BlockchainNetwork?): WalletStore? {
private fun getWalletStore(blockchainNetwork: BlockchainNetwork?): WalletStore? {
if (blockchainNetwork == null) return null
return walletsStores.firstOrNull {
it.blockchainNetwork.derivationPath == blockchainNetwork.derivationPath &&
@ -152,26 +131,12 @@ data class WalletState(
return getWalletStore(currency)?.walletsData?.firstOrNull { it.currency == currency }
}
fun replaceWalletStoreInWalletsStores(wallet: WalletStore?): List<WalletStore> {
if (wallet == null) return walletsStores
var changed = false
val updatedWallets = walletsStores.map {
if (it.blockchainNetwork == wallet.blockchainNetwork) {
changed = true
wallet
} else {
it
}
}
return if (changed) updatedWallets else walletsStores + wallet
}
fun updateWalletData(walletData: WalletData?): WalletState {
if (walletData == null) return this
return updateWalletsData(listOf(walletData))
}
fun updateWalletsData(walletsData: List<WalletData>): WalletState {
private fun updateWalletsData(walletsData: List<WalletData>): WalletState {
val walletStores = walletsData
.map { BlockchainNetwork(it.currency.blockchain, it.currency.derivationPath, emptyList()) }
.distinct().map { getWalletStore(it) }.mapNotNull { it?.updateWallets(walletsData) }
@ -179,12 +144,6 @@ data class WalletState(
return updateWalletsStores(walletStores)
}
fun updateWalletStore(walletStore: WalletStore?): WalletState {
return copy(walletsStores = replaceWalletStoreInWalletsStores(walletStore))
.updateTotalBalance()
.updateProgressState()
}
private fun updateWalletsStores(walletStores: List<WalletStore>): WalletState {
val walletStoresMutable = walletStores.toMutableList()
val updatedWallets = walletsStores.map { oldWalletStore ->
@ -199,54 +158,9 @@ data class WalletState(
}
}
return copy(walletsStores = updatedWallets + walletStoresMutable)
.updateTotalBalance()
.updateProgressState()
}
fun removeWalletData(walletData: WalletData?): WalletState {
if (walletData == null) return this
return when (val currency = walletData.currency) {
is Currency.Blockchain -> {
val walletStores = walletsStores.filterNot {
it.blockchainNetwork.blockchain == currency.blockchain &&
it.blockchainNetwork.derivationPath == currency.derivationPath
}
copy(walletsStores = walletStores)
.updateTotalBalance()
.updateProgressState()
}
is Currency.Token -> {
val walletStore = getWalletStore(walletData.currency)
val walletDataList = walletStore?.walletsData
?.filterNot { it.currency == walletData.currency }
?: emptyList()
val updatedWalletManager = walletStore?.walletManager?.also { it.removeToken(currency.token) }
val updatedWalletStore = walletStore?.copy(
walletsData = walletDataList,
walletManager = updatedWalletManager,
)
updateWalletStore(updatedWalletStore)
}
}
}
private fun updateTotalBalance(): WalletState {
val walletsData = this.walletsStores
.flatMap(WalletStore::walletsData)
return if (walletsData.isNotEmpty()) {
this.copy(
totalBalance = TotalBalance(
state = walletsData.findProgressState(),
fiatAmount = walletsData.calculateTotalFiatAmount(),
fiatCurrency = store.state.globalState.appCurrency,
),
)
} else {
this.copy(totalBalance = null)
}
}
private fun updateProgressState(): WalletState {
val walletsData = this.walletsStores
.flatMap(WalletStore::walletsData)
@ -268,21 +182,6 @@ data class WalletState(
}
}
fun List<WalletData>.replaceSomeWalletsData(newWallets: List<WalletData>): List<WalletData> {
val remainingWallets: MutableList<WalletData> = newWallets.toMutableList()
val updatedWallets = this.map { wallet ->
val newWallet = newWallets
.firstOrNull { wallet.currency == it.currency }
if (newWallet == null) {
wallet
} else {
remainingWallets.remove(newWallet)
newWallet
}
}
return updatedWallets + remainingWallets
}
enum class ProgressState : WidgetState { Loading, Refreshing, Done, Error }
enum class ErrorType { NoInternetConnection }

View file

@ -1,5 +1,6 @@
package com.tangem.tap.features.wallet.redux.middlewares
import com.tangem.common.extensions.guard
import com.tangem.core.analytics.Analytics
import com.tangem.datasource.api.tangemTech.models.CurrenciesResponse
import com.tangem.tap.common.analytics.events.AnalyticsParam
@ -67,13 +68,12 @@ class AppCurrencyMiddleware(
store.dispatch(GlobalAction.ChangeAppCurrency(action.fiatCurrency))
store.dispatch(DetailsAction.ChangeAppCurrency(action.fiatCurrency))
store.dispatch(WalletSelectorAction.ChangeAppCurrency(action.fiatCurrency))
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync
if (selectedUserWallet != null) {
scope.launch {
tapWalletManager.loadData(selectedUserWallet, refresh = true)
}
} else {
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard {
Timber.e("Unable to select currency, no user wallet selected")
return
}
scope.launch {
tapWalletManager.loadData(selectedUserWallet, refresh = true)
}
}

View file

@ -1,8 +1,5 @@
package com.tangem.tap.features.wallet.redux.middlewares
import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.Token
import com.tangem.blockchain.common.WalletManager
import com.tangem.common.doOnSuccess
import com.tangem.common.extensions.guard
import com.tangem.common.flatMap
@ -13,23 +10,14 @@ import com.tangem.tap.common.extensions.addContext
import com.tangem.tap.common.extensions.dispatchDialogShow
import com.tangem.tap.common.extensions.dispatchErrorNotification
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.safeUpdate
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.global.GlobalState
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.extensions.makeWalletManagerForApp
import com.tangem.tap.domain.model.UserWallet
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.demo.isDemoCard
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.models.toCurrencies
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.redux.models.WalletDialog
import com.tangem.tap.features.wallet.redux.reducers.toWallet
import com.tangem.tap.scope
import com.tangem.tap.store
import com.tangem.tap.tangemSdkManager
@ -39,69 +27,19 @@ import com.tangem.tap.walletCurrenciesManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import timber.log.Timber
import java.math.BigDecimal
class MultiWalletMiddleware {
@Suppress("LongMethod", "ComplexMethod")
fun handle(
action: WalletAction.MultiWallet,
walletState: WalletState?,
globalState: GlobalState?,
) {
val globalState = globalState ?: return
when (action) {
is WalletAction.MultiWallet.AddBlockchains -> {
handleAddingWalletManagers(globalState, action.walletManagers)
}
is WalletAction.MultiWallet.SelectWallet -> {
if (action.currency != null) {
store.dispatch(NavigationAction.NavigateTo(AppScreen.WalletDetails))
}
}
is WalletAction.MultiWallet.AddToken -> {
addTokens(listOf(action.token), action.blockchain, walletState, globalState, action.save)
}
is WalletAction.MultiWallet.AddTokens -> {
addTokens(action.tokens, action.blockchain, walletState, globalState, save = false)
}
is WalletAction.MultiWallet.AddBlockchain -> {
action.walletManager?.let {
handleAddingWalletManagers(globalState, listOf(action.walletManager))
}
val currencies: List<Currency> =
(walletState?.currencies ?: emptyList()) + action.blockchain.toCurrencies()
if (action.save && globalState.scanResponse != null) {
scope.launch {
userTokensRepository.saveUserTokens(
card = globalState.scanResponse.card,
tokens = currencies,
)
}
}
store.dispatch(
WalletAction.LoadFiatRate(
coinsList = listOf(
Currency.Blockchain(
action.blockchain.blockchain,
action.blockchain.derivationPath,
),
),
),
)
store.dispatch(
WalletAction.LoadWallet(
blockchain = action.blockchain,
walletManager = action.walletManager,
),
)
}
is WalletAction.MultiWallet.SaveCurrencies -> {
val card = action.card ?: globalState.scanResponse?.card ?: return
scope.launch { userTokensRepository.saveUserTokens(card, action.blockchainNetworks.toCurrencies()) }
}
is WalletAction.MultiWallet.TryToRemoveWallet -> {
val currency = action.currency
val walletManager = walletState?.getWalletManager(currency).guard {
@ -131,47 +69,36 @@ class MultiWalletMiddleware {
}
}
is WalletAction.MultiWallet.RemoveWallet -> {
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync
if (selectedUserWallet != null) {
scope.launch {
walletCurrenciesManager.removeCurrency(
userWallet = selectedUserWallet,
currencyToRemove = action.currency,
)
}
} else {
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard {
Timber.e("Unable to remove wallet, no user wallet selected")
}
}
is WalletAction.MultiWallet.RemoveWallets -> {
val card = globalState.scanResponse?.card.guard {
store.dispatchErrorNotification(TapError.UnsupportedState("card is NULL"))
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
return
}
var currencies = walletState?.currencies ?: emptyList()
currencies = currencies.filterNot { action.currencies.contains(it) }
scope.launch { userTokensRepository.saveUserTokens(card, currencies) }
store.dispatch(WalletAction.MultiWallet.SelectWallet(null))
}
is WalletAction.MultiWallet.ShowWalletBackupWarning -> Unit
is WalletAction.MultiWallet.BackupWallet -> {
store.state.globalState.scanResponse?.let {
Analytics.addContext(it)
store.dispatch(GlobalAction.Onboarding.Start(it, canSkipBackup = false))
store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingWallet))
scope.launch {
walletCurrenciesManager.removeCurrency(
userWallet = selectedUserWallet,
currencyToRemove = action.currency,
)
}
}
is WalletAction.MultiWallet.BackupWallet -> {
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard {
Timber.e("Unable to backup wallet, no user wallet selected")
return
}
val scanResponse = selectedUserWallet.scanResponse
Analytics.addContext(scanResponse)
store.dispatch(GlobalAction.Onboarding.Start(scanResponse, canSkipBackup = false))
store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingWallet))
}
is WalletAction.MultiWallet.AddMissingDerivations -> {
scope.launch { handleBasicAnalyticsEvent() }
}
is WalletAction.MultiWallet.ScanToGetDerivations -> {
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync
if (selectedUserWallet != null) {
scanAndUpdateCard(selectedUserWallet, walletState)
} else {
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard {
Timber.e("Unable to scan to get derivations, no user wallet selected")
return
}
scanAndUpdateCard(selectedUserWallet, walletState)
}
else -> {}
}
@ -203,86 +130,4 @@ class MultiWalletMiddleware {
store.state.globalState.tapWalletManager.loadData(updatedUserWallet, refresh = true)
}
}
private fun addDummyBalances(walletManagers: List<WalletManager>) {
walletManagers.forEach {
if (it.wallet.fundsAvailable(AmountType.Coin) == BigDecimal.ZERO) {
DemoHelper.injectDemoBalance(it)
}
}
}
private fun handleAddingWalletManagers(
globalState: GlobalState,
walletManagers: List<WalletManager>,
) {
globalState.feedbackManager?.infoHolder?.setWalletsInfo(walletManagers)
if (globalState.scanResponse?.isDemoCard() == true) {
addDummyBalances(walletManagers)
}
}
private fun addTokens(
tokens: List<Token>,
blockchainNetwork: BlockchainNetwork,
walletState: WalletState?,
globalState: GlobalState?,
save: Boolean,
) {
if (tokens.isEmpty()) return
val scanResponse = globalState?.scanResponse ?: return
val wmFactory = globalState.tapWalletManager.walletManagerFactory
val walletState = walletState ?: return
val walletManager = walletState.getWalletManager(blockchainNetwork)?.also {
if (save) {
val wallets = tokens.mapNotNull { token -> token.toWallet(walletState, blockchainNetwork) }
val currencies = walletState.updateWalletsData(wallets).currencies
scope.launch { userTokensRepository.saveUserTokens(scanResponse.card, currencies) }
}
} ?: wmFactory.makeWalletManagerForApp(scanResponse, blockchainNetwork)?.also {
store.dispatchOnMain(
WalletAction.MultiWallet.AddBlockchain(
blockchain = blockchainNetwork.updateTokens(tokens),
walletManager = it,
save = save,
),
)
}
store.dispatchOnMain(
WalletAction.LoadFiatRate(
coinsList = tokens.map { token ->
Currency.Token(
token,
blockchainNetwork.blockchain,
blockchainNetwork.derivationPath,
)
},
),
)
if (tokens.isNotEmpty()) walletManager?.addTokens(tokens)
scope.launch {
when (val result = walletManager?.safeUpdate()) {
is com.tangem.common.services.Result.Success -> {
val wallet = result.data
wallet.getTokens()
.filter { tokens.contains(it) }
.mapNotNull { token ->
wallet.getTokenAmount(token)?.let { amount -> Pair(token, amount) }
}
.forEach { (token, tokenAmount) ->
store.dispatchOnMain(
WalletAction.MultiWallet.TokenLoaded(
amount = tokenAmount,
token = token,
blockchain = blockchainNetwork,
),
)
}
}
else -> Unit
}
}
}
}

View file

@ -1,17 +1,11 @@
package com.tangem.tap.features.wallet.redux.middlewares
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tangem.blockchain.blockchains.solana.RentProvider
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.WalletManager
import com.tangem.common.CompletionResult
import com.tangem.common.extensions.isZero
import com.tangem.common.services.Result
import com.tangem.common.extensions.guard
import com.tangem.core.analytics.Analytics
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.converters.BasicEventsPreChecker
import com.tangem.tap.common.analytics.converters.BasicEventsSourceData
import com.tangem.tap.common.analytics.events.AnalyticsParam
@ -19,22 +13,16 @@ import com.tangem.tap.common.analytics.events.Basic
import com.tangem.tap.common.analytics.events.MainScreen
import com.tangem.tap.common.analytics.events.Token
import com.tangem.tap.common.extensions.copyToClipboard
import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
import com.tangem.tap.common.extensions.dispatchErrorNotification
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.dispatchOpenUrl
import com.tangem.tap.common.extensions.dispatchToastNotification
import com.tangem.tap.common.extensions.isGreaterThan
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
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.failedRates
import com.tangem.tap.domain.loadedRates
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.domain.model.WalletStoreModel
import com.tangem.tap.domain.model.builders.UserWalletIdBuilder
@ -43,9 +31,6 @@ import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.home.redux.HomeAction
import com.tangem.tap.features.send.redux.PrepareSendScreen
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.models.PendingTransactionType
import com.tangem.tap.features.wallet.models.filterByCoin
import com.tangem.tap.features.wallet.models.getPendingTransactions
import com.tangem.tap.features.wallet.models.getSendableAmounts
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletData
@ -64,8 +49,6 @@ import com.tangem.tap.userWalletsListManagerSafe
import com.tangem.tap.walletStoresManager
import com.tangem.wallet.R
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.rekotlin.Action
@ -108,97 +91,26 @@ class WalletMiddleware {
when (action) {
is WalletAction.TradeCryptoAction -> tradeCryptoMiddleware.handle(state, action)
is WalletAction.Warnings -> warningsMiddleware.handle(action, globalState)
is WalletAction.MultiWallet -> multiWalletMiddleware.handle(action, walletState, globalState)
is WalletAction.MultiWallet -> multiWalletMiddleware.handle(action, walletState)
is WalletAction.AppCurrencyAction -> appCurrencyMiddleware.handle(action)
is WalletAction.DialogAction -> walletDialogMiddleware.handle(action)
is WalletAction.LoadWallet -> {
scope.launch {
if (action.blockchain == null) {
walletState.walletManagers.map { walletManager ->
async { globalState.tapWalletManager.loadWalletData(walletManager) }
}.awaitAll()
handleBasicAnalyticsEvent()
} else {
val walletManager = walletState.getWalletManager(action.blockchain)
?: action.walletManager
walletManager?.let { globalState.tapWalletManager.loadWalletData(it) }
}
}
}
is WalletAction.LoadWallet.Success -> {
checkForRentWarning(walletState.getWalletManager(action.blockchain))
val coinAmount = action.wallet.amounts[AmountType.Coin]?.value
if (coinAmount?.isZero() == false && walletState.getWalletData(action.blockchain) == null) {
store.dispatch(
WalletAction.MultiWallet.AddBlockchain(
blockchain = action.blockchain,
walletManager = null,
save = true,
),
)
store.dispatch(WalletAction.LoadWallet.Success(action.wallet, action.blockchain))
}
store.dispatch(WalletAction.Warnings.CheckHashesCount.CheckHashesCountOnline)
warningsMiddleware.tryToShowAppRatingWarning(action.wallet)
}
is WalletAction.LoadFiatRate -> {
val appCurrencyId = globalState.appCurrency.code
scope.launch {
val coinsList = when {
action.wallet != null -> {
val wallet = action.wallet
wallet.getTokens()
.map { Currency.Token(it, wallet.blockchain, wallet.publicKey.derivationPath?.rawPath) }
.plus(Currency.Blockchain(wallet.blockchain, wallet.publicKey.derivationPath?.rawPath))
}
action.coinsList != null -> action.coinsList
else -> {
if (walletState.isMultiwalletAllowed) {
walletState.walletsDataFromStores.map { it.currency }
} else {
val derivationPath = walletState.primaryWalletData?.currency?.derivationPath
val primaryBlockchain = walletState.primaryBlockchain
val primaryToken = walletState.primaryToken
listOfNotNull(
primaryBlockchain?.let { Currency.Blockchain(it, derivationPath) },
primaryToken?.let { Currency.Token(it, primaryBlockchain!!, derivationPath) },
)
}
}
}
val ratesResult = globalState.tapWalletManager.rates.loadFiatRate(
currencyId = appCurrencyId,
coinsList = coinsList,
)
when (ratesResult) {
is Result.Success -> {
ratesResult.data.loadedRates.let {
dispatchOnMain(WalletAction.LoadFiatRate.Success(it))
}
ratesResult.data.failedRates.forEach { (currency, throwable) ->
Timber.e(
throwable,
"Loading rates failed for [%s]",
currency.currencySymbol,
)
}
}
is Result.Failure -> {
store.dispatchDebugErrorNotification("LoadFiatRate.Failure")
dispatchOnMain(WalletAction.LoadFiatRate.Failure)
}
}
}
}
is WalletAction.CreateWallet -> {
scope.launch {
val result = tangemSdkManager.createWallet(globalState.scanResponse?.card?.cardId)
when (result) {
when (val result = tangemSdkManager.createWallet(globalState.scanResponse?.card?.cardId)) {
is CompletionResult.Success -> {
val scanResponse = globalState.scanResponse?.copy(card = result.data)
scanResponse?.let { store.onCardScanned(scanResponse) }
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard {
Timber.e("Unable to create wallet, no user wallet selected")
return@launch
}
userWalletsListManager.update(selectedUserWallet.walletId) { userWallet ->
userWallet.copy(
scanResponse = userWallet.scanResponse.copy(
card = result.data,
),
)
}
}
is CompletionResult.Failure -> {}
is CompletionResult.Failure -> Unit
}
}
}
@ -209,50 +121,29 @@ class WalletMiddleware {
store.dispatchOnMain(HomeAction.ReadCard(action.onScanSuccessEvent))
}
}
is WalletAction.LoadCardInfo -> {
val attestationFailed = action.card.attestation.status == Attestation.Status.Failed
store.dispatchOnMain(GlobalAction.SetIfCardVerifiedOnline(!attestationFailed))
scope.launch {
val response = OnlineCardVerifier().getCardInfo(action.card.cardId, action.card.cardPublicKey)
when (response) {
is Result.Success -> {
val actionList = listOf(
WalletAction.SetArtworkId(response.data.artwork?.id),
WalletAction.LoadArtwork(action.card, response.data.artwork?.id),
)
withMainContext { actionList.forEach { store.dispatch(it) } }
}
is Result.Failure -> {}
}
store.dispatchOnMain(WalletAction.Warnings.CheckIfNeeded)
}
}
is WalletAction.LoadData,
is WalletAction.LoadData.Refresh,
-> {
val selectedWallet = userWalletsListManager.selectedUserWalletSync
val selectedWallet = userWalletsListManager.selectedUserWalletSync.guard {
Timber.e("Unable to load/refresh wallets data, no user wallet selected")
return
}
scope.launch {
if (selectedWallet != null) {
globalState.tapWalletManager.loadData(
userWallet = selectedWallet,
refresh = action is WalletAction.LoadData.Refresh,
)
} else {
Timber.e("Unable to load/refresh wallets data, no user wallet selected")
}
globalState.tapWalletManager.loadData(
userWallet = selectedWallet,
refresh = action is WalletAction.LoadData.Refresh,
)
}
}
is NetworkStateChanged -> {
store.dispatch(WalletAction.Warnings.CheckHashesCount.CheckHashesCountOnline)
if (!action.isOnline) return
val selectedUserWallet = userWalletsListManagerSafe?.selectedUserWalletSync
if (selectedUserWallet != null) {
scope.launch { globalState.tapWalletManager.loadData(selectedUserWallet) }
} else {
val selectedUserWallet = userWalletsListManagerSafe?.selectedUserWalletSync.guard {
Timber.e("Unable to proceed with changed network state, no user wallet selected")
return
}
scope.launch { globalState.tapWalletManager.loadData(selectedUserWallet) }
}
is WalletAction.CopyAddress -> {
Analytics.send(Token.Receive.ButtonCopyAddress())
@ -460,48 +351,6 @@ class WalletMiddleware {
tokenRate = tokenRate,
)
}
private fun checkForRentWarning(walletManager: WalletManager?) {
val rentProvider = walletManager as? RentProvider ?: return
scope.launch {
when (val result = rentProvider.minimalBalanceForRentExemption()) {
is com.tangem.blockchain.extensions.Result.Success -> {
fun isNeedToShowWarning(balance: BigDecimal, rentExempt: BigDecimal): Boolean {
return balance < rentExempt
}
val balance = walletManager.wallet.fundsAvailable(AmountType.Coin)
val outgoingTxs = walletManager.wallet.getPendingTransactions(
PendingTransactionType.Outgoing,
).filterByCoin()
val rentExempt = result.data
val show = if (outgoingTxs.isEmpty()) {
isNeedToShowWarning(balance, rentExempt)
} else {
val outgoingAmount = outgoingTxs.sumOf { it.amountValue ?: BigDecimal.ZERO }
val rest = balance.minus(outgoingAmount)
isNeedToShowWarning(rest, rentExempt)
}
val currency = walletManager.wallet.blockchain.currency
if (show) {
dispatchOnMain(
WalletAction.SetWalletRent(
wallet = walletManager.wallet,
minRent = "${rentProvider.rentAmount().stripZeroPlainString()} $currency",
rentExempt = "${rentExempt.stripZeroPlainString()} $currency",
),
)
} else {
dispatchOnMain(WalletAction.RemoveWalletRent(walletManager.wallet))
}
}
is com.tangem.blockchain.extensions.Result.Failure -> {}
}
}
}
}
suspend fun handleBasicAnalyticsEvent() {

View file

@ -1,173 +1,13 @@
package com.tangem.tap.features.wallet.redux.reducers
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tangem.blockchain.blockchains.polkadot.ExistentialDepositProvider
import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.Token
import com.tangem.blockchain.common.WalletManager
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.dispatchToastNotification
import com.tangem.tap.common.extensions.getBlockchainTxHistory
import com.tangem.tap.common.extensions.getTokenTxHistory
import com.tangem.tap.common.extensions.toFiatString
import com.tangem.tap.common.extensions.toFormattedCurrencyString
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.models.WalletRent
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.redux.ProgressState
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletData
import com.tangem.tap.features.wallet.redux.WalletMainButton
import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN
import com.tangem.tap.features.wallet.redux.WalletStore
import com.tangem.tap.features.wallet.ui.BalanceStatus
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
import com.tangem.tap.store
import com.tangem.tap.userWalletsListManager
import com.tangem.wallet.R
import java.math.BigDecimal
class MultiWalletReducer {
@Suppress("LongMethod", "ComplexMethod")
fun reduce(action: WalletAction.MultiWallet, state: WalletState): WalletState {
return when (action) {
is WalletAction.MultiWallet.AddBlockchains -> {
val walletStores: List<WalletStore> = action.blockchains.map { blockchain ->
val walletManager = action.walletManagers.firstOrNull {
it.wallet.blockchain == blockchain.blockchain &&
it.wallet.publicKey.derivationPath?.rawPath == blockchain.derivationPath
}
val wallet = walletManager?.wallet
val walletData = WalletData(
currencyData = BalanceWidgetData(
status = BalanceStatus.Loading,
currency = blockchain.blockchain.fullName,
currencySymbol = blockchain.blockchain.currency,
),
walletAddresses = createAddressList(wallet),
mainButton = WalletMainButton.SendButton(false),
currency = Currency.Blockchain(
blockchain.blockchain,
blockchain.derivationPath,
),
existentialDepositString = getExistentialDeposit(walletManager),
historyTransactions = walletManager?.getBlockchainTxHistory(),
)
WalletStore(
walletManager = walletManager,
blockchainNetwork = blockchain,
walletsData = listOf(walletData),
)
}
state.copy(
walletsStores = walletStores,
selectedCurrency = findSelectedCurrency(
walletsStores = walletStores,
currentSelectedCurrency = state.selectedCurrency,
isMultiWalletAllowed = state.isMultiwalletAllowed,
),
)
}
is WalletAction.MultiWallet.AddBlockchain -> {
val walletManager = action.walletManager ?: state.getWalletManager(action.blockchain)
val wallet = walletManager?.wallet
val walletData = WalletData(
currencyData = BalanceWidgetData(
status = BalanceStatus.Loading,
currency = action.blockchain.blockchain.fullName,
currencySymbol = action.blockchain.blockchain.currency,
),
walletAddresses = createAddressList(wallet),
mainButton = WalletMainButton.SendButton(false),
currency = Currency.Blockchain(
action.blockchain.blockchain,
action.blockchain.derivationPath,
),
existentialDepositString = getExistentialDeposit(walletManager),
historyTransactions = walletManager?.getBlockchainTxHistory(),
)
val walletStore = WalletStore(
walletManager = walletManager,
blockchainNetwork = action.blockchain,
walletsData = listOf(walletData),
)
val newState = state.updateWalletStore(walletStore)
if (wallet != null && wallet.amounts[AmountType.Coin]?.value != null) {
OnWalletLoadedReducer().reduce(wallet, action.blockchain, newState)
} else {
newState
}
}
is WalletAction.MultiWallet.AddTokens -> addTokens(action.tokens, action.blockchain, state)
is WalletAction.MultiWallet.AddToken -> addTokens(listOf(action.token), action.blockchain, state)
is WalletAction.MultiWallet.TokenLoaded -> {
val currency = Currency.fromBlockchainNetwork(action.blockchain, action.token)
val walletManager = state.getWalletManager(currency)
if (walletManager == null) {
val screen = if (userWalletsListManager.hasUserWallets) {
AppScreen.Welcome
} else {
AppScreen.Home
}
store.dispatchOnMain(NavigationAction.PopBackTo(screen))
FirebaseCrashlytics.getInstance().recordException(
IllegalStateException("MultiWallet.TokenLoaded: walletManager is null"),
)
store.dispatchToastNotification(R.string.internal_error_wallet_manager_not_found)
return state
}
val wallet = walletManager.wallet
val pendingTransactions = wallet.getPendingTransactions()
val tokenPendingTransactions = pendingTransactions.filterByToken(action.token)
val tokenBalanceStatus = when {
tokenPendingTransactions.isNotEmpty() -> BalanceStatus.TransactionInProgress
pendingTransactions.isNotEmpty() -> BalanceStatus.SameCurrencyTransactionInProgress
else -> BalanceStatus.VerifiedOnline
}
val tokenWalletData = state.getWalletData(currency)
val isTokenSendButtonEnabled = tokenWalletData?.shouldEnableTokenSendButton() == true &&
pendingTransactions.isEmpty()
val newTokenWalletData = tokenWalletData?.copy(
currencyData = tokenWalletData.currencyData.copy(
status = tokenBalanceStatus,
amount = action.amount.value,
amountFormatted = action.amount.value?.toFormattedCurrencyString(
decimals = action.amount.decimals,
currency = action.amount.currencySymbol,
),
fiatAmountFormatted = tokenWalletData.fiatRate?.let {
action.amount.value?.toFiatString(it, store.state.globalState.appCurrency.symbol)
} ?: UNKNOWN_AMOUNT_SIGN,
blockchainAmount = wallet.amounts[AmountType.Coin]?.value ?: BigDecimal.ZERO,
),
pendingTransactions = pendingTransactions.removeUnknownTransactions(),
mainButton = WalletMainButton.SendButton(isTokenSendButtonEnabled),
currency = Currency.Token(
token = action.token,
blockchain = action.blockchain.blockchain,
derivationPath = action.blockchain.derivationPath,
),
walletRent = findWalletRent(state.getWalletStore(walletManager.wallet)),
historyTransactions = walletManager.getTokenTxHistory(action.token),
)
state.updateWalletData(newTokenWalletData)
}
is WalletAction.MultiWallet.SetIsMultiwalletAllowed ->
state.copy(isMultiwalletAllowed = action.isMultiwalletAllowed)
is WalletAction.MultiWallet.SelectWallet -> {
state.copy(selectedCurrency = action.currency)
}
@ -175,16 +15,6 @@ class MultiWalletReducer {
state.copy(selectedCurrency = action.currency)
}
is WalletAction.MultiWallet.TryToRemoveWallet -> state
is WalletAction.MultiWallet.RemoveWallet -> {
state.removeWalletData(state.getWalletData(action.currency))
}
is WalletAction.MultiWallet.RemoveWallets -> {
var updatedState = state
action.currencies.forEach { updatedState = updatedState.removeWalletData(state.getWalletData(it)) }
updatedState
}
is WalletAction.MultiWallet.SaveCurrencies -> state
is WalletAction.MultiWallet.ShowWalletBackupWarning -> state.copy(showBackupWarning = action.show)
is WalletAction.MultiWallet.ScheduleCheckForMissingDerivation -> state.copy(
derivationsCheckIsScheduled = true,
)
@ -194,39 +24,7 @@ class MultiWalletReducer {
)
is WalletAction.MultiWallet.BackupWallet -> state
is WalletAction.MultiWallet.ScanToGetDerivations -> state.copy(state = ProgressState.Loading)
else -> state
}
}
private fun findWalletRent(walletStore: WalletStore?): WalletRent? {
return walletStore?.walletsData?.firstOrNull { it.walletRent != null }?.walletRent
}
private fun getExistentialDeposit(walletManager: WalletManager?): String? {
return (walletManager as? ExistentialDepositProvider)?.getExistentialDeposit()?.toPlainString()
}
private fun addTokens(tokens: List<Token>, blockchain: BlockchainNetwork, state: WalletState): WalletState {
val wallets = tokens.mapNotNull { token -> token.toWallet(state, blockchain) }
return state.updateWalletsData(wallets)
}
}
fun Token.toWallet(state: WalletState, blockchain: BlockchainNetwork): WalletData? {
val currency = Currency.fromBlockchainNetwork(blockchain, this)
if (state.currencies.contains(currency)) return null
val walletManager = state.getWalletManager(currency)
val walletAddresses = createAddressList(walletManager?.wallet)
return WalletData(
currencyData = BalanceWidgetData(
status = BalanceStatus.Loading,
currency = this.name,
currencySymbol = this.symbol,
),
walletAddresses = walletAddresses,
mainButton = WalletMainButton.SendButton(false),
currency = currency,
historyTransactions = walletManager?.getTokenTxHistory(this),
)
}

View file

@ -1,154 +0,0 @@
package com.tangem.tap.features.wallet.redux.reducers
import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.Wallet
import com.tangem.common.extensions.isZero
import com.tangem.tap.common.extensions.toFiatValue
import com.tangem.tap.common.extensions.toFormattedCurrencyString
import com.tangem.tap.common.extensions.toFormattedFiatValue
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.features.wallet.models.Currency
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.redux.ProgressState
import com.tangem.tap.features.wallet.redux.WalletMainButton
import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN
import com.tangem.tap.features.wallet.redux.replaceSomeWalletsData
import com.tangem.tap.features.wallet.ui.BalanceStatus
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
import com.tangem.tap.store
class OnWalletLoadedReducer {
fun reduce(wallet: Wallet, blockchainNetwork: BlockchainNetwork, walletState: WalletState): WalletState {
return if (!walletState.isMultiwalletAllowed) {
onSingleWalletLoaded(wallet, walletState)
} else {
onMultiWalletLoaded(wallet, blockchainNetwork, walletState)
}
}
@Suppress("LongMethod")
private fun onMultiWalletLoaded(
wallet: Wallet,
blockchainNetwork: BlockchainNetwork,
walletState: WalletState,
): WalletState {
val walletData = walletState.getWalletData(blockchainNetwork) ?: return walletState
val fiatCurrency = store.state.globalState.appCurrency
val coinAmountValue = wallet.amounts[AmountType.Coin]?.value
val formattedAmount = coinAmountValue?.toFormattedCurrencyString(
wallet.blockchain.decimals(),
wallet.blockchain.currency,
)
val pendingTransactions = wallet.getPendingTransactions()
val isCoinSendButtonEnabled = coinAmountValue?.isZero() == false && pendingTransactions.isEmpty()
val balanceStatus = if (pendingTransactions.isNotEmpty()) {
BalanceStatus.TransactionInProgress
} else {
BalanceStatus.VerifiedOnline
}
val fiatAmount = walletData.fiatRate?.let { coinAmountValue?.toFiatValue(it) }
val fiatAmountFormatted = fiatAmount?.toFormattedFiatValue(fiatCurrency.symbol) ?: UNKNOWN_AMOUNT_SIGN
val newWalletData = walletData.copy(
currencyData = walletData.currencyData.copy(
status = balanceStatus,
currency = wallet.blockchain.fullName,
currencySymbol = wallet.blockchain.currency,
blockchainAmount = coinAmountValue,
amount = coinAmountValue,
amountFormatted = formattedAmount,
fiatAmount = fiatAmount,
fiatAmountFormatted = fiatAmountFormatted,
),
pendingTransactions = pendingTransactions.removeUnknownTransactions(),
mainButton = WalletMainButton.SendButton(isCoinSendButtonEnabled),
currency = Currency.fromBlockchainNetwork(blockchainNetwork),
)
val tokens = wallet.getTokens().mapNotNull { token ->
val currency = Currency.fromBlockchainNetwork(blockchainNetwork, token)
val tokenWalletData = walletState.getWalletData(currency)
val tokenPendingTransactions = pendingTransactions.filterByToken(token)
val tokenBalanceStatus = when {
tokenPendingTransactions.isNotEmpty() -> BalanceStatus.TransactionInProgress
pendingTransactions.isNotEmpty() -> BalanceStatus.SameCurrencyTransactionInProgress
else -> BalanceStatus.VerifiedOnline
}
val tokenAmountValue = wallet.getTokenAmount(token)?.value
val tokenFiatAmount = tokenWalletData?.fiatRate?.let { tokenAmountValue?.toFiatValue(it) }
val tokenFiatAmountFormatted = tokenFiatAmount?.toFormattedFiatValue(fiatCurrency.symbol)
?: UNKNOWN_AMOUNT_SIGN
val isTokenSendButtonEnabled = tokenWalletData?.shouldEnableTokenSendButton() == true &&
pendingTransactions.isEmpty()
tokenWalletData?.copy(
currencyData = tokenWalletData.currencyData.copy(
status = tokenBalanceStatus,
blockchainAmount = coinAmountValue,
amount = tokenAmountValue,
amountFormatted = tokenAmountValue?.toFormattedCurrencyString(
token.decimals,
token.symbol,
),
fiatAmount = tokenFiatAmount,
fiatAmountFormatted = tokenFiatAmountFormatted,
),
pendingTransactions = tokenPendingTransactions.removeUnknownTransactions(),
mainButton = WalletMainButton.SendButton(isTokenSendButtonEnabled),
)
}
val newWalletsData = tokens + newWalletData
val walletsData = walletState.walletsDataFromStores.replaceSomeWalletsData(newWalletsData)
return walletState.updateWalletsData(walletsData)
}
private fun onSingleWalletLoaded(wallet: Wallet, walletState: WalletState): WalletState {
if (wallet.blockchain != walletState.primaryBlockchain) return walletState
val fiatCurrencyName = store.state.globalState.appCurrency.code
val amount = wallet.amounts[AmountType.Coin]?.value
val formattedAmount = amount?.toFormattedCurrencyString(
wallet.blockchain.decimals(),
wallet.blockchain.currency,
)
val fiatAmount = walletState.primaryWalletData?.fiatRate?.let { amount?.toFiatValue(it) }
val fiatAmountFormatted = fiatAmount?.toFormattedFiatValue(fiatCurrencyName) ?: UNKNOWN_AMOUNT_SIGN
val pendingTransactions = wallet.getPendingTransactions()
val sendButtonEnabled = amount?.isZero() == false && pendingTransactions.isEmpty()
val balanceStatus = if (pendingTransactions.isNotEmpty()) {
BalanceStatus.TransactionInProgress
} else {
BalanceStatus.VerifiedOnline
}
val walletData = walletState.primaryWalletData?.copy(
currencyData = BalanceWidgetData(
status = balanceStatus,
currency = wallet.blockchain.fullName,
currencySymbol = wallet.blockchain.currency,
blockchainAmount = amount,
amount = amount,
amountFormatted = formattedAmount,
fiatAmount = fiatAmount,
fiatAmountFormatted = fiatAmountFormatted,
),
pendingTransactions = pendingTransactions.removeUnknownTransactions(),
mainButton = WalletMainButton.SendButton(sendButtonEnabled),
)
val wallets = listOfNotNull(walletData)
val updatedStore = walletState.getWalletStore(walletData?.currency)?.updateWallets(wallets)
return walletState.updateWalletStore(updatedStore).copy(
state = ProgressState.Done,
error = null,
)
}
}

View file

@ -13,12 +13,6 @@ fun List<WalletData>.findProgressState(initialState: ProgressState = ProgressSta
.reduce(ProgressState::or)
}
fun List<WalletData>.calculateTotalFiatAmount(): BigDecimal {
return this
.map { it.currencyData.fiatAmount ?: BigDecimal.ZERO }
.reduce(BigDecimal::plus)
}
fun List<WalletData>.calculateTotalCryptoAmount(): BigDecimal {
return this
.map { it.currencyData.amount ?: BigDecimal.ZERO }

View file

@ -1,23 +1,13 @@
package com.tangem.tap.features.wallet.redux.reducers
import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Wallet
import com.tangem.common.extensions.mapNotNullValues
import com.tangem.domain.common.CardDTO
import com.tangem.domain.common.TapWorkarounds.isTestCard
import com.tangem.domain.common.TwinCardNumber
import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.common.extensions.toFiatRateString
import com.tangem.tap.common.extensions.toFiatValue
import com.tangem.tap.common.extensions.toFormattedCurrencyString
import com.tangem.tap.common.extensions.toFormattedFiatValue
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.extensions.getArtworkUrl
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.models.WalletRent
import com.tangem.tap.features.wallet.redux.AddressData
import com.tangem.tap.features.wallet.redux.Artwork
import com.tangem.tap.features.wallet.redux.ErrorType
@ -25,15 +15,12 @@ import com.tangem.tap.features.wallet.redux.ProgressState
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.WalletMainButton
import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.redux.WalletStore
import com.tangem.tap.features.wallet.redux.replaceSomeWalletsData
import com.tangem.tap.features.wallet.ui.BalanceStatus
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
import com.tangem.tap.proxy.AppStateHolder
import org.rekotlin.Action
import java.math.BigDecimal
object WalletReducer {
fun reduce(action: Action, state: AppState, appStateHolder: AppStateHolder): WalletState =
@ -43,7 +30,6 @@ object WalletReducer {
@Suppress("LongMethod", "ComplexMethod")
private fun internalReduce(action: Action, state: AppState, appStateHolder: AppStateHolder): WalletState {
val multiWalletReducer = MultiWalletReducer()
val onWalletLoadedReducer = OnWalletLoadedReducer()
val appCurrencyReducer = AppCurrencyReducer()
if (action !is WalletAction) return state.walletState
@ -53,36 +39,6 @@ private fun internalReduce(action: Action, state: AppState, appStateHolder: AppS
when (action) {
is WalletAction.Warnings -> newState = handleCheckSignedHashesActions(action, newState)
is WalletAction.MultiWallet -> newState = multiWalletReducer.reduce(action, newState)
is WalletAction.ResetState -> {
newState = WalletState(
cardId = action.newCard.cardId,
walletCardsCount = action.newCard.findCardsCount(),
)
}
is WalletAction.SetIfTestnetCard -> newState = newState.copy(isTestnet = action.isTestnet)
is WalletAction.EmptyWallet -> {
newState = newState.copy(
state = ProgressState.Done,
walletsStores = listOf(
WalletStore(
walletManager = null,
blockchainNetwork = BlockchainNetwork(
Blockchain.Unknown,
null,
emptyList(),
),
walletsData = listOf(
WalletData(
currencyData = BalanceWidgetData(BalanceStatus.EmptyCard),
mainButton = WalletMainButton.CreateWalletButton(true),
currency = Currency.Blockchain(Blockchain.Unknown, null),
),
),
),
),
)
}
is WalletAction.LoadData.Failure -> {
when (action.error) {
is TapError.NoInternetConnection -> {
@ -133,7 +89,6 @@ private fun internalReduce(action: Action, state: AppState, appStateHolder: AppS
}
}
}
is WalletAction.LoadData -> {
newState = newState.copy(
state = ProgressState.Loading,
@ -146,152 +101,6 @@ private fun internalReduce(action: Action, state: AppState, appStateHolder: AppS
error = null,
)
}
is WalletAction.LoadWallet -> {
val balanceStatus = if (newState.state == ProgressState.Refreshing) {
BalanceStatus.Refreshing
} else {
BalanceStatus.Loading
}
if (action.blockchain == null) {
val wallets = newState.walletsStores.map { walletStore ->
walletStore.copy(
walletsData = walletStore.walletsData.map { walletData ->
walletData.copy(
currencyData = walletData.currencyData.copy(
status =
if (walletStore.walletManager != null) balanceStatus else BalanceStatus.Unreachable,
currency = walletData.currencyData.currency,
currencySymbol = walletData.currencyData.currencySymbol,
),
mainButton = WalletMainButton.SendButton(false),
)
},
)
}
newState = newState.copy(
state = ProgressState.Loading,
walletsStores = wallets,
)
} else {
val walletManager = newState.getWalletManager(action.blockchain) ?: return newState
val currencies = listOf(Currency.fromBlockchainNetwork(action.blockchain)) +
walletManager.cardTokens.map {
Currency.fromBlockchainNetwork(action.blockchain, it)
}
val newWalletsData = newState.walletsDataFromStores.filter { currencies.contains(it.currency) }
.map { wallet ->
wallet.copy(
currencyData = wallet.currencyData.copy(
status = balanceStatus,
currency = wallet.currencyData.currency,
currencySymbol = wallet.currencyData.currencySymbol,
),
mainButton = WalletMainButton.SendButton(false),
)
}
val walletsData = newState.walletsDataFromStores.replaceSomeWalletsData(newWalletsData)
val walletStore = newState.getWalletStore(action.blockchain)?.updateWallets(walletsData)
newState = newState.updateWalletStore(walletStore)
}
}
is WalletAction.LoadWallet.Success -> newState = onWalletLoadedReducer.reduce(
wallet = action.wallet,
blockchainNetwork = action.blockchain,
walletState = newState,
)
is WalletAction.LoadWallet.NoAccount -> {
val amount = BigDecimal.ZERO
val fiatAmount = BigDecimal.ZERO
val walletData = newState.getWalletData(action.blockchain)?.let { walletData ->
val walletBlockchain = walletData.currency.blockchain
walletData.copy(
currencyData = BalanceWidgetData(
status = BalanceStatus.NoAccount,
currency = action.wallet.blockchain.fullName,
currencySymbol = action.wallet.blockchain.currency,
amount = amount,
amountFormatted = amount.toFormattedCurrencyString(
decimals = walletBlockchain.decimals(),
currency = walletBlockchain.currency,
),
fiatAmount = fiatAmount,
fiatAmountFormatted = fiatAmount.toFormattedFiatValue(
fiatCurrencyName = state.globalState.appCurrency.symbol,
),
amountToCreateAccount = action.amountToCreateAccount,
),
)
}
val updatedWalletStore = newState.getWalletStore(action.blockchain)
?.updateWallets(listOfNotNull(walletData))
newState = newState.updateWalletStore(updatedWalletStore)
}
is WalletAction.LoadWallet.Failure -> {
val message = if (newState.error == ErrorType.NoInternetConnection) {
null
} else {
action.errorMessage
}
val walletStore = newState.getWalletStore(action.wallet)
val walletData = walletStore?.walletsData?.first { it.currency is Currency.Blockchain }
val newWalletData = walletData?.copy(
currencyData = walletData.currencyData.copy(
status = BalanceStatus.Unreachable,
errorMessage = message,
),
)
val tokenWallets = action.wallet.getTokens()
.mapNotNull { token ->
walletStore?.blockchainNetwork?.let {
newState.getWalletData(Currency.fromBlockchainNetwork(it, token))
}
}
.map {
it.copy(
currencyData = it.currencyData.copy(
status = BalanceStatus.Unreachable,
errorMessage = message,
),
)
}
val updatedWallets = walletStore!!.updateWallets(listOfNotNull(newWalletData) + tokenWallets).walletsData
newState = newState.updateWalletsData(updatedWallets)
val progressState =
if (newState.walletsDataFromStores.any { it.currencyData.status == BalanceStatus.Loading }) {
ProgressState.Loading
} else {
ProgressState.Done
}
newState = newState.copy(
state = progressState,
)
}
is WalletAction.SetArtworkId -> {
val cardImage = if (newState.cardImage?.artworkId == action.artworkId) {
newState.cardImage
} else {
null
}
newState = newState.copy(cardImage = cardImage)
}
is WalletAction.LoadFiatRate.Success -> {
newState = setNewFiatRate(action.fiatRates, state.globalState.appCurrency, newState)
}
is WalletAction.LoadArtwork -> {
val artworkUrl = action.card.getArtworkUrl(action.artworkId)
?: when (state.twinCardsState.cardNumber) {
TwinCardNumber.First -> Artwork.TWIN_CARD_1
TwinCardNumber.Second -> Artwork.TWIN_CARD_2
else -> Artwork.DEFAULT_IMG_URL
}
newState = newState.copy(cardImage = Artwork(artworkId = artworkUrl))
}
is WalletAction.TradeCryptoAction -> return newState
is WalletAction.ChangeSelectedAddress -> {
val walletAddresses = newState.getWalletData(newState.selectedCurrency)?.walletAddresses
@ -308,22 +117,9 @@ private fun internalReduce(action: Action, state: AppState, appStateHolder: AppS
),
)
}
is WalletAction.SetWalletRent -> {
val walletStore = newState.getWalletStore(action.wallet) ?: return newState
val walletRent = WalletRent(action.minRent, action.rentExempt)
val walletsData = walletStore.walletsData.map { it.copy(walletRent = walletRent) }
newState = newState.updateWalletsData(walletsData)
}
is WalletAction.RemoveWalletRent -> {
val walletStore = newState.getWalletStore(action.wallet) ?: return newState
val walletsData = walletStore.walletsData.map { it.copy(walletRent = null) }
newState = newState.updateWalletsData(walletsData)
}
is WalletAction.AppCurrencyAction -> {
newState = appCurrencyReducer.reduce(action, newState)
}
is WalletAction.UserTokens.Loading -> newState = newState.copy(loadingUserTokens = true)
is WalletAction.UserTokens.Loaded -> newState = newState.copy(loadingUserTokens = false)
is WalletAction.UserWalletChanged -> with(action.userWallet) {
val card = scanResponse.card
newState = WalletState(
@ -395,20 +191,6 @@ private fun CardDTO.findCardsCount(): Int? {
return (this.backupStatus as? CardDTO.BackupStatus.Active)?.cardCount?.inc()
}
fun createAddressList(wallet: Wallet?, walletAddresses: WalletAddresses? = null): WalletAddresses? {
if (wallet == null) return null
val listOfAddressData = wallet.createAddressesData()
// restore a selected wallet address
var indexOfSelectedWallet = 0
walletAddresses?.let {
val index =
listOfAddressData.indexOfFirst { it.address == walletAddresses.selectedAddress.address }
if (index != -1) indexOfSelectedWallet = index
}
return WalletAddresses(listOfAddressData[indexOfSelectedWallet], listOfAddressData)
}
fun Wallet.createAddressesData(): List<AddressData> {
val listOfAddressData = mutableListOf<AddressData>()
// put a defaultAddress at the first place
@ -440,41 +222,4 @@ private fun handleCheckSignedHashesActions(
is WalletAction.Warnings.Set -> state.copy(mainWarningsList = action.warningList)
else -> state
}
}
private fun setNewFiatRate(
fiatRates: Map<Currency, BigDecimal?>,
appCurrency: FiatCurrency,
state: WalletState,
): WalletState {
val rateFormatter: (BigDecimal) -> String = { rate: BigDecimal ->
rate.toFiatRateString(fiatCurrencyName = appCurrency.symbol)
}
val newWalletsData = fiatRates.mapNotNullValues { it.value }.mapNotNull { (currency, rate) ->
val walletStore = state.getWalletStore(currency) ?: return@mapNotNull null
val wallet = walletStore.walletManager?.wallet
val walletData = state.getWalletData(currency) ?: return@mapNotNull null
val currencyData = walletData.currencyData
var fiatAmount = when (currency) {
is Currency.Blockchain -> wallet?.amounts?.get(AmountType.Coin)?.value?.toFiatValue(rate)
is Currency.Token -> wallet?.getTokenAmount(currency.token)?.value?.toFiatValue(rate)
}
if (currencyData.status == BalanceStatus.NoAccount && fiatAmount == null) {
fiatAmount = BigDecimal.ZERO.setScale(2)
}
val fiatAmountFormatted = fiatAmount?.toFormattedFiatValue(appCurrency.symbol)
walletData.copy(
currencyData = currencyData.copy(
fiatAmountFormatted = fiatAmountFormatted,
fiatAmount = fiatAmount,
),
fiatRate = rate,
fiatRateString = rateFormatter(rate),
)
}
return state.updateWalletsData(newWalletsData)
}

View file

@ -20,8 +20,10 @@ import com.badoo.mvicore.DiffStrategy
import com.badoo.mvicore.ModelWatcher
import com.badoo.mvicore.modelWatcher
import com.tangem.common.doOnResult
import com.tangem.common.extensions.guard
import com.tangem.core.analytics.Analytics
import com.tangem.domain.common.TapWorkarounds.derivationStyle
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.feature.swap.domain.SwapInteractor
import com.tangem.tangem_sdk_new.extensions.dpToPx
import com.tangem.tap.common.SnackbarHandler
@ -61,7 +63,6 @@ import com.tangem.wallet.databinding.FragmentWalletDetailsBinding
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.rekotlin.StoreSubscriber
import timber.log.Timber
import javax.inject.Inject
@ -247,17 +248,16 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
if (currencyData.status != BalanceStatus.Loading && currencyData.status != BalanceStatus.Refreshing) {
Analytics.send(Token.Refreshed())
lifecycleScope.launch(Dispatchers.Default) {
val selectedUserWallet = userWalletsListManagerSafe?.selectedUserWalletSync
if (selectedUserWallet != null) {
walletCurrenciesManager.update(selectedUserWallet, currency)
.doOnResult {
withContext(Dispatchers.Main) {
binding.srlWalletDetails.isRefreshing = false
}
}
} else {
val selectedUserWallet = userWalletsListManagerSafe?.selectedUserWalletSync.guard {
Timber.e("Unable to refresh wallet details screen, no user wallet selected")
return@launch
}
walletCurrenciesManager.update(selectedUserWallet, currency)
.doOnResult {
withMainContext {
binding.srlWalletDetails.isRefreshing = false
}
}
}
}
}

View file

@ -3,7 +3,6 @@ package com.tangem.tap.features.wallet.ui.wallet.saltPay
import android.view.LayoutInflater
import android.view.ViewGroup
import com.tangem.core.analytics.Analytics
import com.tangem.domain.common.extensions.debounce
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.tap.common.ShimmerData
import com.tangem.tap.common.ShimmerRecyclerAdapter
@ -21,7 +20,6 @@ import com.tangem.tap.features.wallet.ui.wallet.WalletView
import com.tangem.tap.features.wallet.ui.wallet.saltPay.rv.HistoryItemData
import com.tangem.tap.features.wallet.ui.wallet.saltPay.rv.HistoryTransactionData
import com.tangem.tap.features.wallet.ui.wallet.saltPay.rv.TxHistoryAdapter
import com.tangem.tap.mainScope
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
import com.tangem.tap.scope
import com.tangem.tap.store
@ -31,7 +29,6 @@ import com.tangem.wallet.databinding.LayoutSaltPayBalanceBinding
import com.tangem.wallet.databinding.LayoutSaltPayTxHistoryBinding
import com.tangem.wallet.databinding.LayoutSaltPayWalletBinding
import kotlinx.coroutines.launch
import org.rekotlin.Action
import timber.log.Timber
/**
@ -41,8 +38,6 @@ class SaltPayWalletView : WalletView() {
private var saltPayBinding: LayoutSaltPayWalletBinding? = null
private val actionDebouncer = debounce<Action>(500, mainScope) { store.dispatch(it) }
private val balanceWidget: LayoutSaltPayBalanceBinding?
get() = saltPayBinding?.lSaltPayBalance
@ -131,7 +126,6 @@ class SaltPayWalletView : WalletView() {
if (tokenData.currencyData.fiatAmount == null) {
veilBalance.veil()
actionDebouncer(WalletAction.LoadFiatRate())
} else {
veilBalance.unVeil()
tvBalance.text = tokenData.currencyData.fiatAmount.formatAmountAsSpannedString(