Updated on 2026-08-14
This commit is contained in:
parent
8b81f309c8
commit
d28e324cd0
22 changed files with 151 additions and 1473 deletions
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.domain.common.extensions.withMainContext
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.StateDialog
|
||||
|
|
@ -78,21 +77,18 @@ fun Store<*>.dispatchDialogHide() {
|
|||
/**
|
||||
* Dispatch action inside a coroutine with the Main dispatcher
|
||||
*/
|
||||
@Deprecated(
|
||||
message = "Use dispatchWithMain instead",
|
||||
replaceWith = ReplaceWith(expression = "dispatchWithMain"),
|
||||
)
|
||||
suspend fun dispatchOnMain(vararg actions: Action) {
|
||||
withMainContext { actions.forEach { store.dispatch(it) } }
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch action
|
||||
*/
|
||||
suspend fun Store<*>.onCardScanned(scanResponse: ScanResponse) {
|
||||
store.state.globalState.tapWalletManager.onCardScanned(scanResponse)
|
||||
}
|
||||
|
||||
fun Store<*>.dispatchOpenUrl(url: String) {
|
||||
store.dispatch(NavigationAction.OpenUrl(url))
|
||||
dispatch(NavigationAction.OpenUrl(url))
|
||||
}
|
||||
|
||||
fun Store<*>.dispatchShare(url: String) {
|
||||
store.dispatch(NavigationAction.Share(url))
|
||||
dispatch(NavigationAction.Share(url))
|
||||
}
|
||||
|
|
@ -1,109 +1,42 @@
|
|||
package com.tangem.tap.domain
|
||||
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.BlockchainSdkConfig
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.blockchain.common.WalletManagerFactory
|
||||
import com.tangem.common.doOnFailure
|
||||
import com.tangem.common.doOnSuccess
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.datasource.config.ConfigManager
|
||||
import com.tangem.domain.common.CardDTO
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.domain.common.TapWorkarounds.isTestCard
|
||||
import com.tangem.domain.common.ThrottlerWithValues
|
||||
import com.tangem.domain.common.extensions.withMainContext
|
||||
import com.tangem.operations.attestation.Attestation
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.safeUpdate
|
||||
import com.tangem.tap.common.extensions.setContext
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.domain.extensions.makePrimaryWalletManager
|
||||
import com.tangem.tap.domain.extensions.makeWalletManagersForApp
|
||||
import com.tangem.tap.domain.model.UserWallet
|
||||
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
|
||||
import com.tangem.tap.domain.walletStores.WalletStoresError
|
||||
import com.tangem.tap.features.demo.isDemoCard
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
|
||||
import com.tangem.tap.features.disclaimer.createDisclaimer
|
||||
import com.tangem.tap.features.disclaimer.redux.DisclaimerAction
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
|
||||
import com.tangem.tap.features.wallet.models.toBlockchainNetworks
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.redux.middlewares.handleBasicAnalyticsEvent
|
||||
import com.tangem.tap.network.NetworkConnectivity
|
||||
import com.tangem.tap.preferencesStorage
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import com.tangem.tap.userTokensRepository
|
||||
import com.tangem.tap.walletStoresManager
|
||||
import com.tangem.utils.coroutines.AppCoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.rekotlin.Action
|
||||
import timber.log.Timber
|
||||
|
||||
class TapWalletManager {
|
||||
val walletManagerFactory: WalletManagerFactory
|
||||
by lazy { WalletManagerFactory(blockchainSdkConfig) }
|
||||
|
||||
// TODO("After adding DI") get dependencies by DI
|
||||
val rates: RatesRepository by lazy {
|
||||
RatesRepository(
|
||||
tangemTechApi = store.state.domainNetworks.tangemTechService.api,
|
||||
dispatchers = AppCoroutineDispatcherProvider(),
|
||||
)
|
||||
}
|
||||
|
||||
private val blockchainSdkConfig by lazy {
|
||||
store.state.globalState.configManager?.config?.blockchainSdkConfig ?: BlockchainSdkConfig()
|
||||
}
|
||||
|
||||
private val walletManagersThrottler =
|
||||
ThrottlerWithValues<BlockchainNetwork, Result<Wallet>>(10000)
|
||||
|
||||
suspend fun loadWalletData(walletManager: WalletManager) {
|
||||
val blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager)
|
||||
val result = if (walletManagersThrottler.isStillThrottled(blockchainNetwork)) {
|
||||
walletManagersThrottler.geValue(blockchainNetwork)!!
|
||||
} else {
|
||||
val safeUpdateResult = walletManager.safeUpdate()
|
||||
walletManagersThrottler.updateThrottlingTo(blockchainNetwork)
|
||||
walletManagersThrottler.setValue(blockchainNetwork, safeUpdateResult)
|
||||
safeUpdateResult
|
||||
}
|
||||
when (result) {
|
||||
is Result.Success -> {
|
||||
dispatchOnMain(WalletAction.LoadWallet.Success(result.data, blockchainNetwork))
|
||||
}
|
||||
is Result.Failure -> {
|
||||
when (result.error) {
|
||||
is TapError.WalletManager.NoAccountError -> {
|
||||
dispatchOnMain(
|
||||
WalletAction.LoadWallet.NoAccount(
|
||||
walletManager.wallet,
|
||||
blockchainNetwork,
|
||||
(result.error as TapError.WalletManager.NoAccountError).customMessage,
|
||||
),
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
dispatchOnMain(
|
||||
WalletAction.LoadWallet.Failure(
|
||||
walletManager.wallet,
|
||||
result.error.localizedMessage,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun onWalletSelected(userWallet: UserWallet, refresh: Boolean) {
|
||||
Analytics.setContext(userWallet.scanResponse)
|
||||
val scanResponse = userWallet.scanResponse
|
||||
|
|
@ -161,32 +94,6 @@ class TapWalletManager {
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun onCardScanned(data: ScanResponse) {
|
||||
walletManagersThrottler.clear()
|
||||
store.state.globalState.feedbackManager?.infoHolder?.setCardInfo(data)
|
||||
updateConfigManager(data)
|
||||
|
||||
withMainContext {
|
||||
store.dispatch(WalletAction.ResetState(data.card))
|
||||
store.dispatch(WalletConnectAction.ResetState)
|
||||
store.dispatch(GlobalAction.SaveScanResponse(data))
|
||||
store.dispatch(WalletAction.SetIfTestnetCard(data.card.isTestCard))
|
||||
store.dispatch(
|
||||
WalletAction.MultiWallet.SetIsMultiwalletAllowed(
|
||||
data.cardTypesResolver.isMultiwalletAllowed(),
|
||||
),
|
||||
)
|
||||
store.dispatch(WalletConnectAction.RestoreSessions(data))
|
||||
store.dispatch(
|
||||
WalletAction.MultiWallet.ShowWalletBackupWarning(
|
||||
show = data.card.settings.isBackupAllowed &&
|
||||
data.card.backupStatus == CardDTO.BackupStatus.NoBackup,
|
||||
),
|
||||
)
|
||||
loadData(data)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateConfigManager(data: ScanResponse) {
|
||||
val configManager = store.state.globalState.configManager
|
||||
val blockchain = data.cardTypesResolver.getBlockchain()
|
||||
|
|
@ -203,126 +110,6 @@ class TapWalletManager {
|
|||
configManager?.resetToDefault(ConfigManager.IS_TOP_UP_ENABLED)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun loadData(data: ScanResponse) {
|
||||
dispatchOnMain(WalletAction.LoadCardInfo(data.card))
|
||||
getActionIfUnknownBlockchainOrEmptyWallet(data)?.let {
|
||||
dispatchOnMain(it)
|
||||
return
|
||||
}
|
||||
|
||||
if (data.cardTypesResolver.isMultiwalletAllowed()) {
|
||||
dispatchOnMain(WalletAction.MultiWallet.ScheduleCheckForMissingDerivation)
|
||||
loadMultiWalletData(data)
|
||||
} else {
|
||||
loadSingleWalletData(data)
|
||||
}
|
||||
|
||||
dispatchOnMain(WalletAction.LoadWallet())
|
||||
}
|
||||
|
||||
private suspend fun loadMultiWalletData(scanResponse: ScanResponse) {
|
||||
loadUserCurrencies(scanResponse, walletManagerFactory)
|
||||
}
|
||||
|
||||
private fun checkIfDerivationsAreMissing(blockchainNetworks: List<BlockchainNetwork>, scanResponse: ScanResponse) {
|
||||
blockchainNetworks.map {
|
||||
if (it.tokens.isNotEmpty()) {
|
||||
WalletAction.MultiWallet.AddTokens(it.tokens, it)
|
||||
}
|
||||
}
|
||||
val missingDerivations = blockchainNetworks
|
||||
.filter {
|
||||
it.derivationPath != null && !scanResponse.hasDerivation(it.blockchain, it.derivationPath)
|
||||
}
|
||||
store.dispatch(WalletAction.MultiWallet.AddMissingDerivations(missingDerivations))
|
||||
}
|
||||
|
||||
private suspend fun loadSingleWalletData(data: ScanResponse) {
|
||||
val blockchain = data.cardTypesResolver.getBlockchain()
|
||||
val primaryWalletManager = walletManagerFactory.makePrimaryWalletManager(data)
|
||||
|
||||
if (blockchain != Blockchain.Unknown && primaryWalletManager != null) {
|
||||
val blockchainNetwork = BlockchainNetwork.fromWalletManager(primaryWalletManager)
|
||||
val actionsList = listOfNotNull<Action>(
|
||||
WalletAction.MultiWallet.AddBlockchains(
|
||||
blockchains = listOf(BlockchainNetwork.fromWalletManager(primaryWalletManager)),
|
||||
walletManagers = listOf(primaryWalletManager),
|
||||
),
|
||||
data.cardTypesResolver.getPrimaryToken()?.let {
|
||||
primaryWalletManager.addToken(it)
|
||||
primaryWalletManager.wallet.setAmount(Amount(it))
|
||||
WalletAction.MultiWallet.AddToken(it, blockchainNetwork, false)
|
||||
},
|
||||
WalletAction.LoadFiatRate(),
|
||||
)
|
||||
dispatchOnMain(*actionsList.toTypedArray())
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun loadUserCurrencies(scanResponse: ScanResponse, walletManagerFactory: WalletManagerFactory) {
|
||||
val userTokens = userTokensRepository.getUserTokens(scanResponse.card)
|
||||
withMainContext {
|
||||
val blockchainNetworks = userTokens.toBlockchainNetworks()
|
||||
val walletManagers = walletManagerFactory.makeWalletManagersForApp(scanResponse, userTokens)
|
||||
store.dispatch(
|
||||
WalletAction.MultiWallet.AddBlockchains(
|
||||
blockchains = blockchainNetworks,
|
||||
walletManagers = walletManagers,
|
||||
),
|
||||
)
|
||||
|
||||
blockchainNetworks.filter { it.tokens.isNotEmpty() }
|
||||
.map {
|
||||
store.dispatch(
|
||||
WalletAction.MultiWallet.AddTokens(
|
||||
tokens = it.tokens,
|
||||
blockchain = it,
|
||||
),
|
||||
)
|
||||
}
|
||||
checkIfDerivationsAreMissing(blockchainNetworks, scanResponse)
|
||||
store.dispatch(WalletAction.LoadFiatRate(coinsList = userTokens))
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun reloadData(data: ScanResponse) {
|
||||
if (data.cardTypesResolver.isMultiwalletAllowed()) {
|
||||
loadUserCurrencies(data, walletManagerFactory)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
getActionIfUnknownBlockchainOrEmptyWallet(data)?.let {
|
||||
store.dispatch(it)
|
||||
return@withContext
|
||||
}
|
||||
if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) {
|
||||
store.dispatch(WalletAction.LoadData.Failure(TapError.NoInternetConnection))
|
||||
return@withContext
|
||||
}
|
||||
|
||||
store.dispatch(WalletAction.LoadWallet())
|
||||
}
|
||||
}
|
||||
|
||||
private fun getActionIfUnknownBlockchainOrEmptyWallet(data: ScanResponse): WalletAction? {
|
||||
return when {
|
||||
// check order is important
|
||||
data.cardTypesResolver.isTangemTwins() && !data.twinsIsTwinned() -> {
|
||||
WalletAction.EmptyWallet
|
||||
}
|
||||
data.cardTypesResolver.getBlockchain() == Blockchain.Unknown &&
|
||||
!data.cardTypesResolver.isMultiwalletAllowed() -> {
|
||||
WalletAction.LoadData.Failure(TapError.UnknownBlockchain)
|
||||
}
|
||||
data.isDemoCard() -> {
|
||||
return null
|
||||
}
|
||||
data.card.wallets.isEmpty() -> {
|
||||
WalletAction.EmptyWallet
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Wallet.getFirstToken(): Token? {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,12 @@ import com.tangem.tap.domain.model.UserWallet
|
|||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
||||
/**
|
||||
* Indicates that the [UserWalletsListManager] implements [UserWalletsListManager.Lockable]
|
||||
* */
|
||||
val UserWalletsListManager.isLockable: Boolean
|
||||
get() = this is UserWalletsListManager.Lockable
|
||||
|
||||
/**
|
||||
* Indicates that the [UserWalletsListManager] is locked
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
)
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.tangem.blockchain.common.AmountType
|
|||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.domain.common.extensions.fromNetworkId
|
||||
import com.tangem.domain.common.extensions.toCoinId
|
||||
import com.tangem.domain.common.extensions.toNetworkId
|
||||
|
|
@ -102,15 +103,14 @@ class UserWalletManagerImpl(
|
|||
val blockchain = requireNotNull(Blockchain.fromNetworkId(currency.networkId)) { "blockchain not found" }
|
||||
val blockchainNetwork = BlockchainNetwork(blockchain, card)
|
||||
|
||||
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync
|
||||
if (selectedUserWallet != null) {
|
||||
walletCurrenciesManager.addCurrencies(
|
||||
userWallet = selectedUserWallet,
|
||||
currenciesToAdd = listOf(currency.toWalletCurrency(blockchainNetwork)),
|
||||
)
|
||||
} else {
|
||||
Timber.e("Unable to add token, selected user wallet is null")
|
||||
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard {
|
||||
Timber.e("Unable to add token, no user wallet selected")
|
||||
return
|
||||
}
|
||||
walletCurrenciesManager.addCurrencies(
|
||||
userWallet = selectedUserWallet,
|
||||
currenciesToAdd = listOf(currency.toWalletCurrency(blockchainNetwork)),
|
||||
)
|
||||
}
|
||||
|
||||
override fun getWalletAddress(networkId: String): String {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue