From 97f0a0435df3f5206b9b0a31c8b7a77413ed4923 Mon Sep 17 00:00:00 2001 From: Richard Ramos Date: Tue, 27 Jul 2021 10:04:05 -0400 Subject: [PATCH] refactor: SNT transaction modal (for buying sticker packs and ens) --- ui/app/AppLayouts/Chat/ChatColumn.qml | 31 +++ .../Sections/Ens/TermsAndConditions.qml | 20 +- .../status/StatusSNTTransactionModal.qml} | 65 +++-- ui/shared/status/StatusStickerMarket.qml | 32 ++- .../status/StatusStickerPackClickPopup.qml | 19 +- .../status/StatusStickerPackPurchaseModal.qml | 254 ------------------ 6 files changed, 126 insertions(+), 295 deletions(-) rename ui/{app/AppLayouts/Profile/Sections/Ens/RegisterENSModal.qml => shared/status/StatusSNTTransactionModal.qml} (78%) delete mode 100644 ui/shared/status/StatusStickerPackPurchaseModal.qml diff --git a/ui/app/AppLayouts/Chat/ChatColumn.qml b/ui/app/AppLayouts/Chat/ChatColumn.qml index dc8a68b311..c40bbb3bc4 100644 --- a/ui/app/AppLayouts/Chat/ChatColumn.qml +++ b/ui/app/AppLayouts/Chat/ChatColumn.qml @@ -632,6 +632,37 @@ Item { Component.onCompleted: { chatInput.textInput.forceActiveFocus(Qt.MouseFocusReason) } + + Connections { + target: chatsModel.stickers + onTransactionWasSent: { + //% "Transaction pending..." + toastMessage.title = qsTr("Transaction pending") + toastMessage.source = "../../../img/loading.svg" + toastMessage.iconColor = Style.current.primary + toastMessage.iconRotates = true + toastMessage.link = `${walletModel.utilsView.etherscanLink}/${txResult}` + toastMessage.open() + } + onTransactionCompleted: { + toastMessage.title = !success ? + //% "Could not buy Stickerpack" + qsTrId("could-not-buy-stickerpack") + : + //% "Stickerpack bought successfully" + qsTrId("stickerpack-bought-successfully"); + if (success) { + toastMessage.source = "../../../img/check-circle.svg" + toastMessage.iconColor = Style.current.success + } else { + toastMessage.source = "../../../img/block-icon.svg" + toastMessage.iconColor = Style.current.danger + } + + toastMessage.link = `${walletModel.utilsView.etherscanLink}/${txHash}` + toastMessage.open() + } + } } } diff --git a/ui/app/AppLayouts/Profile/Sections/Ens/TermsAndConditions.qml b/ui/app/AppLayouts/Profile/Sections/Ens/TermsAndConditions.qml index 7bfa0d938f..e046b6558e 100644 --- a/ui/app/AppLayouts/Profile/Sections/Ens/TermsAndConditions.qml +++ b/ui/app/AppLayouts/Profile/Sections/Ens/TermsAndConditions.qml @@ -32,14 +32,26 @@ Item { function closed() { this.active = false // kill an opened instance } - sourceComponent: RegisterENSModal { - onOpened: { - walletModel.gasView.getGasPricePredictions() + sourceComponent: StatusSNTTransactionModal { + assetPrice: "10" + contractAddress: utilsModel.ensRegisterAddress + estimateGasFunction: function(selectedAccount, uuid) { + if (username === "" || !selectedAccount) return 380000; + return profileModel.ens.registerENSGasEstimate(username, selectedAccount.address) + } + onSendTransaction: function(selectedAddress, gasLimit, gasPrice, password) { + return profileModel.ens.registerENS(username, + selectedAddress, + gasLimit, + gasPrice, + password) + } + onSuccess: function(){ + usernameRegistered(username); } onClosed: { transactionDialog.closed() } - ensUsername: username } } diff --git a/ui/app/AppLayouts/Profile/Sections/Ens/RegisterENSModal.qml b/ui/shared/status/StatusSNTTransactionModal.qml similarity index 78% rename from ui/app/AppLayouts/Profile/Sections/Ens/RegisterENSModal.qml rename to ui/shared/status/StatusSNTTransactionModal.qml index 81af2a1b66..f7bf730bf1 100644 --- a/ui/app/AppLayouts/Profile/Sections/Ens/RegisterENSModal.qml +++ b/ui/shared/status/StatusSNTTransactionModal.qml @@ -2,19 +2,25 @@ import QtQuick 2.13 import QtQuick.Controls 2.13 import QtQuick.Layouts 1.13 import QtQuick.Dialogs 1.3 -import "../../../../../imports" -import "../../../../../shared" -import "../../../../../shared/status" +import "../../imports" +import "../../shared" +import "../../shared/status" ModalPopup { id: root readonly property var asset: JSON.parse(walletModel.tokensView.getStatusToken()) - property string ensUsername: "" - property string ensPrice: "10" + property string assetPrice + property string contractAddress + property var estimateGasFunction: (function(userAddress, uuid) { return 0; }) + property var onSendTransaction: (function(userAddress, gasLimit, gasPrice, password){ return ""; }) + property var onSuccess: (function(){}) + + Component.onCompleted: { + walletModel.gasView.getGasPricePredictions() + } - height: 504 //% "Authorize %1 %2" - title: qsTrId("authorize--1--2").arg(Utils.stripTrailingZeros(ensPrice)).arg(asset.symbol) + title: qsTrId("authorize--1--2").arg(Utils.stripTrailingZeros(assetPrice)).arg(asset.symbol) property MessageDialog sendingError: MessageDialog { id: sendingError @@ -24,12 +30,18 @@ ModalPopup { standardButtons: StandardButton.Ok } + function setAsyncGasLimitResult(uuid, value) { + if (uuid === gasSelector.uuid) { + gasSelector.selectedGasLimit = value + } + } + function sendTransaction() { - let responseStr = profileModel.ens.registerENS(root.ensUsername, - selectFromAccount.selectedAccount.address, - gasSelector.selectedGasLimit, - gasSelector.selectedGasPrice, - transactionSigner.enteredPassword) + let responseStr = onSendTransaction(selectFromAccount.selectedAccount.address, + gasSelector.selectedGasLimit, + gasSelector.selectedGasPrice, + transactionSigner.enteredPassword); + let response = JSON.parse(responseStr) if (!response.success) { @@ -42,13 +54,16 @@ ModalPopup { return sendingError.open() } - usernameRegistered(username); + onSuccess(); + root.close(); } TransactionStackView { id: stack height: parent.height anchors.fill: parent + anchors.leftMargin: Style.current.padding + anchors.rightMargin: Style.current.padding initialItem: group1 isLastGroup: stack.currentGroup === group4 onGroupActivated: { @@ -58,7 +73,7 @@ ModalPopup { TransactionFormGroup { id: group1 //% "Authorize %1 %2" - headerText: qsTrId("authorize--1--2").arg(Utils.stripTrailingZeros(root.ensPrice)).arg(root.asset.symbol) + headerText: qsTrId("authorize--1--2").arg(Utils.stripTrailingZeros(root.assetPrice)).arg(root.asset.symbol) //% "Continue" footerText: qsTrId("continue") showBackBtn: false @@ -77,7 +92,7 @@ ModalPopup { //% "Choose account" label: qsTrId("choose-account") showBalanceForAssetSymbol: root.asset.symbol - minRequiredAssetBalance: root.ensPrice + minRequiredAssetBalance: root.assetPrice onSelectedAccountChanged: if (isValid) { gasSelector.estimateGas() } } RecipientSelector { @@ -85,7 +100,7 @@ ModalPopup { visible: false accounts: walletModel.accountsView.accounts contacts: profileModel.contacts.addedContacts - selectedRecipient: { "address": utilsModel.ensRegisterAddress, "type": RecipientSelector.Type.Address } + selectedRecipient: { "address": contractAddress, "type": RecipientSelector.Type.Address } readOnly: true onSelectedRecipientChanged: if (isValid) { gasSelector.estimateGas() } } @@ -100,11 +115,7 @@ ModalPopup { defaultCurrency: walletModel.balanceView.defaultCurrency width: stack.width property var estimateGas: Backpressure.debounce(gasSelector, 600, function() { - if (!(root.ensUsername !== "" && selectFromAccount.selectedAccount)) { - selectedGasLimit = 380000 - return - } - selectedGasLimit = profileModel.ens.registerENSGasEstimate(root.ensUsername, selectFromAccount.selectedAccount.address) + return root.estimateGasFunction(selectFromAccount.selectedAccount, uuid); }) } GasValidator { @@ -113,14 +124,14 @@ ModalPopup { anchors.bottomMargin: 8 selectedAccount: selectFromAccount.selectedAccount selectedAsset: root.asset - selectedAmount: parseFloat(ensPrice) + selectedAmount: parseFloat(root.assetPrice) selectedGasEthValue: gasSelector.selectedGasEthValue } } TransactionFormGroup { id: group3 //% "Authorize %1 %2" - headerText: qsTrId("authorize--1--2").arg(Utils.stripTrailingZeros(root.ensPrice)).arg(root.asset.symbol) + headerText: qsTrId("authorize--1--2").arg(Utils.stripTrailingZeros(root.assetPrice)).arg(root.asset.symbol) //% "Sign with password" footerText: qsTrId("sign-with-password") @@ -137,15 +148,15 @@ ModalPopup { asset: root.asset currency: walletModel.balanceView.defaultCurrency amount: { - const fiatValue = walletModel.balanceView.getFiatValue(root.ensPrice || 0, root.asset.symbol, currency) - return { "value": root.ensPrice, "fiatValue": fiatValue } + const fiatValue = walletModel.balanceView.getFiatValue(root.assetPrice || 0, root.asset.symbol, currency) + return { "value": root.assetPrice, "fiatValue": fiatValue } } } } TransactionFormGroup { id: group4 //% "Send %1 %2" - headerText: qsTrId("send--1--2").arg(Utils.stripTrailingZeros(root.ensPrice)).arg(root.asset.symbol) + headerText: qsTrId("send--1--2").arg(Utils.stripTrailingZeros(root.assetPrice)).arg(root.asset.symbol) //% "Sign with password" footerText: qsTrId("sign-with-password") @@ -177,13 +188,13 @@ ModalPopup { stack.back() } } - StatusButton { id: btnNext anchors.right: parent.right //% "Next" text: qsTrId("next") enabled: stack.currentGroup.isValid && !stack.currentGroup.isPending + state: stack.currentGroup.isPending ? "pending" : "default" onClicked: { const validity = stack.currentGroup.validate() if (validity.isValid && !validity.isPending) { diff --git a/ui/shared/status/StatusStickerMarket.qml b/ui/shared/status/StatusStickerMarket.qml index c09e44e7ab..fe8bc71f44 100644 --- a/ui/shared/status/StatusStickerMarket.qml +++ b/ui/shared/status/StatusStickerMarket.qml @@ -10,6 +10,8 @@ import "../../app/AppLayouts/Chat/ChatColumn/samples" Item { id: root property var stickerPacks: StickerPackData {} + property var stickerPurchasePopup + signal backClicked signal uninstallClicked(int packId) signal installClicked(var stickers, int packId, int index) @@ -82,22 +84,40 @@ Item { onCancelClicked: root.cancelClicked(packId) onUpdateClicked: root.updateClicked(packId) onBuyClicked: { - openPopup(stickerPackPurchaseModal) + root.stickerPurchasePopup = openPopup(stickerPackPurchaseModal) root.buyClicked(packId) } } } Component { id: stickerPackPurchaseModal - StatusStickerPackPurchaseModal { + StatusSNTTransactionModal { + contractAddress: utilsModel.stickerMarketAddress + assetPrice: price + estimateGasFunction: function(selectedAccount, uuid) { + if (packId < 0 || !selectedAccount || !price) return 325000 + return chatsModel.stickers.estimate(packId, selectedAccount.address, price, uuid) + } + onSendTransaction: function(selectedAddress, gasLimit, gasPrice, password) { + return chatsModel.stickers.buy(packId, + selectedAddress, + price, + gasLimit, + gasPrice, + password) + } onClosed: { destroy() } - stickerPackId: packId - packPrice: price width: stickerPackDetailsPopup.width height: stickerPackDetailsPopup.height - showBackBtn: stickerPackDetailsPopup.opened + } + } + + Connections { + target: chatsModel.stickers + onGasEstimateReturned: { + stickerPurchasePopup.setAsyncGasLimitResult(uuid, estimate) } } @@ -126,7 +146,7 @@ Item { onCancelClicked: root.cancelClicked(packId) onUpdateClicked: root.updateClicked(packId) onBuyClicked: { - openPopup(stickerPackPurchaseModal) + root.stickerPurchasePopup = openPopup(stickerPackPurchaseModal) root.buyClicked(packId) } } diff --git a/ui/shared/status/StatusStickerPackClickPopup.qml b/ui/shared/status/StatusStickerPackClickPopup.qml index 6762133681..4f9350f930 100644 --- a/ui/shared/status/StatusStickerPackClickPopup.qml +++ b/ui/shared/status/StatusStickerPackClickPopup.qml @@ -53,15 +53,26 @@ ModalPopup { anchors.topMargin: Style.current.padding Component { id: stickerPackPurchaseModal - StatusStickerPackPurchaseModal { + StatusSNTTransactionModal { + contractAddress: utilsModel.stickerMarketAddress + assetPrice: price + estimateGasFunction: function(selectedAccount, uuid) { + if (packId < 0 || !selectedAccount || !price) return 325000 + return chatsModel.stickers.estimate(packId, selectedAccount.address, price, uuid) + } + onSendTransaction: function(selectedAddress, gasLimit, gasPrice, password) { + return chatsModel.stickers.buy(packId, + selectedAddress, + price, + gasLimit, + gasPrice, + password) + } onClosed: { destroy() } - stickerPackId: packId - packPrice: price width: stickerPackDetailsPopup.width height: stickerPackDetailsPopup.height - showBackBtn: stickerPackDetailsPopup.opened } } } diff --git a/ui/shared/status/StatusStickerPackPurchaseModal.qml b/ui/shared/status/StatusStickerPackPurchaseModal.qml deleted file mode 100644 index bab51dce96..0000000000 --- a/ui/shared/status/StatusStickerPackPurchaseModal.qml +++ /dev/null @@ -1,254 +0,0 @@ -import QtQuick 2.13 -import QtQuick.Controls 2.13 -import QtQuick.Layouts 1.13 -import QtQuick.Dialogs 1.3 -import "../../imports" -import "../../shared" -import "../../shared/status" - -ModalPopup { - id: root - readonly property var asset: JSON.parse(walletModel.tokensView.getStatusToken()) - property int stickerPackId: -1 - property string packPrice - property bool showBackBtn: false - - Component.onCompleted: { - walletModel.gasView.getGasPricePredictions() - } - - //% "Authorize %1 %2" - title: qsTrId("authorize--1--2").arg(Utils.stripTrailingZeros(packPrice)).arg(asset.symbol) - - property MessageDialog sendingError: MessageDialog { - id: sendingError - //% "Error sending the transaction" - title: qsTrId("error-sending-the-transaction") - icon: StandardIcon.Critical - standardButtons: StandardButton.Ok - } - - function sendTransaction() { - let responseStr = chatsModel.stickers.buy(root.stickerPackId, - selectFromAccount.selectedAccount.address, - root.packPrice, - gasSelector.selectedGasLimit, - gasSelector.selectedGasPrice, - transactionSigner.enteredPassword) - let response = JSON.parse(responseStr) - - if (!response.success) { - if (Utils.isInvalidPasswordMessage(response.result)){ - //% "Wrong password" - transactionSigner.validationError = qsTrId("wrong-password") - return - } - sendingError.text = response.result - return sendingError.open() - } - root.close() - } - - TransactionStackView { - id: stack - height: parent.height - anchors.fill: parent - anchors.leftMargin: Style.current.padding - anchors.rightMargin: Style.current.padding - onGroupActivated: { - root.title = group.headerText - btnNext.text = group.footerText - } - TransactionFormGroup { - id: group1 - //% "Authorize %1 %2" - headerText: qsTrId("authorize--1--2").arg(Utils.stripTrailingZeros(root.packPrice)).arg(root.asset.symbol) - //% "Continue" - footerText: qsTrId("continue") - - StackView.onActivated: { - btnBack.visible = root.showBackBtn - } - - AccountSelector { - id: selectFromAccount - accounts: walletModel.accountsView.accounts - selectedAccount: { - const currAcc = walletModel.accountsView.currentAccount - if (currAcc.walletType !== Constants.watchWalletType) { - return currAcc - } - return null - } - currency: walletModel.balanceView.defaultCurrency - width: stack.width - //% "Choose account" - label: qsTrId("choose-account") - showBalanceForAssetSymbol: root.asset.symbol - minRequiredAssetBalance: root.packPrice - onSelectedAccountChanged: if (isValid) { gasSelector.estimateGas() } - } - RecipientSelector { - id: selectRecipient - visible: false - accounts: walletModel.accountsView.accounts - contacts: profileModel.contacts.addedContacts - selectedRecipient: { "address": utilsModel.stickerMarketAddress, "type": RecipientSelector.Type.Address } - readOnly: true - onSelectedRecipientChanged: if (isValid) { gasSelector.estimateGas() } - } - GasSelector { - id: gasSelector - visible: true - anchors.top: selectFromAccount.bottom - anchors.topMargin: Style.current.bigPadding * 2 - slowestGasPrice: parseFloat(walletModel.gasView.safeLowGasPrice) - fastestGasPrice: parseFloat(walletModel.gasView.fastestGasPrice) - getGasEthValue: walletModel.gasView.getGasEthValue - getFiatValue: walletModel.balanceView.getFiatValue - defaultCurrency: walletModel.balanceView.defaultCurrency - property var estimateGas: Backpressure.debounce(gasSelector, 600, function() { - if (!(root.stickerPackId > -1 && selectFromAccount.selectedAccount && root.packPrice && parseFloat(root.packPrice) > 0)) { - selectedGasLimit = 325000 - return - } - chatsModel.stickers.estimate(root.stickerPackId, selectFromAccount.selectedAccount.address, root.packPrice, uuid) - }) - } - Connections { - target: chatsModel.stickers - onGasEstimateReturned: { - if (uuid === gasSelector.uuid) { - gasSelector.selectedGasLimit = estimate - } - } - } - GasValidator { - id: gasValidator - anchors.bottom: parent.bottom - selectedAccount: selectFromAccount.selectedAccount - selectedAsset: root.asset - selectedAmount: parseFloat(packPrice) - selectedGasEthValue: gasSelector.selectedGasEthValue - } - } - TransactionFormGroup { - id: group3 - //% "Authorize %1 %2" - headerText: qsTrId("authorize--1--2").arg(Utils.stripTrailingZeros(root.packPrice)).arg(root.asset.symbol) - //% "Sign with password" - footerText: qsTrId("sign-with-password") - - StackView.onActivated: { - btnBack.visible = true - } - - TransactionPreview { - id: pvwTransaction - width: stack.width - fromAccount: selectFromAccount.selectedAccount - gas: { - "value": gasSelector.selectedGasEthValue, - "symbol": "ETH", - "fiatValue": gasSelector.selectedGasFiatValue - } - toAccount: selectRecipient.selectedRecipient - asset: root.asset - currency: walletModel.balanceView.defaultCurrency - amount: { - const fiatValue = walletModel.balanceView.getFiatValue(root.packPrice || 0, root.asset.symbol, currency) - return { "value": root.packPrice, "fiatValue": fiatValue } - } - } - } - TransactionFormGroup { - id: group4 - //% "Send %1 %2" - headerText: qsTrId("send--1--2").arg(Utils.stripTrailingZeros(root.packPrice)).arg(root.asset.symbol) - //% "Sign with password" - footerText: qsTrId("sign-with-password") - - TransactionSigner { - id: transactionSigner - width: stack.width - signingPhrase: walletModel.utilsView.signingPhrase - } - } - } - - footer: Item { - width: parent.width - height: btnNext.height - - StatusRoundButton { - id: btnBack - anchors.left: parent.left - icon.name: "arrow-right" - icon.width: 20 - icon.height: 16 - rotation: 180 - onClicked: { - if (stack.isFirstGroup) { - return root.close() - } - stack.back() - } - } - StatusButton { - id: btnNext - anchors.right: parent.right - //% "Next" - text: qsTrId("next") - enabled: stack.currentGroup.isValid && !stack.currentGroup.isPending - state: stack.currentGroup.isPending ? "pending" : "default" - onClicked: { - const isValid = stack.currentGroup.validate() - - if (stack.currentGroup.validate()) { - if (stack.isLastGroup) { - return root.sendTransaction() - } - stack.next() - } - } - } - - Connections { - target: chatsModel.stickers - onTransactionWasSent: { - //% "Transaction pending..." - toastMessage.title = qsTrId("ens-transaction-pending") - toastMessage.source = "../../../img/loading.svg" - toastMessage.iconColor = Style.current.primary - toastMessage.iconRotates = true - toastMessage.link = `${walletModel.utilsView.etherscanLink}/${txResult}` - toastMessage.open() - } - onTransactionCompleted: { - toastMessage.title = !success ? - //% "Could not buy Stickerpack" - qsTrId("could-not-buy-stickerpack") - : - //% "Stickerpack bought successfully" - qsTrId("stickerpack-bought-successfully"); - if (success) { - toastMessage.source = "../../../img/check-circle.svg" - toastMessage.iconColor = Style.current.success - } else { - toastMessage.source = "../../../img/block-icon.svg" - toastMessage.iconColor = Style.current.danger - } - - toastMessage.link = `${walletModel.utilsView.etherscanLink}/${txHash}` - toastMessage.open() - } - } - } -} - -/*##^## -Designer { - D{i:0;autoSize:true;height:480;width:640} -} -##^##*/ -