diff --git a/storybook/PagesModel.qml b/storybook/PagesModel.qml index 07a5a07f0a..f798768535 100644 --- a/storybook/PagesModel.qml +++ b/storybook/PagesModel.qml @@ -45,6 +45,10 @@ ListModel { title: "CommunityMintedTokensView" section: "Views" } + ListElement { + title: "AmountToSendView" + section: "Views" + } ListElement { title: "StatusCommunityCard" section: "Panels" diff --git a/storybook/pages/AmountToSendViewPage.qml b/storybook/pages/AmountToSendViewPage.qml new file mode 100644 index 0000000000..f8166ca9c7 --- /dev/null +++ b/storybook/pages/AmountToSendViewPage.qml @@ -0,0 +1,113 @@ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 + +import AppLayouts.Profile.views 1.0 + +import Storybook 1.0 + +import StatusQ.Core 0.1 + +import utils 1.0 +import shared.views 1.0 + +SplitView { + id: root + + readonly property double maxCryptoBalance: parseFloat(maxCryptoBalanceText.text) + readonly property double rate: parseFloat(rateText.text) + readonly property int decimals: parseInt(decimalsText.text) + + Logs { id: logs } + + SplitView { + orientation: Qt.Vertical + SplitView.fillWidth: true + + Item { + SplitView.fillWidth: true + SplitView.fillHeight: true + + AmountToSend { + id: amountToSendInput + isBridgeTx: false + interactive: true + selectedSymbol: "Crypto" + maxInputBalance: inputIsFiat ? getFiatValue(root.maxCryptoBalance) : root.maxCryptoBalance + currentCurrency: "Fiat" + getFiatValue: function(cryptoValue) { + return cryptoValue * root.rate + } + getCryptoValue: function(fiatValue) { + return fiatValue / root.rate + } + formatCurrencyAmount: function(amount, symbol, options = null, locale = null) { + const currencyAmount = { + amount: amount, + symbol: symbol, + displayDecimals: root.decimals, + stripTrailingZeroes: true + } + return LocaleUtils.currencyAmountToLocaleString(currencyAmount, options) + } + onReCalculateSuggestedRoute: function() { + logs.logEvent("onReCalculateSuggestedRoute") + } + } + } + + LogsAndControlsPanel { + id: logsAndControlsPanel + + SplitView.minimumHeight: 100 + + logsView.logText: logs.logText + } + } + + Pane { + SplitView.minimumWidth: 300 + SplitView.preferredWidth: 300 + + ColumnLayout { + Label { + Layout.topMargin: 10 + Layout.fillWidth: true + text: "Max Crypto Balance" + } + + TextField { + id: maxCryptoBalanceText + background: Rectangle { border.color: 'lightgrey' } + Layout.preferredWidth: 200 + text: "1000000" + } + + Label { + Layout.topMargin: 10 + Layout.fillWidth: true + text: "Fiat/Crypto rate" + } + + TextField { + id: rateText + background: Rectangle { border.color: 'lightgrey' } + Layout.preferredWidth: 200 + text: "10" + } + + Label { + Layout.topMargin: 10 + Layout.fillWidth: true + text: "Decimals" + } + + TextField { + id: decimalsText + background: Rectangle { border.color: 'lightgrey' } + Layout.preferredWidth: 200 + text: "6" + } + } + } +} diff --git a/ui/StatusQ/src/StatusQ/Controls/StatusAccountSelector.qml b/ui/StatusQ/src/StatusQ/Controls/StatusAccountSelector.qml index 364fdaa71c..922dacaf63 100644 --- a/ui/StatusQ/src/StatusQ/Controls/StatusAccountSelector.qml +++ b/ui/StatusQ/src/StatusQ/Controls/StatusAccountSelector.qml @@ -239,7 +239,7 @@ Item { id: txtFiatBalance Layout.rightMargin: 4 font.pixelSize: 15 - text: LocaleUtils.currencyAmountToLocaleString(currencyBalance, {onlyAmount: true}) + text: LocaleUtils.currencyAmountToLocaleString(currencyBalance, {noSymbol: true}) color: Theme.palette.directColor1 } StatusBaseText { diff --git a/ui/StatusQ/src/StatusQ/Core/LocaleUtils.qml b/ui/StatusQ/src/StatusQ/Core/LocaleUtils.qml index 982c2a2667..cce7fc99e4 100644 --- a/ui/StatusQ/src/StatusQ/Core/LocaleUtils.qml +++ b/ui/StatusQ/src/StatusQ/Core/LocaleUtils.qml @@ -8,6 +8,35 @@ QtObject { readonly property var userInputLocale: Qt.locale("en_US") + function integralPartLength(num) { + num = Math.abs(num) + + // According to the JS Reference: + // + // Scientific notation is used if the radix is 10 and the number's + // magnitude (ignoring sign) is greater than or equal to 10^21 or less + // than 10^-6. In this case, the returned string always explicitly + // specifies the sign of the exponent. + // + // In order to take it into account, numbers in scientific notation + // is handled separately. + + if (num < 1) { + return 1 + } + + if (num >= 10**21) { + const split = num.toString().split('e') + if (split.length === 2) { + const base = parseFloat(split[0]) + const exp = parseInt(split[1], 10) + return integralPartLength(base) + exp + } + } + + return num.toFixed().length + } + function fractionalPartLength(num) { if (Number.isInteger(num)) return 0 @@ -86,12 +115,16 @@ QtObject { return "N/A" // Parse options - var optShowOnlyAmount = false + var optNoSymbol = false + var optRawAmount = false var optDisplayDecimals = currencyAmount.displayDecimals var optStripTrailingZeroes = currencyAmount.stripTrailingZeroes if (options) { - if (options.onlyAmount !== undefined) { - optShowOnlyAmount = true + if (options.noSymbol !== undefined) { + optNoSymbol = true + } + if (options.rawAmount !== undefined) { + optRawAmount = true } if (options.minDecimals !== undefined && options.minDecimals > optDisplayDecimals) { optDisplayDecimals = options.minDecimals @@ -101,23 +134,40 @@ QtObject { } } - var amountStr + var amountStr = "" + var amountSuffix = "" + let minAmount = 10**-optDisplayDecimals - if (currencyAmount.amount > 0 && currencyAmount.amount < minAmount && !optShowOnlyAmount) + if (currencyAmount.amount > 0 && currencyAmount.amount < minAmount && !optRawAmount) { // Handle amounts smaller than resolution - amountStr = "<%1".arg(numberToLocaleString(minAmount, optDisplayDecimals, locale)) + amountStr = "<%1".arg(numberToLocaleString(minAmount, displayDecimals, locale)) } else { - // Normal formatting - amountStr = numberToLocaleString(currencyAmount.amount, optDisplayDecimals, locale) + var amount + var displayDecimals + const numIntegerDigits = integralPartLength(currencyAmount.amount) + const maxDigits = 10 + // For large numbers, we use the short scale system (https://en.wikipedia.org/wiki/Long_and_short_scales) + // and 2 decimal digits. + if (numIntegerDigits > maxDigits && !optRawAmount) { + amount = currencyAmount.amount/10**9 // Billion => 9 zeroes + displayDecimals = 2 + amountSuffix = qsTr("B", "Billion") + } else { + // For normal numbers, we show the whole integral part and as many decimal places not + // not to exceed the maximum + amount = currencyAmount.amount + displayDecimals = Math.min(optDisplayDecimals, Math.max(0, maxDigits - numIntegerDigits)) + } + amountStr = numberToLocaleString(amount, displayDecimals, locale) if (optStripTrailingZeroes) { amountStr = stripTrailingZeroes(amountStr, locale) } } // Add symbol - if (currencyAmount.symbol && !optShowOnlyAmount) { - amountStr = "%1 %2".arg(amountStr).arg(currencyAmount.symbol) + if (currencyAmount.symbol && !optNoSymbol) { + amountStr = "%1%2 %3".arg(amountStr).arg(amountSuffix).arg(currencyAmount.symbol) } return amountStr diff --git a/ui/imports/shared/views/AmountToSend.qml b/ui/imports/shared/views/AmountToSend.qml index 12e87e5bb3..09299220a7 100644 --- a/ui/imports/shared/views/AmountToSend.qml +++ b/ui/imports/shared/views/AmountToSend.qml @@ -132,7 +132,7 @@ ColumnLayout { onClicked: { topAmountToSendInput.validate() if(!!topAmountToSendInput.text) { - topAmountToSendInput.text = root.formatCurrencyAmount(bottomItem.bottomAmountToSend, bottomItem.bottomAmountSymbol, {onlyAmount: true}, LocaleUtils.userInputLocale) + topAmountToSendInput.text = root.formatCurrencyAmount(bottomItem.bottomAmountToSend, bottomItem.bottomAmountSymbol, {noSymbol: true, rawAmount: true}, LocaleUtils.userInputLocale) } inputIsFiat = !inputIsFiat d.waitTimer.restart() diff --git a/ui/imports/shared/views/qmldir b/ui/imports/shared/views/qmldir index b3074e9d3c..ff8b0905d0 100644 --- a/ui/imports/shared/views/qmldir +++ b/ui/imports/shared/views/qmldir @@ -11,3 +11,4 @@ ProfileDialogView 1.0 ProfileDialogView.qml AssetsView 1.0 AssetsView.qml HistoryView 1.0 HistoryView.qml DeviceSyncingView 1.0 DeviceSyncingView.qml +AmountToSend 1.0 AmountToSend.qml