Updated on 2026-08-14
This commit is contained in:
parent
770fad5955
commit
e1aee494ef
14 changed files with 174 additions and 36 deletions
|
|
@ -1,10 +1,11 @@
|
|||
package com.tangem.tap.domain.model
|
||||
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.tap.domain.model.WalletDataModel.AddressData
|
||||
import com.tangem.tap.domain.model.WalletDataModel.Status
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.features.wallet.models.PendingTransaction
|
||||
import com.tangem.tap.features.wallet.redux.AddressData
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
|
|
@ -21,7 +22,8 @@ import java.math.BigDecimal
|
|||
data class WalletDataModel(
|
||||
val currency: Currency,
|
||||
val status: Status,
|
||||
val walletAddresses: List<AddressData>,
|
||||
// FIXME: Left only selected wallet address here and move list of wallet addresses to WalletStoreModel
|
||||
val walletAddresses: WalletAddresses?,
|
||||
val existentialDeposit: BigDecimal?,
|
||||
val fiatRate: BigDecimal?,
|
||||
val isCardSingleToken: Boolean,
|
||||
|
|
@ -29,6 +31,18 @@ data class WalletDataModel(
|
|||
val historyTransactions: List<TransactionData>?,
|
||||
) {
|
||||
|
||||
data class WalletAddresses(
|
||||
val selectedAddress: AddressData,
|
||||
val list: List<AddressData>,
|
||||
)
|
||||
|
||||
data class AddressData(
|
||||
val address: String,
|
||||
val type: AddressType,
|
||||
val shareUrl: String,
|
||||
val exploreUrl: String,
|
||||
)
|
||||
|
||||
/**
|
||||
* Represent current status of currency
|
||||
* @property amount Currency amount
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import com.tangem.tap.domain.tokens.models.BlockchainNetwork
|
|||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import java.math.BigDecimal
|
||||
|
||||
// FIXME: Move list of wallet addresses from WalletDataModel to this class
|
||||
/**
|
||||
* Contains info about the blockchain and its currencies
|
||||
*
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.tangem.blockchain.blockchains.polkadot.ExistentialDepositProvider
|
|||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.DerivationStyle
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.common.hdWallet.DerivationPath
|
||||
import com.tangem.domain.common.TapWorkarounds.derivationStyle
|
||||
|
|
@ -107,7 +108,7 @@ private fun BlockchainNetwork.getBlockchainWalletData(
|
|||
return WalletDataModel(
|
||||
currency = currency,
|
||||
status = WalletDataModel.Loading,
|
||||
walletAddresses = walletManager?.wallet?.createAddressesData().orEmpty(),
|
||||
walletAddresses = walletManager?.wallet?.getWalletAddresses(),
|
||||
existentialDeposit = getExistentialDeposit(walletManager),
|
||||
fiatRate = null,
|
||||
isCardSingleToken = false,
|
||||
|
|
@ -131,7 +132,7 @@ private fun BlockchainNetwork.getTokensWalletsData(
|
|||
WalletDataModel(
|
||||
currency = currency,
|
||||
status = WalletDataModel.Loading,
|
||||
walletAddresses = walletManager?.wallet?.createAddressesData().orEmpty(),
|
||||
walletAddresses = walletManager?.wallet?.getWalletAddresses(),
|
||||
existentialDeposit = getExistentialDeposit(walletManager),
|
||||
fiatRate = null,
|
||||
isCardSingleToken = token == primaryToken,
|
||||
|
|
@ -149,7 +150,7 @@ private fun Blockchain.toBlockchainWalletData(walletManager: WalletManager): Wal
|
|||
derivationPath = wallet.publicKey.derivationPath?.rawPath,
|
||||
),
|
||||
status = WalletDataModel.Loading,
|
||||
walletAddresses = wallet.createAddressesData(),
|
||||
walletAddresses = wallet.getWalletAddresses(),
|
||||
existentialDeposit = getExistentialDeposit(walletManager),
|
||||
fiatRate = null,
|
||||
isCardSingleToken = false,
|
||||
|
|
@ -167,7 +168,7 @@ private fun Token.toTokenWalletData(walletManager: WalletManager, primaryToken:
|
|||
derivationPath = wallet.publicKey.derivationPath?.rawPath,
|
||||
),
|
||||
status = WalletDataModel.Loading,
|
||||
walletAddresses = wallet.createAddressesData(),
|
||||
walletAddresses = wallet.getWalletAddresses(),
|
||||
existentialDeposit = getExistentialDeposit(walletManager),
|
||||
fiatRate = null,
|
||||
isCardSingleToken = this == primaryToken,
|
||||
|
|
@ -178,4 +179,19 @@ private fun Token.toTokenWalletData(walletManager: WalletManager, primaryToken:
|
|||
|
||||
private fun getExistentialDeposit(walletManager: WalletManager?): BigDecimal? {
|
||||
return (walletManager as? ExistentialDepositProvider)?.getExistentialDeposit()
|
||||
}
|
||||
|
||||
private fun Wallet.getWalletAddresses(): WalletDataModel.WalletAddresses? {
|
||||
return this.createAddressesData()
|
||||
.takeIf { it.isNotEmpty() }
|
||||
?.map {
|
||||
// TODO: Will be removed in next MR
|
||||
with(it) { WalletDataModel.AddressData(address, type, shareUrl, exploreUrl) }
|
||||
}
|
||||
?.let { addresses ->
|
||||
WalletDataModel.WalletAddresses(
|
||||
list = addresses,
|
||||
selectedAddress = addresses.first(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -11,34 +11,29 @@ sealed class WalletStoresError(code: Int) : TangemError(code) {
|
|||
override val message: String?
|
||||
get() = customMessage
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
class FetchFiatRatesError(
|
||||
currencies: List<String>,
|
||||
override val cause: Throwable?,
|
||||
) : WalletStoresError(60011) {
|
||||
) : WalletStoresError(code = 60011) {
|
||||
override var customMessage: String = "Failed to fetch fiat rates for currencies $currencies"
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
class UnknownBlockchain : WalletStoresError(60012) {
|
||||
class UnknownBlockchain : WalletStoresError(code = 60012) {
|
||||
override var customMessage: String = "Unknown blockchain"
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
object NoInternetConnection : WalletStoresError(60013) {
|
||||
object NoInternetConnection : WalletStoresError(code = 60013) {
|
||||
override var customMessage: String = "No internet connection"
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
class WalletManagerNotCreated(blockchain: Blockchain) : WalletStoresError(60014) {
|
||||
class WalletManagerNotCreated(blockchain: Blockchain) : WalletStoresError(code = 60014) {
|
||||
override var customMessage: String = "Wallet manager can not be created for $blockchain"
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
class UpdateWalletManagerTokensError(
|
||||
blockchain: Blockchain,
|
||||
override val cause: Throwable,
|
||||
) : WalletStoresError(600015) {
|
||||
) : WalletStoresError(code = 600015) {
|
||||
override var customMessage: String = "Unable to update wallet manager tokens for currency $blockchain: $cause"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
package com.tangem.tap.domain.walletStores
|
||||
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.domain.common.util.UserWalletId
|
||||
import com.tangem.tap.domain.model.UserWallet
|
||||
import com.tangem.tap.domain.model.WalletStoreModel
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface WalletStoresManager {
|
||||
|
|
@ -22,6 +24,7 @@ interface WalletStoresManager {
|
|||
* @return [Flow] with [WalletStoreModel] list
|
||||
* */
|
||||
fun get(userWalletId: UserWalletId): Flow<List<WalletStoreModel>>
|
||||
|
||||
suspend fun getSync(userWalletId: UserWalletId): List<WalletStoreModel>
|
||||
|
||||
/**
|
||||
|
|
@ -71,6 +74,12 @@ interface WalletStoresManager {
|
|||
* */
|
||||
suspend fun updateAmounts(userWallets: List<UserWallet>): CompletionResult<Unit>
|
||||
|
||||
suspend fun updateSelectedAddress(
|
||||
userWalletId: UserWalletId,
|
||||
currency: Currency,
|
||||
addressType: AddressType,
|
||||
): CompletionResult<Unit>
|
||||
|
||||
// For provider
|
||||
companion object
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.tap.domain.walletStores.implementation
|
||||
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.doOnSuccess
|
||||
import com.tangem.common.flatMap
|
||||
|
|
@ -18,6 +19,7 @@ import com.tangem.tap.domain.walletStores.WalletStoresManager
|
|||
import com.tangem.tap.domain.walletStores.repository.WalletAmountsRepository
|
||||
import com.tangem.tap.domain.walletStores.repository.WalletManagersRepository
|
||||
import com.tangem.tap.domain.walletStores.repository.WalletStoresRepository
|
||||
import com.tangem.tap.domain.walletStores.repository.implementation.utils.updateSelectedAddress
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.features.wallet.models.toBlockchainNetworks
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
|
@ -103,6 +105,21 @@ internal class DefaultWalletStoresManager(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun updateSelectedAddress(
|
||||
userWalletId: UserWalletId,
|
||||
currency: Currency,
|
||||
addressType: AddressType,
|
||||
): CompletionResult<Unit> {
|
||||
return walletStoresRepository.update(userWalletId) { walletStores ->
|
||||
walletStores
|
||||
.firstOrNull {
|
||||
it.blockchain == currency.blockchain &&
|
||||
it.derivationPath?.rawPath == currency.derivationPath
|
||||
}
|
||||
?.updateSelectedAddress(currency, addressType)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchWalletsIfNeeded(userWallet: UserWallet): CompletionResult<UserWallet> {
|
||||
return if (userWallet.isMultiCurrency) {
|
||||
fetchMultiWallets(userWallet)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
package com.tangem.tap.domain.walletStores.implementation
|
||||
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.domain.common.util.UserWalletId
|
||||
import com.tangem.tap.domain.model.UserWallet
|
||||
import com.tangem.tap.domain.model.WalletStoreModel
|
||||
import com.tangem.tap.domain.walletStores.WalletStoresManager
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
|
||||
|
|
@ -40,4 +42,12 @@ internal class DummyWalletStoresManager : WalletStoresManager {
|
|||
override suspend fun updateAmounts(userWallets: List<UserWallet>): CompletionResult<Unit> {
|
||||
return CompletionResult.Success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun updateSelectedAddress(
|
||||
userWalletId: UserWalletId,
|
||||
currency: Currency,
|
||||
addressType: AddressType,
|
||||
): CompletionResult<Unit> {
|
||||
return CompletionResult.Success(Unit)
|
||||
}
|
||||
}
|
||||
|
|
@ -24,5 +24,19 @@ interface WalletStoresRepository {
|
|||
|
||||
suspend fun storeOrUpdate(userWalletId: UserWalletId, walletStore: WalletStoreModel): CompletionResult<Unit>
|
||||
|
||||
/**
|
||||
* Updates [WalletStoreModel] in storage for user wallet with provided [UserWalletId]
|
||||
*
|
||||
* @param userWalletId [UserWalletId] of user wallet
|
||||
* @param operation Lambda which receives list of [WalletStoreModel] assigned to user wallet with [userWalletId]
|
||||
* and returns updated [WalletStoreModel]. If null returned then do nothing
|
||||
*
|
||||
* @return [CompletionResult] of operation
|
||||
* */
|
||||
suspend fun update(
|
||||
userWalletId: UserWalletId,
|
||||
operation: (List<WalletStoreModel>) -> WalletStoreModel?,
|
||||
): CompletionResult<Unit>
|
||||
|
||||
companion object
|
||||
}
|
||||
|
|
@ -69,6 +69,25 @@ internal class DefaultWalletStoresRepository : WalletStoresRepository {
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun update(
|
||||
userWalletId: UserWalletId,
|
||||
operation: (List<WalletStoreModel>) -> WalletStoreModel?,
|
||||
): CompletionResult<Unit> = catching {
|
||||
val walletStores = getSync(userWalletId).toMutableList()
|
||||
val updatedWalletStore = operation(walletStores) ?: return CompletionResult.Success(Unit)
|
||||
|
||||
val index = walletStores.indexOfFirst { it.isSameWalletStore(updatedWalletStore) }
|
||||
if (index == -1 || updatedWalletStore == walletStores[index]) return CompletionResult.Success(Unit)
|
||||
|
||||
walletStores[index] = updatedWalletStore
|
||||
|
||||
walletStoresStorage.update { prevStores ->
|
||||
prevStores.apply {
|
||||
this[userWalletId] = walletStores
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun HashMap<UserWalletId, List<WalletStoreModel>>.addOrUpdate(
|
||||
userWalletId: UserWalletId,
|
||||
walletStore: WalletStoreModel,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.tap.domain.walletStores.repository.implementation.utils
|
|||
import com.tangem.blockchain.common.AmountType
|
||||
import com.tangem.blockchain.common.BlockchainSdkError
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.common.core.TangemError
|
||||
import com.tangem.tap.common.extensions.getBlockchainTxHistory
|
||||
import com.tangem.tap.common.extensions.getTokenTxHistory
|
||||
|
|
@ -155,7 +156,6 @@ internal fun WalletDataModel.updateWithSelf(newWalletData: WalletDataModel): Wal
|
|||
is WalletDataModel.VerifiedOnline,
|
||||
-> newStatus
|
||||
},
|
||||
walletAddresses = newWalletData.walletAddresses,
|
||||
existentialDeposit = newWalletData.existentialDeposit,
|
||||
fiatRate = newWalletData.fiatRate ?: oldWalletData.fiatRate,
|
||||
)
|
||||
|
|
@ -193,6 +193,31 @@ internal fun List<WalletDataModel>.updateWithSelf(newWalletsData: List<WalletDat
|
|||
return updatedWalletsData
|
||||
}
|
||||
|
||||
internal fun List<WalletDataModel>.updateSelectedAddress(
|
||||
currency: Currency,
|
||||
addressType: AddressType,
|
||||
): List<WalletDataModel> {
|
||||
val index = this.indexOfFirst { it.currency == currency }
|
||||
if (index == -1) return this
|
||||
|
||||
val oldWalletData = this[index]
|
||||
val addresses = oldWalletData.walletAddresses
|
||||
?: return this
|
||||
val selectedAddress = addresses.list
|
||||
.firstOrNull { it.type == addressType }
|
||||
?: addresses.selectedAddress
|
||||
val updatedWalletData = oldWalletData.copy(
|
||||
walletAddresses = addresses.copy(
|
||||
selectedAddress = selectedAddress,
|
||||
),
|
||||
)
|
||||
if (oldWalletData == updatedWalletData) return this
|
||||
|
||||
return this.toMutableList().apply {
|
||||
this[index] = updatedWalletData
|
||||
}
|
||||
}
|
||||
|
||||
internal fun WalletDataModel.isSameWalletData(other: WalletDataModel): Boolean {
|
||||
return this.currency == other.currency
|
||||
}
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
package com.tangem.tap.domain.walletStores.repository.implementation.utils
|
||||
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.common.core.TangemError
|
||||
import com.tangem.domain.common.util.UserWalletId
|
||||
import com.tangem.tap.domain.model.WalletStoreModel
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import timber.log.Timber
|
||||
|
||||
internal inline fun HashMap<UserWalletId, List<WalletStoreModel>>.replaceWalletStore(
|
||||
|
|
@ -91,6 +93,12 @@ internal fun WalletStoreModel.updateWithRent(rent: WalletStoreModel.WalletRent?)
|
|||
)
|
||||
}
|
||||
|
||||
internal fun WalletStoreModel.updateSelectedAddress(currency: Currency, addressType: AddressType): WalletStoreModel {
|
||||
return this.copy(
|
||||
walletsData = walletsData.updateSelectedAddress(currency, addressType),
|
||||
)
|
||||
}
|
||||
|
||||
private inline fun List<WalletStoreModel>.replaceWalletStores(
|
||||
walletStoresToUpdate: List<WalletStoreModel>,
|
||||
update: (walletStore: WalletStoreModel) -> WalletStoreModel,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import com.tangem.tap.domain.model.WalletDataModel
|
|||
import com.tangem.tap.domain.model.WalletStoreModel
|
||||
import com.tangem.tap.features.wallet.models.TotalBalance
|
||||
import com.tangem.tap.features.wallet.models.WalletRent
|
||||
import com.tangem.tap.features.wallet.redux.AddressData
|
||||
import com.tangem.tap.features.wallet.redux.ProgressState
|
||||
import com.tangem.tap.features.wallet.redux.WalletAddresses
|
||||
import com.tangem.tap.features.wallet.redux.WalletData
|
||||
|
|
@ -76,10 +77,15 @@ private fun WalletDataModel.mapToReduxModel(
|
|||
|
||||
return WalletData(
|
||||
currency = currency,
|
||||
walletAddresses = walletAddresses.getOrNull(0)?.let { selectedAddress ->
|
||||
// TODO: Will be updated in next MR
|
||||
walletAddresses = walletAddresses?.let { addresses ->
|
||||
WalletAddresses(
|
||||
selectedAddress = selectedAddress,
|
||||
list = walletAddresses,
|
||||
selectedAddress = with(addresses.selectedAddress) {
|
||||
AddressData(address, type, shareUrl, exploreUrl)
|
||||
},
|
||||
list = addresses.list.map {
|
||||
with(it) { AddressData(address, type, shareUrl, exploreUrl) }
|
||||
},
|
||||
)
|
||||
},
|
||||
existentialDepositString = existentialDeposit?.toPlainString(),
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.tap.features.wallet.redux.middlewares
|
|||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.AmountType
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.core.analytics.Analytics
|
||||
|
|
@ -42,6 +43,7 @@ import com.tangem.tap.store
|
|||
import com.tangem.tap.tangemSdkManager
|
||||
import com.tangem.tap.totalFiatBalanceCalculator
|
||||
import com.tangem.tap.userWalletsListManager
|
||||
import com.tangem.tap.walletStoresManager
|
||||
import com.tangem.utils.coroutines.ifActive
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
|
@ -204,6 +206,24 @@ class WalletMiddleware {
|
|||
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo(screen))
|
||||
}
|
||||
is WalletAction.ChangeSelectedAddress -> {
|
||||
changeSelectedWalletAddress(action.type, walletState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeSelectedWalletAddress(type: AddressType, state: WalletState) {
|
||||
val selectedUserWalletId = userWalletsListManager.selectedUserWalletSync?.walletId.guard {
|
||||
Timber.e("Unable to change selected wallet address, no user wallet selected")
|
||||
return
|
||||
}
|
||||
val selectedCurrency = state.selectedCurrency.guard {
|
||||
Timber.e("Unable to change selected wallet address, no currency selected")
|
||||
return
|
||||
}
|
||||
|
||||
scope.launch(Dispatchers.Default) {
|
||||
walletStoresManager.updateSelectedAddress(selectedUserWalletId, selectedCurrency, type)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import com.tangem.tap.features.wallet.redux.Artwork
|
|||
import com.tangem.tap.features.wallet.redux.ErrorType
|
||||
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.WalletState
|
||||
import com.tangem.tap.features.wallet.redux.WalletStore
|
||||
|
|
@ -102,21 +101,6 @@ private fun internalReduce(action: Action, state: AppState, appStateHolder: AppS
|
|||
)
|
||||
}
|
||||
is WalletAction.TradeCryptoAction -> return newState
|
||||
is WalletAction.ChangeSelectedAddress -> {
|
||||
val walletAddresses = newState.getWalletData(newState.selectedCurrency)?.walletAddresses
|
||||
?: return newState
|
||||
val address = walletAddresses.list.firstOrNull { it.type == action.type }
|
||||
?: return newState
|
||||
|
||||
newState = newState.updateWalletData(
|
||||
newState.selectedWalletData?.copy(
|
||||
walletAddresses = WalletAddresses(
|
||||
selectedAddress = address,
|
||||
list = walletAddresses.list,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
is WalletAction.AppCurrencyAction -> {
|
||||
newState = appCurrencyReducer.reduce(action, newState)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue