diff --git a/src/app/modules/main/wallet_section/controller.nim b/src/app/modules/main/wallet_section/controller.nim index 59e11a1b91..310df385cd 100644 --- a/src/app/modules/main/wallet_section/controller.nim +++ b/src/app/modules/main/wallet_section/controller.nim @@ -43,6 +43,9 @@ proc isMnemonicBackedUp*(self: Controller): bool = proc getCurrencyBalance*(self: Controller): CurrencyAmount = return currencyAmountToItem(self.walletAccountService.getTotalCurrencyBalance(), self.currencyService.getCurrencyFormat(self.getCurrency())) +proc getCurrencyAmount*(self: Controller, amount: float64, symbol: string): CurrencyAmount = + return currencyAmountToItem(amount, self.currencyService.getCurrencyFormat(symbol)) + proc updateCurrency*(self: Controller, currency: string) = self.walletAccountService.updateCurrency(currency) diff --git a/src/app/modules/main/wallet_section/io_interface.nim b/src/app/modules/main/wallet_section/io_interface.nim index fdcbf90bd5..b8d2e304ef 100644 --- a/src/app/modules/main/wallet_section/io_interface.nim +++ b/src/app/modules/main/wallet_section/io_interface.nim @@ -1,3 +1,6 @@ +import ../../shared_models/currency_amount +export CurrencyAmount + type AccessInterface* {.pure inheritable.} = ref object of RootObj ## Abstract class for any input/interaction with this module. @@ -23,6 +26,9 @@ method updateCurrency*(self: AccessInterface, currency: string) {.base.} = method setTotalCurrencyBalance*(self: AccessInterface) {.base.} = raise newException(ValueError, "No implementation available") +method getCurrencyAmount*(self: AccessInterface, amount: float64, symbol: string): CurrencyAmount {.base.} = + raise newException(ValueError, "No implementation available") + # View Delegate Interface # Delegate for the view must be declared here due to use of QtObject and multi # inheritance, which is not well supported in Nim. diff --git a/src/app/modules/main/wallet_section/module.nim b/src/app/modules/main/wallet_section/module.nim index cbed724b1e..7ffbce629c 100644 --- a/src/app/modules/main/wallet_section/module.nim +++ b/src/app/modules/main/wallet_section/module.nim @@ -100,6 +100,9 @@ method switchAccountByAddress*(self: Module, address: string) = method setTotalCurrencyBalance*(self: Module) = self.view.setTotalCurrencyBalance(self.controller.getCurrencyBalance()) +method getCurrencyAmount*(self: Module, amount: float64, symbol: string): CurrencyAmount = + return self.controller.getCurrencyAmount(amount, symbol) + method load*(self: Module) = singletonInstance.engine.setRootContextProperty("walletSection", newQVariant(self.view)) diff --git a/src/app/modules/main/wallet_section/view.nim b/src/app/modules/main/wallet_section/view.nim index b9f91dd723..854a96689a 100644 --- a/src/app/modules/main/wallet_section/view.nim +++ b/src/app/modules/main/wallet_section/view.nim @@ -1,4 +1,4 @@ -import NimQml +import NimQml, json import ./io_interface import ../../shared_models/currency_amount @@ -75,6 +75,13 @@ QtObject: self.currentCurrency = currency self.currentCurrencyChanged() +# Returning a QVariant from a slot with parameters other than "self" won't compile +# proc getCurrencyAmount*(self: View, amount: float, symbol: string): QVariant {.slot.} = +# return newQVariant(self.delegate.getCurrencyAmount(amount, symbol)) + + proc getCurrencyAmountAsJson*(self: View, amount: float, symbol: string): string {.slot.} = + return $self.delegate.getCurrencyAmount(amount, symbol).toJsonNode() + proc setData*(self: View, currency, signingPhrase: string, mnemonicBackedUp: bool) = self.currentCurrency = currency self.signingPhrase = signingPhrase diff --git a/src/app_service/service/token/service.nim b/src/app_service/service/token/service.nim index da25360fff..a2f79cacd2 100644 --- a/src/app_service/service/token/service.nim +++ b/src/app_service/service/token/service.nim @@ -21,6 +21,14 @@ logScope: include async_tasks +const ETHEREUM_SYMBOL = "ETH" +const CRYPTO_SUB_UNITS_TO_FACTOR = { + "WEI": (ETHEREUM_SYMBOL, 1e-18), + "KWEI": (ETHEREUM_SYMBOL, 1e-15), + "MWEI": (ETHEREUM_SYMBOL, 1e-12), + "GWEI": (ETHEREUM_SYMBOL, 1e-9), +}.toTable() + # Signals which may be emitted by this service: const SIGNAL_TOKEN_HISTORICAL_DATA_LOADED* = "tokenHistoricalDataLoaded" const SIGNAL_BALANCE_HISTORY_DATA_READY* = "tokenBalanceHistoryDataReady" @@ -130,26 +138,35 @@ QtObject: proc getTokenPriceCacheKey(crypto: string, fiat: string) : string = return renameSymbol(crypto) & renameSymbol(fiat) + proc getCryptoKeyAndFactor(crypto: string) : (string, float64) = + let cryptoKey = renameSymbol(crypto) + return CRYPTO_SUB_UNITS_TO_FACTOR.getOrDefault(cryptoKey, (cryptoKey, 1.0)) + proc isCachedTokenPriceRecent*(self: Service, crypto: string, fiat: string): bool = - let cacheKey = getTokenPriceCacheKey(crypto, fiat) + let (cryptoKey, _) = getCryptoKeyAndFactor(crypto) + + let cacheKey = getTokenPriceCacheKey(cryptoKey, fiat) return self.priceCache.isCached(cacheKey) proc getCachedTokenPrice*(self: Service, crypto: string, fiat: string): float64 = - let cacheKey = getTokenPriceCacheKey(crypto, fiat) + let (cryptoKey, factor) = getCryptoKeyAndFactor(crypto) + + let cacheKey = getTokenPriceCacheKey(cryptoKey, fiat) if self.priceCache.hasKey(cacheKey): - return self.priceCache.get(cacheKey) + return self.priceCache.get(cacheKey) * factor else: return 0.0 - proc getTokenPrice*(self: Service, crypto: string, fiat: string, fetchIfNotAvailable: bool = true): float64 = - let cacheKey = getTokenPriceCacheKey(crypto, fiat) + proc getTokenPrice*(self: Service, crypto: string, fiat: string): float64 = + let fiatKey = renameSymbol(fiat) + let (cryptoKey, factor) = getCryptoKeyAndFactor(crypto) + + let cacheKey = getTokenPriceCacheKey(cryptoKey, fiatKey) if self.priceCache.isCached(cacheKey): - return self.priceCache.get(cacheKey) + return self.priceCache.get(cacheKey) * factor var prices = initTable[string, Table[string, float]]() try: - let cryptoKey = renameSymbol(crypto) - let fiatKey = renameSymbol(fiat) let response = backend.fetchPrices(@[cryptoKey], @[fiatKey]) for (symbol, pricePerCurrency) in response.result.pairs: prices[symbol] = initTable[string, float]() @@ -157,7 +174,7 @@ QtObject: prices[symbol][currency] = price.getFloat self.updateCachedTokenPrice(cryptoKey, fiatKey, prices[cryptoKey][fiatKey]) - return prices[cryptoKey][fiatKey] + return prices[cryptoKey][fiatKey] * factor except Exception as e: let errDesription = e.msg error "error: ", errDesription diff --git a/ui/StatusQ/src/StatusQ/Core/LocaleUtils.qml b/ui/StatusQ/src/StatusQ/Core/LocaleUtils.qml index aba16cf8e0..12c32b398c 100644 --- a/ui/StatusQ/src/StatusQ/Core/LocaleUtils.qml +++ b/ui/StatusQ/src/StatusQ/Core/LocaleUtils.qml @@ -27,20 +27,28 @@ QtObject { return num.toLocaleString(locale, 'f', precision) } - function currencyAmountToLocaleString(currencyAmount, locale) { - if (!locale) { - console.log("Unspecified locale for: " + JSON.stringify(currencyAmount)) - locale = Qt.locale() + function numberFromLocaleString(num, locale = null) { + locale = locale || Qt.locale() + + return Number.fromLocaleString(locale, num) + } + + function currencyAmountToLocaleString(currencyAmount, options = null, locale = null) { + locale = locale || Qt.locale() + + if (!currencyAmount) { + return "N/A" } if (typeof(currencyAmount) !== "object") { console.log("Wrong type for currencyAmount: " + JSON.stringify(currencyAmount)) + console.trace() return NaN } var amountStr = numberToLocaleString(currencyAmount.amount, currencyAmount.displayDecimals, locale) if (currencyAmount.stripTrailingZeroes) { amountStr = stripTrailingZeroes(amountStr, locale) } - if (currencyAmount.symbol) { + if (currencyAmount.symbol && !(options && options.onlyAmount)) { amountStr = "%1 %2".arg(amountStr).arg(currencyAmount.symbol) } return amountStr diff --git a/ui/app/AppLayouts/Browser/popups/BrowserWalletMenu.qml b/ui/app/AppLayouts/Browser/popups/BrowserWalletMenu.qml index 9e538abea9..5b4896a6e4 100644 --- a/ui/app/AppLayouts/Browser/popups/BrowserWalletMenu.qml +++ b/ui/app/AppLayouts/Browser/popups/BrowserWalletMenu.qml @@ -204,10 +204,12 @@ Popup { AssetsView { id: assetsTab + locale: RootStore.locale account: WalletStore.dappBrowserAccount } HistoryView { id: historyTab + locale: RootStore.locale account: WalletStore.dappBrowserAccount } } diff --git a/ui/app/AppLayouts/Wallet/panels/WalletHeader.qml b/ui/app/AppLayouts/Wallet/panels/WalletHeader.qml index 80dbd75bb6..b0a61516f0 100644 --- a/ui/app/AppLayouts/Wallet/panels/WalletHeader.qml +++ b/ui/app/AppLayouts/Wallet/panels/WalletHeader.qml @@ -46,7 +46,7 @@ Item { font.pixelSize: 28 font.bold: true color: Theme.palette.baseColor1 - text: LocaleUtils.currencyAmountToLocaleString(root.currentAccount.currencyBalance, root.locale) + text: LocaleUtils.currencyAmountToLocaleString(root.currentAccount.currencyBalance) } } diff --git a/ui/app/AppLayouts/Wallet/views/LeftTabView.qml b/ui/app/AppLayouts/Wallet/views/LeftTabView.qml index 400fcfef75..fb2f491bf0 100644 --- a/ui/app/AppLayouts/Wallet/views/LeftTabView.qml +++ b/ui/app/AppLayouts/Wallet/views/LeftTabView.qml @@ -82,7 +82,7 @@ Rectangle { objectName: "walletLeftListAmountValue" color: Style.current.textColor text: { - LocaleUtils.currencyAmountToLocaleString(RootStore.totalCurrencyBalance, RootStore.currencyStore.locale) + LocaleUtils.currencyAmountToLocaleString(RootStore.totalCurrencyBalance) } selectByMouse: true cursorVisible: true @@ -117,7 +117,7 @@ Rectangle { width: ListView.view.width highlighted: RootStore.currentAccount.name === model.name title: model.name - subTitle: LocaleUtils.currencyAmountToLocaleString(model.currencyBalance, RootStore.currencyStore.locale) + subTitle: LocaleUtils.currencyAmountToLocaleString(model.currencyBalance) asset.emoji: !!model.emoji ? model.emoji: "" asset.color: model.color asset.name: !model.emoji ? "filled-account": "" diff --git a/ui/app/AppLayouts/Wallet/views/RightTabView.qml b/ui/app/AppLayouts/Wallet/views/RightTabView.qml index 340170d2bd..7cfc5bf44a 100644 --- a/ui/app/AppLayouts/Wallet/views/RightTabView.qml +++ b/ui/app/AppLayouts/Wallet/views/RightTabView.qml @@ -104,6 +104,7 @@ Item { } } HistoryView { + locale: RootStore.locale account: RootStore.currentAccount onLaunchTransactionDetail: { transactionDetailView.transaction = transaction @@ -127,6 +128,7 @@ Item { } TransactionDetailView { id: transactionDetailView + locale: RootStore.locale Layout.fillWidth: true Layout.fillHeight: true sendModal: root.sendModal diff --git a/ui/imports/shared/controls/AssetDelegate.qml b/ui/imports/shared/controls/AssetDelegate.qml index 12c7ff1cab..dcd9bf2856 100644 --- a/ui/imports/shared/controls/AssetDelegate.qml +++ b/ui/imports/shared/controls/AssetDelegate.qml @@ -54,7 +54,7 @@ Item { anchors.leftMargin: Style.current.smallPadding font.pixelSize: 15 color: Style.current.secondaryText - text: LocaleUtils.currencyAmountToLocaleString(enabledNetworkBalance, root.locale) + text: LocaleUtils.currencyAmountToLocaleString(enabledNetworkBalance) } StyledText { @@ -65,6 +65,6 @@ Item { anchors.rightMargin: 0 font.pixelSize: 15 font.strikeout: false - text: LocaleUtils.currencyAmountToLocaleString(enabledNetworkCurrencyBalance, root.locale) + text: LocaleUtils.currencyAmountToLocaleString(enabledNetworkCurrencyBalance) } } diff --git a/ui/imports/shared/controls/GasSelector.qml b/ui/imports/shared/controls/GasSelector.qml index 1280f36269..7e15eafdaf 100644 --- a/ui/imports/shared/controls/GasSelector.qml +++ b/ui/imports/shared/controls/GasSelector.qml @@ -16,11 +16,12 @@ Item { property string selectedTokenSymbol property string currentCurrency - property string currentCurrencySymbol property var bestRoutes: [] property var getGasEthValue: function () {} property var getFiatValue: function () {} + property var getCurrencyAmount: function () {} + property var locale width: parent.width height: visible ? advancedGasSelector.height + Style.current.halfPadding : 0 @@ -48,19 +49,20 @@ Item { statusListItemIcon.active: true statusListItemIcon.opacity: modelData.isFirstSimpleTx title: qsTr("%1 transaction fee").arg(modelData.fromNetwork.chainName) - subTitle: "%1 eth".arg(LocaleUtils.numberToLocaleString(parseFloat(totalGasAmount))) - property string totalGasAmount : { + subTitle: LocaleUtils.currencyAmountToLocaleString(totalGasAmountEth) + property var totalGasAmountEth: { let maxFees = modelData.gasFees.maxFeePerGasM let gasPrice = modelData.gasFees.eip1559Enabled ? maxFees : modelData.gasFees.gasPrice return root.getGasEthValue(gasPrice , modelData.gasAmount) } + property var totalGasAmountFiat: root.getFiatValue(totalGasAmountEth.amount, "ETH", root.currentCurrency) statusListItemSubTitle.width: listItem.width/2 - Style.current.smallPadding statusListItemSubTitle.elide: Text.ElideMiddle statusListItemSubTitle.wrapMode: Text.NoWrap components: [ StatusBaseText { Layout.alignment: Qt.AlignRight - text: "%1%2".arg(currentCurrencySymbol).arg(LocaleUtils.numberToLocaleString(parseFloat(root.getFiatValue(totalGasAmount, "ETH", root.currentCurrency)))) + text: LocaleUtils.currencyAmountToLocaleString(totalGasAmountFiat) font.pixelSize: 15 color: Theme.palette.baseColor1 width: listItem.width/2 - Style.current.padding @@ -82,7 +84,9 @@ Item { statusListItemIcon.active: true statusListItemIcon.opacity: modelData.isFirstSimpleTx title: qsTr("Approve %1 %2 Bridge").arg(modelData.fromNetwork.chainName).arg(root.selectedTokenSymbol) - subTitle: "%1 eth".arg(LocaleUtils.numberToLocaleString(modelData.approvalGasFees)) + property var approvalGasFees: root.getCurrencyAmount(modelData.approvalGasFees, "ETH") + property var approvalGasFeesFiat: root.getFiatValue(approvalGasFees.amount, "ETH", root.currentCurrency) + subTitle: LocaleUtils.currencyAmountToLocaleString(approvalGasFees) statusListItemSubTitle.width: listItem.width/2 - Style.current.smallPadding statusListItemSubTitle.elide: Text.ElideMiddle statusListItemSubTitle.wrapMode: Text.NoWrap @@ -90,7 +94,7 @@ Item { components: [ StatusBaseText { Layout.alignment: Qt.AlignRight - text: "%1%2".arg(currentCurrencySymbol).arg(LocaleUtils.numberToLocaleString(parseFloat(root.getFiatValue(modelData.approvalGasFees, "ETH", root.currentCurrency)))) + text: LocaleUtils.currencyAmountToLocaleString(approvalGasFeesFiat) font.pixelSize: 15 color: Theme.palette.baseColor1 width: listItem.width/2 - Style.current.padding @@ -113,14 +117,16 @@ Item { statusListItemIcon.active: true statusListItemIcon.opacity: modelData.isFirstBridgeTx title: qsTr("%1 -> %2 bridge").arg(modelData.fromNetwork.chainName).arg(modelData.toNetwork.chainName) - subTitle: "%1 %2".arg(LocaleUtils.numberToLocaleString(modelData.tokenFees)).arg(root.selectedTokenSymbol) + property var tokenFees: root.getCurrencyAmount(modelData.tokenFees, root.selectedTokenSymbol) + property var tokenFeesFiat: root.getFiatValue(tokenFees.amount, root.selectedTokenSymbol, root.currentCurrency) + subTitle: LocaleUtils.currencyAmountToLocaleString(tokenFees) visible: modelData.bridgeName !== "Simple" statusListItemSubTitle.width: 100 statusListItemSubTitle.elide: Text.ElideMiddle components: [ StatusBaseText { Layout.alignment: Qt.AlignRight - text: "%1%2".arg(currentCurrencySymbol).arg(LocaleUtils.numberToLocaleString(parseFloat(root.getFiatValue(modelData.tokenFees, root.selectedTokenSymbol, root.currentCurrency)))) + text: LocaleUtils.currencyAmountToLocaleString(tokenFeesFiat) font.pixelSize: 15 color: Theme.palette.baseColor1 width: listItem2.width/2 - Style.current.padding diff --git a/ui/imports/shared/controls/TokenBalancePerChainDelegate.qml b/ui/imports/shared/controls/TokenBalancePerChainDelegate.qml index 10d55efbc2..abbdb218ff 100644 --- a/ui/imports/shared/controls/TokenBalancePerChainDelegate.qml +++ b/ui/imports/shared/controls/TokenBalancePerChainDelegate.qml @@ -16,7 +16,7 @@ StatusListItem { signal tokenSelected(var selectedToken) title: name - label: LocaleUtils.currencyAmountToLocaleString(enabledNetworkCurrencyBalance, root.locale) + label: LocaleUtils.currencyAmountToLocaleString(enabledNetworkCurrencyBalance) asset.name: symbol ? Style.png("tokens/" + symbol) : "" asset.isImage: true asset.width: 32 @@ -42,7 +42,7 @@ StatusListItem { id: expandedItem StatusListItemTag { height: 16 - title: LocaleUtils.currencyAmountToLocaleString(balance, root.locale) + title: LocaleUtils.currencyAmountToLocaleString(balance) titleText.font.pixelSize: 12 closeButtonVisible: false bgColor: "transparent" diff --git a/ui/imports/shared/controls/TokenDelegate.qml b/ui/imports/shared/controls/TokenDelegate.qml index 79e447e778..5e3838ac4e 100644 --- a/ui/imports/shared/controls/TokenDelegate.qml +++ b/ui/imports/shared/controls/TokenDelegate.qml @@ -12,7 +12,7 @@ StatusListItem { id: root property var locale title: name - subTitle: LocaleUtils.currencyAmountToLocaleString(enabledNetworkBalance, root.locale) + subTitle: LocaleUtils.currencyAmountToLocaleString(enabledNetworkBalance) asset.name: symbol ? Style.png("tokens/" + symbol) : "" asset.isImage: true components: [ @@ -25,7 +25,7 @@ StatusListItem { anchors.right: parent.right font.pixelSize: 15 font.strikeout: false - text: LocaleUtils.currencyAmountToLocaleString(enabledNetworkCurrencyBalance, root.locale) + text: LocaleUtils.currencyAmountToLocaleString(enabledNetworkCurrencyBalance) } Row { anchors.horizontalCenter: parent.horizontalCenter @@ -35,7 +35,7 @@ StatusListItem { font.pixelSize: 15 font.strikeout: false color: valueColumn.textColor - text: LocaleUtils.currencyAmountToLocaleString(currencyPrice, root.locale) + text: LocaleUtils.currencyAmountToLocaleString(currencyPrice) } Rectangle { width: 1 diff --git a/ui/imports/shared/controls/TransactionDelegate.qml b/ui/imports/shared/controls/TransactionDelegate.qml index 0a0ef515ab..bd1c71d386 100644 --- a/ui/imports/shared/controls/TransactionDelegate.qml +++ b/ui/imports/shared/controls/TransactionDelegate.qml @@ -11,13 +11,13 @@ import shared 1.0 StatusListItem { id: root + property var locale property var modelData property string symbol property bool isIncoming property int transferStatus - property string currentCurrency - property string cryptoValue - property string fiatValue + property var cryptoValue + property var fiatValue property string networkIcon property string networkColor property string networkName @@ -62,13 +62,13 @@ StatusListItem { } StatusBaseText { id: cryptoValueText - text: "%1 %2".arg(cryptoValue).arg(resolvedSymbol) + text: LocaleUtils.currencyAmountToLocaleString(cryptoValue) color: Theme.palette.directColor1 } } StatusBaseText { Layout.alignment: Qt.AlignRight - text: "%1 %2".arg(fiatValue).arg(currentCurrency.toUpperCase()) + text: LocaleUtils.currencyAmountToLocaleString(fiatValue) font.pixelSize: 15 color: Theme.palette.baseColor1 } diff --git a/ui/imports/shared/popups/AccountsModalHeader.qml b/ui/imports/shared/popups/AccountsModalHeader.qml index b4de66cb2c..412443819b 100644 --- a/ui/imports/shared/popups/AccountsModalHeader.qml +++ b/ui/imports/shared/popups/AccountsModalHeader.qml @@ -89,7 +89,7 @@ StatusFloatingButtonsSelector { popupMenuDelegate: StatusListItem { implicitWidth: 272 title: name - subTitle: LocaleUtils.currencyAmountToLocaleString(currencyBalance, locale) + subTitle: LocaleUtils.currencyAmountToLocaleString(currencyBalance) asset.emoji: !!emoji ? emoji: "" asset.color: model.color asset.name: !emoji ? "filled-account": "" diff --git a/ui/imports/shared/popups/SendModal.qml b/ui/imports/shared/popups/SendModal.qml index 0e49f78c00..9551304e59 100644 --- a/ui/imports/shared/popups/SendModal.qml +++ b/ui/imports/shared/popups/SendModal.qml @@ -33,6 +33,7 @@ StatusDialog { property var store: TransactionStore{} property var contactsStore: store.contactStore + property var currencyStore: store.currencyStore property var selectedAccount: store.currentAccount property var bestRoutes property string addressText @@ -60,7 +61,7 @@ StatusDialog { popup.selectedAccount.address, recipientAddress, assetSelector.selectedAsset.symbol, - amountToSendInput.cryptoValueToSend, + amountToSendInput.cryptoValueToSend.amount, d.uuid, JSON.stringify(popup.bestRoutes) ) @@ -69,7 +70,7 @@ StatusDialog { property var recalculateRoutesAndFees: Backpressure.debounce(popup, 600, function() { if(!!popup.selectedAccount && !!assetSelector.selectedAsset && d.recipientReady && amountToSendInput.input.valid) { popup.isLoading = true - let amount = Math.round(parseFloat(amountToSendInput.cryptoValueToSend) * Math.pow(10, assetSelector.selectedAsset.decimals)) + let amount = Math.round(amountToSendInput.cryptoValueToSend.amount * Math.pow(10, assetSelector.selectedAsset.decimals)) popup.store.suggestedRoutes(popup.selectedAccount.address, amount.toString(16), assetSelector.selectedAsset.symbol, store.disabledChainIdsFromList, store.disabledChainIdsToList, store.preferredChainIds, popup.sendType, store.lockedInAmounts) @@ -81,7 +82,7 @@ StatusDialog { readonly property int errorType: !amountToSendInput.input.valid ? Constants.SendAmountExceedsBalance : (networkSelector.bestRoutes && networkSelector.bestRoutes.length <= 0 && !!amountToSendInput.input.text && recipientReady && !popup.isLoading) ? Constants.NoRoute : Constants.NoError - readonly property var maxFiatBalance: !!assetSelector.selectedAsset ? (amountToSendInput.cryptoFiatFlipped ? + readonly property var maxFiatBalance: !!assetSelector.selectedAsset ? (amountToSendInput.inputIsFiat ? assetSelector.selectedAsset.totalCurrencyBalance : assetSelector.selectedAsset.totalBalance): undefined readonly property bool errorMode: popup.isLoading || !recipientReady ? false : errorType !== Constants.NoError || networkSelector.errorMode || isNaN(amountToSendInput.input.text) @@ -95,9 +96,9 @@ StatusDialog { readonly property string uuid: Utils.uuid() property bool isPendingTx: false property string totalTimeEstimate - property string totalFeesInEth - property string totalFeesInFiat - property double totalAmountToReceive: 0 + property var totalFeesInEth + property var totalFeesInFiat + property var totalAmountToReceive property Timer waitTimer: Timer { interval: 1500 @@ -156,6 +157,7 @@ StatusDialog { header: AccountsModalHeader { anchors.top: parent.top anchors.topMargin: -height - 18 + locale: popup.store.locale model: popup.store.accounts selectedAccount: popup.selectedAccount changeSelectedAccount: function(newAccount, newIndex) { @@ -244,7 +246,7 @@ StatusDialog { StatusListItemTag { Layout.alignment: Qt.AlignVCenter | Qt.AlignRight Layout.preferredHeight: 22 - title: d.maxFiatBalance && d.maxFiatBalance.amount > 0 ? qsTr("Max: %1").arg(LocaleUtils.currencyAmountToLocaleString(d.maxFiatBalance, popup.store.locale)) : qsTr("No balances active") + title: d.maxFiatBalance && d.maxFiatBalance.amount > 0 ? qsTr("Max: %1").arg(LocaleUtils.currencyAmountToLocaleString(d.maxFiatBalance)) : qsTr("No balances active") closeButtonVisible: false titleText.font.pixelSize: 12 bgColor: amountToSendInput.input.valid ? Theme.palette.primaryColor3 : Theme.palette.dangerColor2 @@ -263,10 +265,16 @@ StatusDialog { maxFiatBalance: d.maxFiatBalance currentCurrency: popup.store.currentCurrency getFiatValue: function(cryptoValue) { - return popup.store.getFiatValue(cryptoValue, selectedAsset.symbol, currentCurrency) + return selectedAsset ? popup.currencyStore.getFiatValue(cryptoValue, selectedAsset.symbol, currentCurrency) : undefined } getCryptoValue: function(fiatValue) { - return popup.store.getFiatValue(fiatValue, selectedAsset.symbol, currentCurrency) + return selectedAsset ? popup.currencyStore.getCryptoValue(fiatValue, selectedAsset.symbol, currentCurrency) : undefined + } + getFiatCurrencyAmount: function(fiatValue) { + return popup.currencyStore.getCurrencyAmount(fiatValue, currentCurrency) + } + getCryptoCurrencyAmount: function(cryptoValue) { + return selectedAsset ? popup.currencyStore.getCurrencyAmount(cryptoValue, selectedAsset.symbol) : undefined } onReCalculateSuggestedRoute: popup.recalculateRoutesAndFees() } @@ -280,8 +288,12 @@ StatusDialog { isLoading: popup.isLoading selectedAsset: assetSelector.selectedAsset isBridgeTx: popup.isBridgeTx - amountToReceive: d.totalAmountToReceive - cryptoFiatFlipped: amountToSendInput.cryptoFiatFlipped + cryptoValueToReceive: d.totalAmountToReceive + inputIsFiat: amountToSendInput.inputIsFiat + currentCurrency: popup.store.currentCurrency + getFiatValue: function(cryptoValue) { + return popup.currencyStore.getFiatValue(cryptoValue, selectedAsset.symbol, currentCurrency) + } } } TokenListView { @@ -416,8 +428,8 @@ StatusDialog { store: popup.store interactive: popup.interactive selectedAccount: popup.selectedAccount - amountToSend: isNaN(parseFloat(amountToSendInput.cryptoValueToSend)) ? 0 : parseFloat(amountToSendInput.cryptoValueToSend) - requiredGasInEth:d.totalFeesInEth + amountToSend: amountToSendInput.cryptoValueToSend + requiredGasInEth: d.totalFeesInEth selectedAsset: assetSelector.selectedAsset onReCalculateSuggestedRoute: popup.recalculateRoutesAndFees() visible: d.recipientReady && !!assetSelector.selectedAsset @@ -449,10 +461,10 @@ StatusDialog { footer: SendModalFooter { nextButtonText: popup.isBridgeTx ? qsTr("Bridge") : qsTr("Send") - maxFiatFees: popup.isLoading ? "..." : "%1 %2".arg(LocaleUtils.numberToLocaleString(d.totalFeesInFiat, -1, popup.store.locale)).arg(popup.store.currentCurrency.toUpperCase()) + maxFiatFees: popup.isLoading ? "..." : LocaleUtils.currencyAmountToLocaleString(d.totalFeesInFiat) totalTimeEstimate: popup.isLoading? "..." : d.totalTimeEstimate pending: d.isPendingTx || popup.isLoading - visible: d.recipientReady && !isNaN(amountToSendInput.cryptoValueToSend) && !d.errorMode + visible: d.recipientReady && amountToSendInput.cryptoValueToSend.amount > 0 && !d.errorMode onNextButtonClicked: popup.sendTransaction() } @@ -472,10 +484,11 @@ StatusDialog { popup.bestRoutes = response.suggestedRoutes.best let gasTimeEstimate = response.suggestedRoutes.gasTimeEstimate d.totalTimeEstimate = popup.store.getLabelForEstimatedTxTime(gasTimeEstimate.totalTime) - d.totalFeesInEth = gasTimeEstimate.totalFeesInEth - d.totalFeesInFiat = parseFloat(popup.store.getFiatValue( gasTimeEstimate.totalFeesInEth, "ETH", popup.store.currentCurrency)) + - parseFloat(popup.store.getFiatValue(gasTimeEstimate.totalTokenFees, fees.selectedTokenSymbol, popup.store.currentCurrency)) - d.totalAmountToReceive = popup.store.getWei2Eth(response.suggestedRoutes.amountToReceive, assetSelector.selectedAsset.decimals) + d.totalFeesInEth = popup.currencyStore.getCurrencyAmount(gasTimeEstimate.totalFeesInEth, "ETH") + let totalFeesInFiat = popup.currencyStore.getFiatValue( gasTimeEstimate.totalFeesInEth, "ETH", popup.store.currentCurrency).amount + + popup.currencyStore.getFiatValue(gasTimeEstimate.totalTokenFees, fees.selectedTokenSymbol, popup.store.currentCurrency).amount + d.totalFeesInFiat = popup.currencyStore.getCurrencyAmount(totalFeesInFiat, popup.store.currentCurrency) + d.totalAmountToReceive = popup.currencyStore.getCurrencyAmount(popup.store.getWei2Eth(response.suggestedRoutes.amountToReceive, assetSelector.selectedAsset.decimals), fees.selectedTokenSymbol) networkSelector.toNetworksList = response.suggestedRoutes.toNetworks popup.isLoading = false } diff --git a/ui/imports/shared/stores/CurrenciesStore.qml b/ui/imports/shared/stores/CurrenciesStore.qml index a182a046ed..898232ead6 100644 --- a/ui/imports/shared/stores/CurrenciesStore.qml +++ b/ui/imports/shared/stores/CurrenciesStore.qml @@ -1,11 +1,16 @@ import QtQuick 2.13 import utils 1.0 +import "../../../app/AppLayouts/Profile/stores" QtObject { id: root property var locale: Qt.locale(localAppSettings.language) + // Some token+currency-related functions are implemented in the profileSectionModule. + // We should probably refactor this and move those functions to some Wallet module. + property ProfileSectionStore profileSectionStore: ProfileSectionStore {} + property string currentCurrency: walletSection.currentCurrency property int currentCurrencyModelIndex: { for (var i=0; i lockedItem !== undefined && lockedItem.chainID === chainID) if(index === -1) { lockedInAmounts.push({"chainID": chainID, "value": amount.toString(16)}) @@ -253,18 +241,6 @@ QtObject { return undefined } - const jsonObj = selectedAccount.getTokenBalanceOnChainAsJson(chainId, tokenSymbol) - if (jsonObj === "") { - console.warn("failed to get balance, returned json is empty") - return undefined - } - - const obj = JSON.parse(jsonObj) - if (obj.error) { - console.warn("failed to get balance, json parse error: ", obj.error) - return undefined - } - - return obj + return JSON.parse(selectedAccount.getTokenBalanceOnChainAsJson(chainId, tokenSymbol)) } } diff --git a/ui/imports/shared/views/AmountToReceive.qml b/ui/imports/shared/views/AmountToReceive.qml index 44c5345389..5465ebdd76 100644 --- a/ui/imports/shared/views/AmountToReceive.qml +++ b/ui/imports/shared/views/AmountToReceive.qml @@ -5,6 +5,7 @@ import StatusQ.Core 0.1 import StatusQ.Core.Theme 0.1 import utils 1.0 +import shared.stores 1.0 ColumnLayout { id: root @@ -13,21 +14,24 @@ ColumnLayout { property var store property var selectedAsset property bool isLoading: false - property string amountToReceive + property var cryptoValueToReceive property bool isBridgeTx: false - property bool cryptoFiatFlipped: false + property bool inputIsFiat: false + property string currentCurrency + property var getFiatValue: function(cryptoValue) {} QtObject { id: d - function formatValue(value) { - const precision = (value === 0 ? 2 : 0) - return LocaleUtils.numberToLocaleString(value, precision, root.locale) - } readonly property string fiatValue: { - if(!root.selectedAsset || !amountToReceive) - return formatValue(0) - let cryptoValue = root.store.getFiatValue(amountToReceive, root.selectedAsset.symbol, root.store.currentCurrency) - return formatValue(parseFloat(cryptoValue)) + if(!root.selectedAsset || !cryptoValueToReceive) + return LocaleUtils.numberToLocaleString(0, 2) + let fiatValue = root.getFiatValue(cryptoValueToReceive.amount, root.selectedAsset.symbol, RootStore.currentCurrency) + return LocaleUtils.currencyAmountToLocaleString(fiatValue) + } + readonly property string cryptoValue: { + if(!root.selectedAsset || !cryptoValueToReceive) + return LocaleUtils.numberToLocaleString(0, 2) + return LocaleUtils.currencyAmountToLocaleString(cryptoValueToReceive) } } @@ -45,31 +49,17 @@ ColumnLayout { StatusBaseText { id: amountToReceiveText Layout.alignment: Qt.AlignVCenter - text: isLoading ? "..." : cryptoFiatFlipped ? d.fiatValue: amountToReceive + text: isLoading ? "..." : inputIsFiat ? d.fiatValue : d.cryptoValue font.pixelSize: Utils.getFontSizeBasedOnLetterCount(text) color: Theme.palette.directColor1 } - StatusBaseText { - Layout.alignment: Qt.AlignVCenter - text: isLoading ? "..." : root.store.currentCurrency.toUpperCase() - font.pixelSize: amountToReceiveText.font.pixelSize - color: Theme.palette.directColor1 - visible: cryptoFiatFlipped - } } RowLayout { Layout.alignment: Qt.AlignRight | Qt.AlignBottom StatusBaseText { id: txtFiatBalance Layout.alignment: Qt.AlignLeft | Qt.AlignTop - text: isLoading ? "..." : cryptoFiatFlipped ? amountToReceive : d.fiatValue - font.pixelSize: 13 - color: Theme.palette.directColor5 - } - StatusBaseText { - Layout.alignment: Qt.AlignTop - Layout.leftMargin: 4 - text: isLoading ? "..." : !cryptoFiatFlipped ? root.store.currentCurrency.toUpperCase() : !!root.selectedAsset ? root.selectedAsset.symbol.toUpperCase() : "" + text: isLoading ? "..." : inputIsFiat ? d.cryptoValue : d.fiatValue font.pixelSize: 13 color: Theme.palette.directColor5 } diff --git a/ui/imports/shared/views/AmountToSend.qml b/ui/imports/shared/views/AmountToSend.qml index c5758fddf1..f372ea4c70 100644 --- a/ui/imports/shared/views/AmountToSend.qml +++ b/ui/imports/shared/views/AmountToSend.qml @@ -12,54 +12,54 @@ import utils 1.0 ColumnLayout { id: root - property alias input: amountToSendInput + property alias input: topAmountToSendInput property var locale property var selectedAsset property bool isBridgeTx: false property bool interactive: false property var maxFiatBalance - property bool cryptoFiatFlipped: false - property string cryptoValueToSend: !cryptoFiatFlipped ? amountToSendInput.text : txtFiatBalance.text + property bool inputIsFiat: false + property var cryptoValueToSend + Binding { + target: root + property: "cryptoValueToSend" + value: root.selectedAsset, !inputIsFiat ? getCryptoCurrencyAmount(LocaleUtils.numberFromLocaleString(topAmountToSendInput.text)) : getCryptoValue(fiatValueToSend ? fiatValueToSend.amount : 0.0) + delayed: true + } + property var fiatValueToSend + Binding { + target: root + property: "fiatValueToSend" + value: root.selectedAsset, inputIsFiat ? getFiatCurrencyAmount(LocaleUtils.numberFromLocaleString(topAmountToSendInput.text)) : getFiatValue(cryptoValueToSend ? cryptoValueToSend.amount : 0.0) + delayed: true + } property string currentCurrency property var getFiatValue: function(cryptoValue) {} property var getCryptoValue: function(fiatValue) {} + property var getFiatCurrencyAmount: function(fiatValue) {} + property var getCryptoCurrencyAmount: function(cryptoValue) {} signal reCalculateSuggestedRoute() QtObject { id: d - readonly property string zeroString: formatValue(0, 2) + readonly property string zeroString: LocaleUtils.numberToLocaleString(0, 2) property Timer waitTimer: Timer { interval: 1000 onTriggered: reCalculateSuggestedRoute() } - function formatValue(value, precision) { - const precisionVal = !!precision ? precision : (value === 0 ? 2 : 0) - return LocaleUtils.numberToLocaleString(value, precisionVal, root.locale) - } - function getFiatValue(value) { - if(!root.selectedAsset || !value) - return zeroString - let cryptoValue = root.getFiatValue(value) - return formatValue(parseFloat(cryptoValue)) - } - function getCryptoValue(value) { - if(!root.selectedAsset || !value) - return zeroString - let cryptoValue = root.getCryptoValue(value) - return formatValue(parseFloat(cryptoValue)) - } - } - onSelectedAssetChanged: { - if(!!root.selectedAsset) { - txtFiatBalance.text = !cryptoFiatFlipped ? d.getFiatValue(amountToSendInput.text): d.getCryptoValue(amountToSendInput.text) + function formatValue(value) { + if (!value) { + return zeroString + } + return LocaleUtils.currencyAmountToLocaleString(value) } } onMaxFiatBalanceChanged: { - floatValidator.top = maxFiatBalance.amount + floatValidator.top = maxFiatBalance ? maxFiatBalance.amount : 0.0 input.validate() } @@ -72,9 +72,11 @@ ColumnLayout { color: Theme.palette.directColor1 } RowLayout { + id: topItem + property var topAmountToSend: !inputIsFiat ? cryptoValueToSend : fiatValueToSend Layout.alignment: Qt.AlignLeft AmountInputWithCursor { - id: amountToSendInput + id: topAmountToSendInput Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft Layout.maximumWidth: 163 Layout.preferredWidth: (!!text) ? input.edit.paintedWidth : textMetrics.advanceWidth @@ -91,45 +93,29 @@ ColumnLayout { ] TextMetrics { id: textMetrics - text: amountToSendInput.placeholderText - font: amountToSendInput.input.placeholder.font + text: topAmountToSendInput.placeholderText + font: topAmountToSendInput.input.placeholder.font } Keys.onReleased: { - const amount = amountToSendInput.text.trim() + const amount = topAmountToSendInput.text.trim() if (!Utils.containsOnlyDigits(amount) || isNaN(amount)) { return } - txtFiatBalance.text = !cryptoFiatFlipped ? d.getFiatValue(amount): d.getCryptoValue(amount) d.waitTimer.restart() } } - StatusBaseText { - Layout.alignment: Qt.AlignVCenter - text: root.currentCurrency.toUpperCase() - font.pixelSize: amountToSendInput.input.edit.font.pixelSize - color: Theme.palette.baseColor1 - visible: cryptoFiatFlipped - } } Item { - id: fiatBalanceLayout + id: bottomItem + property var bottomAmountToSend: inputIsFiat ? cryptoValueToSend : fiatValueToSend Layout.alignment: Qt.AlignLeft | Qt.AlignBottom - Layout.preferredWidth: txtFiatBalance.width + currencyText.width - Layout.preferredHeight: txtFiatBalance.height + Layout.preferredWidth: txtBottom.width + Layout.preferredHeight: txtBottom.height StatusBaseText { - id: txtFiatBalance + id: txtBottom anchors.top: parent.top anchors.left: parent.left - text: d.getFiatValue(amountToSendInput.text) - font.pixelSize: 13 - color: Theme.palette.directColor5 - } - StatusBaseText { - id: currencyText - anchors.top: parent.top - anchors.left: txtFiatBalance.right - anchors.leftMargin: 4 - text: !cryptoFiatFlipped ? root.currentCurrency.toUpperCase() : !!root.selectedAsset ? root.selectedAsset.symbol.toUpperCase() : "" + text: d.formatValue(bottomItem.bottomAmountToSend) font.pixelSize: 13 color: Theme.palette.directColor5 } @@ -137,13 +123,12 @@ ColumnLayout { anchors.fill: parent cursorShape: Qt.PointingHandCursor onClicked: { - cryptoFiatFlipped = !cryptoFiatFlipped - amountToSendInput.validate() - if(!!amountToSendInput.text) { - const tempVal = Number.fromLocaleString(txtFiatBalance.text) - txtFiatBalance.text = !!amountToSendInput.text ? amountToSendInput.text : d.zeroString - amountToSendInput.text = tempVal + topAmountToSendInput.validate() + if(!!topAmountToSendInput.text) { + topAmountToSendInput.text = LocaleUtils.currencyAmountToLocaleString(bottomItem.bottomAmountToSend, {onlyAmount: true}) } + inputIsFiat = !inputIsFiat + d.waitTimer.restart() } } } diff --git a/ui/imports/shared/views/AssetsDetailView.qml b/ui/imports/shared/views/AssetsDetailView.qml index 24fccb1554..a4e1fce350 100644 --- a/ui/imports/shared/views/AssetsDetailView.qml +++ b/ui/imports/shared/views/AssetsDetailView.qml @@ -58,8 +58,8 @@ Item { asset.name: token && token.symbol ? Style.png("tokens/%1".arg(token.symbol)) : "" asset.isImage: true primaryText: token ? token.name : "" - secondaryText: token ? LocaleUtils.currencyAmountToLocaleString(token.enabledNetworkBalance, RootStore.locale) : "" - tertiaryText: token ? LocaleUtils.currencyAmountToLocaleString(token.enabledNetworkCurrencyBalance, RootStore.locale) : "" + secondaryText: token ? LocaleUtils.currencyAmountToLocaleString(token.enabledNetworkBalance) : "" + tertiaryText: token ? LocaleUtils.currencyAmountToLocaleString(token.enabledNetworkCurrencyBalance) : "" balances: token && token.balances ? token.balances : null getNetworkColor: function(chainId){ return RootStore.getNetworkColor(chainId) @@ -68,7 +68,7 @@ Item { return RootStore.getNetworkIcon(chainId) } formatBalance: function(balance){ - return LocaleUtils.currencyAmountToLocaleString(balance, RootStore.locale) + return LocaleUtils.currencyAmountToLocaleString(balance) } } @@ -228,7 +228,7 @@ Item { fontColor: (Theme.palette.name === "dark") ? '#909090' : '#939BA1', padding: 8, callback: function(value, index, ticks) { - return LocaleUtils.numberToLocaleString(value, -1, RootStore.locale) + return LocaleUtils.numberToLocaleString(value) }, } }] @@ -267,17 +267,17 @@ Item { InformationTile { maxWidth: parent.width primaryText: qsTr("Market Cap") - secondaryText: token && token.marketCap ? LocaleUtils.currencyAmountToLocaleString(token.marketCap, RootStore.locale) : "---" + secondaryText: token && token.marketCap ? LocaleUtils.currencyAmountToLocaleString(token.marketCap) : "---" } InformationTile { maxWidth: parent.width primaryText: qsTr("Day Low") - secondaryText: token && token.lowDay ? LocaleUtils.currencyAmountToLocaleString(token.lowDay, RootStore.locale) : "---" + secondaryText: token && token.lowDay ? LocaleUtils.currencyAmountToLocaleString(token.lowDay) : "---" } InformationTile { maxWidth: parent.width primaryText: qsTr("Day High") - secondaryText: token && token.highDay ? LocaleUtils.currencyAmountToLocaleString(token.highDay, RootStore.locale) : "---" + secondaryText: token && token.highDay ? LocaleUtils.currencyAmountToLocaleString(token.highDay) : "---" } Item { Layout.fillWidth: true diff --git a/ui/imports/shared/views/FeesView.qml b/ui/imports/shared/views/FeesView.qml index 1d5e01e6b3..f315e07b42 100644 --- a/ui/imports/shared/views/FeesView.qml +++ b/ui/imports/shared/views/FeesView.qml @@ -7,15 +7,18 @@ import StatusQ.Core.Theme 0.1 import utils 1.0 +import shared.stores 1.0 + import "../controls" Rectangle { id: root - property string gasFiatAmount + property var gasFiatAmount property bool isLoading: false property var bestRoutes property var store + property var currencyStore: store.currencyStore property var selectedTokenSymbol property int errorType: Constants.NoError @@ -56,7 +59,7 @@ Rectangle { anchors.right: parent.right anchors.rightMargin: Style.current.padding id: totalFeesAdvanced - text: root.isLoading ? "..." : root.gasFiatAmount + text: root.isLoading ? "..." : LocaleUtils.currencyAmountToLocaleString(root.gasFiatAmount) font.pixelSize: 15 color: Theme.palette.directColor1 visible: !!root.bestRoutes && root.bestRoutes !== undefined && root.bestRoutes.length > 0 @@ -64,11 +67,12 @@ Rectangle { } GasSelector { id: gasSelector + locale: root.store.locale width: parent.width - getGasEthValue: root.store.getGasEthValue - getFiatValue: root.store.getFiatValue - currentCurrency: root.store.currencyStore.currentCurrency - currentCurrencySymbol: root.store.currencyStore.currentCurrencySymbol + getGasEthValue: root.currencyStore.getGasEthValue + getFiatValue: root.currencyStore.getFiatValue + getCurrencyAmount: root.currencyStore.getCurrencyAmount + currentCurrency: root.currencyStore.currentCurrency visible: root.errorType === Constants.NoError && !root.isLoading bestRoutes: root.bestRoutes selectedTokenSymbol: root.selectedTokenSymbol diff --git a/ui/imports/shared/views/HistoryView.qml b/ui/imports/shared/views/HistoryView.qml index 84f8775db2..fc5f8abdb8 100644 --- a/ui/imports/shared/views/HistoryView.qml +++ b/ui/imports/shared/views/HistoryView.qml @@ -17,6 +17,7 @@ import "../controls" ColumnLayout { id: historyView + property var locale property var account property int pageSize: 20 // number of transactions per page property bool isLoading: false @@ -113,17 +114,18 @@ ColumnLayout { Component { id: transactionDelegate TransactionDelegate { - isIncoming: modelData !== undefined && !!modelData ? modelData.to === account.address: false - currentCurrency: RootStore.currentCurrency - cryptoValue: modelData !== undefined && !!modelData ? RootStore.hex2Eth(modelData.value) : "" + locale: historyView.locale + property bool modelDataValid: modelData !== undefined && !!modelData + isIncoming: modelDataValid ? modelData.to === account.address: false + cryptoValue: modelDataValid ? RootStore.getCurrencyAmount(RootStore.hex2Eth(modelData.value), resolvedSymbol) : "" fiatValue: RootStore.getFiatValue(cryptoValue, resolvedSymbol, RootStore.currentCurrency) - networkIcon: modelData !== undefined && !!modelData ? RootStore.getNetworkIcon(modelData.chainId) : "" - networkColor: modelData !== undefined && !!modelData ? RootStore.getNetworkColor(modelData.chainId) : "" - networkName: modelData !== undefined && !!modelData ? RootStore.getNetworkShortName(modelData.chainId) : "" - symbol: modelData !== undefined && !!modelData ? !!modelData.symbol ? modelData.symbol : RootStore.findTokenSymbolByAddress(modelData.contract) : "" - transferStatus: modelData !== undefined && !!modelData ? RootStore.hex2Dec(modelData.txStatus) : "" - shortTimeStamp: modelData !== undefined && !!modelData ? LocaleUtils.formatTime(modelData.timestamp * 1000, Locale.ShortFormat) : "" - savedAddressName: modelData !== undefined && !!modelData ? RootStore.getNameForSavedWalletAddress(modelData.to) : "" + networkIcon: modelDataValid ? RootStore.getNetworkIcon(modelData.chainId) : "" + networkColor: modelDataValid ? RootStore.getNetworkColor(modelData.chainId) : "" + networkName: modelDataValid ? RootStore.getNetworkShortName(modelData.chainId) : "" + symbol: modelDataValid ? !!modelData.symbol ? modelData.symbol : RootStore.findTokenSymbolByAddress(modelData.contract) : "" + transferStatus: modelDataValid ? RootStore.hex2Dec(modelData.txStatus) : "" + shortTimeStamp: modelDataValid ? LocaleUtils.formatTime(modelData.timestamp * 1000, Locale.ShortFormat) : "" + savedAddressName: modelDataValid ? RootStore.getNameForSavedWalletAddress(modelData.to) : "" onClicked: launchTransactionDetail(modelData) } } diff --git a/ui/imports/shared/views/NetworkCardsComponent.qml b/ui/imports/shared/views/NetworkCardsComponent.qml index 0c143bdbb6..f37b832f80 100644 --- a/ui/imports/shared/views/NetworkCardsComponent.qml +++ b/ui/imports/shared/views/NetworkCardsComponent.qml @@ -16,18 +16,20 @@ Item { id: root property var store + property var locale property var bestRoutes property var selectedAccount property var selectedAsset property var allNetworks property bool customMode: false - property double amountToSend: 0 - property double requiredGasInEth: 0 - property bool errorMode: d.customAmountToSend > root.amountToSend + property var amountToSend + property var requiredGasInEth + property bool errorMode: !root.amountToSend || d.customAmountToSend > root.amountToSend.amount property bool interactive: true property bool showPreferredChains: false property var weiToEth: function(wei) {} property var reCalculateSuggestedRoute: function() {} + property var getCryptoCurrencyAmount: function(cryptoValue) {} property int errorType: Constants.NoError property bool isLoading @@ -50,7 +52,7 @@ Item { d.customAmountToSend = 0 for(var i = 0; i lockedItem !== undefined && lockedItem.chainID === model.chainId) !== -1 preCalculatedAdvancedText: { let index = store.lockedInAmounts.findIndex(lockedItem => lockedItem!== undefined && lockedItem.chainID === model.chainId) if(locked && index !== -1) { - return root.weiToEth(parseInt(store.lockedInAmounts[index].value, 16)) + let amount = root.weiToEth(parseInt(store.lockedInAmounts[index].value, 16)) + return LocaleUtils.numberToLocaleString(amount.amount) } else return LocaleUtils.numberToLocaleString(fromNetwork.amountToSend) } - maxAdvancedValue: tokenBalanceOnChain.amount - state: tokenBalanceOnChain.amount === 0 || !hasGas ? + maxAdvancedValue: tokenBalanceOnChain ? tokenBalanceOnChain.amount : 0.0 + state: !tokenBalanceOnChain || tokenBalanceOnChain.amount === 0 || !hasGas ? "unavailable" : - (root.errorMode || !advancedInput.valid) && (parseFloat(advancedInputText) !== 0) ? "error" : "default" + (root.errorMode || !advancedInput.valid) && (advancedInputCurrencyAmount.amount !== 0) ? "error" : "default" cardIcon.source: Style.svg(model.iconUrl) disabledText: qsTr("Disabled") disableText: qsTr("Disable") @@ -130,9 +134,9 @@ Item { } onCardLocked: { - store.addLockedInAmount(model.chainId, advancedInputText, root.selectedAsset.decimals, isLocked) + store.addLockedInAmount(model.chainId, advancedInputCurrencyAmount.amount, root.selectedAsset.decimals, isLocked) d.calculateCustomAmounts() - if(!locked || (d.customAmountToSend <= root.amountToSend && advancedInput.valid)) + if(!locked || (d.customAmountToSend <= root.amountToSend.amount && advancedInput.valid)) root.reCalculateSuggestedRoute() } } @@ -141,7 +145,7 @@ Item { BalanceExceeded { Layout.fillWidth: true Layout.alignment: Qt.AlignVCenter - amountToSend: root.amountToSend + amountToSend: root.amountToSend ? root.amountToSend.amount : 0.0 isLoading: root.isLoading errorType: root.errorType } @@ -166,9 +170,10 @@ Item { property int routeOnNetwork: 0 property int bentLine: 0 property double amountToReceive: 0 + property var currencyAmountToReceive: root.getCryptoCurrencyAmount(amountToReceive) property bool preferred: store.preferredChainIds.includes(model.chainId) primaryText: model.chainName - secondaryText: LocaleUtils.numberToLocaleString(amountToReceive) + secondaryText: LocaleUtils.currencyAmountToLocaleString(currencyAmountToReceive) tertiaryText: state === "unpreferred" ? qsTr("UNPREFERRED") : "" state: !preferred ? "unpreferred" : "default" opacity: preferred || showPreferredChains ? 1 : 0 @@ -252,8 +257,8 @@ Item { xOffset = (fromN.y - toN.y > 0 ? -1 : 1) * toN.bentLine * 16 let amountToSend = weiToEth(bestRoutes[i].amountIn) let amountToReceive = weiToEth(bestRoutes[i].amountOut) - fromN.amountToSend = amountToSend - toN.amountToReceive += amountToReceive + fromN.amountToSend = amountToSend.amount + toN.amountToReceive += amountToReceive.amount fromN.routeOnNetwork += 1 toN.routeOnNetwork += 1 toN.bentLine = toN.objectName !== fromN.objectName diff --git a/ui/imports/shared/views/NetworkSelector.qml b/ui/imports/shared/views/NetworkSelector.qml index 0aab2d5e63..b93b3b1a19 100644 --- a/ui/imports/shared/views/NetworkSelector.qml +++ b/ui/imports/shared/views/NetworkSelector.qml @@ -2,6 +2,7 @@ import QtQuick.Layouts 1.13 import utils 1.0 +import shared.stores 1.0 import StatusQ.Controls 0.1 import StatusQ.Popups 0.1 @@ -17,10 +18,11 @@ Item { implicitHeight: visible ? tabBar.height + stackLayout.height + Style.current.xlPadding : 0 property var store + property var currencyStore : store.currencyStore property var selectedAccount property var selectedAsset - property double amountToSend: 0 - property double requiredGasInEth: 0 + property var amountToSend + property var requiredGasInEth property var bestRoutes property bool isLoading: false property bool advancedOrCustomMode: (tabBar.currentIndex === 1) || (tabBar.currentIndex === 2) @@ -78,13 +80,14 @@ Item { amountToSend: root.amountToSend isLoading: root.isLoading store: root.store + locale: root.store.locale selectedAsset: root.selectedAsset selectedAccount: root.selectedAccount errorMode: root.errorMode errorType: root.errorType toNetworksList: root.toNetworksList weiToEth: function(wei) { - return "%1 %2".arg(LocaleUtils.numberToLocaleString(parseFloat(store.getWei2Eth(wei, selectedAsset.decimals)))).arg(selectedAsset.symbol) + return root.currencyStore.getCurrencyAmount(parseFloat(store.getWei2Eth(wei, selectedAsset.decimals)), selectedAsset.symbol) } reCalculateSuggestedRoute: function() { root.reCalculateSuggestedRoute() @@ -114,7 +117,10 @@ Item { isBridgeTx: root.isBridgeTx errorType: root.errorType weiToEth: function(wei) { - return parseFloat(store.getWei2Eth(wei, selectedAsset.decimals)) + return root.currencyStore.getCurrencyAmount(parseFloat(store.getWei2Eth(wei, selectedAsset.decimals)), selectedAsset.symbol) + } + getCryptoCurrencyAmount: function(cryptoValue) { + return selectedAsset ? root.currencyStore.getCurrencyAmount(parseFloat(cryptoValue), selectedAsset.symbol) : undefined } } } diff --git a/ui/imports/shared/views/NetworksAdvancedCustomRoutingView.qml b/ui/imports/shared/views/NetworksAdvancedCustomRoutingView.qml index f3c27c0d89..c5b5ca7392 100644 --- a/ui/imports/shared/views/NetworksAdvancedCustomRoutingView.qml +++ b/ui/imports/shared/views/NetworksAdvancedCustomRoutingView.qml @@ -16,14 +16,15 @@ ColumnLayout { property var store property var selectedAccount - property double amountToSend: 0 - property double requiredGasInEth: 0 + property var amountToSend + property var requiredGasInEth property bool customMode: false property var selectedAsset property var bestRoutes property bool isLoading: false property bool errorMode: networksLoader.item ? networksLoader.item.errorMode : false property var weiToEth: function(wei) {} + property var getCryptoCurrencyAmount: function(cryptoValue) {} property bool interactive: true property bool isBridgeTx: false property bool showUnpreferredNetworks: preferredToggleButton.checked @@ -81,6 +82,7 @@ ColumnLayout { visible: active sourceComponent: NetworkCardsComponent { store: root.store + locale: root.store.locale selectedAccount: root.selectedAccount allNetworks: root.store.allNetworks amountToSend: root.amountToSend @@ -93,6 +95,7 @@ ColumnLayout { showPreferredChains: preferredToggleButton.checked bestRoutes: root.bestRoutes weiToEth: root.weiToEth + getCryptoCurrencyAmount: root.getCryptoCurrencyAmount interactive: root.interactive errorType: root.errorType isLoading: root.isLoading diff --git a/ui/imports/shared/views/NetworksSimpleRoutingView.qml b/ui/imports/shared/views/NetworksSimpleRoutingView.qml index 5bdd9df209..c1de3da918 100644 --- a/ui/imports/shared/views/NetworksSimpleRoutingView.qml +++ b/ui/imports/shared/views/NetworksSimpleRoutingView.qml @@ -16,8 +16,9 @@ RowLayout { id: root property var store + property var locale property var bestRoutes - property double amountToSend: 0 + property var amountToSend property bool isLoading: false property bool isBridgeTx: false property var selectedAsset @@ -79,7 +80,7 @@ RowLayout { Layout.fillWidth: true Layout.alignment: Qt.AlignHCenter Layout.topMargin: Style.current.bigPadding - amountToSend: root.amountToSend + amountToSend: root.amountToSend ? root.amountToSend.amount : 0.0 isLoading: root.isLoading errorType: root.errorType } @@ -98,12 +99,14 @@ RowLayout { implicitWidth: 410 title: modelData.chainName subTitle: { - let index = store.lockedInAmounts.findIndex(lockedItem => lockedItem !== undefined && lockedItem.chainID === modelData.chainId) + let index = store.lockedInAmounts.findIndex(lockedItem => lockedItem !== undefined && lockedItem.chainID === modelData.chainId) + var amountOut if(!root.errorMode || index === -1) - return root.weiToEth(modelData.amountOut) + amountOut = root.weiToEth(modelData.amountOut) else { - return root.weiToEth(parseInt(store.lockedInAmounts[index].value, 16)) + amountOut = root.weiToEth(parseInt(store.lockedInAmounts[index].value, 16)) } + return LocaleUtils.currencyAmountToLocaleString(amountOut) } statusListItemSubTitle.color: root.errorMode ? Theme.palette.dangerColor1 : Theme.palette.primaryColor1 asset.width: 32 @@ -128,7 +131,7 @@ RowLayout { title: chainName property bool tokenBalanceOnChainValid: selectedAccount && selectedAccount !== undefined && selectedAsset !== undefined property var tokenBalanceOnChain: tokenBalanceOnChainValid ? root.store.getTokenBalanceOnChain(selectedAccount, chainId, selectedAsset.symbol) : undefined - subTitle: tokenBalanceOnChain ? LocaleUtils.currencyAmountToLocaleString(tokenBalanceOnChain, root.store.locale) : "N/A" + subTitle: tokenBalanceOnChain ? LocaleUtils.currencyAmountToLocaleString(tokenBalanceOnChain) : "N/A" statusListItemSubTitle.color: Theme.palette.primaryColor1 asset.width: 32 asset.height: 32 diff --git a/ui/imports/shared/views/TabAddressSelectorView.qml b/ui/imports/shared/views/TabAddressSelectorView.qml index dc4c98df03..71eb142fc7 100644 --- a/ui/imports/shared/views/TabAddressSelectorView.qml +++ b/ui/imports/shared/views/TabAddressSelectorView.qml @@ -153,7 +153,7 @@ Item { objectName: model.name height: visible ? 64 : 0 title: !!model.name ? model.name : "" - subTitle: LocaleUtils.currencyAmountToLocaleString(model.currencyBalance, root.locale) + subTitle: LocaleUtils.currencyAmountToLocaleString(model.currencyBalance) asset.emoji: !!model.emoji ? model.emoji: "" asset.color: model.color asset.name: !model.emoji ? "filled-account": "" diff --git a/ui/imports/shared/views/TransactionDetailView.qml b/ui/imports/shared/views/TransactionDetailView.qml index 7f0dce6819..65d6e0dbaa 100644 --- a/ui/imports/shared/views/TransactionDetailView.qml +++ b/ui/imports/shared/views/TransactionDetailView.qml @@ -18,6 +18,7 @@ import "../controls" Item { id: root + property var locale property var currentAccount: RootStore.currentAccount property var contactsStore property var transaction @@ -57,18 +58,19 @@ Item { objectName: "transactionDetailHeader" width: parent.width + locale: root.locale modelData: transaction isIncoming: d.isIncoming - currentCurrency: RootStore.currentCurrency - cryptoValue: root.transaction !== undefined && !!root.transaction ? RootStore.hex2Eth(transaction.value): "" - fiatValue: root.transaction !== undefined && !!root.transaction ? RootStore.getFiatValue(cryptoValue, resolvedSymbol, RootStore.currentCurrency): "" - networkIcon: root.transaction !== undefined && !!root.transaction ? RootStore.getNetworkIcon(transaction.chainId): "" - networkColor: root.transaction !== undefined && !!root.transaction ? RootStore.getNetworkColor(transaction.chainId): "" - networkName: root.transaction !== undefined && !!root.transaction ? RootStore.getNetworkShortName(transaction.chainId): "" - symbol: root.transaction !== undefined && !!root.transaction ? RootStore.findTokenSymbolByAddress(transaction.contract): "" - transferStatus: root.transaction !== undefined && !!root.transaction ? RootStore.hex2Dec(transaction.txStatus): "" - shortTimeStamp: root.transaction !== undefined && !!root.transaction ? LocaleUtils.formatTime(transaction.timestamp * 1000, Locale.ShortFormat): "" - savedAddressName: root.transaction !== undefined && !!root.transaction ? RootStore.getNameForSavedWalletAddress(transaction.to): "" + property bool transactionValid: root.transaction !== undefined && !!root.transaction + cryptoValue: transactionValid ? RootStore.getCurrencyAmount(RootStore.hex2Eth(transaction.value), resolvedSymbol): undefined + fiatValue: transactionValid ? RootStore.getFiatValue(cryptoValue, resolvedSymbol, RootStore.currentCurrency): "" + networkIcon: transactionValid ? RootStore.getNetworkIcon(transaction.chainId): "" + networkColor: transactionValid ? RootStore.getNetworkColor(transaction.chainId): "" + networkName: transactionValid ? RootStore.getNetworkShortName(transaction.chainId): "" + symbol: transactionValid ? RootStore.findTokenSymbolByAddress(transaction.contract): "" + transferStatus: transactionValid ? RootStore.hex2Dec(transaction.txStatus): "" + shortTimeStamp: transactionValid ? LocaleUtils.formatTime(transaction.timestamp * 1000, Locale.ShortFormat): "" + savedAddressName: transactionValid ? RootStore.getNameForSavedWalletAddress(transaction.to): "" title: d.isIncoming ? qsTr("Received %1 %2 from %3").arg(cryptoValue).arg(resolvedSymbol).arg(d.from) : qsTr("Sent %1 %2 to %3").arg(cryptoValue).arg(resolvedSymbol).arg(d.to) sensor.enabled: false @@ -154,18 +156,19 @@ Item { spacing: 8 TransactionDelegate { width: parent.width + locale: root.locale modelData: transaction isIncoming: d.isIncoming - currentCurrency: RootStore.currentCurrency - cryptoValue: root.transaction !== undefined && !!root.transaction ? RootStore.hex2Eth(transaction.value): "" + property bool transactionValid: root.transaction !== undefined && !!root.transaction + cryptoValue: transactionValid ? RootStore.getCurrencyAmount(RootStore.hex2Eth(transaction.value), resolvedSymbol): "" fiatValue: RootStore.getFiatValue(cryptoValue, resolvedSymbol, RootStore.currentCurrency) - networkIcon: root.transaction !== undefined && !!root.transaction ? RootStore.getNetworkIcon(transaction.chainId) : "" - networkColor: root.transaction !== undefined && !!root.transaction ? RootStore.getNetworkColor(transaction.chainId): "" - networkName: root.transaction !== undefined && !!root.transaction ? RootStore.getNetworkShortName(transaction.chainId): "" - symbol: root.transaction !== undefined && !!root.transaction ? RootStore.findTokenSymbolByAddress(transaction.contract): "" - transferStatus: root.transaction !== undefined && !!root.transaction ? RootStore.hex2Dec(transaction.txStatus): "" - shortTimeStamp: root.transaction !== undefined && !!root.transaction ? LocaleUtils.formatTime(transaction.timestamp * 1000, Locale.ShortFormat): "" - savedAddressName: root.transaction !== undefined && !!root.transaction ? RootStore.getNameForSavedWalletAddress(transaction.to): "" + networkIcon: transactionValid ? RootStore.getNetworkIcon(transaction.chainId) : "" + networkColor: transactionValid ? RootStore.getNetworkColor(transaction.chainId): "" + networkName: transactionValid ? RootStore.getNetworkShortName(transaction.chainId): "" + symbol: transactionValid ? RootStore.findTokenSymbolByAddress(transaction.contract): "" + transferStatus: transactionValid ? RootStore.hex2Dec(transaction.txStatus): "" + shortTimeStamp: transactionValid ? LocaleUtils.formatTime(transaction.timestamp * 1000, Locale.ShortFormat): "" + savedAddressName: transactionValid ? RootStore.getNameForSavedWalletAddress(transaction.to): "" title: d.isIncoming ? qsTr("Received %1 %2 from %3").arg(cryptoValue).arg(resolvedSymbol).arg(d.from) : qsTr("Sent %1 %2 to %3").arg(cryptoValue).arg(resolvedSymbol).arg(d.to) sensor.enabled: false