Updated on 2026-08-14
This commit is contained in:
commit
0905bb1ad9
35 changed files with 474 additions and 303 deletions
|
|
@ -17,10 +17,11 @@ import com.tangem.tap.features.onboarding.AddressInfoBottomSheetDialog
|
|||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
|
||||
import com.tangem.tap.features.onboarding.products.twins.ui.dialog.CreateWalletInterruptDialog
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupDialog
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingDialog
|
||||
import com.tangem.tap.features.onboarding.products.wallet.saltPay.dialog.InterruptOnboardingDialog
|
||||
import com.tangem.tap.features.onboarding.products.wallet.saltPay.dialog.NoFundsForActivationDialog
|
||||
import com.tangem.tap.features.onboarding.products.wallet.saltPay.dialog.RegistrationErrorDialog
|
||||
import com.tangem.tap.features.onboarding.products.wallet.saltPay.dialog.SaltPayDialog
|
||||
import com.tangem.tap.features.onboarding.products.wallet.saltPay.dialog.TryToInterruptRegistrationDialog
|
||||
import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.AddMoreBackupCardsDialog
|
||||
import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.BackupInProgressDialog
|
||||
import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.ConfirmDiscardingBackupDialog
|
||||
|
|
@ -72,8 +73,8 @@ class DialogManager : StoreSubscriber<GlobalState> {
|
|||
is AppDialog.ScanFailsDialog -> ScanFailsDialog.create(context)
|
||||
is AppDialog.AddressInfoDialog -> AddressInfoBottomSheetDialog(state.dialog, context)
|
||||
is AppDialog.TestActionsDialog -> TestActionsBottomSheetDialog(state.dialog, context)
|
||||
is TwinCardsAction.Wallet.ShowInterruptDialog ->
|
||||
CreateWalletInterruptDialog.create(state.dialog, context)
|
||||
is TwinCardsAction.Wallet.ShowInterruptDialog -> CreateWalletInterruptDialog.create(state.dialog, context)
|
||||
is OnboardingDialog.InterruptOnboarding -> InterruptOnboardingDialog.create(context, state.dialog)
|
||||
is WalletConnectDialog.UnsupportedCard ->
|
||||
SimpleAlertDialog.create(
|
||||
titleRes = R.string.wallet_connect_title,
|
||||
|
|
@ -109,10 +110,8 @@ class DialogManager : StoreSubscriber<GlobalState> {
|
|||
ChooseNetworkDialog.create(state.dialog.session, state.dialog.networks, context)
|
||||
is WalletConnectDialog.ClipboardOrScanQr ->
|
||||
ClipboardOrScanQrDialog.create(state.dialog.clipboardUri, context)
|
||||
is WalletConnectDialog.RequestTransaction ->
|
||||
TransactionDialog.create(state.dialog.dialogData, context)
|
||||
is WalletConnectDialog.PersonalSign ->
|
||||
PersonalSignDialog.create(state.dialog.data, context)
|
||||
is WalletConnectDialog.RequestTransaction -> TransactionDialog.create(state.dialog.dialogData, context)
|
||||
is WalletConnectDialog.PersonalSign -> PersonalSignDialog.create(state.dialog.data, context)
|
||||
is WalletConnectDialog.BnbTransactionDialog ->
|
||||
BnbTransactionDialog.create(
|
||||
data = state.dialog.data,
|
||||
|
|
@ -132,7 +131,6 @@ class DialogManager : StoreSubscriber<GlobalState> {
|
|||
is BackupDialog.UnfinishedBackupFound -> UnfinishedBackupFoundDialog.create(context)
|
||||
is BackupDialog.ConfirmDiscardingBackup -> ConfirmDiscardingBackupDialog.create(context)
|
||||
is SaltPayDialog.Activation.NoGas -> NoFundsForActivationDialog.create(context)
|
||||
is SaltPayDialog.Activation.TryToInterrupt -> TryToInterruptRegistrationDialog.create(context, state.dialog)
|
||||
is SaltPayDialog.Activation.OnError -> RegistrationErrorDialog.create(context, state.dialog)
|
||||
is WalletDialog.CurrencySelectionDialog -> CurrencySelectionDialog.create(state.dialog, context)
|
||||
is WalletDialog.ChooseTradeActionDialog -> ChooseTradeActionBottomSheetDialog(context)
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ fun BigDecimal.toFormattedFiatValue(
|
|||
): String {
|
||||
val fiatValue = this.setScale(2, RoundingMode.HALF_UP)
|
||||
.let { if (formatWithSpaces) it.formatWithSpaces() else it }
|
||||
return " ${fiatValue} $fiatCurrencyName"
|
||||
return " $fiatValue $fiatCurrencyName"
|
||||
}
|
||||
|
||||
fun BigDecimal.stripZeroPlainString(): String = this.stripTrailingZeros().toPlainString()
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ class ConfigManager {
|
|||
),
|
||||
infuraProjectId = values.infuraProjectId,
|
||||
tronGridApiKey = values.tronGridApiKey,
|
||||
saltPayAuthToken = values.saltPay.credentials.token,
|
||||
),
|
||||
appsFlyerDevKey = values.appsFlyer.appsFlyerDevKey,
|
||||
amplitudeApiKey = values.amplitudeApiKey,
|
||||
|
|
@ -129,6 +130,7 @@ class ConfigManager {
|
|||
subdomain = values.bscQuiknodeSubdomain,
|
||||
),
|
||||
infuraProjectId = values.infuraProjectId,
|
||||
saltPayAuthToken = values.saltPay.credentials.token,
|
||||
),
|
||||
appsFlyerDevKey = values.appsFlyer.appsFlyerDevKey,
|
||||
amplitudeApiKey = values.amplitudeApiKey,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import java.math.BigDecimal
|
|||
* @param existentialDeposit Amount that must be held on currency's balance, if balance is below that amount all
|
||||
* founds will be destroyed. Null if currency don't have existential deposit
|
||||
* @param fiatRate Wallet's fiat rate, used to calculate fiat balance. Null if not provided
|
||||
* @param isCardSingleToken shows that [Currency] is a card token
|
||||
* */
|
||||
data class WalletDataModel(
|
||||
val currency: Currency,
|
||||
|
|
@ -21,6 +22,7 @@ data class WalletDataModel(
|
|||
val walletAddresses: List<AddressData>,
|
||||
val existentialDeposit: BigDecimal?,
|
||||
val fiatRate: BigDecimal?,
|
||||
val isCardSingleToken: Boolean,
|
||||
) {
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -6,10 +6,12 @@ import com.tangem.common.hdWallet.DerivationPath
|
|||
import com.tangem.domain.common.util.UserWalletId
|
||||
import com.tangem.tap.domain.model.WalletStoreModel.WalletRent
|
||||
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* Contains info about the blockchain and its currencies
|
||||
*
|
||||
* @param userWalletId ID of the associated [UserWallet]
|
||||
* @param blockchain [Blockchain] of this WalletStore
|
||||
* @param derivationPath [DerivationPath] of this store, null if the card does not support the
|
||||
|
|
@ -21,6 +23,9 @@ import java.math.BigDecimal
|
|||
* TODO: Remove after WalletMiddleware refactoring
|
||||
* @param walletManager [WalletManager], may be null if it fails to create this manager.
|
||||
* TODO: Remove after WalletMiddleware refactoring
|
||||
*
|
||||
* @property blockchainWalletData Returns the [WalletDataModel] of the blockchain of this wallet store
|
||||
* or throw [NoSuchElementException] if this wallet store not contains [WalletDataModel] of the blockchain
|
||||
* */
|
||||
data class WalletStoreModel(
|
||||
val userWalletId: UserWalletId,
|
||||
|
|
@ -34,6 +39,9 @@ data class WalletStoreModel(
|
|||
val walletManager: WalletManager?,
|
||||
) {
|
||||
|
||||
val blockchainWalletData: WalletDataModel
|
||||
get() = walletsData.first { it.currency is Currency.Blockchain }
|
||||
|
||||
/**
|
||||
* Represents wallet blockchain rent
|
||||
* @param rent Amount that will be charged in overtime if the blockchain does not have an amount greater than
|
||||
|
|
|
|||
|
|
@ -73,13 +73,13 @@ private class WalletMangerWalletStoreBuilderImpl(
|
|||
override fun build(): WalletStoreModel {
|
||||
val wallet = walletManager.wallet
|
||||
val blockchainWalletData = wallet.blockchain.toBlockchainWalletData(walletManager)
|
||||
val tokenWalletsData = wallet.getTokens().firstOrNull()?.toTokenWalletData(walletManager)
|
||||
val tokenWalletsData = walletManager.cardTokens.map { it.toTokenWalletData(walletManager) }
|
||||
|
||||
return WalletStoreModel(
|
||||
userWalletId = userWalletId,
|
||||
blockchain = wallet.blockchain,
|
||||
derivationPath = wallet.publicKey.derivationPath,
|
||||
walletsData = (listOf(blockchainWalletData) + listOfNotNull(tokenWalletsData)),
|
||||
walletsData = (listOf(blockchainWalletData) + tokenWalletsData),
|
||||
walletRent = null,
|
||||
walletManager = walletManager,
|
||||
blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager),
|
||||
|
|
@ -97,6 +97,7 @@ private fun BlockchainNetwork.getBlockchainWalletData(walletManager: WalletManag
|
|||
walletAddresses = walletManager?.wallet?.createAddressesData().orEmpty(),
|
||||
existentialDeposit = getExistentialDeposit(walletManager),
|
||||
fiatRate = null,
|
||||
isCardSingleToken = false,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -113,6 +114,7 @@ private fun BlockchainNetwork.getTokensWalletsData(walletManager: WalletManager?
|
|||
walletAddresses = walletManager?.wallet?.createAddressesData().orEmpty(),
|
||||
existentialDeposit = getExistentialDeposit(walletManager),
|
||||
fiatRate = null,
|
||||
isCardSingleToken = walletManager?.cardTokens?.contains(token) ?: false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -128,6 +130,7 @@ private fun Blockchain.toBlockchainWalletData(walletManager: WalletManager): Wal
|
|||
walletAddresses = wallet.createAddressesData(),
|
||||
existentialDeposit = getExistentialDeposit(walletManager),
|
||||
fiatRate = null,
|
||||
isCardSingleToken = false,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -143,6 +146,7 @@ private fun Token.toTokenWalletData(walletManager: WalletManager): WalletDataMod
|
|||
walletAddresses = wallet.createAddressesData(),
|
||||
existentialDeposit = getExistentialDeposit(walletManager),
|
||||
fiatRate = null,
|
||||
isCardSingleToken = walletManager.cardTokens.contains(this),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -70,7 +70,6 @@ object ScanCardProcessor {
|
|||
|
||||
result
|
||||
.doOnFailure { error ->
|
||||
onProgressStateChange(false)
|
||||
onScanStateChange(false)
|
||||
onFailure(error)
|
||||
}
|
||||
|
|
@ -87,7 +86,6 @@ object ScanCardProcessor {
|
|||
nextHandler = { scanResponse1 ->
|
||||
showDisclaimerIfNeed(
|
||||
scanResponse = scanResponse1,
|
||||
onProgressStateChange = onProgressStateChange,
|
||||
disclaimerWillShow = disclaimerWillShow,
|
||||
onFailure = onFailure,
|
||||
nextHandler = { scanResponse2 ->
|
||||
|
|
@ -144,7 +142,6 @@ object ScanCardProcessor {
|
|||
private suspend inline fun showDisclaimerIfNeed(
|
||||
scanResponse: ScanResponse,
|
||||
crossinline disclaimerWillShow: () -> Unit = {},
|
||||
crossinline onProgressStateChange: suspend (showProgress: Boolean) -> Unit,
|
||||
crossinline nextHandler: suspend (ScanResponse) -> Unit,
|
||||
crossinline onFailure: suspend (error: TangemError) -> Unit,
|
||||
) {
|
||||
|
|
@ -167,7 +164,6 @@ object ScanCardProcessor {
|
|||
},
|
||||
onDismiss = {
|
||||
scope.launch(Dispatchers.Main) {
|
||||
onProgressStateChange(false)
|
||||
onFailure(TangemSdkError.UserCancelled())
|
||||
}
|
||||
},
|
||||
|
|
@ -208,14 +204,12 @@ object ScanCardProcessor {
|
|||
} else {
|
||||
delay(DELAY_SDK_DIALOG_CLOSE)
|
||||
onSuccess(scanResponse)
|
||||
onProgressStateChange(false)
|
||||
}
|
||||
}
|
||||
is Result.Failure -> {
|
||||
SaltPayExceptionHandler.handle(result.error)
|
||||
delay(DELAY_SDK_DIALOG_CLOSE)
|
||||
onFailure(TangemSdkError.ExceptionError(result.error))
|
||||
onProgressStateChange(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -223,10 +217,10 @@ object ScanCardProcessor {
|
|||
delay(DELAY_SDK_DIALOG_CLOSE)
|
||||
if (scanResponse.card.backupStatus?.isActive == false) {
|
||||
showSaltPayTapVisaLogoCardDialog()
|
||||
onProgressStateChange(false)
|
||||
} else {
|
||||
onSuccess(scanResponse)
|
||||
}
|
||||
onProgressStateChange(false)
|
||||
}
|
||||
} else {
|
||||
if (OnboardingHelper.isOnboardingCase(scanResponse)) {
|
||||
|
|
@ -242,7 +236,6 @@ object ScanCardProcessor {
|
|||
} else {
|
||||
delay(DELAY_SDK_DIALOG_CLOSE)
|
||||
onSuccess(scanResponse)
|
||||
onProgressStateChange(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,8 @@ internal class DefaultTotalFiatBalanceCalculator : TotalFiatBalanceCalculator {
|
|||
|
||||
private fun Sequence<WalletDataModel>.mapToStatus(): Sequence<TotalFiatBalanceStatus> {
|
||||
return this.map { walletData ->
|
||||
when (walletData.status) {
|
||||
if (walletData.fiatRate == null) TotalFiatBalanceStatus.Error
|
||||
else when (walletData.status) {
|
||||
is WalletDataModel.VerifiedOnline,
|
||||
is WalletDataModel.SameCurrencyTransactionInProgress,
|
||||
is WalletDataModel.TransactionInProgress,
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import com.tangem.tap.domain.walletStores.repository.WalletAmountsRepository
|
|||
import com.tangem.tap.domain.walletStores.repository.implementation.utils.replaceWalletStore
|
||||
import com.tangem.tap.domain.walletStores.repository.implementation.utils.replaceWalletStores
|
||||
import com.tangem.tap.domain.walletStores.repository.implementation.utils.updateWithAmounts
|
||||
import com.tangem.tap.domain.walletStores.repository.implementation.utils.updateWithDemoAmounts
|
||||
import com.tangem.tap.domain.walletStores.repository.implementation.utils.updateWithError
|
||||
import com.tangem.tap.domain.walletStores.repository.implementation.utils.updateWithFiatRates
|
||||
import com.tangem.tap.domain.walletStores.repository.implementation.utils.updateWithMissedDerivation
|
||||
|
|
@ -34,6 +35,7 @@ import com.tangem.tap.domain.walletStores.repository.implementation.utils.update
|
|||
import com.tangem.tap.domain.walletStores.repository.implementation.utils.updateWithUnreachable
|
||||
import com.tangem.tap.domain.walletStores.storage.WalletManagerStorage
|
||||
import com.tangem.tap.domain.walletStores.storage.WalletStoresStorage
|
||||
import com.tangem.tap.features.demo.DemoHelper
|
||||
import com.tangem.tap.features.wallet.models.PendingTransactionType
|
||||
import com.tangem.tap.features.wallet.models.filterByCoin
|
||||
import com.tangem.tap.features.wallet.models.getPendingTransactions
|
||||
|
|
@ -207,6 +209,8 @@ internal class DefaultWalletAmountsRepository(
|
|||
updateWalletStoreWithAmounts(
|
||||
walletStore = walletStore,
|
||||
updatedWallet = walletManager.wallet,
|
||||
// fixme move DemoHelper to Demo core module maybe
|
||||
isDemo = DemoHelper.isDemoCardId(scanResponse.card.cardId),
|
||||
)
|
||||
}
|
||||
.flatMap { fetchWalletStoreRentIfNeeded(walletStore, walletManager) }
|
||||
|
|
@ -296,6 +300,7 @@ internal class DefaultWalletAmountsRepository(
|
|||
private suspend fun updateWalletStoreWithAmounts(
|
||||
walletStore: WalletStoreModel,
|
||||
updatedWallet: Wallet,
|
||||
isDemo: Boolean,
|
||||
) = withContext(Dispatchers.Default) {
|
||||
Timber.d(
|
||||
"""
|
||||
|
|
@ -309,7 +314,11 @@ internal class DefaultWalletAmountsRepository(
|
|||
prevState.replaceWalletStore(
|
||||
walletStoreToUpdate = walletStore,
|
||||
update = {
|
||||
it.updateWithAmounts(wallet = updatedWallet)
|
||||
if (isDemo) {
|
||||
it.updateWithDemoAmounts(wallet = updatedWallet)
|
||||
} else {
|
||||
it.updateWithAmounts(wallet = updatedWallet)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import com.tangem.common.core.TangemError
|
|||
import com.tangem.tap.domain.extensions.amountToCreateAccount
|
||||
import com.tangem.tap.domain.getFirstToken
|
||||
import com.tangem.tap.domain.model.WalletDataModel
|
||||
import com.tangem.tap.features.demo.DemoHelper
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.features.wallet.models.getPendingTransactions
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -74,12 +75,25 @@ internal fun WalletDataModel.updateWithAmount(wallet: Wallet): WalletDataModel {
|
|||
)
|
||||
}
|
||||
|
||||
internal fun WalletDataModel.updateWithDemoAmount(wallet: Wallet): WalletDataModel {
|
||||
val amount = DemoHelper.config.getBalance(wallet.blockchain)
|
||||
return this.copy(
|
||||
status = WalletDataModel.VerifiedOnline(amount = amount.value ?: BigDecimal.ZERO),
|
||||
)
|
||||
}
|
||||
|
||||
internal fun List<WalletDataModel>.updateWithAmounts(wallet: Wallet): List<WalletDataModel> {
|
||||
return this.map { walletData ->
|
||||
walletData.updateWithAmount(wallet)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun List<WalletDataModel>.updateWithDemoAmounts(wallet: Wallet): List<WalletDataModel> {
|
||||
return this.map { walletData ->
|
||||
walletData.updateWithDemoAmount(wallet)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun WalletDataModel.updateWithError(
|
||||
wallet: Wallet,
|
||||
error: TangemError,
|
||||
|
|
@ -164,7 +178,7 @@ internal fun List<WalletDataModel>.updateWithSelf(
|
|||
val updatedWalletsData = arrayListOf<WalletDataModel>()
|
||||
|
||||
newWalletsData.forEach { newWalletData ->
|
||||
val walletDataToUpdate = oldWalletsData.find(newWalletData::isSameWalletData)
|
||||
val walletDataToUpdate = oldWalletsData.firstOrNull(newWalletData::isSameWalletData)
|
||||
if (walletDataToUpdate != null) {
|
||||
updatedWalletsData.add(walletDataToUpdate.updateWithSelf(newWalletData))
|
||||
} else {
|
||||
|
|
@ -176,5 +190,5 @@ internal fun List<WalletDataModel>.updateWithSelf(
|
|||
}
|
||||
|
||||
internal fun WalletDataModel.isSameWalletData(other: WalletDataModel): Boolean {
|
||||
return currency == other.currency
|
||||
return this.currency == other.currency
|
||||
}
|
||||
|
|
@ -49,6 +49,14 @@ internal fun WalletStoreModel.updateWithAmounts(
|
|||
)
|
||||
}
|
||||
|
||||
internal fun WalletStoreModel.updateWithDemoAmounts(
|
||||
wallet: Wallet,
|
||||
): WalletStoreModel {
|
||||
return this.copy(
|
||||
walletsData = walletsData.updateWithDemoAmounts(wallet = wallet),
|
||||
)
|
||||
}
|
||||
|
||||
internal fun WalletStoreModel.updateWithFiatRates(
|
||||
rates: Map<String, Double>,
|
||||
): WalletStoreModel {
|
||||
|
|
@ -96,6 +104,9 @@ private inline fun List<WalletStoreModel>.replaceWalletStores(
|
|||
|
||||
walletStoresToUpdate.forEach { walletStoreToUpdate ->
|
||||
val index = mutableStores.indexOfFirst(walletStoreToUpdate::isSameWalletStore)
|
||||
// Can be possible if user hides wallet store when it's tokens is loading
|
||||
if (index == -1) return@forEach
|
||||
|
||||
val currentWalletStore = mutableStores[index]
|
||||
val updatedWalletStore = update(currentWalletStore)
|
||||
|
||||
|
|
@ -116,6 +127,7 @@ private inline fun List<WalletStoreModel>.replaceWalletStores(
|
|||
}
|
||||
|
||||
internal fun WalletStoreModel.isSameWalletStore(other: WalletStoreModel): Boolean {
|
||||
return this.blockchain == other.blockchain &&
|
||||
return this.userWalletId == other.userWalletId &&
|
||||
this.blockchain == other.blockchain &&
|
||||
this.derivationPath == other.derivationPath
|
||||
}
|
||||
|
|
@ -95,6 +95,7 @@ fun ProgressButton(
|
|||
Button(
|
||||
modifier = modifier,
|
||||
onClick = onClick,
|
||||
enabled = !inProgress,
|
||||
colors = ButtonDefaults.textButtonColors(
|
||||
backgroundColor = backgroundColor,
|
||||
contentColor = contentColor,
|
||||
|
|
|
|||
|
|
@ -79,10 +79,9 @@ private fun readCard(analyticsEvent: AnalyticsEvent?) = scope.launch {
|
|||
onProgressStateChange = { showProgress ->
|
||||
if (showProgress) {
|
||||
changeButtonState(ButtonState.PROGRESS)
|
||||
} else {
|
||||
changeButtonState(ButtonState.ENABLED)
|
||||
}
|
||||
// else { //todo hide this because
|
||||
// changeButtonState(ButtonState.ENABLED)
|
||||
// }
|
||||
},
|
||||
onScanStateChange = { scanInProgress ->
|
||||
store.dispatch(HomeAction.ScanInProgress(scanInProgress))
|
||||
|
|
@ -103,19 +102,23 @@ private fun readCard(analyticsEvent: AnalyticsEvent?) = scope.launch {
|
|||
scope.launch { store.onUserWalletSelected(userWallet) }
|
||||
}
|
||||
.doOnResult {
|
||||
changeButtonState(ButtonState.ENABLED)
|
||||
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Wallet))
|
||||
navigateTo(AppScreen.Wallet)
|
||||
}
|
||||
} else {
|
||||
store.onCardScanned(scanResponse)
|
||||
changeButtonState(ButtonState.ENABLED)
|
||||
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Wallet))
|
||||
navigateTo(AppScreen.Wallet)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun navigateTo(appScreen: AppScreen) {
|
||||
store.dispatchOnMain(NavigationAction.NavigateTo(appScreen))
|
||||
delay(200)
|
||||
changeButtonState(ButtonState.ENABLED)
|
||||
}
|
||||
|
||||
private fun changeButtonState(state: ButtonState) {
|
||||
store.dispatchOnMain(HomeAction.ChangeScanCardButtonState(IndeterminateProgressButton(state)))
|
||||
}
|
||||
|
|
@ -22,7 +22,6 @@ import com.tangem.tap.domain.tokens.models.BlockchainNetwork
|
|||
import com.tangem.tap.features.demo.DemoHelper
|
||||
import com.tangem.tap.features.home.redux.HomeAction
|
||||
import com.tangem.tap.features.onboarding.OnboardingHelper
|
||||
import com.tangem.tap.features.onboarding.products.wallet.saltPay.dialog.SaltPayDialog
|
||||
import com.tangem.tap.features.onboarding.products.wallet.saltPay.redux.OnboardingSaltPayAction
|
||||
import com.tangem.tap.features.onboarding.products.wallet.saltPay.redux.OnboardingSaltPayState
|
||||
import com.tangem.tap.features.wallet.redux.Artwork
|
||||
|
|
@ -188,12 +187,7 @@ private fun handleWalletAction(action: Action, state: () -> AppState?, dispatch:
|
|||
}
|
||||
newAction?.let { store.dispatch(it) }
|
||||
}
|
||||
OnboardingWalletAction.OnBackPressed -> {
|
||||
when {
|
||||
onboardingWalletState.isSaltPay -> handleOnBackPressedSaltPay(onboardingWalletState)
|
||||
else -> handleOnBackPressed(onboardingWalletState)
|
||||
}
|
||||
}
|
||||
OnboardingWalletAction.OnBackPressed -> handleOnBackPressed(onboardingWalletState)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -408,30 +402,33 @@ private fun initSaltPayOnBackupFinishedIfNeeded(
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleOnBackPressedSaltPay(state: OnboardingWalletState) {
|
||||
private fun handleOnBackPressed(state: OnboardingWalletState) {
|
||||
when (state.backupState.backupStep) {
|
||||
BackupStep.Finished -> {
|
||||
store.dispatchDialogShow(
|
||||
SaltPayDialog.Activation.TryToInterrupt(
|
||||
onOk = { store.dispatch(NavigationAction.PopBackTo()) },
|
||||
onCancel = { /* do nothing */ },
|
||||
),
|
||||
)
|
||||
BackupStep.InitBackup, BackupStep.ScanOriginCard, BackupStep.AddBackupCards, BackupStep.EnterAccessCode,
|
||||
BackupStep.ReenterAccessCode, BackupStep.SetAccessCode, BackupStep.WritePrimaryCard,
|
||||
-> {
|
||||
showInterruptOnboardingDialog()
|
||||
}
|
||||
is BackupStep.WriteBackupCard -> {
|
||||
store.dispatch(GlobalAction.ShowDialog(BackupDialog.BackupInProgress))
|
||||
}
|
||||
BackupStep.Finished -> {
|
||||
if (state.isSaltPay) {
|
||||
showInterruptOnboardingDialog()
|
||||
} else {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
}
|
||||
else -> handleOnBackPressed(state)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleOnBackPressed(state: OnboardingWalletState) {
|
||||
when (state.backupState.backupStep) {
|
||||
BackupStep.InitBackup, BackupStep.Finished -> store.dispatch(NavigationAction.PopBackTo())
|
||||
BackupStep.ScanOriginCard, BackupStep.AddBackupCards, BackupStep.EnterAccessCode,
|
||||
BackupStep.ReenterAccessCode, BackupStep.SetAccessCode, BackupStep.WritePrimaryCard,
|
||||
-> {
|
||||
store.dispatch(BackupAction.DiscardBackup)
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
is BackupStep.WriteBackupCard ->
|
||||
store.dispatch(GlobalAction.ShowDialog(BackupDialog.BackupInProgress))
|
||||
}
|
||||
private fun showInterruptOnboardingDialog() {
|
||||
store.dispatchDialogShow(
|
||||
OnboardingDialog.InterruptOnboarding(
|
||||
onOk = {
|
||||
store.dispatch(BackupAction.DiscardBackup)
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.tap.features.onboarding.products.wallet.redux
|
|||
|
||||
import android.graphics.Bitmap
|
||||
import com.tangem.common.CardFilter
|
||||
import com.tangem.common.extensions.VoidCallback
|
||||
import com.tangem.domain.common.SaltPayWorkaround
|
||||
import com.tangem.tap.common.redux.StateDialog
|
||||
import com.tangem.tap.features.onboarding.products.wallet.saltPay.redux.OnboardingSaltPayState
|
||||
|
|
@ -104,9 +105,13 @@ sealed class BackupStep {
|
|||
object Finished : BackupStep()
|
||||
}
|
||||
|
||||
sealed class OnboardingDialog : StateDialog {
|
||||
data class InterruptOnboarding(val onOk: VoidCallback) : BackupDialog()
|
||||
}
|
||||
|
||||
sealed class BackupDialog : StateDialog {
|
||||
object AddMoreBackupCards : StateDialog
|
||||
object BackupInProgress : StateDialog
|
||||
object UnfinishedBackupFound : StateDialog
|
||||
object ConfirmDiscardingBackup : StateDialog
|
||||
object AddMoreBackupCards : BackupDialog()
|
||||
object BackupInProgress : BackupDialog()
|
||||
object UnfinishedBackupFound : BackupDialog()
|
||||
object ConfirmDiscardingBackup : BackupDialog()
|
||||
}
|
||||
|
|
@ -38,11 +38,11 @@ class GnosisRegistrator(
|
|||
}
|
||||
|
||||
private val otpProcessorContractAddress: String = when (walletManager.wallet.blockchain) {
|
||||
Blockchain.SaltPay -> "0x3B4397C817A26521Df8bD01a949AFDE2251d91C2"
|
||||
Blockchain.SaltPay -> "0xc659f4FEd7A84a188F54cBA4A7a49D77c1a20522"
|
||||
else -> throw IllegalArgumentException("GnosisRegistrator supports only the SaltPay blockchain")
|
||||
}
|
||||
|
||||
private val addressTreasureSafe = "0x8e9260a049d3Aa9ac60D0d4F27017320E0e2396B"
|
||||
private val addressTreasureSafe = "0x24A3c2382497075b6D93258f5938f7B661c06318"
|
||||
|
||||
private val atomicNonce = AtomicLong()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.tap.features.onboarding.products.wallet.saltPay
|
||||
|
||||
import com.tangem.tap.common.zendesk.ZendeskConfig
|
||||
import org.spongycastle.util.encoders.Base64.toBase64String
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -8,12 +9,14 @@ import com.tangem.tap.common.zendesk.ZendeskConfig
|
|||
data class SaltPayConfig(
|
||||
val zendesk: ZendeskConfig,
|
||||
val kycProvider: KYCProvider,
|
||||
val credentials: Credentials,
|
||||
) {
|
||||
companion object {
|
||||
fun stub(): SaltPayConfig {
|
||||
return SaltPayConfig(
|
||||
zendesk = ZendeskConfig("", "", "", "", ""),
|
||||
kycProvider = KYCProvider("", "", "", ""),
|
||||
credentials = Credentials("", ""),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -24,4 +27,11 @@ data class KYCProvider(
|
|||
val externalIdParameterKey: String,
|
||||
val sidParameterKey: String,
|
||||
val sidValue: String,
|
||||
)
|
||||
)
|
||||
|
||||
data class Credentials(
|
||||
val user: String,
|
||||
val password: String,
|
||||
) {
|
||||
val token: String by lazy { "Basic ${toBase64String("$user:$password".toByteArray())}" }
|
||||
}
|
||||
|
|
@ -4,20 +4,21 @@ import android.app.Dialog
|
|||
import android.content.Context
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.tangem.tap.common.extensions.dispatchDialogHide
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingDialog
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class TryToInterruptRegistrationDialog {
|
||||
class InterruptOnboardingDialog {
|
||||
companion object {
|
||||
fun create(context: Context, dialog: SaltPayDialog.Activation.TryToInterrupt): Dialog {
|
||||
fun create(context: Context, dialog: OnboardingDialog.InterruptOnboarding): Dialog {
|
||||
return AlertDialog.Builder(context).apply {
|
||||
setTitle(context.getString(R.string.onboarding_exit_alert_title))
|
||||
setMessage(context.getString(R.string.onboarding_exit_alert_message))
|
||||
setPositiveButton(R.string.common_ok) { _, _ -> dialog.onOk() }
|
||||
setNegativeButton(R.string.common_cancel) { _, _ -> dialog.onCancel() }
|
||||
setNegativeButton(R.string.common_cancel) { _, _ -> }
|
||||
setOnDismissListener { store.dispatchDialogHide() }
|
||||
setCancelable(false)
|
||||
}.create()
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.tap.features.onboarding.products.wallet.saltPay.dialog
|
||||
|
||||
import com.tangem.common.extensions.VoidCallback
|
||||
import com.tangem.tap.common.redux.StateDialog
|
||||
import com.tangem.tap.features.onboarding.products.wallet.saltPay.message.SaltPayActivationError
|
||||
|
||||
|
|
@ -11,6 +10,5 @@ sealed class SaltPayDialog : StateDialog {
|
|||
sealed class Activation : SaltPayDialog() {
|
||||
object NoGas : SaltPayDialog()
|
||||
data class OnError(val error: SaltPayActivationError) : SaltPayDialog()
|
||||
data class TryToInterrupt(val onOk: VoidCallback, val onCancel: VoidCallback) : SaltPayDialog()
|
||||
}
|
||||
}
|
||||
|
|
@ -6,11 +6,11 @@ import com.tangem.blockchain.extensions.successOr
|
|||
import com.tangem.common.Filter
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.domain.common.extensions.successOr
|
||||
import com.tangem.domain.common.extensions.withMainContext
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.datasource.api.paymentology.KYCStatus
|
||||
import com.tangem.datasource.api.paymentology.RegistrationResponse
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.domain.common.extensions.successOr
|
||||
import com.tangem.domain.common.extensions.withMainContext
|
||||
import com.tangem.tap.common.analytics.events.Onboarding
|
||||
import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
|
|
@ -286,7 +286,7 @@ suspend fun SaltPayActivationManager.update(
|
|||
return Result.Failure(ex)
|
||||
}
|
||||
|
||||
// checkGasIfNeeded(this, newStep).successOr { return it }
|
||||
checkGasIfNeeded(this, newStep).successOr { return it }
|
||||
|
||||
Timber.d("update: success: %s", newStep)
|
||||
return Result.Success(newStep)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.tap.features.wallet.redux.middlewares
|
||||
|
||||
import com.tangem.blockchain.common.AmountType
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.tap.common.extensions.stripZeroPlainString
|
||||
import com.tangem.tap.common.extensions.toFiatRateString
|
||||
|
|
@ -10,7 +10,6 @@ import com.tangem.tap.common.extensions.toFormattedFiatValue
|
|||
import com.tangem.tap.domain.model.TotalFiatBalance
|
||||
import com.tangem.tap.domain.model.WalletDataModel
|
||||
import com.tangem.tap.domain.model.WalletStoreModel
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.features.wallet.models.TotalBalance
|
||||
import com.tangem.tap.features.wallet.models.WalletRent
|
||||
import com.tangem.tap.features.wallet.redux.ProgressState
|
||||
|
|
@ -24,11 +23,9 @@ import com.tangem.tap.features.wallet.ui.TokenData
|
|||
import com.tangem.tap.store
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal fun List<WalletStoreModel>.mapToReduxModels(
|
||||
isMultiWalletAllowed: Boolean,
|
||||
): List<WalletStore> {
|
||||
internal fun List<WalletStoreModel>.mapToReduxModels(): List<WalletStore> {
|
||||
return this.map { walletStoreModel ->
|
||||
walletStoreModel.mapToReduxModel(isMultiWalletAllowed)
|
||||
walletStoreModel.mapToReduxModel()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -44,100 +41,150 @@ internal fun TotalFiatBalance.mapToReduxModel(): TotalBalance {
|
|||
)
|
||||
}
|
||||
|
||||
internal fun WalletStoreModel.mapToReduxModel(
|
||||
isMultiWalletAllowed: Boolean,
|
||||
): WalletStore {
|
||||
internal fun WalletStoreModel.mapToReduxModel(): WalletStore {
|
||||
val appCurrencySymbol = store.state.globalState.appCurrency.symbol
|
||||
return WalletStore(
|
||||
walletManager = walletManager,
|
||||
blockchainNetwork = blockchainNetwork,
|
||||
walletsData = walletsData.mapToReduxModel(isMultiWalletAllowed, walletRent),
|
||||
walletsData = walletsData.mapToReduxModels(walletRent, appCurrencySymbol),
|
||||
)
|
||||
.updateTokenModels(blockchainWalletData.status.amount)
|
||||
.setupIfHadCardSingleToken(
|
||||
blockchain = blockchain,
|
||||
walletsDataModel = walletsData,
|
||||
appCurrencySymbol = appCurrencySymbol,
|
||||
blockchainWalletData = blockchainWalletData.mapToReduxModel(
|
||||
walletRent = walletRent,
|
||||
appCurrencySymbol = appCurrencySymbol,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun List<WalletDataModel>.mapToReduxModel(
|
||||
isMultiWalletAllowed: Boolean,
|
||||
private fun List<WalletDataModel>.mapToReduxModels(
|
||||
walletRent: WalletStoreModel.WalletRent?,
|
||||
appCurrencySymbol: String,
|
||||
): List<WalletData> {
|
||||
return this.map { walletDataModel ->
|
||||
with(walletDataModel) {
|
||||
val amount = status.amount
|
||||
val amountFormatted = amount.toFormattedCurrencyString(
|
||||
decimals = currency.decimals,
|
||||
currency = currency.currencySymbol,
|
||||
)
|
||||
val appCurrency = store.state.globalState.appCurrency
|
||||
val fiatAmount = fiatRate?.let { status.amount.toFiatValue(it) }
|
||||
val fiatAmountFormatted = fiatAmount
|
||||
?.takeIf { !status.isErrorStatus }
|
||||
?.toFormattedFiatValue(appCurrency.symbol)
|
||||
val fiatRateFormatted = fiatRate?.toFiatRateString(appCurrency.symbol)
|
||||
val blockchainAmountValue = getBlockchainAmount()
|
||||
|
||||
WalletData(
|
||||
currency = currency,
|
||||
walletAddresses = walletAddresses.getOrNull(0)?.let { selectedAddress ->
|
||||
WalletAddresses(
|
||||
selectedAddress = selectedAddress,
|
||||
list = walletAddresses,
|
||||
)
|
||||
},
|
||||
existentialDepositString = existentialDeposit?.toPlainString(),
|
||||
fiatRate = fiatRate,
|
||||
fiatRateString = fiatRateFormatted,
|
||||
pendingTransactions = status.pendingTransactions,
|
||||
mainButton = WalletMainButton.SendButton(
|
||||
enabled = !blockchainAmountValue.isZero()
|
||||
&& !status.amount.isZero()
|
||||
&& status.pendingTransactions.isEmpty(),
|
||||
),
|
||||
walletRent = walletRent?.let {
|
||||
WalletRent(
|
||||
minRentValue = "${it.rent.stripZeroPlainString()} ${currency.blockchain.currency}",
|
||||
rentExemptValue = "${it.exemptionAmount.stripZeroPlainString()} ${currency.blockchain.currency}",
|
||||
)
|
||||
},
|
||||
currencyData = BalanceWidgetData(
|
||||
status = when (status) {
|
||||
is WalletDataModel.Loading -> BalanceStatus.Loading
|
||||
is WalletDataModel.NoAccount -> BalanceStatus.NoAccount
|
||||
is WalletDataModel.SameCurrencyTransactionInProgress -> BalanceStatus.SameCurrencyTransactionInProgress
|
||||
is WalletDataModel.TransactionInProgress -> BalanceStatus.TransactionInProgress
|
||||
is WalletDataModel.Unreachable -> BalanceStatus.Unreachable
|
||||
is WalletDataModel.MissedDerivation -> BalanceStatus.MissedDerivation
|
||||
is WalletDataModel.VerifiedOnline -> BalanceStatus.VerifiedOnline
|
||||
},
|
||||
currency = currency.currencyName,
|
||||
currencySymbol = currency.currencySymbol,
|
||||
blockchainAmount = blockchainAmountValue,
|
||||
amount = amount,
|
||||
amountFormatted = amountFormatted,
|
||||
fiatAmount = fiatAmount,
|
||||
fiatAmountFormatted = fiatAmountFormatted,
|
||||
token = when {
|
||||
!isMultiWalletAllowed && currency is Currency.Token -> {
|
||||
TokenData(
|
||||
amount = amount,
|
||||
amountFormatted = amountFormatted,
|
||||
fiatAmount = fiatAmount,
|
||||
fiatAmountFormatted = fiatAmountFormatted,
|
||||
tokenSymbol = currency.currencySymbol,
|
||||
fiatRate = fiatRate,
|
||||
fiatRateString = fiatRateFormatted,
|
||||
)
|
||||
}
|
||||
else -> null
|
||||
},
|
||||
amountToCreateAccount = (status as? WalletDataModel.NoAccount)
|
||||
?.amountToCreateAccount
|
||||
?.toString(),
|
||||
errorMessage = status.errorMessage,
|
||||
),
|
||||
)
|
||||
}
|
||||
walletDataModel.mapToReduxModel(walletRent, appCurrencySymbol)
|
||||
}
|
||||
}
|
||||
|
||||
private fun WalletDataModel.getBlockchainAmount(): BigDecimal {
|
||||
val walletStore = store.state.walletState.getWalletStore(currency) ?: return BigDecimal.ZERO
|
||||
return walletStore.walletManager?.wallet?.amounts?.get(AmountType.Coin)?.value ?: BigDecimal.ZERO
|
||||
private fun WalletDataModel.mapToReduxModel(
|
||||
walletRent: WalletStoreModel.WalletRent?,
|
||||
appCurrencySymbol: String,
|
||||
): WalletData {
|
||||
val amount = status.amount
|
||||
val amountFormatted = amount.toFormattedCurrencyString(
|
||||
decimals = currency.decimals,
|
||||
currency = currency.currencySymbol,
|
||||
)
|
||||
val fiatAmount = fiatRate?.let { status.amount.toFiatValue(it) }
|
||||
val fiatAmountFormatted = fiatAmount
|
||||
?.takeIf { !status.isErrorStatus }
|
||||
?.toFormattedFiatValue(appCurrencySymbol)
|
||||
val fiatRateFormatted = fiatRate?.toFiatRateString(appCurrencySymbol)
|
||||
|
||||
return WalletData(
|
||||
currency = currency,
|
||||
walletAddresses = walletAddresses.getOrNull(0)?.let { selectedAddress ->
|
||||
WalletAddresses(
|
||||
selectedAddress = selectedAddress,
|
||||
list = walletAddresses,
|
||||
)
|
||||
},
|
||||
existentialDepositString = existentialDeposit?.toPlainString(),
|
||||
fiatRate = fiatRate,
|
||||
fiatRateString = fiatRateFormatted,
|
||||
pendingTransactions = status.pendingTransactions,
|
||||
mainButton = WalletMainButton.SendButton(
|
||||
enabled = !status.amount.isZero() && status.pendingTransactions.isEmpty(),
|
||||
),
|
||||
walletRent = walletRent?.let {
|
||||
WalletRent(
|
||||
minRentValue = "${it.rent.stripZeroPlainString()} ${currency.blockchain.currency}",
|
||||
rentExemptValue = "${it.exemptionAmount.stripZeroPlainString()} ${currency.blockchain.currency}",
|
||||
)
|
||||
},
|
||||
currencyData = BalanceWidgetData(
|
||||
status = when (status) {
|
||||
is WalletDataModel.Loading -> BalanceStatus.Loading
|
||||
is WalletDataModel.NoAccount -> BalanceStatus.NoAccount
|
||||
is WalletDataModel.SameCurrencyTransactionInProgress -> BalanceStatus.SameCurrencyTransactionInProgress
|
||||
is WalletDataModel.TransactionInProgress -> BalanceStatus.TransactionInProgress
|
||||
is WalletDataModel.Unreachable -> BalanceStatus.Unreachable
|
||||
is WalletDataModel.MissedDerivation -> BalanceStatus.MissedDerivation
|
||||
is WalletDataModel.VerifiedOnline -> BalanceStatus.VerifiedOnline
|
||||
},
|
||||
currency = currency.currencyName,
|
||||
currencySymbol = currency.currencySymbol,
|
||||
blockchainAmount = BigDecimal.ZERO,
|
||||
amount = amount,
|
||||
amountFormatted = amountFormatted,
|
||||
fiatAmount = fiatAmount,
|
||||
fiatAmountFormatted = fiatAmountFormatted,
|
||||
token = null,
|
||||
amountToCreateAccount = (status as? WalletDataModel.NoAccount)
|
||||
?.amountToCreateAccount
|
||||
?.toString(),
|
||||
errorMessage = status.errorMessage,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun WalletStore.updateTokenModels(blockchainAmount: BigDecimal): WalletStore {
|
||||
val updatedTokensWalletData = walletsData.filter { it.currency.isToken() }.map {
|
||||
it.copy(
|
||||
mainButton = when (it.mainButton) {
|
||||
is WalletMainButton.SendButton -> {
|
||||
WalletMainButton.SendButton(it.mainButton.enabled && !blockchainAmount.isZero())
|
||||
}
|
||||
is WalletMainButton.CreateWalletButton -> it.mainButton
|
||||
},
|
||||
currencyData = it.currencyData.copy(
|
||||
blockchainAmount = blockchainAmount,
|
||||
),
|
||||
)
|
||||
}
|
||||
return updateWallets(updatedTokensWalletData)
|
||||
}
|
||||
|
||||
private fun WalletStore.setupIfHadCardSingleToken(
|
||||
blockchain: Blockchain,
|
||||
walletsDataModel: List<WalletDataModel>,
|
||||
appCurrencySymbol: String,
|
||||
blockchainWalletData: WalletData,
|
||||
): WalletStore {
|
||||
// Card with single token contains only 2 model - blockchain and token
|
||||
if (walletsData.size != 2) return this
|
||||
|
||||
val cardSingleTokenWalletData = walletsDataModel.firstOrNull {
|
||||
it.currency.isToken() && it.currency.blockchain == blockchain && it.isCardSingleToken
|
||||
} ?: return this
|
||||
|
||||
val blockchainWalletDataWithSingleToken = blockchainWalletData.copy(
|
||||
currencyData = blockchainWalletData.currencyData.copy(
|
||||
token = cardSingleTokenWalletData.toTokenData(appCurrencySymbol),
|
||||
),
|
||||
)
|
||||
return updateWallets(listOf(blockchainWalletDataWithSingleToken))
|
||||
}
|
||||
|
||||
private fun WalletDataModel.toTokenData(appCurrencySymbol: String): TokenData {
|
||||
val amount = status.amount
|
||||
val fiatAmount = fiatRate?.let { status.amount.toFiatValue(it) }
|
||||
|
||||
return TokenData(
|
||||
amount = amount,
|
||||
amountFormatted = amount.toFormattedCurrencyString(
|
||||
decimals = currency.decimals,
|
||||
currency = currency.currencySymbol,
|
||||
),
|
||||
fiatAmount = fiatAmount,
|
||||
fiatAmountFormatted = fiatAmount
|
||||
?.takeIf { !status.isErrorStatus }
|
||||
?.toFormattedFiatValue(appCurrencySymbol),
|
||||
tokenSymbol = currency.currencySymbol,
|
||||
fiatRate = fiatRate,
|
||||
fiatRateString = fiatRate?.toFiatRateString(appCurrencySymbol),
|
||||
)
|
||||
}
|
||||
|
|
@ -318,7 +318,7 @@ class WalletMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
val reduxWalletStores = wallStores.mapToReduxModels(state.isMultiwalletAllowed)
|
||||
val reduxWalletStores = wallStores.mapToReduxModels()
|
||||
store.dispatchOnMain(
|
||||
WalletAction.WalletStoresChanged.UpdateWalletStores(
|
||||
reduxWalletStores = reduxWalletStores,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ import androidx.lifecycle.lifecycleScope
|
|||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.transition.TransitionInflater
|
||||
import by.kirich1409.viewbindingdelegate.viewBinding
|
||||
import com.badoo.mvicore.DiffStrategy
|
||||
import com.badoo.mvicore.ModelWatcher
|
||||
import com.badoo.mvicore.modelWatcher
|
||||
import com.tangem.common.doOnResult
|
||||
import com.tangem.core.analytics.Analytics
|
||||
|
|
@ -39,6 +41,8 @@ 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.PendingTransaction
|
||||
import com.tangem.tap.features.wallet.models.WalletWarning
|
||||
import com.tangem.tap.features.wallet.redux.AddressData
|
||||
import com.tangem.tap.features.wallet.redux.ErrorType
|
||||
import com.tangem.tap.features.wallet.redux.ProgressState
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
|
|
@ -67,7 +71,13 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
|
|||
|
||||
private val binding: FragmentWalletDetailsBinding by viewBinding(FragmentWalletDetailsBinding::bind)
|
||||
|
||||
private val walletDataWatcher = modelWatcher<WalletData> {
|
||||
private val walletDataWatcher: ModelWatcher<WalletData> = modelWatcher {
|
||||
val addressCardStrategy: DiffStrategy<WalletData> = { old, new ->
|
||||
old.currency != new.currency ||
|
||||
old.walletAddresses?.selectedAddress != new.walletAddresses?.selectedAddress ||
|
||||
old.shouldShowMultipleAddress() != new.shouldShowMultipleAddress()
|
||||
}
|
||||
|
||||
WalletData::pendingTransactions {
|
||||
showPendingTransactionsIfPresent(it)
|
||||
}
|
||||
|
|
@ -77,25 +87,35 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
|
|||
WalletData::currencyData {
|
||||
setupBalanceData(it)
|
||||
}
|
||||
WalletData::walletAddresses { walletAddresses ->
|
||||
setupCopyAndShareButtons(walletAddresses?.selectedAddress?.address)
|
||||
}
|
||||
WalletData::assembleWarnings { warnings ->
|
||||
handleWarnings(warnings)
|
||||
}
|
||||
(WalletData::currencyData or WalletData::currency) { walletData ->
|
||||
setupCurrency(walletData.currencyData, walletData.currency)
|
||||
setupSwipeRefresh(walletData.currencyData, walletData.currency)
|
||||
}
|
||||
watch({ it }, addressCardStrategy) { walletData ->
|
||||
setupAddressCard(
|
||||
shouldShowMultipleAddress = walletData.shouldShowMultipleAddress(),
|
||||
selectedAddress = walletData.walletAddresses?.selectedAddress,
|
||||
currency = walletData.currency,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val walletStateWatcher = modelWatcher<WalletState> {
|
||||
(WalletState::selectedCurrency or WalletState::selectedWalletData) { state ->
|
||||
val selectedWalletData = state.selectedWalletData
|
||||
if (selectedWalletData != null) {
|
||||
walletDataWatcher.invoke(selectedWalletData)
|
||||
setupButtons(selectedWalletData, state.isExchangeServiceFeatureOn)
|
||||
setupAddressCard(selectedWalletData)
|
||||
handleWarnings(selectedWalletData)
|
||||
private val walletStateWatcher: ModelWatcher<WalletState> = modelWatcher {
|
||||
WalletState::selectedWalletData { selectedWallet ->
|
||||
if (selectedWallet != null) {
|
||||
walletDataWatcher.invoke(selectedWallet)
|
||||
}
|
||||
}
|
||||
(WalletState::selectedCurrency or WalletState::isExchangeServiceFeatureOn) { state ->
|
||||
if (state.selectedWalletData != null) {
|
||||
setupButtons(state.selectedWalletData!!, state.isExchangeServiceFeatureOn)
|
||||
(WalletState::selectedWalletData or WalletState::isExchangeServiceFeatureOn) { state ->
|
||||
val selectedWallet = state.selectedWalletData
|
||||
if (selectedWallet != null) {
|
||||
setupButtonsRow(selectedWallet, state.isExchangeServiceFeatureOn)
|
||||
}
|
||||
}
|
||||
(WalletState::state or WalletState::error) { state ->
|
||||
|
|
@ -132,8 +152,6 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
|
|||
override fun onStop() {
|
||||
super.onStop()
|
||||
store.unsubscribe(this)
|
||||
walletDataWatcher.clear()
|
||||
walletStateWatcher.clear()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
|
|
@ -147,6 +165,11 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
|
|||
setupTestActionButton()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
clearWatchers()
|
||||
}
|
||||
|
||||
private fun setupTransactionsRecyclerView() = with(binding) {
|
||||
pendingTransactionAdapter = PendingTransactionsAdapter()
|
||||
rvPendingTransaction.layoutManager = LinearLayoutManager(requireContext())
|
||||
|
|
@ -251,19 +274,21 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
|
|||
currencyData.status == BalanceStatus.Refreshing
|
||||
}
|
||||
|
||||
private fun setupButtons(selectedWallet: WalletData, isExchangeServiceFeatureOn: Boolean) = with(binding) {
|
||||
lWalletDetails.btnCopy.setOnClickListener {
|
||||
selectedWallet.walletAddresses?.selectedAddress?.address?.let { addressString ->
|
||||
store.dispatch(WalletAction.CopyAddress(addressString, requireContext()))
|
||||
private fun setupCopyAndShareButtons(walletAddress: String?) {
|
||||
binding.lWalletDetails.btnCopy.setOnClickListener {
|
||||
if (walletAddress != null) {
|
||||
store.dispatch(WalletAction.CopyAddress(walletAddress, requireContext()))
|
||||
}
|
||||
}
|
||||
lWalletDetails.btnShare.setOnClickListener {
|
||||
selectedWallet.walletAddresses?.selectedAddress?.address?.let { addressString ->
|
||||
store.dispatch(WalletAction.ShareAddress(addressString, requireContext()))
|
||||
binding.lWalletDetails.btnShare.setOnClickListener {
|
||||
if (walletAddress != null) {
|
||||
store.dispatch(WalletAction.ShareAddress(walletAddress, requireContext()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rowButtons.updateButtonsVisibility(
|
||||
private fun setupButtonsRow(selectedWallet: WalletData, isExchangeServiceFeatureOn: Boolean) {
|
||||
binding.rowButtons.updateButtonsVisibility(
|
||||
exchangeServiceFeatureOn = isExchangeServiceFeatureOn,
|
||||
buyAllowed = selectedWallet.isAvailableToBuy,
|
||||
sellAllowed = selectedWallet.isAvailableToSell,
|
||||
|
|
@ -271,9 +296,9 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
|
|||
)
|
||||
}
|
||||
|
||||
private fun handleWarnings(selectedWallet: WalletData) = with(binding) {
|
||||
private fun handleWarnings(warnings: List<WalletWarning>) = with(binding) {
|
||||
val converter = WalletWarningConverter(requireContext())
|
||||
val warningDetails = selectedWallet.assembleWarnings().map { converter.convert(it) }
|
||||
val warningDetails = warnings.map { converter.convert(it) }
|
||||
|
||||
warningMessagesAdapter.submitList(warningDetails)
|
||||
rvWarningMessages.show(warningDetails.isNotEmpty())
|
||||
|
|
@ -294,51 +319,59 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
|
|||
binding.rvPendingTransaction.show(pendingTransactions.isNotEmpty())
|
||||
}
|
||||
|
||||
private fun setupAddressCard(state: WalletData) = with(binding.lWalletDetails) {
|
||||
if (state.walletAddresses != null) {
|
||||
if (state.shouldShowMultipleAddress() && state.currency is Currency.Blockchain) {
|
||||
(cardBalance as? ViewGroup)?.beginDelayedTransition()
|
||||
chipGroupAddressType.show()
|
||||
chipGroupAddressType.fitChipsByGroupWidth()
|
||||
private fun setupAddressCard(
|
||||
shouldShowMultipleAddress: Boolean,
|
||||
selectedAddress: AddressData?,
|
||||
currency: Currency,
|
||||
) = with(binding.lWalletDetails) {
|
||||
if (selectedAddress == null) return@with
|
||||
|
||||
val checkedId =
|
||||
MultipleAddressUiHelper.typeToId(state.walletAddresses.selectedAddress.type)
|
||||
if (checkedId != View.NO_ID) chipGroupAddressType.check(checkedId)
|
||||
setupAddressTypeChips(shouldShowMultipleAddress, selectedAddress, currency)
|
||||
|
||||
chipGroupAddressType.setOnCheckedChangeListener { group, checkedId ->
|
||||
if (checkedId == -1) return@setOnCheckedChangeListener
|
||||
val type =
|
||||
MultipleAddressUiHelper.idToType(checkedId, state.currency.blockchain)
|
||||
type?.let { store.dispatch(WalletAction.ChangeSelectedAddress(type)) }
|
||||
}
|
||||
} else {
|
||||
chipGroupAddressType.hide()
|
||||
}
|
||||
tvAddress.text = state.walletAddresses.selectedAddress.address
|
||||
tvExplore.setOnClickListener {
|
||||
store.dispatch(
|
||||
WalletAction.ExploreAddress(
|
||||
state.walletAddresses.selectedAddress.exploreUrl,
|
||||
requireContext(),
|
||||
),
|
||||
)
|
||||
}
|
||||
ivQrCode.setImageBitmap(state.walletAddresses.selectedAddress.shareUrl.toQrCode())
|
||||
tvAddress.text = selectedAddress.address
|
||||
tvExplore.setOnClickListener {
|
||||
store.dispatch(WalletAction.ExploreAddress(selectedAddress.exploreUrl, requireContext()))
|
||||
}
|
||||
ivQrCode.setImageBitmap(selectedAddress.shareUrl.toQrCode())
|
||||
|
||||
tvReceiveMessage.text = when (val currency = state.currency) {
|
||||
is Currency.Blockchain -> tvReceiveMessage.getString(
|
||||
id = R.string.address_qr_code_message_format,
|
||||
currency.blockchain.fullName,
|
||||
currency.currencySymbol,
|
||||
currency.blockchain.fullName,
|
||||
)
|
||||
is Currency.Token -> tvReceiveMessage.getString(
|
||||
id = R.string.address_qr_code_message_format,
|
||||
currency.token.name,
|
||||
currency.currencySymbol,
|
||||
currency.blockchain.fullName,
|
||||
)
|
||||
tvReceiveMessage.text = when (currency) {
|
||||
is Currency.Blockchain -> tvReceiveMessage.getString(
|
||||
id = R.string.address_qr_code_message_format,
|
||||
currency.blockchain.fullName,
|
||||
currency.currencySymbol,
|
||||
currency.blockchain.fullName,
|
||||
)
|
||||
is Currency.Token -> tvReceiveMessage.getString(
|
||||
id = R.string.address_qr_code_message_format,
|
||||
currency.token.name,
|
||||
currency.currencySymbol,
|
||||
currency.blockchain.fullName,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupAddressTypeChips(
|
||||
shouldShowMultipleAddress: Boolean,
|
||||
selectedAddress: AddressData,
|
||||
currency: Currency,
|
||||
) = with(binding.lWalletDetails) {
|
||||
if (shouldShowMultipleAddress && currency is Currency.Blockchain) {
|
||||
(cardBalance as? ViewGroup)?.beginDelayedTransition()
|
||||
chipGroupAddressType.show()
|
||||
chipGroupAddressType.fitChipsByGroupWidth()
|
||||
|
||||
val checkedId =
|
||||
MultipleAddressUiHelper.typeToId(selectedAddress.type)
|
||||
if (checkedId != View.NO_ID) chipGroupAddressType.check(checkedId)
|
||||
|
||||
chipGroupAddressType.setOnCheckedChangeListener { _, checkedId ->
|
||||
if (checkedId == -1) return@setOnCheckedChangeListener
|
||||
val type =
|
||||
MultipleAddressUiHelper.idToType(checkedId, currency.blockchain)
|
||||
type?.let { store.dispatch(WalletAction.ChangeSelectedAddress(type)) }
|
||||
}
|
||||
} else {
|
||||
chipGroupAddressType.hide()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -425,6 +458,11 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
|
|||
inflater.inflate(R.menu.menu_wallet_details, menu)
|
||||
}
|
||||
|
||||
private fun clearWatchers() {
|
||||
walletDataWatcher.clear()
|
||||
walletStateWatcher.clear()
|
||||
}
|
||||
|
||||
private fun TextView.setWarningStatus(mainMessage: Int, error: String? = null) {
|
||||
val text = getString(mainMessage).appendIfNotNull(error, "\nError: ")
|
||||
setStatus(text, R.color.warning, R.drawable.ic_warning_small)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import com.tangem.tap.common.extensions.show
|
|||
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.PendingTransaction
|
||||
import com.tangem.tap.features.wallet.models.PendingTransactionType
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletData
|
||||
import com.tangem.tap.features.wallet.redux.WalletMainButton
|
||||
|
|
@ -72,8 +73,11 @@ class SingleWalletView : WalletView() {
|
|||
}
|
||||
|
||||
private fun showPendingTransactionsIfPresent(pendingTransactions: List<PendingTransaction>) {
|
||||
pendingTransactionAdapter.submitList(pendingTransactions)
|
||||
binding?.rvPendingTransaction?.show(pendingTransactions.isNotEmpty())
|
||||
val knownTransactions = pendingTransactions.filterNot {
|
||||
it.type == PendingTransactionType.Unknown
|
||||
}
|
||||
pendingTransactionAdapter.submitList(knownTransactions)
|
||||
binding?.rvPendingTransaction?.show(knownTransactions.isNotEmpty())
|
||||
}
|
||||
|
||||
private fun setupBalance(state: WalletState, primaryWallet: WalletData) {
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ internal sealed interface WalletSelectorAction : Action {
|
|||
val walletsStores: Map<UserWalletId, List<WalletStoreModel>>,
|
||||
) : WalletSelectorAction
|
||||
|
||||
data class BalanceLoaded(
|
||||
val userWalletModel: UserWalletModel,
|
||||
data class BalancesLoaded(
|
||||
val userWalletModels: List<UserWalletModel>,
|
||||
) : WalletSelectorAction
|
||||
|
||||
object UnlockWithBiometry : WalletSelectorAction {
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ import kotlinx.coroutines.flow.firstOrNull
|
|||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Middleware
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class WalletSelectorMiddleware {
|
||||
val middleware: Middleware<AppState> = { _, appStateProvider ->
|
||||
|
|
@ -81,7 +80,7 @@ internal class WalletSelectorMiddleware {
|
|||
is WalletSelectorAction.SelectedWalletChanged,
|
||||
is WalletSelectorAction.UnlockWithBiometry.Error,
|
||||
is WalletSelectorAction.UnlockWithBiometry.Success,
|
||||
is WalletSelectorAction.BalanceLoaded,
|
||||
is WalletSelectorAction.BalancesLoaded,
|
||||
is WalletSelectorAction.IsLockedChanged,
|
||||
is WalletSelectorAction.HandleError,
|
||||
is WalletSelectorAction.CloseError,
|
||||
|
|
@ -104,20 +103,10 @@ internal class WalletSelectorMiddleware {
|
|||
state: WalletSelectorState,
|
||||
) {
|
||||
if (updatedWalletStores.isNotEmpty()) scope.launch(Dispatchers.Default) {
|
||||
state.wallets
|
||||
.associateWith { updatedWalletStores[it.id] }
|
||||
.forEach { (wallet, walletStores) ->
|
||||
val isWalletTokensEmpty = (wallet.type as? UserWalletModel.Type.MultiCurrency)?.tokensCount == 0
|
||||
val updatedWallet = if (walletStores == null && isWalletTokensEmpty) {
|
||||
wallet.copy(fiatBalance = TotalFiatBalance.Loaded(BigDecimal.ZERO))
|
||||
} else {
|
||||
wallet.updateWalletStoresAndCalculateFiatBalance(walletStores.orEmpty())
|
||||
}
|
||||
|
||||
if (wallet != updatedWallet) {
|
||||
store.dispatchOnMain(WalletSelectorAction.BalanceLoaded(updatedWallet))
|
||||
}
|
||||
}
|
||||
val updatedWallets = state.wallets.updateWalletStoresAndCalculateFiatBalance(updatedWalletStores)
|
||||
if (updatedWallets != state.wallets) {
|
||||
store.dispatchOnMain(WalletSelectorAction.BalancesLoaded(updatedWallets))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -314,18 +303,28 @@ internal class WalletSelectorMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun List<UserWalletModel>.updateWalletStoresAndCalculateFiatBalance(
|
||||
walletStores: Map<UserWalletId, List<WalletStoreModel>>,
|
||||
): List<UserWalletModel> {
|
||||
return this
|
||||
.associateWith { walletStores[it.id] }
|
||||
.map { (wallet, walletStores) ->
|
||||
wallet.updateWalletStoresAndCalculateFiatBalance(walletStores)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun UserWalletModel.updateWalletStoresAndCalculateFiatBalance(
|
||||
walletStores: List<WalletStoreModel>,
|
||||
walletStores: List<WalletStoreModel>?,
|
||||
): UserWalletModel {
|
||||
return this.copy(
|
||||
type = when (type) {
|
||||
is UserWalletModel.Type.MultiCurrency -> type.copy(
|
||||
tokensCount = walletStores.flatMap { it.walletsData }.size,
|
||||
tokensCount = walletStores?.flatMap { it.walletsData }?.size ?: 0,
|
||||
)
|
||||
is UserWalletModel.Type.SingleCurrency -> type
|
||||
},
|
||||
fiatBalance = totalFiatBalanceCalculator.calculate(
|
||||
walletStores = walletStores,
|
||||
walletStores = walletStores.orEmpty(),
|
||||
initial = TotalFiatBalance.Loading,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ internal object WalletSelectorReducer {
|
|||
is WalletSelectorAction.IsLockedChanged -> state.copy(
|
||||
isLocked = action.isLocked,
|
||||
)
|
||||
is WalletSelectorAction.BalanceLoaded -> state.copy(
|
||||
wallets = state.wallets.updateWithBalance(action.userWalletModel),
|
||||
is WalletSelectorAction.BalancesLoaded -> state.copy(
|
||||
wallets = action.userWalletModels,
|
||||
)
|
||||
is WalletSelectorAction.HandleError -> state.copy(error = action.error)
|
||||
is WalletSelectorAction.CloseError -> state.copy(error = null)
|
||||
|
|
@ -85,19 +85,6 @@ internal object WalletSelectorReducer {
|
|||
}
|
||||
}
|
||||
|
||||
private fun List<UserWalletModel>.updateWithBalance(
|
||||
userWalletModel: UserWalletModel,
|
||||
): List<UserWalletModel> {
|
||||
return ArrayList(this).apply {
|
||||
val index = indexOfFirst { it.id == userWalletModel.id }
|
||||
if (index == -1) {
|
||||
add(userWalletModel)
|
||||
} else {
|
||||
this[index] = userWalletModel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun UserWallet.getType(prevType: UserWalletModel.Type? = null): UserWalletModel.Type {
|
||||
return if (isMultiCurrency) {
|
||||
UserWalletModel.Type.MultiCurrency(
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import com.tangem.tap.features.walletSelector.redux.WalletSelectorState
|
|||
import com.tangem.tap.features.walletSelector.ui.model.MultiCurrencyUserWalletItem
|
||||
import com.tangem.tap.features.walletSelector.ui.model.SingleCurrencyUserWalletItem
|
||||
import com.tangem.tap.features.walletSelector.ui.model.UserWalletItem
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal fun WalletSelectorScreenState.updateWithNewState(
|
||||
newState: WalletSelectorState,
|
||||
|
|
@ -50,10 +51,14 @@ private fun List<UserWalletModel>.toUiModels(
|
|||
): Sequence<UserWalletItem> {
|
||||
return this.asSequence().map { userWalletModel ->
|
||||
with(userWalletModel) {
|
||||
val balance = UserWalletItem.Balance(
|
||||
amount = fiatBalance.amount.toFormattedFiatValue(appCurrency.symbol),
|
||||
isLoading = fiatBalance is TotalFiatBalance.Loading,
|
||||
)
|
||||
val formatAmount = { amount: BigDecimal ->
|
||||
amount.toFormattedFiatValue(appCurrency.symbol)
|
||||
}
|
||||
val balance = when (fiatBalance) {
|
||||
is TotalFiatBalance.Error -> UserWalletItem.Balance.Error(formatAmount(fiatBalance.amount))
|
||||
is TotalFiatBalance.Loaded -> UserWalletItem.Balance.Loaded(formatAmount(fiatBalance.amount))
|
||||
is TotalFiatBalance.Loading -> UserWalletItem.Balance.Loading
|
||||
}
|
||||
when (type) {
|
||||
is UserWalletModel.Type.MultiCurrency -> MultiCurrencyUserWalletItem(
|
||||
id = id,
|
||||
|
|
|
|||
|
|
@ -9,9 +9,8 @@ import com.tangem.tap.features.walletSelector.ui.model.UserWalletItem
|
|||
internal object MockData {
|
||||
private val multiCurrencyUserWallet = MultiCurrencyUserWalletItem(
|
||||
id = UserWalletId("wallet_1"),
|
||||
balance = UserWalletItem.Balance(
|
||||
balance = UserWalletItem.Balance.Loaded(
|
||||
amount = "6781.05 $",
|
||||
isLoading = false,
|
||||
),
|
||||
name = "Wallet",
|
||||
imageUrl = "https://app.tangem.com/cards/card_default.png",
|
||||
|
|
@ -22,9 +21,8 @@ internal object MockData {
|
|||
|
||||
private val singleCurrencyUserWallet = SingleCurrencyUserWalletItem(
|
||||
id = UserWalletId("wallet_4"),
|
||||
balance = UserWalletItem.Balance(
|
||||
balance = UserWalletItem.Balance.Loaded(
|
||||
amount = "6781.05 $",
|
||||
isLoading = false,
|
||||
),
|
||||
name = "Wallet",
|
||||
imageUrl = "https://app.tangem.com/cards/card_default.png",
|
||||
|
|
|
|||
|
|
@ -180,15 +180,24 @@ private fun TokensInfo(
|
|||
if (isLocked) {
|
||||
LockedPlaceholder()
|
||||
} else {
|
||||
if (balance.isLoading) {
|
||||
LoadingTokensInfo(
|
||||
isMultiCurrencyWallet = tokensCount != null,
|
||||
)
|
||||
} else {
|
||||
LoadedTokensInfo(
|
||||
balanceAmount = balance.amount,
|
||||
tokensCount = tokensCount,
|
||||
)
|
||||
when (balance) {
|
||||
is UserWalletItem.Balance.Error -> {
|
||||
LoadedTokensInfo(
|
||||
balanceAmount = balance.amount,
|
||||
tokensCount = tokensCount,
|
||||
showWarning = true,
|
||||
)
|
||||
}
|
||||
is UserWalletItem.Balance.Loaded -> {
|
||||
LoadedTokensInfo(
|
||||
balanceAmount = balance.amount,
|
||||
tokensCount = tokensCount,
|
||||
showWarning = false,
|
||||
)
|
||||
}
|
||||
is UserWalletItem.Balance.Loading -> {
|
||||
LoadingTokensInfo(isMultiCurrencyWallet = tokensCount != null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -234,17 +243,31 @@ private fun LoadedTokensInfo(
|
|||
modifier: Modifier = Modifier,
|
||||
balanceAmount: String,
|
||||
tokensCount: Int?,
|
||||
showWarning: Boolean,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
horizontalAlignment = Alignment.End,
|
||||
) {
|
||||
Text(
|
||||
text = balanceAmount,
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.End,
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4),
|
||||
) {
|
||||
Text(
|
||||
text = balanceAmount,
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.End,
|
||||
)
|
||||
if (showWarning) {
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size16),
|
||||
painter = painterResource(id = R.drawable.ic_alert_24),
|
||||
tint = TangemTheme.colors.icon.attention,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
if (tokensCount != null) {
|
||||
SpacerH2()
|
||||
Text(
|
||||
|
|
|
|||
|
|
@ -9,10 +9,13 @@ internal sealed interface UserWalletItem {
|
|||
val balance: Balance
|
||||
val isLocked: Boolean
|
||||
|
||||
data class Balance(
|
||||
val amount: String,
|
||||
val isLoading: Boolean,
|
||||
)
|
||||
sealed interface Balance {
|
||||
object Loading : Balance
|
||||
|
||||
data class Error(val amount: String) : Balance
|
||||
|
||||
data class Loaded(val amount: String) : Balance
|
||||
}
|
||||
}
|
||||
|
||||
internal data class MultiCurrencyUserWalletItem(
|
||||
|
|
|
|||
|
|
@ -151,13 +151,12 @@
|
|||
android:id="@+id/rv_pending_transaction"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:overScrollMode="never"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/l_card_total_balance"
|
||||
app:layout_goneMarginTop="12dp" />
|
||||
app:layout_constraintTop_toBottomOf="@id/rv_warning_messages" />
|
||||
|
||||
<include
|
||||
android:id="@+id/l_card_balance"
|
||||
|
|
@ -194,7 +193,7 @@
|
|||
android:nestedScrollingEnabled="false"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/rv_pending_transaction" />
|
||||
app:layout_constraintTop_toBottomOf="@id/l_card_total_balance" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/pb_loading_user_tokens"
|
||||
|
|
@ -206,7 +205,7 @@
|
|||
android:nestedScrollingEnabled="false"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/rv_pending_transaction" />
|
||||
app:layout_constraintTop_toBottomOf="@id/rv_warning_messages" />
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/barrier_wallets"
|
||||
|
|
|
|||
|
|
@ -59,7 +59,8 @@ object Versions {
|
|||
// endregion Other libraries
|
||||
|
||||
// region Tangem
|
||||
const val tangemBlockchainSdk = "develop-151"
|
||||
const val tangemBlockchainSdk = "develop-152"
|
||||
// const val tangemBlockchainSdk = "0.0.1"
|
||||
|
||||
const val tangemCardSgk = "develop-179"
|
||||
// endregion Tangem
|
||||
|
|
|
|||
9
core/ui/src/main/res/drawable/ic_alert_24.xml
Normal file
9
core/ui/src/main/res/drawable/ic_alert_24.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M11,15H13V17H11V15ZM11,7H13V13H11V7ZM12,2C6.47,2 2,6.5 2,12C2,14.652 3.054,17.196 4.929,19.071C5.858,20 6.96,20.736 8.173,21.239C9.386,21.741 10.687,22 12,22C14.652,22 17.196,20.946 19.071,19.071C20.946,17.196 22,14.652 22,12C22,10.687 21.741,9.386 21.239,8.173C20.736,6.96 20,5.858 19.071,4.929C18.142,4 17.04,3.264 15.827,2.761C14.614,2.259 13.313,2 12,2ZM12,20C9.878,20 7.843,19.157 6.343,17.657C4.843,16.157 4,14.122 4,12C4,9.878 4.843,7.843 6.343,6.343C7.843,4.843 9.878,4 12,4C14.122,4 16.157,4.843 17.657,6.343C19.157,7.843 20,9.878 20,12C20,14.122 19.157,16.157 17.657,17.657C16.157,19.157 14.122,20 12,20Z" />
|
||||
</vector>
|
||||
|
|
@ -11,7 +11,7 @@ object SaltPayWorkaround {
|
|||
Blockchain.SaltPay -> Token(
|
||||
name = "WXDAI",
|
||||
symbol = "wxDAI",
|
||||
contractAddress = "0x4346186e7461cB4DF06bCFCB4cD591423022e417",
|
||||
contractAddress = "0x4200000000000000000000000000000000000006",
|
||||
decimals = 18,
|
||||
id = "wrapped-xdai",
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue