diff --git a/src/app_service/service/transaction/service.nim b/src/app_service/service/transaction/service.nim index 2f85505266..9336f7820f 100644 --- a/src/app_service/service/transaction/service.nim +++ b/src/app_service/service/transaction/service.nim @@ -130,7 +130,7 @@ QtObject: proc confirmTransactionStatus(self: Service, pendingTransactions: JsonNode) = for trx in pendingTransactions.getElems(): let transactionReceipt = self.getTransactionReceipt(trx["network_id"].getInt, trx["hash"].getStr) - if transactionReceipt.kind != JNull: + if transactionReceipt != nil and transactionReceipt.kind != JNull: self.deletePendingTransaction(trx["network_id"].getInt, trx["hash"].getStr) let ev = TransactionMinedArgs( data: trx["additionalData"].getStr, diff --git a/ui/imports/shared/controls/AssetAndAmountInput.qml b/ui/imports/shared/controls/AssetAndAmountInput.qml index 7bba39b91b..7bc03c3661 100644 --- a/ui/imports/shared/controls/AssetAndAmountInput.qml +++ b/ui/imports/shared/controls/AssetAndAmountInput.qml @@ -172,7 +172,7 @@ Item { tokenAssetSourceFn: function (symbol) { return symbol ? Style.png("tokens/" + symbol) : defaultToken } - searchTokenSymbolByAddress: function (address) { + searchTokenSymbolByAddressFn: function (address) { if(popup.selectedAccount) { return popup.selectedAccount.findTokenSymbolByAddress(address) } diff --git a/ui/imports/shared/panels/StatusAssetSelector.qml b/ui/imports/shared/panels/StatusAssetSelector.qml index a90daa6fa1..695c7214f7 100644 --- a/ui/imports/shared/panels/StatusAssetSelector.qml +++ b/ui/imports/shared/panels/StatusAssetSelector.qml @@ -23,7 +23,7 @@ Item { property var tokenAssetSourceFn: function (symbol) { return "" } - property var searchTokenSymbolByAddress: function (address) { + property var searchTokenSymbolByAddressFn: function (address) { return "" } @@ -74,7 +74,7 @@ Item { filters: [ ExpressionFilter { expression: { - var tokenSymbolByAddress = searchTokenSymbolByAddress(d.searchString) + var tokenSymbolByAddress = searchTokenSymbolByAddressFn(d.searchString) return symbol.startsWith(d.searchString.toUpperCase()) || name.toUpperCase().startsWith(d.searchString.toUpperCase()) || (tokenSymbolByAddress!=="" && symbol.startsWith(tokenSymbolByAddress)) } } diff --git a/ui/imports/shared/popups/SendModal.qml b/ui/imports/shared/popups/SendModal.qml index f0ba2aa3ec..d015215a2c 100644 --- a/ui/imports/shared/popups/SendModal.qml +++ b/ui/imports/shared/popups/SendModal.qml @@ -22,7 +22,6 @@ import "../views" StatusDialog { id: popup - property alias stack: stack property alias addressText: recipientSelector.input.text property var store @@ -39,29 +38,29 @@ StatusDialog { function sendTransaction() { let recipientAddress = Utils.isValidAddress(popup.addressText) ? popup.addressText : d.resolvedENSAddress - stack.currentGroup.isPending = true let success = false + d.isPending = true success = popup.store.transfer( - popup.selectedAccount.address, - recipientAddress, - assetSelector.selectedAsset.symbol, - amountToSendInput.text, - gasSelector.selectedGasLimit, - gasSelector.suggestedFees.eip1559Enabled ? "" : gasSelector.selectedGasPrice, - gasSelector.selectedTipLimit, - gasSelector.selectedOverallLimit, - transactionSigner.enteredPassword, - networkSelector.selectedNetwork.chainId, - stack.uuid, - gasSelector.suggestedFees.eip1559Enabled, - ) + popup.selectedAccount.address, + recipientAddress, + assetSelector.selectedAsset.symbol, + amountToSendInput.text, + gasSelector.selectedGasLimit, + gasSelector.suggestedFees.eip1559Enabled ? "" : gasSelector.selectedGasPrice, + gasSelector.selectedTipLimit, + gasSelector.selectedOverallLimit, + transactionSigner.enteredPassword, + networkSelector.selectedNetwork.chainId, + d.uuid, + gasSelector.suggestedFees.eip1559Enabled, + ) } property var recalculateRoutesAndFees: Backpressure.debounce(popup, 600, function(disabledChainIds) { if (disabledChainIds === undefined) disabledChainIds = [] networkSelector.suggestedRoutes = popup.store.suggestedRoutes( - popup.selectedAccount.address, amountToSendInput.text, assetSelector.selectedAsset.symbol, disabledChainIds - ) + popup.selectedAccount.address, amountToSendInput.text, assetSelector.selectedAsset.symbol, disabledChainIds + ) if (networkSelector.suggestedRoutes.length) { networkSelector.selectedNetwork = networkSelector.suggestedRoutes[0] gasSelector.suggestedFees = popup.store.suggestedFees(networkSelector.suggestedRoutes[0].chainId) @@ -73,6 +72,11 @@ StatusDialog { } }) + enum StackGroup { + SendDetailsGroup = 0, + AuthenticationGroup = 1 + } + QtObject { id: d readonly property string maxFiatBalance: Utils.stripTrailingZeros(parseFloat(assetSelector.selectedAsset.totalBalance).toFixed(4)) @@ -86,13 +90,16 @@ StatusDialog { }) property string resolvedENSAddress onIsReadyChanged: { - if(!isReady && stack.isLastGroup) - stack.back() + if(!isReady && isLastGroup) + stack.currentIndex = SendModal.StackGroup.SendDetailsGroup } + readonly property string uuid: Utils.uuid() + readonly property bool isLastGroup: stack.currentIndex === (stack.count - 1) + property bool isPending: false } width: 556 - height: 595 + topMargin: 64 + header.height padding: 0 background: StatusDialogBackground { @@ -125,121 +132,148 @@ StatusDialog { } } - TransactionStackView { + StackLayout { id: stack - property alias currentGroup: stack.currentGroup - TransactionFormGroup { + anchors.fill: parent + currentIndex: 0 + clip: true + ColumnLayout { id: group1 - anchors.fill: parent - color: Theme.palette.baseColor3 + Layout.preferredWidth: parent.width + spacing: Style.current.padding - ColumnLayout { - id: assetAndAmmountSelector - anchors.top: parent.top - anchors.right: parent.right - anchors.left: parent.left - anchors.leftMargin: Style.current.xlPadding - anchors.rightMargin: Style.current.xlPadding - z: 1 + Rectangle { + Layout.preferredWidth: parent.width + Layout.preferredHeight: assetAndAmmountSelector.height + Style.current.padding + color: Theme.palette.baseColor3 - RowLayout { - spacing: 16 - StatusBaseText { - text: qsTr("Send") - font.pixelSize: 15 - color: Theme.palette.directColor1 - Layout.alignment: Qt.AlignVCenter - } - StatusListItemTag { - title: assetSelector.selectedAsset.totalBalance > 0 ? qsTr("Max: ") + (assetSelector.selectedAsset ? d.maxFiatBalance : "0.00") : qsTr("No balances active") - closeButtonVisible: false - titleText.font.pixelSize: 12 - Layout.preferredHeight: 22 - Layout.preferredWidth: childrenRect.width - color: d.errorMode ? Theme.palette.dangerColor2 : Theme.palette.primaryColor3 - titleText.color: d.errorMode ? Theme.palette.dangerColor1 : Theme.palette.primaryColor1 - } + layer.enabled: scrollView.contentY > -8 + layer.effect: DropShadow { + verticalOffset: 2 + radius: 16 + samples: 17 + color: Theme.palette.dropShadow } - Item { - Layout.fillWidth: true - Layout.preferredHeight: childrenRect.height - AmountInputWithCursor { - id: amountToSendInput - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: -Style.current.padding - width: parent.width - assetSelector.width - placeholderText: "0.00" + " " + assetSelector.selectedAsset.symbol - errorMessageCmp.anchors.rightMargin: -100 - input.edit.color: d.errorMode ? Theme.palette.dangerColor1 : Theme.palette.directColor1 - validators: [ - StatusFloatValidator{ - id: floatValidator - bottom: 0 - top: d.maxFiatBalance - errorMessage: qsTr("Please enter a valid amount") - } - ] - Keys.onReleased: { - let amount = amountToSendInput.text.trim() - if (!Utils.containsOnlyDigits(amount) || isNaN(amount)) { - return - } - if (amount === "") { - txtFiatBalance.text = "0.00" - } else { - txtFiatBalance.text = popup.store.getFiatValue(amount, assetSelector.selectedAsset.symbol, popup.store.currentCurrency) - } - gasSelector.estimateGas() - popup.recalculateRoutesAndFees() + + Column { + id: assetAndAmmountSelector + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: Style.current.xlPadding + anchors.rightMargin: Style.current.xlPadding + z: 1 + + Row { + spacing: 16 + StatusBaseText { + anchors.verticalCenter: parent.verticalCenter + text: qsTr("Send") + font.pixelSize: 15 + color: Theme.palette.directColor1 + } + StatusListItemTag { + height: 22 + width: childrenRect.width + title: assetSelector.selectedAsset.totalBalance > 0 ? qsTr("Max: %1").arg(assetSelector.selectedAsset ? d.maxFiatBalance : "0.00") : qsTr("No balances active") + closeButtonVisible: false + titleText.font.pixelSize: 12 + color: d.errorMode ? Theme.palette.dangerColor2 : Theme.palette.primaryColor3 + titleText.color: d.errorMode ? Theme.palette.dangerColor1 : Theme.palette.primaryColor1 } } - StatusAssetSelector { - id: assetSelector - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - assets: popup.selectedAccount.assets - defaultToken: Style.png("tokens/DEFAULT-TOKEN@3x") - getCurrencyBalanceString: function (currencyBalance) { - return Utils.toLocaleString(currencyBalance.toFixed(2), popup.store.locale, {"currency": true}) + " " + popup.store.currentCurrency.toUpperCase() - } - tokenAssetSourceFn: function (symbol) { - return symbol ? Style.png("tokens/" + symbol) : defaultToken - } - searchTokenSymbolByAddress: function (address) { - if(popup.selectedAccount) { - return popup.selectedAccount.findTokenSymbolByAddress(address) + Item { + width: parent.width + height: amountToSendInput.height + AmountInputWithCursor { + id: amountToSendInput + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: -Style.current.padding + width: parent.width - assetSelector.width + placeholderText: "0.00 %1".arg(assetSelector.selectedAsset.symbol) + input.edit.color: d.errorMode ? Theme.palette.dangerColor1 : Theme.palette.directColor1 + validators: [ + StatusFloatValidator{ + id: floatValidator + bottom: 0 + top: d.maxFiatBalance + errorMessage: "" + } + ] + Keys.onReleased: { + let amount = amountToSendInput.text.trim() + + if (!Utils.containsOnlyDigits(amount) || isNaN(amount)) { + return + } + if (amount === "") { + txtFiatBalance.text = "0.00" + } else { + txtFiatBalance.text = popup.store.getFiatValue(amount, assetSelector.selectedAsset.symbol, popup.store.currentCurrency) + } + gasSelector.estimateGas() + popup.recalculateRoutesAndFees() } - return "" } - onSelectedAssetChanged: { - if (!assetSelector.selectedAsset) { - return + StatusAssetSelector { + id: assetSelector + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + assets: popup.selectedAccount.assets + defaultToken: Style.png("tokens/DEFAULT-TOKEN@3x") + getCurrencyBalanceString: function (currencyBalance) { + return "%1 %2".arg(Utils.toLocaleString(currencyBalance.toFixed(2), popup.store.locale, {"currency": true})).arg(popup.store.currentCurrency.toUpperCase()) } - if (amountToSendInput.text === "" || isNaN(amountToSendInput.text)) { - return + tokenAssetSourceFn: function (symbol) { + return symbol ? Style.png("tokens/%1".arg(symbol)) : defaultToken + } + searchTokenSymbolByAddressFn: function (address) { + if(popup.selectedAccount) { + return popup.selectedAccount.findTokenSymbolByAddress(address) + } + return "" + } + onSelectedAssetChanged: { + if (!assetSelector.selectedAsset) { + return + } + if (amountToSendInput.text === "" || isNaN(amountToSendInput.text)) { + return + } + txtFiatBalance.text = popup.store.getFiatValue(amountToSendInput.text, assetSelector.selectedAsset.symbol, popup.store.currentCurrency) + gasSelector.estimateGas() + popup.recalculateRoutesAndFees() } - txtFiatBalance.text = popup.store.getFiatValue(amountToSendInput.text, assetSelector.selectedAsset.symbol, popup.store.currentCurrency) - gasSelector.estimateGas() - popup.recalculateRoutesAndFees() } } - } - RowLayout { - Layout.alignment: Qt.AlignLeft - StyledTextField { + StatusInput { id: txtFiatBalance - color: txtFiatBalance.activeFocus ? Style.current.textColor : Style.current.secondaryText + anchors.left: parent.left + anchors.leftMargin: -12 + leftPadding: 0 + rightPadding: 0 font.weight: Font.Medium - font.pixelSize: 12 - inputMethodHints: Qt.ImhFormattedNumbersOnly + font.pixelSize: 13 + input.placeholderFont.pixelSize: 13 + input.leftPadding: 0 + input.rightPadding: 0 + input.topPadding: 0 + input.bottomPadding: 0 + input.edit.padding: 0 + input.background.color: "transparent" + input.background.border.width: 0 + input.edit.color: txtFiatBalance.input.edit.activeFocus ? Theme.palette.directColor1 : Theme.palette.baseColor1 text: "0.00" - selectByMouse: true - background: Rectangle { - color: Style.current.transparent + placeholderText: "0.00" + input.implicitHeight: 15 + implicitWidth: txtFiatBalance.input.edit.contentWidth + 50 + input.rightComponent: StatusBaseText { + id: currencyText + text: popup.store.currentCurrency.toUpperCase() + font.pixelSize: 13 + color: Theme.palette.directColor5 } - padding: 0 Keys.onReleased: { let balance = txtFiatBalance.text.trim() if (balance === "" || isNaN(balance)) { @@ -249,47 +283,20 @@ StatusDialog { // amountToSendInput.text = root.getCryptoValue(balance, popup.store.currentCurrency, assetSelector.selectedAsset.symbol) } } - StatusBaseText { - id: currencyText - text: popup.store.currentCurrency.toUpperCase() - font.pixelSize: 13 - color: Theme.palette.directColor5 - } } } - Rectangle { - id: border - anchors.top: assetAndAmmountSelector.bottom - anchors.topMargin: Style.current.padding - anchors.left: parent.left - anchors.right: parent.right - - height: 1 - color: Theme.palette.directColor8 - visible: false - } - - DropShadow { - anchors.fill: border - horizontalOffset: 0 - verticalOffset: 2 - radius: 8.0 - samples: 17 - color: Theme.palette.directColor1 - source: border - } - StatusScrollView { id: scrollView - height: stack.height - assetAndAmmountSelector.height - Style.current.bigPadding - width: parent.width - anchors.top: border.bottom - anchors.left: parent.left + Layout.fillHeight: true + Layout.preferredWidth: parent.width + contentHeight: layout.height + contentWidth: parent.width z: 0 objectName: "sendModalScroll" - ColumnLayout { + Column { + id: layout width: scrollView.availableWidth spacing: Style.current.halfPadding anchors.left: parent.left @@ -298,9 +305,11 @@ StatusDialog { id: recipientSelector property bool isPending: false - Layout.fillWidth: true - Layout.leftMargin: Style.current.bigPadding - Layout.rightMargin: Style.current.bigPadding + width: parent.width + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: Style.current.bigPadding + anchors.rightMargin: Style.current.bigPadding label: qsTr("To") placeholderText: qsTr("Enter an ENS name or address") @@ -351,18 +360,25 @@ StatusDialog { TabAddressSelectorView { id: addressSelector + width: parent.width + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: Style.current.bigPadding + anchors.rightMargin: Style.current.bigPadding store: popup.store onContactSelected: { recipientSelector.input.text = address } - Layout.fillWidth: true - Layout.leftMargin: Style.current.bigPadding - Layout.rightMargin: Style.current.bigPadding visible: !d.recipientReady } NetworkSelector { id: networkSelector + width: parent.width + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: Style.current.bigPadding + anchors.rightMargin: Style.current.bigPadding store: popup.store selectedAccount: popup.selectedAccount amountToSend: isNaN(parseFloat(amountToSendInput.text)) ? 0 : parseFloat(amountToSendInput.text) @@ -374,9 +390,6 @@ StatusDialog { gasSelector.updateGasEthValue() } onReCalculateSuggestedRoute: popup.recalculateRoutesAndFees(disabledChainIds) - Layout.fillWidth: true - Layout.leftMargin: Style.current.bigPadding - Layout.rightMargin: Style.current.bigPadding visible: d.recipientReady } @@ -384,10 +397,12 @@ StatusDialog { id: fees radius: 13 color: Theme.palette.indirectColor1 - Layout.preferredHeight: text.height + gasSelector.height + gasValidator.height + Style.current.xlPadding - Layout.fillWidth: true - Layout.leftMargin: Style.current.bigPadding - Layout.rightMargin: Style.current.bigPadding + height: text.height + gasSelector.height + gasValidator.height + Style.current.xlPadding + width: parent.width + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: Style.current.bigPadding + anchors.rightMargin: Style.current.bigPadding visible: d.recipientReady RowLayout { @@ -403,12 +418,12 @@ StatusDialog { radius: 8 asset.name: "fees" } - ColumnLayout { + Column { Layout.alignment: Qt.AlignTop | Qt.AlignHCenter Layout.preferredWidth: fees.width - feesIcon.width - Style.current.xlPadding StatusBaseText { id: text - Layout.maximumWidth: 410 + width: 410 font.pixelSize: 15 font.weight: Font.Medium color: Theme.palette.directColor1 @@ -417,7 +432,7 @@ StatusDialog { } GasSelector { id: gasSelector - Layout.fillWidth: true + width: parent.width getGasEthValue: popup.store.getGasEthValue getFiatValue: popup.store.getFiatValue getEstimatedTime: popup.store.getEstimatedTime @@ -437,12 +452,12 @@ StatusDialog { var recipientAddress = popup.launchedFromChat ? popup.preSelectedRecipient.address : popup.addressText let gasEstimate = JSON.parse(popup.store.estimateGas( - popup.selectedAccount.address, - recipientAddress, - assetSelector.selectedAsset.symbol, - amountToSendInput.text, - chainID, - "")) + popup.selectedAccount.address, + recipientAddress, + assetSelector.selectedAsset.symbol, + amountToSendInput.text, + chainID, + "")) if (!gasEstimate.success) { console.warn("error estimating gas: ", gasEstimate.error.message) @@ -455,10 +470,10 @@ StatusDialog { } GasValidator { id: gasValidator - Layout.fillWidth: true + width: parent.width selectedAccount: popup.selectedAccount selectedAmount: amountToSendInput.text === "" ? 0.0 : - parseFloat(amountToSendInput.text) + parseFloat(amountToSendInput.text) selectedAsset: assetSelector.selectedAsset selectedGasEthValue: gasSelector.selectedGasEthValue selectedNetwork: networkSelector.selectedNetwork ? networkSelector.selectedNetwork: null @@ -469,15 +484,10 @@ StatusDialog { } } } - TransactionFormGroup { - id: group4 - - color: Theme.palette.baseColor3 - - StackView.onActivated: { - transactionSigner.forceActiveFocus(Qt.MouseFocusReason) - } + Column{ + id: group2 + Layout.preferredWidth: parent.width TransactionSigner { id: transactionSigner anchors.left: parent.left @@ -492,39 +502,34 @@ StatusDialog { footer: SendModalFooter { maxFiatFees: gasSelector.maxFiatFees estimatedTxTimeFlag: gasSelector.estimatedTxTimeFlag - currentGroupPending: stack.currentGroup.isPending - currentGroupValid: stack.currentGroup.isValid - isLastGroup: stack.isLastGroup + pending: d.isPending + isLastGroup: d.isLastGroup visible: d.isReady && !isNaN(parseFloat(amountToSendInput.text)) && gasValidator.isValid onNextButtonClicked: { - const validity = stack.currentGroup.validate() - if (validity.isValid && !validity.isPending) { - if (stack.isLastGroup) { - return popup.sendTransaction() - } - - if(gasSelector.suggestedFees.eip1559Enabled && stack.currentGroup === group1 && gasSelector.advancedMode){ - if(gasSelector.showPriceLimitWarning || gasSelector.showTipLimitWarning){ - Global.openPopup(transactionSettingsConfirmationPopupComponent, { - currentBaseFee: gasSelector.suggestedFees.baseFee, - currentMinimumTip: gasSelector.perGasTipLimitFloor, - currentAverageTip: gasSelector.perGasTipLimitAverage, - tipLimit: gasSelector.selectedTipLimit, - suggestedTipLimit: gasSelector.perGasTipLimitFloor, - priceLimit: gasSelector.selectedOverallLimit, - suggestedPriceLimit: gasSelector.suggestedFees.baseFee + gasSelector.perGasTipLimitFloor, - showPriceLimitWarning: gasSelector.showPriceLimitWarning, - showTipLimitWarning: gasSelector.showTipLimitWarning, - onConfirm: function(){ - stack.next(); - } - }) - return - } - } - - stack.next() + if (isLastGroup) { + return popup.sendTransaction() } + + if(gasSelector.suggestedFees.eip1559Enabled && gasSelector.advancedMode){ + if(gasSelector.showPriceLimitWarning || gasSelector.showTipLimitWarning){ + Global.openPopup(transactionSettingsConfirmationPopupComponent, { + currentBaseFee: gasSelector.suggestedFees.baseFee, + currentMinimumTip: gasSelector.perGasTipLimitFloor, + currentAverageTip: gasSelector.perGasTipLimitAverage, + tipLimit: gasSelector.selectedTipLimit, + suggestedTipLimit: gasSelector.perGasTipLimitFloor, + priceLimit: gasSelector.selectedOverallLimit, + suggestedPriceLimit: gasSelector.suggestedFees.baseFee + gasSelector.perGasTipLimitFloor, + showPriceLimitWarning: gasSelector.showPriceLimitWarning, + showTipLimitWarning: gasSelector.showTipLimitWarning, + onConfirm: function(){ + stack.currentIndex = SendModal.StackGroup.AuthenticationGroup + } + }) + return + } + } + stack.currentIndex = SendModal.StackGroup.AuthenticationGroup } } @@ -536,11 +541,10 @@ StatusDialog { Connections { target: popup.store.walletSectionTransactionsInst onTransactionSent: { + d.isPending = false try { let response = JSON.parse(txResult) - if (response.uuid !== stack.uuid) return - - stack.currentGroup.isPending = false + if (response.uuid !== d.uuid) return if (!response.success) { if (Utils.isInvalidPasswordMessage(response.result)){ diff --git a/ui/imports/shared/views/SendModalFooter.qml b/ui/imports/shared/views/SendModalFooter.qml index cc30463a00..ee902fda93 100644 --- a/ui/imports/shared/views/SendModalFooter.qml +++ b/ui/imports/shared/views/SendModalFooter.qml @@ -1,6 +1,7 @@ import QtQuick 2.13 import QtQuick.Controls 2.13 import QtQuick.Layouts 1.13 +import QtGraphicalEffects 1.13 import utils 1.0 @@ -14,8 +15,7 @@ Rectangle { property string maxFiatFees: "" property int estimatedTxTimeFlag: Constants.transactionEstimatedTime.unknown - property bool currentGroupPending: true - property bool currentGroupValid: false + property bool pending: true property bool isLastGroup: false signal nextButtonClicked() @@ -29,16 +29,12 @@ Rectangle { radius: 8 color: Theme.palette.baseColor3 - Rectangle { - anchors.top: parent.top - width: parent.width - height: parent.radius - color: parent.color - - StatusModalDivider { - anchors.top: parent.top - width: parent.width - } + layer.enabled: true + layer.effect: DropShadow { + verticalOffset: 2 + radius: 16 + samples: 17 + color: Theme.palette.dropShadow } RowLayout { @@ -95,8 +91,8 @@ Rectangle { size: StatusBaseButton.Size.Large normalColor: Theme.palette.primaryColor2 disabledColor: Theme.palette.baseColor2 - enabled: currentGroupValid && !currentGroupPending - loading: currentGroupPending + enabled: !footer.pending + loading: footer.pending onClicked: nextButtonClicked() } } diff --git a/ui/imports/utils/Utils.qml b/ui/imports/utils/Utils.qml index dcf28e6610..75e1707c85 100644 --- a/ui/imports/utils/Utils.qml +++ b/ui/imports/utils/Utils.qml @@ -714,7 +714,7 @@ QtObject { } function containsOnlyDigits(msg) { - var reg = new RegExp('^[0-9]$') + var reg = new RegExp('[+-]?([0-9]*[.])?[0-9]+') return reg.test(msg) }