Updated on 2026-08-14
This commit is contained in:
commit
13d9e4cc82
26 changed files with 610 additions and 600 deletions
|
|
@ -5,7 +5,12 @@ import android.content.Context
|
|||
import com.tangem.tap.common.redux.AppDialog
|
||||
import com.tangem.tap.common.redux.global.GlobalState
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectDialog
|
||||
import com.tangem.tap.features.details.ui.walletconnect.dialogs.*
|
||||
import com.tangem.tap.features.details.ui.walletconnect.dialogs.ApproveWcSessionDialog
|
||||
import com.tangem.tap.features.details.ui.walletconnect.dialogs.BnbTransactionDialog
|
||||
import com.tangem.tap.features.details.ui.walletconnect.dialogs.ClipboardOrScanQrDialog
|
||||
import com.tangem.tap.features.details.ui.walletconnect.dialogs.PersonalSignDialog
|
||||
import com.tangem.tap.features.details.ui.walletconnect.dialogs.SimpleAlertDialog
|
||||
import com.tangem.tap.features.details.ui.walletconnect.dialogs.TransactionDialog
|
||||
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
|
||||
|
|
@ -16,6 +21,7 @@ import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.ConfirmDisc
|
|||
import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.UnfinishedBackupFoundDialog
|
||||
import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog
|
||||
import com.tangem.tap.features.wallet.ui.dialogs.SimpleOkDialog
|
||||
import com.tangem.tap.features.wallet.ui.wallet.CurrencySelectionDialog
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import org.rekotlin.StoreSubscriber
|
||||
|
|
@ -54,7 +60,10 @@ 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 AppDialog.CurrencySelectionDialog ->
|
||||
CurrencySelectionDialog.create(state.dialog, context)
|
||||
is TwinCardsAction.Wallet.ShowInterruptDialog ->
|
||||
CreateWalletInterruptDialog.create(state.dialog, context)
|
||||
is WalletConnectDialog.UnsupportedCard ->
|
||||
SimpleAlertDialog.create(
|
||||
titleRes = R.string.wallet_connect,
|
||||
|
|
|
|||
|
|
@ -39,10 +39,23 @@ fun BigDecimal.toFormattedCurrencyString(
|
|||
return "$formattedAmount $currency"
|
||||
}
|
||||
|
||||
fun BigDecimal.toFiatString(rateValue: BigDecimal, fiatCurrencyName: String): String {
|
||||
fun BigDecimal.toFiatRateString(
|
||||
fiatCurrencyName: String
|
||||
): String {
|
||||
val value = this
|
||||
.setScale(2, RoundingMode.HALF_UP)
|
||||
.formatWithSpaces()
|
||||
return "$value $fiatCurrencyName"
|
||||
}
|
||||
|
||||
fun BigDecimal.toFiatString(
|
||||
rateValue: BigDecimal,
|
||||
fiatCurrencyName: String,
|
||||
formatWithSpaces: Boolean = false
|
||||
): String {
|
||||
var fiatValue = rateValue.multiply(this)
|
||||
fiatValue = fiatValue.setScale(2, RoundingMode.HALF_UP)
|
||||
return "≈ ${fiatCurrencyName} $fiatValue"
|
||||
return fiatValue.toFormattedFiatValue(fiatCurrencyName, formatWithSpaces)
|
||||
}
|
||||
|
||||
fun BigDecimal.toFiatValue(rateValue: BigDecimal): BigDecimal {
|
||||
|
|
@ -50,8 +63,12 @@ fun BigDecimal.toFiatValue(rateValue: BigDecimal): BigDecimal {
|
|||
return fiatValue.setScale(2, RoundingMode.HALF_UP)
|
||||
}
|
||||
|
||||
fun BigDecimal.toFormattedFiatValue(fiatCurrencyName: String): String {
|
||||
return "≈ ${fiatCurrencyName} $this"
|
||||
fun BigDecimal.toFormattedFiatValue(
|
||||
fiatCurrencyName: String,
|
||||
formatWithSpaces: Boolean = false
|
||||
): String {
|
||||
val fiatValue = if (formatWithSpaces) this.formatWithSpaces() else this
|
||||
return " ${fiatValue} $fiatCurrencyName"
|
||||
}
|
||||
|
||||
fun BigDecimal.stripZeroPlainString(): String = this.stripTrailingZeros().toPlainString()
|
||||
|
|
@ -111,4 +128,33 @@ fun BigDecimal.formatAmountAsSpannedString(
|
|||
append(' ')
|
||||
append(currencySymbol)
|
||||
}
|
||||
}
|
||||
|
||||
fun BigDecimal.formatWithSpaces(): String {
|
||||
val str = this.toString()
|
||||
var integerStr = str.substringBefore('.')
|
||||
val reminderStr = str.substringAfter('.')
|
||||
val packets = arrayListOf<String>()
|
||||
|
||||
var index: Int = integerStr.length
|
||||
while (0 < index) {
|
||||
if (index <= 3) {
|
||||
packets.add(0, integerStr)
|
||||
break
|
||||
}
|
||||
index -= 3
|
||||
packets.add(integerStr.substring(startIndex = index))
|
||||
integerStr = integerStr.substring(startIndex = 0, endIndex = index)
|
||||
}
|
||||
|
||||
return buildString {
|
||||
packets.forEachIndexed { index, packet ->
|
||||
append(packet)
|
||||
if (index != packets.lastIndex) append(' ')
|
||||
}
|
||||
if (reminderStr.isNotBlank()) {
|
||||
append('.')
|
||||
append(reminderStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,5 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.app.Activity
|
||||
import android.content.*
|
||||
import android.content.res.Resources
|
||||
|
|
@ -192,45 +190,27 @@ fun View.getString(resId: Int, vararg formatArgs: Any?): String {
|
|||
return context.getString(resId, formatArgs)
|
||||
}
|
||||
|
||||
fun View.showAnimated(durationMillis: Long = 300) {
|
||||
this.animateVisibility(
|
||||
show = true,
|
||||
durationMillis = durationMillis
|
||||
)
|
||||
}
|
||||
|
||||
fun View.hideAnimated(
|
||||
durationMillis: Long = 300,
|
||||
hiddenVisibility: Int = View.GONE
|
||||
) {
|
||||
this.animateVisibility(
|
||||
show = false,
|
||||
durationMillis = durationMillis,
|
||||
hiddenVisibility = hiddenVisibility
|
||||
)
|
||||
}
|
||||
|
||||
private fun View.animateVisibility(
|
||||
fun View.animateVisibility(
|
||||
show: Boolean,
|
||||
durationMillis: Long = 300,
|
||||
durationMillis: Long = SHORT_ANIMATION_DURATION,
|
||||
hiddenVisibility: Int = View.GONE
|
||||
) {
|
||||
if (this.isVisible == show) return
|
||||
if (show) {
|
||||
this.alpha = 0f
|
||||
this.isVisible = true
|
||||
this.animate()
|
||||
.alpha(1f)
|
||||
.setDuration(durationMillis)
|
||||
.setListener(null)
|
||||
.withStartAction {
|
||||
this.alpha = 0f
|
||||
this.isVisible = true
|
||||
}
|
||||
} else {
|
||||
this.animate()
|
||||
.alpha(0f)
|
||||
.setDuration(durationMillis)
|
||||
.setListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator?) {
|
||||
this@animateVisibility.visibility = hiddenVisibility
|
||||
}
|
||||
})
|
||||
.withStartAction {
|
||||
this.visibility = hiddenVisibility
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val SHORT_ANIMATION_DURATION = 80L
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.tap.common.redux
|
|||
|
||||
import com.tangem.common.extensions.VoidCallback
|
||||
import com.tangem.tap.common.TestAction
|
||||
import com.tangem.tap.common.entities.FiatCurrency
|
||||
import com.tangem.tap.features.wallet.redux.AddressData
|
||||
import com.tangem.tap.features.wallet.redux.Currency
|
||||
|
||||
|
|
@ -12,7 +13,12 @@ interface StateDialog
|
|||
|
||||
sealed class AppDialog : StateDialog {
|
||||
data class SimpleOkDialog(val header: String, val message: String, val onOk: VoidCallback? = null) : AppDialog()
|
||||
data class SimpleOkDialogRes(val headerId: Int, val messageId: Int, val onOk: VoidCallback? = null) : AppDialog()
|
||||
data class SimpleOkDialogRes(
|
||||
val headerId: Int,
|
||||
val messageId: Int,
|
||||
val onOk: VoidCallback? = null
|
||||
) : AppDialog()
|
||||
|
||||
object ScanFailsDialog : AppDialog()
|
||||
data class AddressInfoDialog(
|
||||
val currency: Currency,
|
||||
|
|
@ -22,4 +28,9 @@ sealed class AppDialog : StateDialog {
|
|||
data class TestActionsDialog(
|
||||
val actionsList: List<TestAction>
|
||||
) : AppDialog()
|
||||
|
||||
data class CurrencySelectionDialog(
|
||||
val currenciesList: List<FiatCurrency>,
|
||||
val currentAppCurrency: FiatCurrency,
|
||||
) : AppDialog()
|
||||
}
|
||||
|
|
@ -34,17 +34,17 @@ class TapWalletManager {
|
|||
store.state.globalState.configManager?.config?.blockchainSdkConfig ?: BlockchainSdkConfig()
|
||||
}
|
||||
|
||||
private val walletManagersThrottler = ThrottlerWithValues<Blockchain, Result<Wallet>>(10000)
|
||||
private val walletManagersThrottler = ThrottlerWithValues<BlockchainNetwork, Result<Wallet>>(10000)
|
||||
|
||||
suspend fun loadWalletData(walletManager: WalletManager) {
|
||||
val blockchain = walletManager.wallet.blockchain
|
||||
val blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager)
|
||||
val result = if (walletManagersThrottler.isStillThrottled(blockchain)) {
|
||||
walletManagersThrottler.geValue(blockchain)!!
|
||||
val result = if (walletManagersThrottler.isStillThrottled(blockchainNetwork)) {
|
||||
walletManagersThrottler.geValue(blockchainNetwork)!!
|
||||
} else {
|
||||
val safeUpdateResult = walletManager.safeUpdate()
|
||||
walletManagersThrottler.updateThrottlingTo(blockchain)
|
||||
walletManagersThrottler.setValue(blockchain, safeUpdateResult)
|
||||
walletManagersThrottler.updateThrottlingTo(blockchainNetwork)
|
||||
walletManagersThrottler.setValue(blockchainNetwork, safeUpdateResult)
|
||||
safeUpdateResult
|
||||
}
|
||||
when (result) {
|
||||
|
|
|
|||
|
|
@ -4,9 +4,7 @@ import com.tangem.blockchain.common.Wallet
|
|||
import com.tangem.common.card.Card
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.domain.common.TwinCardNumber
|
||||
import com.tangem.network.api.tangemTech.TangemTechService
|
||||
import com.tangem.operations.pins.CheckUserCodesResponse
|
||||
import com.tangem.tap.common.entities.FiatCurrency
|
||||
import com.tangem.tap.common.redux.NotificationAction
|
||||
import com.tangem.tap.domain.termsOfUse.CardTou
|
||||
import com.tangem.wallet.R
|
||||
|
|
@ -18,9 +16,6 @@ sealed class DetailsAction : Action {
|
|||
val scanResponse: ScanResponse,
|
||||
val wallets: List<Wallet>,
|
||||
val cardTou: CardTou,
|
||||
val fiatCurrencyName: FiatCurrency,
|
||||
val fiatCurrencies: List<FiatCurrency>? = null,
|
||||
val tangemTechService: TangemTechService,
|
||||
) : DetailsAction()
|
||||
|
||||
object ShowDisclaimer : DetailsAction()
|
||||
|
|
@ -46,13 +41,6 @@ sealed class DetailsAction : Action {
|
|||
|
||||
object CreateBackup : DetailsAction()
|
||||
|
||||
sealed class AppCurrencyAction : DetailsAction() {
|
||||
data class SetCurrencies(val currencies: List<FiatCurrency>) : AppCurrencyAction()
|
||||
object ChooseAppCurrency : AppCurrencyAction()
|
||||
object Cancel : AppCurrencyAction()
|
||||
data class SelectAppCurrency(val fiatCurrency: FiatCurrency) : AppCurrencyAction()
|
||||
}
|
||||
|
||||
sealed class ManageSecurity : DetailsAction() {
|
||||
data class CheckCurrentSecurityOption(val card: Card) : ManageSecurity()
|
||||
data class SetCurrentOption(val userCodes: CheckUserCodesResponse) : ManageSecurity()
|
||||
|
|
|
|||
|
|
@ -3,24 +3,23 @@ package com.tangem.tap.features.details.redux
|
|||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.card.FirmwareVersion
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.network.api.tangemTech.CurrenciesResponse
|
||||
import com.tangem.operations.pins.CheckUserCodesResponse
|
||||
import com.tangem.tap.*
|
||||
import com.tangem.tap.common.analytics.Analytics
|
||||
import com.tangem.tap.common.analytics.AnalyticsParam
|
||||
import com.tangem.tap.common.entities.FiatCurrency
|
||||
import com.tangem.tap.common.extensions.dispatchNotification
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
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.currenciesRepository
|
||||
import com.tangem.tap.features.disclaimer.redux.DisclaimerAction
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.CreateTwinWalletMode
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
|
||||
import com.tangem.tap.features.wallet.models.hasSendableAmountsOrPendingTransactions
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
|
@ -28,15 +27,12 @@ import org.rekotlin.Middleware
|
|||
|
||||
class DetailsMiddleware {
|
||||
private val eraseWalletMiddleware = EraseWalletMiddleware()
|
||||
private val appCurrencyMiddleware = AppCurrencyMiddleware()
|
||||
private val manageSecurityMiddleware = ManageSecurityMiddleware()
|
||||
val detailsMiddleware: Middleware<AppState> = { _, _ ->
|
||||
{ next ->
|
||||
{ action ->
|
||||
when (action) {
|
||||
is DetailsAction.PrepareScreen -> prepareData(action)
|
||||
is DetailsAction.ResetToFactory -> eraseWalletMiddleware.handle(action)
|
||||
is DetailsAction.AppCurrencyAction -> appCurrencyMiddleware.handle(action)
|
||||
is DetailsAction.ManageSecurity -> manageSecurityMiddleware.handle(action)
|
||||
is DetailsAction.ShowDisclaimer -> {
|
||||
store.dispatch(DisclaimerAction.ShowAcceptedDisclaimer)
|
||||
|
|
@ -69,48 +65,6 @@ class DetailsMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
private fun prepareData(action: DetailsAction.PrepareScreen) {
|
||||
val fiatCurrenciesPrefStorage = preferencesStorage.fiatCurrenciesPrefStorage
|
||||
val storedFiatCurrencies = fiatCurrenciesPrefStorage.restore()
|
||||
if (storedFiatCurrencies.isNotEmpty()) {
|
||||
store.dispatch(
|
||||
DetailsAction.AppCurrencyAction.SetCurrencies(
|
||||
currencies = storedFiatCurrencies.mapToUiModel()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
scope.launch {
|
||||
val tangemTechService = action.tangemTechService
|
||||
when (val result = tangemTechService.currencies()) {
|
||||
is Result.Success -> {
|
||||
val currenciesList = result.data.currencies
|
||||
if (currenciesList.isNotEmpty() &&
|
||||
currenciesList.toSet() != storedFiatCurrencies.toSet()
|
||||
) {
|
||||
fiatCurrenciesPrefStorage.save(currenciesList)
|
||||
dispatchOnMain(
|
||||
DetailsAction.AppCurrencyAction.SetCurrencies(
|
||||
currencies = currenciesList.mapToUiModel()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
is Result.Failure -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<CurrenciesResponse.Currency>.mapToUiModel(): List<FiatCurrency> {
|
||||
return this.map {
|
||||
FiatCurrency(
|
||||
code = it.code,
|
||||
name = it.name,
|
||||
symbol = it.unit
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class EraseWalletMiddleware {
|
||||
fun handle(action: DetailsAction.ResetToFactory) {
|
||||
when (action) {
|
||||
|
|
@ -158,22 +112,6 @@ class DetailsMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
class AppCurrencyMiddleware {
|
||||
fun handle(action: DetailsAction.AppCurrencyAction) {
|
||||
when (action) {
|
||||
is DetailsAction.AppCurrencyAction.SelectAppCurrency -> {
|
||||
store.state.globalState.tapWalletManager.rates.clear()
|
||||
preferencesStorage.fiatCurrenciesPrefStorage
|
||||
.saveAppCurrency(action.fiatCurrency)
|
||||
store.dispatch(GlobalAction.ChangeAppCurrency(action.fiatCurrency))
|
||||
store.dispatch(WalletAction.LoadFiatRate())
|
||||
}
|
||||
else -> { /* no-op */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ManageSecurityMiddleware {
|
||||
fun handle(action: DetailsAction.ManageSecurity) {
|
||||
when (action) {
|
||||
|
|
|
|||
|
|
@ -29,9 +29,6 @@ private fun internalReduce(action: Action, state: AppState): DetailsState {
|
|||
is DetailsAction.ResetToFactory -> {
|
||||
handleEraseWallet(action, detailsState)
|
||||
}
|
||||
is DetailsAction.AppCurrencyAction -> {
|
||||
handleAppCurrencyAction(action, detailsState)
|
||||
}
|
||||
is DetailsAction.ManageSecurity -> {
|
||||
handleSecurityAction(action, detailsState)
|
||||
}
|
||||
|
|
@ -48,10 +45,6 @@ private fun handlePrepareScreen(
|
|||
scanResponse = action.scanResponse,
|
||||
wallets = action.wallets,
|
||||
cardInfo = action.scanResponse.card.toCardInfo(),
|
||||
appCurrencyState = state.appCurrencyState.copy(
|
||||
currentFiatCurrency = action.fiatCurrencyName,
|
||||
showAppCurrencyDialog = false,
|
||||
),
|
||||
cardTermsOfUseUrl = action.cardTou.getUrl(action.scanResponse.card),
|
||||
createBackupAllowed = action.scanResponse.card.backupStatus == Card.BackupStatus.NoBackup,
|
||||
)
|
||||
|
|
@ -93,31 +86,6 @@ private fun handleEraseWallet(
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleAppCurrencyAction(
|
||||
action: DetailsAction.AppCurrencyAction, state: DetailsState,
|
||||
): DetailsState {
|
||||
return when (action) {
|
||||
is DetailsAction.AppCurrencyAction.SetCurrencies -> {
|
||||
state.copy(appCurrencyState = state.appCurrencyState.copy(fiatCurrencies = action.currencies))
|
||||
}
|
||||
DetailsAction.AppCurrencyAction.ChooseAppCurrency -> {
|
||||
state.copy(appCurrencyState = state.appCurrencyState.copy(showAppCurrencyDialog = true))
|
||||
}
|
||||
DetailsAction.AppCurrencyAction.Cancel -> {
|
||||
state.copy(appCurrencyState = state.appCurrencyState.copy(showAppCurrencyDialog = false))
|
||||
}
|
||||
is DetailsAction.AppCurrencyAction.SelectAppCurrency -> {
|
||||
state.copy(
|
||||
appCurrencyState = state.appCurrencyState.copy(
|
||||
currentFiatCurrency = action.fiatCurrency,
|
||||
showAppCurrencyDialog = false
|
||||
)
|
||||
)
|
||||
}
|
||||
else -> state
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleSecurityAction(
|
||||
action: DetailsAction.ManageSecurity, state: DetailsState,
|
||||
): DetailsState {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import android.net.Uri
|
|||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.tap.common.entities.Button
|
||||
import com.tangem.tap.common.entities.FiatCurrency
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
|
||||
import com.tangem.tap.store
|
||||
import org.rekotlin.StateType
|
||||
|
|
@ -15,7 +14,6 @@ data class DetailsState(
|
|||
val scanResponse: ScanResponse? = null,
|
||||
val wallets: List<Wallet> = emptyList(),
|
||||
val cardInfo: CardInfo? = null,
|
||||
val appCurrencyState: AppCurrencyState = AppCurrencyState(),
|
||||
val eraseWalletState: EraseWalletState? = null,
|
||||
val confirmScreenState: ConfirmScreenState? = null,
|
||||
val securityScreenState: SecurityScreenState? = null,
|
||||
|
|
@ -48,10 +46,4 @@ data class SecurityScreenState(
|
|||
val buttonProceed: Button = Button(true),
|
||||
)
|
||||
|
||||
enum class SecurityOption { LongTap, PassCode, AccessCode }
|
||||
|
||||
data class AppCurrencyState(
|
||||
val currentFiatCurrency: FiatCurrency = FiatCurrency.Default,
|
||||
val showAppCurrencyDialog: Boolean = false,
|
||||
val fiatCurrencies: List<FiatCurrency>? = null,
|
||||
)
|
||||
enum class SecurityOption { LongTap, PassCode, AccessCode }
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
package com.tangem.tap.features.details.ui
|
||||
|
||||
import android.content.Context
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.tangem.tap.common.entities.FiatCurrency
|
||||
import com.tangem.tap.features.details.redux.DetailsAction
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
|
||||
class CurrencySelectionDialog {
|
||||
|
||||
var dialog: AlertDialog? = null
|
||||
|
||||
fun show(
|
||||
currenciesList: List<FiatCurrency>,
|
||||
currentAppCurrency: FiatCurrency,
|
||||
context: Context
|
||||
) {
|
||||
if (dialog == null) {
|
||||
val currenciesToShow = currenciesList
|
||||
.map { it.displayName }
|
||||
.toTypedArray()
|
||||
var currentSelection = currenciesList
|
||||
.indexOfFirst { it.code == currentAppCurrency.code }
|
||||
|
||||
dialog = AlertDialog.Builder(context)
|
||||
.setTitle(context.getString(R.string.details_row_title_currency))
|
||||
.setNegativeButton(context.getString(R.string.common_cancel)) { _, _ ->
|
||||
store.dispatch(DetailsAction.AppCurrencyAction.Cancel)
|
||||
}
|
||||
.setPositiveButton(context.getString(R.string.common_done)) { _, _ ->
|
||||
val selectedCurrency = currenciesList[currentSelection]
|
||||
store.dispatch(
|
||||
DetailsAction.AppCurrencyAction.SelectAppCurrency(
|
||||
fiatCurrency = selectedCurrency
|
||||
)
|
||||
)
|
||||
}
|
||||
.setOnDismissListener {
|
||||
store.dispatch(DetailsAction.AppCurrencyAction.Cancel)
|
||||
}
|
||||
.setSingleChoiceItems(currenciesToShow, currentSelection) { _, which ->
|
||||
currentSelection = which
|
||||
}.show()
|
||||
}
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
dialog = null
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -24,7 +24,6 @@ import org.rekotlin.StoreSubscriber
|
|||
|
||||
class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<DetailsState> {
|
||||
|
||||
private var currencySelectionDialog = CurrencySelectionDialog()
|
||||
private val binding: FragmentDetailsBinding by viewBinding(FragmentDetailsBinding::bind)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
|
@ -115,11 +114,6 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<Det
|
|||
store.dispatch(DetailsAction.CreateBackup)
|
||||
}
|
||||
|
||||
tvAppCurrency.text = state.appCurrencyState.currentFiatCurrency.code
|
||||
|
||||
tvAppCurrencyTitle.setOnClickListener {
|
||||
store.dispatch(DetailsAction.AppCurrencyAction.ChooseAppCurrency)
|
||||
}
|
||||
tvSendFeedback.setOnClickListener {
|
||||
store.dispatch(GlobalAction.SendFeedback(FeedbackEmail()))
|
||||
}
|
||||
|
|
@ -140,17 +134,6 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<Det
|
|||
null -> null
|
||||
}
|
||||
currentSecurity?.let { tvSecurity.text = getString(it) }
|
||||
|
||||
if (state.appCurrencyState.showAppCurrencyDialog &&
|
||||
!state.appCurrencyState.fiatCurrencies.isNullOrEmpty()) {
|
||||
currencySelectionDialog.show(
|
||||
currenciesList = state.appCurrencyState.fiatCurrencies,
|
||||
currentAppCurrency = state.appCurrencyState.currentFiatCurrency,
|
||||
context = requireContext()
|
||||
)
|
||||
} else {
|
||||
currencySelectionDialog.clear()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,17 +1,22 @@
|
|||
package com.tangem.tap.features.wallet.redux
|
||||
|
||||
import android.content.Context
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.tap.common.entities.FiatCurrency
|
||||
import com.tangem.tap.common.redux.ErrorAction
|
||||
import com.tangem.tap.common.redux.NotificationAction
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
|
||||
import com.tangem.tap.domain.tokens.BlockchainNetwork
|
||||
import com.tangem.wallet.R
|
||||
import org.rekotlin.Action
|
||||
import java.math.BigDecimal
|
||||
import org.rekotlin.Action
|
||||
|
||||
sealed class WalletAction : Action {
|
||||
|
||||
|
|
@ -98,7 +103,10 @@ sealed class WalletAction : Action {
|
|||
data class LoadFiatRate(
|
||||
val wallet: Wallet? = null, val coinsList: List<Currency>? = null,
|
||||
) : WalletAction() {
|
||||
data class Success(val fiatRate: Pair<Currency, BigDecimal?>) : WalletAction()
|
||||
data class Success(
|
||||
val fiatRates: Map<Currency, BigDecimal?>
|
||||
) : WalletAction()
|
||||
|
||||
object Failure : WalletAction()
|
||||
}
|
||||
|
||||
|
|
@ -163,4 +171,9 @@ sealed class WalletAction : Action {
|
|||
) : WalletAction()
|
||||
|
||||
data class RemoveWalletRent(val wallet: Wallet) : WalletAction()
|
||||
|
||||
sealed class AppCurrencyAction : WalletAction() {
|
||||
object ChooseAppCurrency : AppCurrencyAction()
|
||||
data class SelectAppCurrency(val fiatCurrency: FiatCurrency) : AppCurrencyAction()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,13 @@
|
|||
package com.tangem.tap.features.wallet.redux
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.AmountType
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.DerivationStyle
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.blockchain.extensions.isAboveZero
|
||||
import com.tangem.common.extensions.isZero
|
||||
|
|
@ -25,8 +31,8 @@ import com.tangem.tap.features.wallet.ui.BalanceStatus
|
|||
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
|
||||
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
|
||||
import com.tangem.tap.store
|
||||
import org.rekotlin.StateType
|
||||
import java.math.BigDecimal
|
||||
import org.rekotlin.StateType
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
|
||||
data class WalletState(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
package com.tangem.tap.features.wallet.redux.middlewares
|
||||
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.network.api.tangemTech.CurrenciesResponse
|
||||
import com.tangem.network.api.tangemTech.TangemTechService
|
||||
import com.tangem.tap.common.entities.FiatCurrency
|
||||
import com.tangem.tap.common.extensions.dispatchDialogShow
|
||||
import com.tangem.tap.common.redux.AppDialog
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.domain.TapWalletManager
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.persistence.FiatCurrenciesPrefStorage
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class AppCurrencyMiddleware(
|
||||
private val tangemTechService: TangemTechService,
|
||||
private val tapWalletManager: TapWalletManager,
|
||||
private val fiatCurrenciesPrefStorage: FiatCurrenciesPrefStorage,
|
||||
private val appCurrencyProvider: () -> FiatCurrency,
|
||||
) {
|
||||
fun handle(action: WalletAction.AppCurrencyAction) {
|
||||
when (action) {
|
||||
is WalletAction.AppCurrencyAction.ChooseAppCurrency -> showSelector()
|
||||
is WalletAction.AppCurrencyAction.SelectAppCurrency -> selectCurrency(action)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showSelector() {
|
||||
val storedFiatCurrencies = fiatCurrenciesPrefStorage.restore()
|
||||
if (storedFiatCurrencies.isNotEmpty()) {
|
||||
store.dispatchDialogShow(
|
||||
AppDialog.CurrencySelectionDialog(
|
||||
currenciesList = storedFiatCurrencies.mapToUiModel(),
|
||||
currentAppCurrency = appCurrencyProvider.invoke()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
scope.launch {
|
||||
when (val result = tangemTechService.currencies()) {
|
||||
is Result.Success -> {
|
||||
val currenciesList = result.data.currencies
|
||||
if (currenciesList.isNotEmpty() &&
|
||||
currenciesList.toSet() != storedFiatCurrencies.toSet()
|
||||
) {
|
||||
fiatCurrenciesPrefStorage.save(currenciesList)
|
||||
store.dispatchDialogShow(
|
||||
AppDialog.CurrencySelectionDialog(
|
||||
currenciesList = storedFiatCurrencies.mapToUiModel(),
|
||||
currentAppCurrency = appCurrencyProvider.invoke()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
is Result.Failure -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun selectCurrency(action: WalletAction.AppCurrencyAction.SelectAppCurrency) {
|
||||
tapWalletManager.rates.clear()
|
||||
fiatCurrenciesPrefStorage.saveAppCurrency(action.fiatCurrency)
|
||||
store.dispatch(GlobalAction.ChangeAppCurrency(action.fiatCurrency))
|
||||
store.dispatch(WalletAction.LoadFiatRate())
|
||||
}
|
||||
|
||||
private fun List<CurrenciesResponse.Currency>.mapToUiModel(): List<FiatCurrency> {
|
||||
return this.map {
|
||||
FiatCurrency(
|
||||
code = it.code,
|
||||
name = it.name,
|
||||
symbol = it.unit
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,7 +15,13 @@ import com.tangem.domain.common.extensions.withMainContext
|
|||
import com.tangem.operations.attestation.Attestation
|
||||
import com.tangem.operations.attestation.OnlineCardVerifier
|
||||
import com.tangem.tap.common.analytics.Analytics
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.extensions.copyToClipboard
|
||||
import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
|
||||
import com.tangem.tap.common.extensions.dispatchDialogShow
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.onCardScanned
|
||||
import com.tangem.tap.common.extensions.shareText
|
||||
import com.tangem.tap.common.extensions.stripZeroPlainString
|
||||
import com.tangem.tap.common.redux.AppDialog
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
|
|
@ -29,11 +35,17 @@ import com.tangem.tap.features.home.redux.HomeAction
|
|||
import com.tangem.tap.features.send.redux.PrepareSendScreen
|
||||
import com.tangem.tap.features.wallet.models.PendingTransactionType
|
||||
import com.tangem.tap.features.wallet.models.getPendingTransactions
|
||||
import com.tangem.tap.features.wallet.redux.*
|
||||
import com.tangem.tap.features.wallet.redux.Currency
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletData
|
||||
import com.tangem.tap.features.wallet.redux.WalletState
|
||||
import com.tangem.tap.features.wallet.redux.WalletStore
|
||||
import com.tangem.tap.network.NetworkStateChanged
|
||||
import com.tangem.tap.preferencesStorage
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import java.math.BigDecimal
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -41,12 +53,19 @@ import org.rekotlin.Action
|
|||
import org.rekotlin.DispatchFunction
|
||||
import org.rekotlin.Middleware
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
|
||||
class WalletMiddleware {
|
||||
private val tradeCryptoMiddleware = TradeCryptoMiddleware()
|
||||
private val warningsMiddleware = WarningsMiddleware()
|
||||
private val multiWalletMiddleware = MultiWalletMiddleware()
|
||||
private val appCurrencyMiddleware by lazy(mode = LazyThreadSafetyMode.NONE) {
|
||||
AppCurrencyMiddleware(
|
||||
tangemTechService = store.state.domainNetworks.tangemTechService,
|
||||
tapWalletManager = store.state.globalState.tapWalletManager,
|
||||
fiatCurrenciesPrefStorage = preferencesStorage.fiatCurrenciesPrefStorage,
|
||||
appCurrencyProvider = { store.state.globalState.appCurrency },
|
||||
)
|
||||
}
|
||||
|
||||
val walletMiddleware: Middleware<AppState> = { dispatch, state ->
|
||||
{ next ->
|
||||
|
|
@ -71,6 +90,7 @@ class WalletMiddleware {
|
|||
walletState,
|
||||
globalState
|
||||
)
|
||||
is WalletAction.AppCurrencyAction -> appCurrencyMiddleware.handle(action)
|
||||
is WalletAction.LoadWallet -> {
|
||||
scope.launch {
|
||||
if (action.blockchain == null) {
|
||||
|
|
@ -121,11 +141,15 @@ class WalletMiddleware {
|
|||
)
|
||||
when (ratesResult) {
|
||||
is Result.Success -> {
|
||||
ratesResult.data.loadedRates.forEach {
|
||||
dispatchOnMain(WalletAction.LoadFiatRate.Success(it.toPair()))
|
||||
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)
|
||||
Timber.e(
|
||||
throwable,
|
||||
"Loading rates failed for [%s]",
|
||||
currency.currencySymbol
|
||||
)
|
||||
}
|
||||
}
|
||||
is Result.Failure -> {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.tap.features.wallet.redux.reducers
|
||||
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletState
|
||||
|
||||
class AppCurrencyReducer {
|
||||
fun reduce(
|
||||
action: WalletAction.AppCurrencyAction,
|
||||
state: WalletState,
|
||||
): WalletState {
|
||||
return when (action) {
|
||||
is WalletAction.AppCurrencyAction.SelectAppCurrency,
|
||||
is WalletAction.AppCurrencyAction.ChooseAppCurrency -> state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,6 @@ import com.tangem.tap.common.extensions.toFormattedCurrencyString
|
|||
import com.tangem.tap.common.extensions.toFormattedFiatValue
|
||||
import com.tangem.tap.domain.getFirstToken
|
||||
import com.tangem.tap.domain.tokens.BlockchainNetwork
|
||||
import com.tangem.tap.features.wallet.models.TotalBalance
|
||||
import com.tangem.tap.features.wallet.models.removeUnknownTransactions
|
||||
import com.tangem.tap.features.wallet.models.toPendingTransactions
|
||||
import com.tangem.tap.features.wallet.redux.*
|
||||
|
|
@ -17,7 +16,6 @@ import com.tangem.tap.features.wallet.ui.BalanceStatus
|
|||
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
|
||||
import com.tangem.tap.features.wallet.ui.TokenData
|
||||
import com.tangem.tap.store
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
|
||||
class OnWalletLoadedReducer {
|
||||
|
|
@ -65,7 +63,7 @@ class OnWalletLoadedReducer {
|
|||
amount = coinAmountValue,
|
||||
amountFormatted = formattedAmount,
|
||||
fiatAmount = fiatAmount,
|
||||
fiatAmountFormatted = fiatAmount?.toFormattedFiatValue(fiatCurrency.code)
|
||||
fiatAmountFormatted = fiatAmount?.toFormattedFiatValue(fiatCurrency.symbol)
|
||||
),
|
||||
pendingTransactions = pendingTransactions.removeUnknownTransactions(),
|
||||
mainButton = WalletMainButton.SendButton(coinSendButton),
|
||||
|
|
@ -109,12 +107,6 @@ class OnWalletLoadedReducer {
|
|||
val newWallets = tokens + newWalletData
|
||||
val wallets = walletState.replaceSomeWallets((newWallets))
|
||||
|
||||
val totalBalance = TotalBalance(
|
||||
state = wallets.findTotalBalanceState(),
|
||||
fiatAmount = wallets.calculateTotalFiatAmount(),
|
||||
fiatCurrency = fiatCurrency,
|
||||
)
|
||||
|
||||
val state = if (wallets.any { it.currencyData.status == BalanceStatus.Loading }) {
|
||||
ProgressState.Loading
|
||||
} else {
|
||||
|
|
@ -122,7 +114,9 @@ class OnWalletLoadedReducer {
|
|||
}
|
||||
return walletState
|
||||
.updateWalletsData(wallets)
|
||||
.updateTotalBalance(totalBalance)
|
||||
.updateTotalBalance(
|
||||
totalBalance = obtainTotalBalance(wallets, fiatCurrency)
|
||||
)
|
||||
.copy(
|
||||
state = state,
|
||||
error = null
|
||||
|
|
@ -192,48 +186,4 @@ class OnWalletLoadedReducer {
|
|||
state = ProgressState.Done, error = null
|
||||
)
|
||||
}
|
||||
|
||||
private fun List<WalletData>.findTotalBalanceState(): TotalBalance.State {
|
||||
return this.mapToTotalBalanceState()
|
||||
.fold(initial = TotalBalance.State.Loading) { accState, newState ->
|
||||
accState or newState
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<WalletData>.calculateTotalFiatAmount(): BigDecimal {
|
||||
return this.map { it.currencyData.fiatAmount ?: BigDecimal.ZERO }
|
||||
.reduce(BigDecimal::plus)
|
||||
}
|
||||
|
||||
private fun List<WalletData>.mapToTotalBalanceState(): List<TotalBalance.State> {
|
||||
return this.map {
|
||||
when (it.currencyData.status) {
|
||||
BalanceStatus.VerifiedOnline,
|
||||
BalanceStatus.SameCurrencyTransactionInProgress,
|
||||
BalanceStatus.TransactionInProgress -> TotalBalance.State.Success
|
||||
BalanceStatus.Unreachable,
|
||||
BalanceStatus.NoAccount,
|
||||
BalanceStatus.EmptyCard,
|
||||
BalanceStatus.UnknownBlockchain -> TotalBalance.State.SomeTokensFailed
|
||||
BalanceStatus.Loading,
|
||||
null -> TotalBalance.State.Loading
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
infix fun TotalBalance.State.or(newState: TotalBalance.State): TotalBalance.State {
|
||||
return when (this) {
|
||||
TotalBalance.State.Loading -> when (newState) {
|
||||
TotalBalance.State.Loading -> this
|
||||
TotalBalance.State.SomeTokensFailed,
|
||||
TotalBalance.State.Success -> newState
|
||||
}
|
||||
TotalBalance.State.Success,
|
||||
TotalBalance.State.SomeTokensFailed -> when (newState) {
|
||||
TotalBalance.State.Loading,
|
||||
TotalBalance.State.SomeTokensFailed -> newState
|
||||
TotalBalance.State.Success -> this
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package com.tangem.tap.features.wallet.redux.reducers
|
||||
|
||||
import com.tangem.tap.common.entities.FiatCurrency
|
||||
import com.tangem.tap.features.wallet.models.TotalBalance
|
||||
import com.tangem.tap.features.wallet.redux.WalletData
|
||||
import com.tangem.tap.features.wallet.ui.BalanceStatus
|
||||
import java.math.BigDecimal
|
||||
|
||||
fun obtainTotalBalance(
|
||||
wallets: List<WalletData>,
|
||||
appCurrency: FiatCurrency
|
||||
): TotalBalance {
|
||||
return TotalBalance(
|
||||
state = wallets.findTotalBalanceState(),
|
||||
fiatAmount = wallets.calculateTotalFiatAmount(),
|
||||
fiatCurrency = appCurrency
|
||||
)
|
||||
}
|
||||
|
||||
private fun List<WalletData>.findTotalBalanceState(): TotalBalance.State {
|
||||
return this.mapToTotalBalanceState()
|
||||
.fold(initial = TotalBalance.State.Loading) { accState, newState ->
|
||||
accState or newState
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<WalletData>.calculateTotalFiatAmount(): BigDecimal {
|
||||
return this.map { it.currencyData.fiatAmount ?: BigDecimal.ZERO }
|
||||
.reduce(BigDecimal::plus)
|
||||
}
|
||||
|
||||
private fun List<WalletData>.mapToTotalBalanceState(): List<TotalBalance.State> {
|
||||
return this.map {
|
||||
when (it.currencyData.status) {
|
||||
BalanceStatus.VerifiedOnline,
|
||||
BalanceStatus.SameCurrencyTransactionInProgress,
|
||||
BalanceStatus.TransactionInProgress -> TotalBalance.State.Success
|
||||
BalanceStatus.Unreachable,
|
||||
BalanceStatus.NoAccount,
|
||||
BalanceStatus.EmptyCard,
|
||||
BalanceStatus.UnknownBlockchain -> TotalBalance.State.SomeTokensFailed
|
||||
BalanceStatus.Loading,
|
||||
null -> TotalBalance.State.Loading
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
infix fun TotalBalance.State.or(newState: TotalBalance.State): TotalBalance.State {
|
||||
return when (this) {
|
||||
TotalBalance.State.Loading -> when (newState) {
|
||||
TotalBalance.State.Loading -> this
|
||||
TotalBalance.State.SomeTokensFailed,
|
||||
TotalBalance.State.Success -> newState
|
||||
}
|
||||
TotalBalance.State.Success,
|
||||
TotalBalance.State.SomeTokensFailed -> when (newState) {
|
||||
TotalBalance.State.Loading,
|
||||
TotalBalance.State.SomeTokensFailed -> newState
|
||||
TotalBalance.State.Success -> this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,25 +3,38 @@ 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.TwinCardNumber
|
||||
import com.tangem.tap.common.entities.FiatCurrency
|
||||
import com.tangem.tap.common.extensions.toFiatRateString
|
||||
import com.tangem.tap.common.extensions.toFiatString
|
||||
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.getFirstToken
|
||||
import com.tangem.tap.domain.tokens.BlockchainNetwork
|
||||
import com.tangem.tap.features.wallet.models.WalletRent
|
||||
import com.tangem.tap.features.wallet.redux.*
|
||||
import com.tangem.tap.features.wallet.redux.AddressData
|
||||
import com.tangem.tap.features.wallet.redux.Artwork
|
||||
import com.tangem.tap.features.wallet.redux.Currency
|
||||
import com.tangem.tap.features.wallet.redux.ErrorType
|
||||
import com.tangem.tap.features.wallet.redux.ProgressState
|
||||
import com.tangem.tap.features.wallet.redux.TradeCryptoState
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletAddresses
|
||||
import com.tangem.tap.features.wallet.redux.WalletData
|
||||
import com.tangem.tap.features.wallet.redux.WalletDialog
|
||||
import com.tangem.tap.features.wallet.redux.WalletMainButton
|
||||
import com.tangem.tap.features.wallet.redux.WalletRent
|
||||
import com.tangem.tap.features.wallet.redux.WalletState
|
||||
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 org.rekotlin.Action
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
import org.rekotlin.Action
|
||||
|
||||
class WalletReducer {
|
||||
companion object {
|
||||
|
|
@ -33,6 +46,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
|
||||
val multiWalletReducer = MultiWalletReducer()
|
||||
val onWalletLoadedReducer = OnWalletLoadedReducer()
|
||||
val appCurrencyReducer = AppCurrencyReducer()
|
||||
|
||||
if (action !is WalletAction) return state.walletState
|
||||
|
||||
|
|
@ -261,7 +275,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
}
|
||||
|
||||
is WalletAction.LoadFiatRate.Success ->
|
||||
newState = setNewFiatRate(action.fiatRate, state.globalState.appCurrency, newState)
|
||||
newState = setNewFiatRate(action.fiatRates, state.globalState.appCurrency, newState)
|
||||
is WalletAction.LoadArtwork -> {
|
||||
val artworkUrl = action.card.getArtworkUrl(action.artworkId)
|
||||
?: when (state.twinCardsState.cardNumber) {
|
||||
|
|
@ -315,6 +329,9 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
val walletsData = walletStore.walletsData.map { it.copy(walletRent = null) }
|
||||
newState = newState.updateWalletsData(walletsData)
|
||||
}
|
||||
is WalletAction.AppCurrencyAction -> {
|
||||
newState = appCurrencyReducer.reduce(action, newState)
|
||||
}
|
||||
else -> { /* no-op */
|
||||
}
|
||||
}
|
||||
|
|
@ -370,52 +387,71 @@ private fun handleCheckSignedHashesActions(
|
|||
|
||||
|
||||
private fun setNewFiatRate(
|
||||
fiatRate: Pair<Currency, BigDecimal?>,
|
||||
fiatRates: Map<Currency, BigDecimal?>,
|
||||
appCurrency: FiatCurrency,
|
||||
state: WalletState
|
||||
): WalletState {
|
||||
val rate = fiatRate.second ?: return state
|
||||
val rateFormatted = rate.toFormattedCurrencyString(
|
||||
decimals = 2,
|
||||
currency = appCurrency.code,
|
||||
roundingMode = RoundingMode.HALF_UP
|
||||
)
|
||||
val currency = fiatRate.first
|
||||
val rateFormatter: (BigDecimal) -> String = { rate: BigDecimal ->
|
||||
rate.toFiatRateString(
|
||||
fiatCurrencyName = appCurrency.symbol
|
||||
)
|
||||
}
|
||||
|
||||
return if (!state.isMultiwalletAllowed) {
|
||||
setSingleWalletFiatRate(rate, rateFormatted, currency, appCurrency, state)
|
||||
return if (state.isMultiwalletAllowed) {
|
||||
setMultiWalletFiatRate(
|
||||
fiatRates = fiatRates.mapNotNullValues { it.value },
|
||||
rateFormatter = rateFormatter,
|
||||
appCurrency = appCurrency,
|
||||
state = state
|
||||
)
|
||||
} else {
|
||||
setMultiWalletFiatRate(rate, rateFormatted, currency, appCurrency, state)
|
||||
val fiatRate = fiatRates.entries.firstOrNull()
|
||||
val currency = fiatRate?.key ?: return state
|
||||
val rate = fiatRate.value ?: return state
|
||||
|
||||
setSingleWalletFiatRate(
|
||||
rate = rate,
|
||||
rateFormatted = rateFormatter(rate),
|
||||
currency = currency,
|
||||
appCurrency = appCurrency,
|
||||
state = state
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setMultiWalletFiatRate(
|
||||
rate: BigDecimal,
|
||||
rateFormatted: String,
|
||||
currency: Currency,
|
||||
fiatRates: Map<Currency, BigDecimal>,
|
||||
rateFormatter: (BigDecimal) -> String,
|
||||
appCurrency: FiatCurrency,
|
||||
state: WalletState
|
||||
): WalletState {
|
||||
val newWalletsData = fiatRates.mapNotNull { (currency, rate) ->
|
||||
val walletStore = state.getWalletStore(currency) ?: return state
|
||||
val wallet = walletStore.walletManager?.wallet
|
||||
val walletData = state.getWalletData(currency) ?: return state
|
||||
|
||||
val walletStore = state.getWalletStore(currency) ?: return state
|
||||
val wallet = walletStore.walletManager?.wallet
|
||||
val walletData = state.getWalletData(currency) ?: return state
|
||||
|
||||
val fiatAmount = when (currency) {
|
||||
is Currency.Blockchain ->
|
||||
wallet?.amounts?.get(AmountType.Coin)?.value?.toFiatValue(rate)
|
||||
is Currency.Token ->
|
||||
wallet?.getTokenAmount(currency.token)?.value?.toFiatValue(rate)
|
||||
val fiatAmount = when (currency) {
|
||||
is Currency.Blockchain ->
|
||||
wallet?.amounts?.get(AmountType.Coin)?.value?.toFiatValue(rate)
|
||||
is Currency.Token ->
|
||||
wallet?.getTokenAmount(currency.token)?.value?.toFiatValue(rate)
|
||||
}
|
||||
val fiatAmountFormatted = fiatAmount?.toFormattedFiatValue(appCurrency.symbol)
|
||||
state.getWalletData(currency)?.copy(
|
||||
currencyData = walletData.currencyData.copy(
|
||||
fiatAmountFormatted = fiatAmountFormatted,
|
||||
fiatAmount = fiatAmount
|
||||
),
|
||||
fiatRate = rate,
|
||||
fiatRateString = rateFormatter(rate)
|
||||
)
|
||||
}
|
||||
val fiatAmountFormatted = fiatAmount?.toFormattedFiatValue(appCurrency.code)
|
||||
val newWalletData = state.getWalletData(currency)?.copy(
|
||||
currencyData = walletData.currencyData.copy(
|
||||
fiatAmountFormatted = fiatAmountFormatted,
|
||||
fiatAmount = fiatAmount
|
||||
),
|
||||
fiatRate = rate, fiatRateString = rateFormatted
|
||||
)
|
||||
return state.updateWalletData(newWalletData)
|
||||
|
||||
return state
|
||||
.updateWalletsData(newWalletsData)
|
||||
.updateTotalBalance(
|
||||
totalBalance = obtainTotalBalance(newWalletsData, appCurrency)
|
||||
)
|
||||
}
|
||||
|
||||
private fun setSingleWalletFiatRate(
|
||||
|
|
|
|||
|
|
@ -178,8 +178,6 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
scanNoteResponse,
|
||||
store.state.walletState.walletManagers.map { it.wallet },
|
||||
CardTou(),
|
||||
store.state.globalState.appCurrency,
|
||||
tangemTechService = store.state.domainNetworks.tangemTechService
|
||||
))
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Details))
|
||||
true
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import com.tangem.blockchain.common.Blockchain
|
|||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.domain.common.TapWorkarounds.derivationStyle
|
||||
import com.tangem.tap.common.extensions.getString
|
||||
import com.tangem.tap.common.extensions.hide
|
||||
import com.tangem.tap.common.extensions.loadCurrenciesIcon
|
||||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.features.wallet.redux.Currency
|
||||
|
|
@ -63,9 +62,7 @@ class WalletAdapter
|
|||
|
||||
fun bind(wallet: WalletData) = with(binding) {
|
||||
tvCurrency.text = wallet.currencyData.currency
|
||||
tvAmount.text = wallet.currencyData.amountFormatted?.takeWhile { !it.isWhitespace() }
|
||||
tvCurrencySymbol.text =
|
||||
wallet.currencyData.amountFormatted?.takeLastWhile { !it.isWhitespace() }
|
||||
tvAmount.text = wallet.currencyData.amountFormatted.orEmpty()
|
||||
tvAmountFiat.text = wallet.currencyData.fiatAmountFormatted
|
||||
tvExchangeRate.text = wallet.fiatRateString
|
||||
cardWallet.setOnClickListener {
|
||||
|
|
@ -74,8 +71,8 @@ class WalletAdapter
|
|||
val blockchain = wallet.currency.blockchain
|
||||
val token = (wallet.currency as? Currency.Token)?.token
|
||||
|
||||
val isCustom =
|
||||
wallet.currency.isCustomCurrency(store.state.globalState.scanResponse?.card?.derivationStyle)
|
||||
val isCustom = wallet.currency
|
||||
.isCustomCurrency(store.state.globalState.scanResponse?.card?.derivationStyle)
|
||||
tvExchangeRate.show(!isCustom)
|
||||
tvCustomCurrency.show(isCustom)
|
||||
|
||||
|
|
@ -86,43 +83,35 @@ class WalletAdapter
|
|||
)
|
||||
|
||||
when (wallet.currencyData.status) {
|
||||
BalanceStatus.VerifiedOnline, BalanceStatus.SameCurrencyTransactionInProgress -> hideWarning(isCustom)
|
||||
BalanceStatus.Loading -> {
|
||||
hideWarning(isCustom)
|
||||
if (wallet.currencyData.amountFormatted == null) {
|
||||
tvExchangeRate.text = root.getString(R.string.wallet_balance_loading)
|
||||
}
|
||||
BalanceStatus.VerifiedOnline,
|
||||
BalanceStatus.SameCurrencyTransactionInProgress -> hideMessage()
|
||||
BalanceStatus.Loading -> if (wallet.currencyData.amountFormatted == null) {
|
||||
showMessage(root.getString(R.string.wallet_balance_loading))
|
||||
}
|
||||
BalanceStatus.TransactionInProgress ->
|
||||
showWarning(root.getString(R.string.wallet_balance_tx_in_progress), isCustom)
|
||||
showMessage(root.getString(R.string.wallet_balance_tx_in_progress))
|
||||
BalanceStatus.Unreachable ->
|
||||
showWarning(root.getString(R.string.wallet_balance_blockchain_unreachable), isCustom)
|
||||
showMessage(root.getString(R.string.wallet_balance_blockchain_unreachable))
|
||||
|
||||
BalanceStatus.NoAccount ->
|
||||
showWarning(root.getString(R.string.wallet_error_no_account), isCustom)
|
||||
showMessage(root.getString(R.string.wallet_error_no_account))
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showWarning(message: String, isCustom: Boolean = false) {
|
||||
toggleWarning(true, isCustom)
|
||||
binding.tvStatusErrorMessage.text = message
|
||||
private fun showMessage(message: String) {
|
||||
toggleMessage(true)
|
||||
binding.tvStatus.text = message
|
||||
}
|
||||
|
||||
private fun hideWarning(isCustom: Boolean = false) {
|
||||
toggleWarning(false, isCustom)
|
||||
private fun hideMessage() {
|
||||
toggleMessage(false)
|
||||
}
|
||||
|
||||
private fun toggleWarning(show: Boolean, isCustom: Boolean = false) {
|
||||
if (!show) {
|
||||
binding.tvExchangeRate.show(!isCustom)
|
||||
binding.tvCustomCurrency.show(isCustom)
|
||||
} else {
|
||||
binding.tvExchangeRate.hide()
|
||||
binding.tvCustomCurrency.hide()
|
||||
}
|
||||
binding.tvStatusErrorMessage.show(show)
|
||||
private fun toggleMessage(show: Boolean) {
|
||||
binding.tvAmount.show(!show)
|
||||
binding.tvStatus.show(show)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package com.tangem.tap.features.wallet.ui.wallet
|
||||
|
||||
import android.content.Context
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.tangem.tap.common.extensions.dispatchDialogHide
|
||||
import com.tangem.tap.common.redux.AppDialog
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
|
||||
object CurrencySelectionDialog {
|
||||
fun create(
|
||||
dialog: AppDialog.CurrencySelectionDialog,
|
||||
context: Context
|
||||
): AlertDialog {
|
||||
val currenciesToShow = dialog.currenciesList
|
||||
.map { it.displayName }
|
||||
.toTypedArray()
|
||||
var currentSelection = dialog.currenciesList
|
||||
.indexOfFirst { it.code == dialog.currentAppCurrency.code }
|
||||
|
||||
return AlertDialog.Builder(context)
|
||||
.setTitle(context.getString(R.string.details_row_title_currency))
|
||||
.setNegativeButton(context.getString(R.string.common_cancel)) { _, _ -> /* no-op */ }
|
||||
.setPositiveButton(context.getString(R.string.common_done)) { _, _ ->
|
||||
val selectedCurrency = dialog.currenciesList[currentSelection]
|
||||
store.dispatch(
|
||||
WalletAction.AppCurrencyAction.SelectAppCurrency(
|
||||
fiatCurrency = selectedCurrency
|
||||
)
|
||||
)
|
||||
}
|
||||
.setOnDismissListener {
|
||||
store.dispatchDialogHide()
|
||||
}
|
||||
.setSingleChoiceItems(currenciesToShow, currentSelection) { _, which ->
|
||||
currentSelection = which
|
||||
}
|
||||
.create()
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,10 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||
import com.tangem.common.card.Card
|
||||
import com.tangem.domain.common.TapWorkarounds.derivationStyle
|
||||
import com.tangem.domain.common.TapWorkarounds.isTestCard
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.extensions.animateVisibility
|
||||
import com.tangem.tap.common.extensions.formatAmountAsSpannedString
|
||||
import com.tangem.tap.common.extensions.hide
|
||||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.common.redux.StateDialog
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
|
|
@ -134,23 +137,16 @@ class MultiWalletView : WalletView {
|
|||
binding: FragmentWalletBinding,
|
||||
totalBalance: TotalBalance,
|
||||
) = with(binding.lCardTotalBalance) {
|
||||
when (totalBalance.state) {
|
||||
TotalBalance.State.Loading -> {
|
||||
pbLoading.showAnimated()
|
||||
tvBalance.hideAnimated(hiddenVisibility = View.INVISIBLE)
|
||||
tvProcessing.hideAnimated()
|
||||
}
|
||||
TotalBalance.State.SomeTokensFailed -> {
|
||||
tvBalance.showAnimated()
|
||||
pbLoading.hideAnimated()
|
||||
tvProcessing.showAnimated()
|
||||
}
|
||||
TotalBalance.State.Success -> {
|
||||
tvBalance.showAnimated()
|
||||
pbLoading.hideAnimated()
|
||||
tvProcessing.hideAnimated()
|
||||
}
|
||||
}
|
||||
tvBalance.animateVisibility(
|
||||
show = totalBalance.state != TotalBalance.State.Loading,
|
||||
hiddenVisibility = View.INVISIBLE
|
||||
)
|
||||
pbLoading.animateVisibility(
|
||||
show = totalBalance.state == TotalBalance.State.Loading
|
||||
)
|
||||
tvProcessing.animateVisibility(
|
||||
show = totalBalance.state == TotalBalance.State.SomeTokensFailed
|
||||
)
|
||||
|
||||
tvBalance.text = totalBalance.fiatAmount.formatAmountAsSpannedString(
|
||||
currencySymbol = totalBalance.fiatCurrency.symbol
|
||||
|
|
@ -158,7 +154,7 @@ class MultiWalletView : WalletView {
|
|||
tvCurrencyName.text = totalBalance.fiatCurrency.code
|
||||
|
||||
tvCurrencyName.setOnClickListener {
|
||||
// TODO: Open app currency selector
|
||||
store.dispatch(WalletAction.AppCurrencyAction.ChooseAppCurrency)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -216,47 +216,20 @@
|
|||
android:background="@color/separatorGrey2"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_settings_title" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_app_currency"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:drawablePadding="15dp"
|
||||
android:textColor="@color/darkGray1"
|
||||
android:textSize="16sp"
|
||||
app:drawableEndCompat="@drawable/ic_angle_bracket_right"
|
||||
app:drawableTint="@color/darkGray1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_settings_title"
|
||||
tools:text="USD" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_app_currency_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="7dp"
|
||||
android:paddingBottom="7dp"
|
||||
android:text="@string/details_row_title_currency"
|
||||
android:textColor="@color/darkGray6"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_settings_title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_disclaimer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawablePadding="15dp"
|
||||
android:paddingTop="7dp"
|
||||
android:paddingBottom="7dp"
|
||||
android:text="@string/disclaimer_title"
|
||||
android:textColor="@color/darkGray6"
|
||||
android:textSize="16sp"
|
||||
app:drawableEndCompat="@drawable/ic_angle_bracket_right"
|
||||
app:drawableTint="@color/darkGray1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_app_currency_title" />
|
||||
android:layout_height="wrap_content"
|
||||
android:drawablePadding="15dp"
|
||||
android:paddingTop="7dp"
|
||||
android:paddingBottom="7dp"
|
||||
android:text="@string/disclaimer_title"
|
||||
android:textColor="@color/darkGray6"
|
||||
android:textSize="16sp"
|
||||
app:drawableEndCompat="@drawable/ic_angle_bracket_right"
|
||||
app:drawableTint="@color/darkGray1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_settings_title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_card_tou"
|
||||
|
|
@ -306,4 +279,4 @@
|
|||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
|
|
|||
|
|
@ -1,205 +1,165 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/card_wallet"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:background="@android:color/white"
|
||||
android:elevation="3dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/card_wallet"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="76dp">
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:background="@android:color/white"
|
||||
android:elevation="3dp">
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:id="@+id/iv_currency"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@drawable/shape_circle" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_token_letter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="25sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="@id/iv_currency"
|
||||
app:layout_constraintEnd_toEndOf="@id/iv_currency"
|
||||
app:layout_constraintStart_toStartOf="@id/iv_currency"
|
||||
app:layout_constraintTop_toTopOf="@id/iv_currency"
|
||||
tools:text="J" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_percent="0.5" />
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp">
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:id="@+id/iv_currency"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@drawable/shape_circle" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_currency"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="2dp"
|
||||
android:textColor="@color/darkGray6"
|
||||
android:textSize="17sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="@id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@+id/tv_amount"
|
||||
app:layout_constraintStart_toEndOf="@id/iv_currency"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Binance Smart Chain Optimal" />
|
||||
android:id="@+id/tv_token_letter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="25sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="@id/iv_currency"
|
||||
app:layout_constraintEnd_toEndOf="@id/iv_currency"
|
||||
app:layout_constraintStart_toStartOf="@id/iv_currency"
|
||||
app:layout_constraintTop_toTopOf="@id/iv_currency"
|
||||
tools:text="J" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintGuide_percent="0.45"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
android:id="@+id/guideline"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_percent="0.5" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_amount"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="end"
|
||||
android:maxEms="12"
|
||||
android:maxLength="12"
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/darkGray6"
|
||||
android:textSize="17sp"
|
||||
android:textStyle="bold"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintEnd_toStartOf="@+id/tv_currency_symbol"
|
||||
app:layout_constraintHorizontal_bias="1.0"
|
||||
app:layout_constraintStart_toStartOf="@+id/guideline2"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="1234567890.1234567890" />
|
||||
android:id="@+id/tv_currency"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/darkGray6"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
app:lineHeight="24dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/guideline"
|
||||
app:layout_constraintStart_toEndOf="@id/iv_currency"
|
||||
app:layout_constraintEnd_toStartOf="@+id/tv_amount"
|
||||
tools:text="Binance Smart Chain Optimal" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintGuide_percent="0.5"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_exchange_rate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:textColor="@color/darkGray2"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/iv_currency"
|
||||
app:layout_constraintTop_toTopOf="@id/guideline"
|
||||
tools:text="USD 3 588" />
|
||||
android:id="@+id/tv_amount_fiat"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxEms="12"
|
||||
android:maxLength="12"
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/darkGray6"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
android:textAlignment="viewEnd"
|
||||
app:lineHeight="24dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/guideline"
|
||||
app:layout_constraintStart_toEndOf="@+id/guideline2"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
tools:text="12300.43 $" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_custom_currency"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingEnd="4dp"
|
||||
android:paddingTop="3dp"
|
||||
android:paddingBottom="3dp"
|
||||
android:textColor="@color/darkGray2"
|
||||
android:textSize="14sp"
|
||||
android:background="@drawable/shape_rectangle_rounded_4"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/iv_currency"
|
||||
app:layout_constraintTop_toTopOf="@id/guideline"
|
||||
android:text="@string/common_custom" />
|
||||
android:id="@+id/tv_amount"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxEms="12"
|
||||
android:maxLength="12"
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/darkGray2"
|
||||
android:textSize="12sp"
|
||||
android:textAlignment="viewEnd"
|
||||
app:lineHeight="20dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/guideline"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline2"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
tools:text="2.002134 BTC" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_status_error_message"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:textColor="@color/warning"
|
||||
android:textSize="14sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/tv_amount_fiat"
|
||||
app:layout_constraintStart_toEndOf="@id/iv_currency"
|
||||
app:layout_constraintTop_toTopOf="@id/guideline"
|
||||
tools:text="@string/lorem_ipsum" />
|
||||
android:id="@+id/tv_status"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
android:textColor="@color/darkGray2"
|
||||
android:textSize="12sp"
|
||||
android:textAlignment="viewEnd"
|
||||
app:lineHeight="20dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/guideline"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline2"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
tools:text="Unreachable..." />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_status_loading"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:drawablePadding="5dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:text="@string/wallet_balance_loading"
|
||||
android:textColor="@color/darkGray6"
|
||||
android:textSize="13sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
android:id="@+id/tv_exchange_rate"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/darkGray2"
|
||||
android:textSize="12sp"
|
||||
app:lineHeight="20dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/guideline"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/iv_currency"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline2"
|
||||
tools:text="46 908 $" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_currency_symbol"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:gravity="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/darkGray6"
|
||||
android:textSize="17sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_weight="1"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="BTC" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_amount_fiat"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:textColor="@color/darkGray2"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/guideline"
|
||||
tools:text="0.43 USD" />
|
||||
android:id="@+id/tv_custom_currency"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:textColor="@color/darkGray2"
|
||||
android:textSize="14sp"
|
||||
android:text="@string/common_custom"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintTop_toBottomOf="@id/guideline"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/iv_currency"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline2" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
<item name="android:windowContentOverlay">@null</item>
|
||||
<item name="actionMenuTextColor">@color/menu_accent_color</item>
|
||||
|
||||
<item name="materialCardViewStyle">@style/Widget.AppTheme.CardView</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme" parent="BaseAppTheme" />
|
||||
|
|
@ -156,6 +157,10 @@
|
|||
<item name="android:textColor">@color/selector_chip_shop_text</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.AppTheme.CardView" parent="Widget.MaterialComponents.CardView">
|
||||
<item name="cardCornerRadius">8dp</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
||||
<!-- android:fontFamily="sans-serif" // roboto regular -->
|
||||
|
|
@ -163,4 +168,4 @@
|
|||
<!-- android:fontFamily="sans-serif-condensed" // roboto condensed -->
|
||||
<!-- android:fontFamily="sans-serif-black" // roboto black -->
|
||||
<!-- android:fontFamily="sans-serif-thin" // roboto thin (android 4.2) -->
|
||||
<!-- android:fontFamily="sans-serif-medium" // roboto medium (android 5.0) -->
|
||||
<!-- android:fontFamily="sans-serif-medium" // roboto medium (android 5.0) -->
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue