From ca8a0028a8e9bc2b028bcc2601bb246a875b7af6 Mon Sep 17 00:00:00 2001
From: Alex Jbanca <47811206+alexjba@users.noreply.github.com>
Date: Fri, 12 Jul 2024 00:00:15 +0300
Subject: [PATCH] feat(WalletConnect): Implement sign request modal (#15520)
* feat(WalletConnect): Implement sign request modal
1. Implementing sign request modal based on SignTransactionModalBase
2. Adding storybook page
3. Integrate it in the app
4. Removing DAppRequestModal
5. Update RoundImageWithBadge to preserve aspect ratio between badge and main image
* fix(WalletConnect): Remove unneeded properties from WalletConnectService API
Removing `selectedAccountAddress` and `loginType`. These properties are now passed through DAppsWorkflow API
* fix(WalletConnect): Removing unnecessary changes
---
storybook/pages/DAppRequestModalPage.qml | 196 ----------
storybook/pages/DAppSignRequestModalPage.qml | 137 +++++++
storybook/pages/DAppsWorkflowPage.qml | 3 +-
.../qmlTests/tests/tst_DAppsWorkflow.qml | 8 +-
.../tests/tst_SwapApproveCapModal.qml | 6 +-
.../qmlTests/tests/tst_SwapSignModal.qml | 8 +-
ui/StatusQ/src/assets.qrc | 2 +
ui/StatusQ/src/assets/img/icons/collapse.svg | 8 +
ui/StatusQ/src/assets/img/icons/expand.svg | 8 +
.../Wallet/panels/DAppsWorkflow.qml | 92 +++--
.../AppLayouts/Wallet/panels/WalletHeader.qml | 2 +
.../popups/SignTransactionModalBase.qml | 66 +++-
.../services/dapps/DAppsRequestHandler.qml | 7 +
.../services/dapps/WalletConnectService.qml | 2 -
.../popups/walletconnect/DAppRequestModal.qml | 362 ------------------
.../walletconnect/DAppSignRequestModal.qml | 192 ++++++++++
.../walletconnect/RoundImageWithBadge.qml | 10 +-
.../walletconnect/panels/ContentPanel.qml | 78 ++--
ui/imports/shared/popups/walletconnect/qmldir | 2 +-
ui/imports/shared/stores/DAppsStore.qml | 1 -
20 files changed, 540 insertions(+), 650 deletions(-)
delete mode 100644 storybook/pages/DAppRequestModalPage.qml
create mode 100644 storybook/pages/DAppSignRequestModalPage.qml
create mode 100644 ui/StatusQ/src/assets/img/icons/collapse.svg
create mode 100644 ui/StatusQ/src/assets/img/icons/expand.svg
delete mode 100644 ui/imports/shared/popups/walletconnect/DAppRequestModal.qml
create mode 100644 ui/imports/shared/popups/walletconnect/DAppSignRequestModal.qml
diff --git a/storybook/pages/DAppRequestModalPage.qml b/storybook/pages/DAppRequestModalPage.qml
deleted file mode 100644
index d32bb6259e..0000000000
--- a/storybook/pages/DAppRequestModalPage.qml
+++ /dev/null
@@ -1,196 +0,0 @@
-import QtQuick 2.15
-import QtQuick.Controls 2.15
-import QtQuick.Layouts 1.15
-import QtQml 2.15
-import Qt.labs.settings 1.0
-import QtTest 1.15
-
-import StatusQ.Core 0.1
-import StatusQ.Core.Utils 0.1
-import StatusQ.Controls 0.1
-import StatusQ.Components 0.1
-import StatusQ.Core.Theme 0.1
-import StatusQ.Popups.Dialog 0.1
-
-import Models 1.0
-import Storybook 1.0
-
-import shared.popups.walletconnect 1.0
-
-import SortFilterProxyModel 0.2
-
-import AppLayouts.Wallet.panels 1.0
-import AppLayouts.Wallet.services.dapps.types 1.0
-
-import utils 1.0
-import shared.stores 1.0
-
-Item {
- id: root
-
- function openModal() {
- modal.open()
- }
-
- // qml Splitter
- SplitView {
- anchors.fill: parent
-
- ColumnLayout {
- SplitView.fillWidth: true
-
- Component.onCompleted: root.openModal()
-
- DAppRequestModal {
- id: modal
-
- anchors.centerIn: parent
-
- spacing: 8
-
- dappName: settings.dappName
- dappUrl: settings.dappUrl
- dappIcon: settings.dappIcon
- payloadData: d.currentPayload ? d.currentPayload.payloadData : null
- method: d.currentPayload ? d.currentPayload.method : ""
- maxFeesText: d.currentPayload ? d.currentPayload.maxFeesText : ""
- maxFeesEthText: d.currentPayload ? d.currentPayload.maxFeesEthText : ""
- enoughFunds: settings.enoughFunds
- estimatedTimeText: d.currentPayload ? d.currentPayload.estimatedTimeText : ""
-
- account: d.selectedAccount
- network: d.selectedNetwork
-
- onSign: {
- console.log("Sign button clicked")
- }
- onReject: {
- console.log("Reject button clicked")
- }
- }
-
- StatusButton {
- id: openButton
-
- Layout.alignment: Qt.AlignHCenter
- Layout.margins: 20
-
- text: "Open DAppRequestModal"
-
- onClicked: root.openModal()
- }
-
- ColumnLayout {}
- }
-
- ColumnLayout {
- id: optionsSpace
-
- TextField {
- id: dappNameTextField
-
- text: settings.dappName
- onTextChanged: settings.dappName = text
- }
- TextField {
- id: dappUrlTextField
-
- text: settings.dappUrl
- onTextChanged: settings.dappUrl = text
- }
- TextField {
- id: dappIconTextField
-
- text: settings.dappIcon
- onTextChanged: settings.dappIcon = text
- }
- TextField {
- id: accountDisplayTextField
-
- text: settings.accountDisplay
- onTextChanged: settings.accountDisplay = text
- }
- StatusComboBox {
- id: methodsComboBox
-
- model: d.methodsModel
- control.textRole: "method"
- currentIndex: settings.payloadMethod
- onCurrentIndexChanged: {
- d.currentPayload = null
- settings.payloadMethod = currentIndex
- d.currentPayload = d.payloadOptions[currentIndex]
- }
- }
- StatusCheckBox {
- id: enoughFundsCheckBox
-
- text: "Enough funds"
- checked: settings.enoughFunds
- onCheckedChanged: settings.enoughFunds = checked
- }
-
- Item { Layout.fillHeight: true }
- }
- }
-
- Settings {
- id: settings
-
- property string dappName: "OpenSea"
- property string dappUrl: "opensea.io"
- property string dappIcon: "https://opensea.io/static/images/logos/opensea-logo.svg"
- property string accountDisplay: "helloworld"
- property int payloadMethod: 0
- property bool enoughFunds: true
- }
-
- QtObject {
- id: d
-
- Component.onCompleted: methodsModel.append(payloadOptions)
-
- readonly property var accountsModel: WalletAccountsModel{}
- readonly property var selectedAccount: accountsModel.data[0]
-
- readonly property var selectedNetwork: NetworksModel.flatNetworks.get(0)
-
- readonly property ListModel methodsModel: ListModel {}
- property var currentPayload: payloadOptions[settings.payloadMethod]
- property string maxFeesText: ""
- property string estimatedTimeText: ""
-
- readonly property var payloadOptions: [
- {
- payloadData: {"message":"This is a message to sign.\nSigning this will prove ownership of the account."},
- method: SessionRequest.methods.personalSign.name,
- maxFeesText: "",
- maxFeesEthText: "",
- estimatedTimeText: ""
- },
- {
- payloadData: {"message": "{\"types\":{\"EIP712Domain\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"chainId\",\"type\":\"uint256\"},{\"name\":\"verifyingContract\",\"type\":\"address\"}],\"Person\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"wallet\",\"type\":\"address\"}],\"Mail\":[{\"name\":\"from\",\"type\":\"Person\"},{\"name\":\"to\",\"type\":\"Person\"},{\"name\":\"contents\",\"type\":\"string\"}]},\"primaryType\":\"Mail\",\"domain\":{\"name\":\"Ether Mail\",\"version\":\"1\",\"chainId\":1,\"verifyingContract\":\"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC\"},\"message\":{\"from\":{\"name\":\"Cow\",\"wallet\":\"0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826\"},\"to\":{\"name\":\"Bob\",\"wallet\":\"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB\"},\"contents\":\"Hello, Bob!\"}}"},
- method: SessionRequest.methods.signTypedData_v4.name,
- maxFeesText: "",
- maxFeesEthText: "",
- estimatedTimeText: ""
- },
- {
- payloadData: {"tx":{"data":"0x","from":"0xE2d622C817878dA5143bBE06866ca8E35273Ba8a","gasLimit":"0x5208","gasPrice":"0x048ddbc5","nonce":"0x2a","to":"0xE2d622C817878dA5143bBE06866ca8E35273Ba8a","value":"0x00"}},
- method: SessionRequest.methods.signTransaction.name,
- maxFeesText: "1.82 EUR",
- maxFeesEthText: "0.0001 ETH",
- estimatedTimeText: "3-5 mins"
- },
- {
- payloadData: {"tx":{"data":"0x","from":"0xE2d622C817878dA5143bBE06866ca8E35273Ba8a","gasLimit":"0x5208","gasPrice":"0x048ddbc5","nonce":"0x2a","to":"0xE2d622C817878dA5143bBE06866ca8E35273Ba8a","value":"0x00"}},
- method: SessionRequest.methods.sendTransaction.name,
- maxFeesText: "0.92 EUR",
- maxFeesEthText: "0.00005 ETH",
- estimatedTimeText: "1-2 mins"
- }
- ]
- }
-}
-
-// category: Wallet
diff --git a/storybook/pages/DAppSignRequestModalPage.qml b/storybook/pages/DAppSignRequestModalPage.qml
new file mode 100644
index 0000000000..edf7bb3db4
--- /dev/null
+++ b/storybook/pages/DAppSignRequestModalPage.qml
@@ -0,0 +1,137 @@
+// category: Popups
+
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+import QtQuick.Layouts 1.15
+import shared.popups.walletconnect 1.0
+import utils 1.0
+import Storybook 1.0
+
+SplitView {
+ id: root
+
+ PopupBackground {
+ SplitView.fillWidth: true
+ SplitView.fillHeight: true
+ Button {
+ anchors.centerIn: parent
+ text: "Open"
+ onClicked: dappSignRequestModal.visible = true
+ }
+
+ DAppSignRequestModal {
+ id: dappSignRequestModal
+
+ loginType: loginType.currentValue
+ visible: true
+ modal: false
+ closePolicy: Popup.NoAutoClose
+ dappUrl: "https://example.com"
+ dappIcon: "https://picsum.photos/200/200"
+ dappName: "OpenSea"
+ accountColor: "blue"
+ accountName: "Account Name"
+ accountAddress: "0xE2d622C817878dA5143bBE06866ca8E35273Ba8"
+ networkName: "Ethereum"
+ networkIconPath: "https://picsum.photos/200/200"
+
+ currentCurrency: "EUR"
+ fiatFees: fiatFees.text
+ cryptoFees: "0.001"
+ estimatedTime: "3-5 minutes"
+ feesLoading: feesLoading.checked
+ hasFees: hasFees.checked
+ enoughFundsForTransaction: enoughFeesForTransaction.checked
+ enoughFundsForFees: enoughFeesForGas.checked
+
+ // sun emoji
+ accountEmoji: "\u2600"
+ requestPayload: controls.contentToSign[contentToSignComboBox.currentIndex]
+ signingTransaction: signingTransaction.checked
+
+ onAccepted: print ("Accepted")
+ onRejected: print ("Rejected")
+ }
+ }
+ Pane {
+ id: controls
+ SplitView.preferredWidth: 300
+ SplitView.fillHeight: true
+
+ readonly property var contentToSign: ['{
+ "id": 1714038548266495,
+ "params": {
+ "chainld": "eip155:11155111",
+ "request": {
+ "expiryTimestamp": 1714038848,
+ "method": "eth_signTransaction",
+ "params": [{
+ "data": "0x",
+ "from": "0xE2d622C817878dA5143bBE06866ca8E35273Ba8",
+ "gasLimit": "0x5208",
+ "gasPrice": "0xa677ef31",
+ "nonce": "0x27",
+ "to": "0xE2d622C817878dA5143bBE06866ca8E35273Ba8a",
+ "value": "0x00"
+ }]
+ }
+ },
+ "topic": "a0f85b23a1f3a540d85760a523963165fb92169d57320c",
+ "verifyContext": {
+ "verified": {
+ "isScam": false,
+ "origin": "https://react-app.walletconnect.com/",
+ "validation": "VALID",
+ "verifyUrl": "https://verify.walletconnect.com/"
+ }
+ }
+ }',
+ '"tx":{"data":"0x","from":"0xE2d622C817878dA5143bBE06866ca8E35273Ba8a","gasLimit":"0x5208","gasPrice":"0x048ddbc5","nonce":"0x2a","to":"0xE2d622C817878dA5143bBE06866ca8E35273Ba8a","value":"0x00"}',
+ ""
+ ]
+
+ ColumnLayout {
+ TextField {
+ id: fiatFees
+ text: "1.54"
+ }
+ ComboBox {
+ id: loginType
+ model: [{name: "Password", value: Constants.LoginType.Password}, {name: "Biometrics", value: Constants.LoginType.Biometrics}, {name: "Keycard", value: Constants.LoginType.Keycard}]
+ textRole: "name"
+ valueRole: "value"
+ currentIndex: 0
+ }
+ ComboBox {
+ id: contentToSignComboBox
+ model: ["Long content to sign", "Short content to sign", "Empty content to sign"]
+ currentIndex: 0
+ }
+ CheckBox {
+ id: enoughFeesForTransaction
+ text: "Enough fees for transaction"
+ checked: true
+ }
+ CheckBox {
+ id: enoughFeesForGas
+ text: "Enough fees for gas"
+ checked: true
+ }
+ CheckBox {
+ id: feesLoading
+ text: "Fees loading"
+ checked: false
+ }
+ CheckBox {
+ id: hasFees
+ text: "Has fees"
+ checked: true
+ }
+ CheckBox {
+ id: signingTransaction
+ text: "Signing transaction"
+ checked: false
+ }
+ }
+ }
+}
diff --git a/storybook/pages/DAppsWorkflowPage.qml b/storybook/pages/DAppsWorkflowPage.qml
index f278db1b70..73212670f5 100644
--- a/storybook/pages/DAppsWorkflowPage.qml
+++ b/storybook/pages/DAppsWorkflowPage.qml
@@ -61,6 +61,8 @@ Item {
spacing: 8
wcService: walletConnectService
+ loginType: Constants.LoginType.Biometrics
+ selectedAccountAddress: ""
}
}
ColumnLayout {}
@@ -373,7 +375,6 @@ Item {
}
walletRootStore: QObject {
- property string selectedAddress: ""
property var filteredFlatModel: SortFilterProxyModel {
sourceModel: NetworksModel.flatNetworks
filters: ValueFilter { roleName: "isTest"; value: settings.testNetworks; }
diff --git a/storybook/qmlTests/tests/tst_DAppsWorkflow.qml b/storybook/qmlTests/tests/tst_DAppsWorkflow.qml
index d7daed93fc..7c764613af 100644
--- a/storybook/qmlTests/tests/tst_DAppsWorkflow.qml
+++ b/storybook/qmlTests/tests/tst_DAppsWorkflow.qml
@@ -121,7 +121,6 @@ Item {
id: walletStoreComponent
QtObject {
- property string selectedAddress: ""
readonly property ListModel filteredFlatModel: ListModel {
ListElement { chainId: 1 }
ListElement {
@@ -503,6 +502,7 @@ Item {
Component {
id: componentUnderTest
DAppsWorkflow {
+ loginType: Constants.LoginType.Password
}
}
@@ -605,9 +605,9 @@ Item {
verify(popup.visible)
compare(popup.dappName, td.session.peer.metadata.name)
- compare(popup.account.name, td.account.name)
- compare(popup.account.address, td.account.address)
- compare(popup.network.chainId, td.network.chainId)
+ compare(popup.accountName, td.account.name)
+ compare(popup.accountAddress, td.account.address)
+ compare(popup.networkName, td.network.chainName)
popup.close()
waitForRendering(controlUnderTest)
diff --git a/storybook/qmlTests/tests/tst_SwapApproveCapModal.qml b/storybook/qmlTests/tests/tst_SwapApproveCapModal.qml
index 4b7b308580..cb7b63f39e 100644
--- a/storybook/qmlTests/tests/tst_SwapApproveCapModal.qml
+++ b/storybook/qmlTests/tests/tst_SwapApproveCapModal.qml
@@ -93,16 +93,16 @@ Item {
.arg(controlUnderTest.formatBigNumber(controlUnderTest.fromTokenAmount)).arg(controlUnderTest.fromTokenSymbol)
.arg(controlUnderTest.accountName).arg(controlUnderTest.serviceProviderURL).arg(controlUnderTest.networkName))
- const fromImageHidden = findChild(controlUnderTest.contentItem, "fromImage")
+ const fromImageHidden = findChild(controlUnderTest.contentItem, "fromImageIdenticon")
compare(fromImageHidden.visible, false)
const fromImage = findChild(controlUnderTest.contentItem, "fromImageIdenticon")
verify(!!fromImage)
compare(fromImage.asset.emoji, controlUnderTest.accountEmoji)
compare(fromImage.asset.color, controlUnderTest.accountColor)
- const toImage = findChild(controlUnderTest.contentItem, "toImage")
+ const toImage = findChild(controlUnderTest.contentItem, "toImageIdenticon")
verify(!!toImage)
- compare(toImage.image.source, Constants.tokenIcon(controlUnderTest.fromTokenSymbol))
+ compare(toImage.asset.name, Constants.tokenIcon(controlUnderTest.fromTokenSymbol))
// spending cap box
const spendingCapBox = findChild(controlUnderTest.contentItem, "spendingCapBox")
diff --git a/storybook/qmlTests/tests/tst_SwapSignModal.qml b/storybook/qmlTests/tests/tst_SwapSignModal.qml
index ab37cae83c..beaa50e282 100644
--- a/storybook/qmlTests/tests/tst_SwapSignModal.qml
+++ b/storybook/qmlTests/tests/tst_SwapSignModal.qml
@@ -95,12 +95,12 @@ Item {
const headerText = findChild(controlUnderTest.contentItem, "headerText")
verify(!!headerText)
compare(headerText.text, qsTr("Swap 1000.123456789 SNT to 1.42 ETH in %1 on %2").arg(controlUnderTest.accountName).arg(controlUnderTest.networkName))
- const fromImage = findChild(controlUnderTest.contentItem, "fromImage")
+ const fromImage = findChild(controlUnderTest.contentItem, "fromImageIdenticon")
verify(!!fromImage)
- compare(fromImage.image.source, Constants.tokenIcon(controlUnderTest.fromTokenSymbol))
- const toImage = findChild(controlUnderTest.contentItem, "toImage")
+ compare(fromImage.asset.name, Constants.tokenIcon(controlUnderTest.fromTokenSymbol))
+ const toImage = findChild(controlUnderTest.contentItem, "toImageIdenticon")
verify(!!toImage)
- compare(toImage.image.source, Constants.tokenIcon(controlUnderTest.toTokenSymbol))
+ compare(toImage.asset.name, Constants.tokenIcon(controlUnderTest.toTokenSymbol))
// pay box
const payBox = findChild(controlUnderTest.contentItem, "payBox")
diff --git a/ui/StatusQ/src/assets.qrc b/ui/StatusQ/src/assets.qrc
index af5b276c17..e6c3245fbf 100644
--- a/ui/StatusQ/src/assets.qrc
+++ b/ui/StatusQ/src/assets.qrc
@@ -165,6 +165,7 @@
assets/img/icons/checkmark.svg
assets/img/icons/chevron-down.svg
assets/img/icons/chevron-up.svg
+ assets/img/icons/collapse.svg
assets/img/icons/clear.svg
assets/img/icons/close-circle.svg
assets/img/icons/close.svg
@@ -187,6 +188,7 @@
assets/img/icons/emojis.svg
assets/img/icons/ETH.png
assets/img/icons/exchange.svg
+ assets/img/icons/expand.svg
assets/img/icons/external.svg
assets/img/icons/external-link.svg
assets/img/icons/face-id.svg
diff --git a/ui/StatusQ/src/assets/img/icons/collapse.svg b/ui/StatusQ/src/assets/img/icons/collapse.svg
new file mode 100644
index 0000000000..c764b4aa0e
--- /dev/null
+++ b/ui/StatusQ/src/assets/img/icons/collapse.svg
@@ -0,0 +1,8 @@
+
diff --git a/ui/StatusQ/src/assets/img/icons/expand.svg b/ui/StatusQ/src/assets/img/icons/expand.svg
new file mode 100644
index 0000000000..820a769ffd
--- /dev/null
+++ b/ui/StatusQ/src/assets/img/icons/expand.svg
@@ -0,0 +1,8 @@
+
diff --git a/ui/app/AppLayouts/Wallet/panels/DAppsWorkflow.qml b/ui/app/AppLayouts/Wallet/panels/DAppsWorkflow.qml
index a716fa0b59..dd2c6d1201 100644
--- a/ui/app/AppLayouts/Wallet/panels/DAppsWorkflow.qml
+++ b/ui/app/AppLayouts/Wallet/panels/DAppsWorkflow.qml
@@ -11,13 +11,15 @@ import shared.popups.walletconnect 1.0
import AppLayouts.Wallet.services.dapps 1.0
import AppLayouts.Wallet.services.dapps.types 1.0
-import shared.stores 1.0
import utils 1.0
DappsComboBox {
id: root
required property WalletConnectService wcService
+ // Values mapped to Constants.LoginType
+ required property int loginType
+ property string selectedAccountAddress
signal pairWCReady()
@@ -85,7 +87,7 @@ DappsComboBox {
}
]
}
- selectedAccountAddress: root.wcService.selectedAccountAddress
+ selectedAccountAddress: root.selectedAccountAddress
dAppUrl: proposalMedatada.url
dAppName: proposalMedatada.name
@@ -116,26 +118,62 @@ DappsComboBox {
property SessionRequestResolved request: null
- sourceComponent: DAppRequestModal {
- account: request.account
- network: request.network
-
- dappName: request.dappName
- dappUrl: request.dappUrl
- dappIcon: request.dappIcon
-
- payloadData: request.data
- method: request.method
- maxFeesText: request.maxFeesText
- maxFeesEthText: request.maxFeesEthText
- enoughFunds: request.enoughFunds
- estimatedTimeText: request.estimatedTimeText
-
+ sourceComponent: DAppSignRequestModal {
+ objectName: "dappsRequestModal"
+ loginType: request.account.migragedToKeycard ? Constants.LoginType.Keycard : root.loginType
visible: true
- onClosed: sessionRequestLoader.active = false
+ dappUrl: request.dappUrl
+ dappIcon: request.dappIcon
+ dappName: request.dappName
- onSign: {
+ accountColor: request.account.color
+ accountName: request.account.name
+ accountAddress: request.account.address
+ accountEmoji: request.account.emoji
+
+ networkName: request.network.chainName
+ networkIconPath: Style.svg(request.network.iconUrl)
+
+ currentCurrency: ""
+ fiatFees: request.maxFeesText
+ cryptoFees: request.maxFeesEthText
+ estimatedTime: request.estimatedTimeText
+ feesLoading: !request.maxFeesText || !request.maxFeesEthText
+ hasFees: signingTransaction
+ enoughFundsForTransaction: request.enoughFunds
+ enoughFundsForFees: request.enoughFunds
+
+ signingTransaction: request.method === SessionRequest.methods.signTransaction.name || request.method === SessionRequest.methods.sendTransaction.name
+ requestPayload: {
+ switch(request.method) {
+ case SessionRequest.methods.personalSign.name:
+ return SessionRequest.methods.personalSign.getMessageFromData(request.data)
+ case SessionRequest.methods.sign.name: {
+ return SessionRequest.methods.sign.getMessageFromData(request.data)
+ }
+ case SessionRequest.methods.signTypedData_v4.name: {
+ const stringPayload = SessionRequest.methods.signTypedData_v4.getMessageFromData(request.data)
+ return JSON.stringify(JSON.parse(stringPayload), null, 2)
+ }
+ case SessionRequest.methods.signTypedData.name: {
+ const stringPayload = SessionRequest.methods.signTypedData.getMessageFromData(root.payloadData)
+ return JSON.stringify(JSON.parse(stringPayload), null, 2)
+ }
+ case SessionRequest.methods.signTransaction.name: {
+ const jsonPayload = SessionRequest.methods.signTransaction.getTxObjFromData(request.data)
+ return JSON.stringify(jsonPayload, null, 2)
+ }
+ case SessionRequest.methods.sendTransaction.name: {
+ const jsonPayload = SessionRequest.methods.sendTransaction.getTxObjFromData(request.data)
+ return JSON.stringify(jsonPayload, null, 2)
+ }
+ }
+ }
+
+ onClosed: Qt.callLater( () => sessionRequestLoader.active = false)
+
+ onAccepted: {
if (!request) {
console.error("Error signing: request is null")
return
@@ -143,28 +181,32 @@ DappsComboBox {
root.wcService.requestHandler.authenticate(request)
}
- onReject: {
+ onRejected: {
let userRejected = true
root.wcService.requestHandler.rejectSessionRequest(request, userRejected)
- close()
}
Connections {
target: root.wcService.requestHandler
function onMaxFeesUpdated(maxFees, maxFeesWei, haveEnoughFunds, symbol) {
- maxFeesText = `${maxFees.toFixed(2)} ${symbol}`
+ fiatFees = maxFees
+ currentCurrency = symbol
+
var ethStr = "?"
try {
ethStr = globalUtils.wei2Eth(maxFeesWei, 9)
} catch (e) {
// ignore error in case of tests and storybook where we don't have access to globalUtils
}
- maxFeesEthText = `${ethStr} ETH`
- enoughFunds = haveEnoughFunds
+ cryptoFees = ethStr
+ enoughFundsForTransaction = haveEnoughFunds
+ enoughFundsForFees = haveEnoughFunds
+ feesLoading = false
+ hasFees = !!maxFees
}
function onEstimatedTimeUpdated(minMinutes, maxMinutes) {
- estimatedTimeText = qsTr("%1-%2mins").arg(minMinutes).arg(maxMinutes)
+ estimatedTime = qsTr("%1-%2mins").arg(minMinutes).arg(maxMinutes)
}
}
}
diff --git a/ui/app/AppLayouts/Wallet/panels/WalletHeader.qml b/ui/app/AppLayouts/Wallet/panels/WalletHeader.qml
index a9944edca8..c8c8d9ae3a 100644
--- a/ui/app/AppLayouts/Wallet/panels/WalletHeader.qml
+++ b/ui/app/AppLayouts/Wallet/panels/WalletHeader.qml
@@ -128,6 +128,8 @@ Item {
enabled: !!Global.walletConnectService
wcService: Global.walletConnectService
+ loginType: root.store.loginType
+ selectedAccountAddress: root.walletStore.selectedAddress
}
StatusButton {
diff --git a/ui/app/AppLayouts/Wallet/popups/SignTransactionModalBase.qml b/ui/app/AppLayouts/Wallet/popups/SignTransactionModalBase.qml
index f145bd9fb0..c9b88707f3 100644
--- a/ui/app/AppLayouts/Wallet/popups/SignTransactionModalBase.qml
+++ b/ui/app/AppLayouts/Wallet/popups/SignTransactionModalBase.qml
@@ -2,6 +2,7 @@ import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQml.Models 2.15
+import QtGraphicalEffects 1.15
import StatusQ 0.1
import StatusQ.Core 0.1
@@ -24,6 +25,7 @@ StatusDialog {
property Component headerIconComponent
property bool feesLoading
+ property bool signButtonEnabled: true
property ObjectModel leftFooterContents
property ObjectModel rightFooterContents: ObjectModel {
@@ -39,7 +41,7 @@ StatusDialog {
StatusButton {
objectName: "signButton"
id: signButton
- interactive: !root.feesLoading
+ interactive: !root.feesLoading && root.signButtonEnabled
icon.name: Constants.authenticationIconByType[root.loginType]
text: qsTr("Sign")
onClicked: root.accept() // close and emit accepted() signal
@@ -51,11 +53,14 @@ StatusDialog {
property url fromImageSource
property alias fromImageSmartIdenticon: fromImageSmartIdenticon
property url toImageSource
+ readonly property alias toImageSmartIdenticon: toImageSmartIdenticon
property alias headerMainText: headerMainText.text
- property alias headerSubTextLayout: headerSubTextLayout.children
+ readonly property alias headerSubTextLayout: headerSubTextLayout.children
property string infoTagText
+ readonly property alias infoTag: infoTag
+ property bool showHeaderDivider: true
- default property alias contents: contentsLayout.children
+ default property alias contents: contentsLayout.data
width: 480
padding: 0
@@ -127,26 +132,48 @@ StatusDialog {
id: fromImageSmartIdenticon
width: 40
height: 40
+ asset.name: root.fromImageSource
+ asset.width: 40
+ asset.height: 40
asset.bgWidth: 40
asset.bgHeight: 40
+ asset.color: "transparent"
+ asset.bgColor: "transparent"
visible: !!asset.name
+ layer.enabled: toImageSmartIdenticon.visible
+ layer.effect: OpacityMask {
+ id: mask
+ invert: true
+
+ maskSource: Item {
+ width: mask.width + 4
+ height: mask.height + 4
+
+ Rectangle {
+ anchors.centerIn: parent
+ anchors.horizontalCenterOffset: toImageSmartIdenticon.width - 10
+
+ width: parent.width
+ height: width
+ radius: width / 2
+ }
+ }
+ }
}
- StatusRoundedImage {
- objectName: "fromImage"
- width: 42
- height: 42
- border.width: 2
- border.color: "transparent"
- image.source: root.fromImageSource
- visible: root.fromImageSource.toString() !== ""
- }
- StatusRoundedImage {
- objectName: "toImage"
- width: 42
- height: 42
- border.width: 2
- border.color: Theme.palette.statusBadge.foregroundColor
- image.source: root.toImageSource
+
+ StatusSmartIdenticon {
+ objectName: "toImageIdenticon"
+ id: toImageSmartIdenticon
+ width: 40
+ height: 40
+ asset.bgWidth: 40
+ asset.bgHeight: 40
+ visible: !!asset.name || !!asset.source
+ asset.name: root.toImageSource
+ asset.width: 40
+ asset.height: 40
+ asset.color: "transparent"
+ asset.bgColor: "transparent"
}
}
@@ -182,6 +209,7 @@ StatusDialog {
StatusDialogDivider {
Layout.fillWidth: true
Layout.bottomMargin: Style.current.bigPadding
+ visible: root.showHeaderDivider
}
ColumnLayout {
diff --git a/ui/app/AppLayouts/Wallet/services/dapps/DAppsRequestHandler.qml b/ui/app/AppLayouts/Wallet/services/dapps/DAppsRequestHandler.qml
index 3be258f1e4..f865c38a2c 100644
--- a/ui/app/AppLayouts/Wallet/services/dapps/DAppsRequestHandler.qml
+++ b/ui/app/AppLayouts/Wallet/services/dapps/DAppsRequestHandler.qml
@@ -159,6 +159,13 @@ QObject {
obj.resolveDappInfoFromSession(session)
root.sessionRequest(obj)
// TODO #15192: update maxFees
+ if (!event.params.request.params[0].gasLimit || !event.params.request.params[0].gasPrice) {
+ root.maxFeesUpdated(0, 0, true, "")
+ root.estimatedTimeUpdated(0, 0)
+ return
+ }
+
+
let gasLimit = parseFloat(parseInt(event.params.request.params[0].gasLimit, 16));
let gasPrice = parseFloat(parseInt(event.params.request.params[0].gasPrice, 16));
let maxFees = gasLimit * gasPrice
diff --git a/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml b/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml
index 488d1a9f09..b607cce9ab 100644
--- a/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml
+++ b/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml
@@ -32,8 +32,6 @@ QObject {
required property DAppsStore store
required property var walletRootStore
- readonly property string selectedAccountAddress: walletRootStore.selectedAddress
-
readonly property alias dappsModel: dappsProvider.dappsModel
readonly property alias requestHandler: requestHandler
diff --git a/ui/imports/shared/popups/walletconnect/DAppRequestModal.qml b/ui/imports/shared/popups/walletconnect/DAppRequestModal.qml
deleted file mode 100644
index fb8b4969bf..0000000000
--- a/ui/imports/shared/popups/walletconnect/DAppRequestModal.qml
+++ /dev/null
@@ -1,362 +0,0 @@
-import QtQuick 2.15
-import QtQuick.Layouts 1.15
-import QtQml.Models 2.15
-
-import StatusQ.Core 0.1
-import StatusQ.Popups.Dialog 0.1
-import StatusQ.Controls 0.1
-import StatusQ.Components 0.1
-import StatusQ.Core.Theme 0.1
-import StatusQ.Core.Utils 0.1 as StatusQ
-
-import shared.popups.walletconnect.panels 1.0
-import utils 1.0
-
-import AppLayouts.Wallet.services.dapps.types 1.0
-
-StatusDialog {
- id: root
-
- objectName: "dappsRequestModal"
-
- implicitWidth: 480
-
- required property string dappName
- required property string dappUrl
- required property url dappIcon
- required property string method
- required property var payloadData
- property string maxFeesText: ""
- property string maxFeesEthText: ""
- property bool enoughFunds: false
- property string estimatedTimeText: ""
-
- required property var account
- property var network: null
-
- signal sign()
- signal reject()
-
- title: qsTr("Sign request")
-
- padding: 20
-
- onPayloadDataChanged: d.updateDisplay()
- onMethodChanged: d.updateDisplay()
- Component.onCompleted: d.updateDisplay()
-
- contentItem: StatusScrollView {
- id: scrollView
- padding: 0
- ColumnLayout {
- spacing: 20
- clip: true
-
- width: scrollView.availableWidth
-
- IntentionPanel {
- Layout.fillWidth: true
-
- dappName: root.dappName
- dappIcon: root.dappIcon
- account: root.account
-
- userDisplayNaming: d.userDisplayNaming
- }
-
- ContentPanel {
- Layout.fillWidth: true
- Layout.maximumHeight: 340
-
- payloadToDisplay: d.payloadToDisplay
- }
-
- // TODO: externalize as a TargetPanel
- ColumnLayout {
- spacing: 8
-
- StatusBaseText {
- text: qsTr("Sign with")
- font.pixelSize: 13
- color: Theme.palette.directColor1
- }
-
- // TODO #14762: implement proper control to display the accounts details
- Rectangle {
- Layout.fillWidth: true
- Layout.preferredHeight: 76
-
- radius: 8
- border.width: 1
- border.color: Theme.palette.baseColor2
- color: "transparent"
-
- RowLayout {
- spacing: 12
- anchors.fill: parent
- anchors.margins: 16
-
- StatusSmartIdenticon {
- width: 40
- height: 40
-
- asset: StatusAssetSettings {
- color: Theme.palette.primaryColor1
- isImage: false
- isLetterIdenticon: true
- useAcronymForLetterIdenticon: false
- emoji: root.account.emoji
- }
- }
-
- ColumnLayout {
- Layout.alignment: Qt.AlignLeft
-
- StatusBaseText {
- text: root.account.name
-
- Layout.alignment: Qt.AlignLeft
-
- font.pixelSize: 13
- }
- StatusBaseText {
- text: StatusQ.Utils.elideAndFormatWalletAddress(root.account.address, 6, 4)
-
- Layout.alignment: Qt.AlignLeft
-
- font.pixelSize: 13
-
- color: Theme.palette.baseColor1
- }
- }
-
- Item {Layout.fillWidth: true }
- }
- }
-
- StatusBaseText {
- text: qsTr("Network")
- font.pixelSize: 13
- color: Theme.palette.directColor1
- }
-
- // TODO #14762: implement proper control to display the chain
- Rectangle {
- Layout.fillWidth: true
- Layout.preferredHeight: 76
-
- visible: root.network !== null
-
- radius: 8
- border.width: 1
- border.color: Theme.palette.baseColor2
- color: "transparent"
-
- RowLayout {
- spacing: 12
- anchors.fill: parent
- anchors.margins: 16
-
- StatusSmartIdenticon {
- width: 40
- height: 40
-
- asset: StatusAssetSettings {
- isImage: true
- name: !!root.network ? Style.svg("tiny/" + root.network.iconUrl) : ""
- }
- }
-
- StatusBaseText {
- text: !!root.network ? root.network.chainName : ""
-
- Layout.alignment: Qt.AlignLeft
-
- font.pixelSize: 13
- }
- Item {Layout.fillWidth: true }
- }
- }
-
- StatusBaseText {
- text: qsTr("Fees")
- font.pixelSize: 13
- color: Theme.palette.directColor1
- visible: d.isTransaction()
- }
-
- Rectangle {
- Layout.fillWidth: true
- Layout.preferredHeight: 76
-
- visible: root.network !== null && d.isTransaction()
-
- radius: 8
- border.width: 1
- border.color: Theme.palette.baseColor2
- color: "transparent"
-
- RowLayout {
- spacing: 12
- anchors.fill: parent
- anchors.margins: 16
-
- StatusBaseText {
- text: qsTr("Max. fees on %1").arg(!!root.network && root.network.chainName)
-
- Layout.alignment: Qt.AlignLeft | Qt.AlignTop
-
- font.pixelSize: 13
- color: Theme.palette.baseColor1
- }
-
- Item {Layout.fillWidth: true }
-
- ColumnLayout {
- StatusBaseText {
- text: root.maxFeesText
-
- Layout.alignment: Qt.AlignRight
-
- font.pixelSize: 13
- color: root.enoughFunds ? Theme.palette.directColor1 : Theme.palette.dangerColor1
- }
-
- StatusBaseText {
- text: root.maxFeesEthText
-
- Layout.alignment: Qt.AlignRight
-
- font.pixelSize: 13
- color: root.enoughFunds ? Theme.palette.baseColor1 : Theme.palette.dangerColor1
- }
- }
- }
- }
- }
- }
- }
-
- header: StatusDialogHeader {
- leftComponent: Item {
- width: 46
- height: 46
-
- StatusSmartIdenticon {
- anchors.fill: parent
- anchors.margins: 3
-
- asset: StatusAssetSettings {
- width: 40
- height: 40
- bgRadius: bgWidth / 2
- imgIsIdenticon: false
- isImage: true
- useAcronymForLetterIdenticon: false
- name: root.dappIcon
- }
- bridgeBadge.visible: true
- bridgeBadge.width: 16
- bridgeBadge.height: 16
- bridgeBadge.image.source: "assets/sign.svg"
- bridgeBadge.border.width: 3
- bridgeBadge.border.color: "transparent"
- bridgeBadge.color: Theme.palette.miscColor1
- }
- }
- headline.title: qsTr("Sign request")
- headline.subtitle: root.dappUrl
- }
-
- footer: StatusDialogFooter {
- id: footer
-
- leftButtons: ObjectModel {
- MaxFeesDisplay {
- maxFeesText: root.maxFeesText
- feesTextColor: root.enoughFunds ? Theme.palette.directColor1 : Theme.palette.dangerColor1
- }
- Item {
- width: 20
- }
- EstimatedTimeDisplay {
- estimatedTimeText: root.estimatedTimeText
- visible: !!root.estimatedTimeText
- }
- }
-
- rightButtons: ObjectModel {
- StatusButton {
- objectName: "rejectButton"
-
- height: 44
- text: qsTr("Reject")
-
- onClicked: {
- root.reject()
- }
- }
- StatusButton {
- height: 44
- text: qsTr("Sign")
-
- onClicked: {
- root.sign()
- }
- }
- }
- }
-
- QtObject {
- id: d
-
- property string payloadToDisplay: ""
- property string userDisplayNaming: ""
-
- function isTransaction() {
- return root.method === SessionRequest.methods.signTransaction.name || root.method === SessionRequest.methods.sendTransaction.name
- }
-
- function updateDisplay() {
- if (!root.payloadData)
- return
-
- switch (root.method) {
- case SessionRequest.methods.personalSign.name: {
- payloadToDisplay = SessionRequest.methods.personalSign.getMessageFromData(root.payloadData)
- userDisplayNaming = SessionRequest.methods.personalSign.requestDisplay
- break
- }
- case SessionRequest.methods.sign.name: {
- payloadToDisplay = SessionRequest.methods.sign.getMessageFromData(root.payloadData)
- userDisplayNaming = SessionRequest.methods.sign.requestDisplay
- break
- }
- case SessionRequest.methods.signTypedData_v4.name: {
- let messageObject = SessionRequest.methods.signTypedData_v4.getMessageFromData(root.payloadData)
- payloadToDisplay = JSON.stringify(JSON.parse(messageObject), null, 2)
- userDisplayNaming = SessionRequest.methods.signTypedData_v4.requestDisplay
- break
- }
- case SessionRequest.methods.signTypedData.name: {
- let messageObject = SessionRequest.methods.signTypedData.getMessageFromData(root.payloadData)
- payloadToDisplay = JSON.stringify(JSON.parse(messageObject), null, 2)
- userDisplayNaming = SessionRequest.methods.signTypedData.requestDisplay
- break
- }
- case SessionRequest.methods.signTransaction.name: {
- let tx = SessionRequest.methods.signTransaction.getTxObjFromData(root.payloadData)
- payloadToDisplay = JSON.stringify(tx, null, 2)
- userDisplayNaming = SessionRequest.methods.signTransaction.requestDisplay
- break
- }
- case SessionRequest.methods.sendTransaction.name: {
- let tx = SessionRequest.methods.sendTransaction.getTxObjFromData(root.payloadData)
- payloadToDisplay = JSON.stringify(tx, null, 2)
- userDisplayNaming = SessionRequest.methods.sendTransaction.requestDisplay
- break
- }
- }
- }
- }
-}
diff --git a/ui/imports/shared/popups/walletconnect/DAppSignRequestModal.qml b/ui/imports/shared/popups/walletconnect/DAppSignRequestModal.qml
new file mode 100644
index 0000000000..613138eba9
--- /dev/null
+++ b/ui/imports/shared/popups/walletconnect/DAppSignRequestModal.qml
@@ -0,0 +1,192 @@
+import QtQuick 2.15
+import QtQuick.Layouts 1.15
+import QtQml.Models 2.15
+
+import StatusQ 0.1
+import StatusQ.Controls 0.1
+import StatusQ.Core 0.1
+import StatusQ.Core.Theme 0.1
+import StatusQ.Core.Utils 0.1 as SQUtils
+import AppLayouts.Wallet.popups 1.0
+import AppLayouts.Wallet.panels 1.0
+
+import shared.popups.walletconnect.panels 1.0
+
+import utils 1.0
+
+SignTransactionModalBase {
+ id: root
+
+ required property bool signingTransaction
+ // DApp info
+ required property url dappUrl
+ required property url dappIcon
+ required property string dappName
+ // Payload to sign
+ required property string requestPayload
+ // Account
+ required property color accountColor
+ required property string accountName
+ required property string accountEmoji
+ required property string accountAddress
+ // Network
+ required property string networkName
+ required property string networkIconPath
+ // Fees
+ required property string currentCurrency
+ required property string fiatFees
+ required property string cryptoFees
+ required property string estimatedTime
+ required property bool hasFees
+
+ property bool enoughFundsForTransaction: true
+ property bool enoughFundsForFees: false
+
+ signButtonEnabled: enoughFundsForTransaction && enoughFundsForFees
+ title: qsTr("Sign Request")
+ subtitle: SQUtils.StringUtils.extractDomainFromLink(root.dappUrl)
+ headerIconComponent: RoundImageWithBadge {
+ imageUrl: root.dappIcon
+ width: 40
+ height: 40
+ }
+
+ gradientColor: Utils.setColorAlpha(root.accountColor, 0.05) // 5% of wallet color
+ headerMainText: root.signingTransaction ? qsTr("%1 wants you to sign this transaction with %2").arg(root.dappName).arg(root.accountName) :
+ qsTr("%1 wants you to sign this message with %2").arg(root.dappName).arg(root.accountName)
+
+ fromImageSmartIdenticon.asset.name: "filled-account"
+ fromImageSmartIdenticon.asset.emoji: root.accountEmoji
+ fromImageSmartIdenticon.asset.color: root.accountColor
+ fromImageSmartIdenticon.asset.isLetterIdenticon: !!root.accountEmoji
+ toImageSmartIdenticon.asset.name: Style.svg("sign")
+ toImageSmartIdenticon.asset.bgColor: Theme.palette.primaryColor3
+ toImageSmartIdenticon.asset.width: 24
+ toImageSmartIdenticon.asset.height: 24
+ toImageSmartIdenticon.asset.color: Theme.palette.primaryColor1
+
+ infoTagText: qsTr("Only sign if you trust the dApp")
+ infoTag.states: [
+ State {
+ name: "insufficientFunds"
+ when: !root.enoughFundsForTransaction
+ PropertyChanges {
+ target: infoTag
+ asset.color: Theme.palette.dangerColor1
+ tagPrimaryLabel.color: Theme.palette.dangerColor1
+ backgroundColor: Theme.palette.dangerColor3
+ bgBorderColor: Theme.palette.dangerColor2
+ tagPrimaryLabel.text: qsTr("Insufficient funds for transaction")
+ }
+ }
+ ]
+ showHeaderDivider: !root.requestPayload
+
+ leftFooterContents: ObjectModel {
+ RowLayout {
+ Layout.leftMargin: 4
+ spacing: Style.current.bigPadding
+ ColumnLayout {
+ spacing: 2
+ StatusBaseText {
+ text: qsTr("Max fees:")
+ color: Theme.palette.baseColor1
+ font.pixelSize: Style.current.additionalTextSize
+ }
+ StatusTextWithLoadingState {
+ Layout.fillWidth: true
+ objectName: "footerFiatFeesText"
+ text: "%1 %2".arg(formatBigNumber(root.fiatFees)).arg(root.currentCurrency)
+ loading: root.feesLoading
+ customColor: root.enoughFundsForFees ? Theme.palette.directColor1 : Theme.palette.dangerColor1
+ elide: Qt.ElideMiddle
+ Binding on text {
+ when: !root.hasFees
+ value: qsTr("No fees")
+ }
+ }
+ }
+ ColumnLayout {
+ spacing: 2
+ visible: root.hasFees
+ StatusBaseText {
+ text: qsTr("Est. time:")
+ color: Theme.palette.baseColor1
+ font.pixelSize: Style.current.additionalTextSize
+ }
+ StatusTextWithLoadingState {
+ objectName: "footerEstimatedTime"
+ text: root.estimatedTime
+ loading: root.feesLoading
+ }
+ }
+ }
+ }
+
+ // Payload
+ ContentPanel {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ payloadToDisplay: root.requestPayload
+ visible: !!root.requestPayload
+ }
+
+ // Account
+ SignInfoBox {
+ Layout.fillWidth: true
+ Layout.bottomMargin: Style.current.bigPadding
+ objectName: "accountBox"
+ caption: qsTr("Sign with")
+ primaryText: root.accountName
+ secondaryText: SQUtils.Utils.elideAndFormatWalletAddress(root.accountAddress)
+ asset.name: "filled-account"
+ asset.emoji: root.accountEmoji
+ asset.color: root.accountColor
+ asset.isLetterIdenticon: !!root.accountEmoji
+ }
+
+ // Network
+ SignInfoBox {
+ Layout.fillWidth: true
+ Layout.bottomMargin: Style.current.bigPadding
+ objectName: "networkBox"
+ caption: qsTr("Network")
+ primaryText: root.networkName
+ icon: root.networkIconPath
+ }
+
+ // Fees
+ SignInfoBox {
+ Layout.fillWidth: true
+ Layout.bottomMargin: Style.current.bigPadding
+ objectName: "feesBox"
+ caption: qsTr("Fees")
+ primaryText: qsTr("Max. fees on %1").arg(root.networkName)
+ secondaryText: " "
+ enabled: false
+ visible: root.hasFees
+ components: [
+ ColumnLayout {
+ spacing: 2
+ StatusTextWithLoadingState {
+ objectName: "fiatFeesText"
+ Layout.alignment: Qt.AlignRight
+ text: "%1 %2".arg(formatBigNumber(root.fiatFees)).arg(root.currentCurrency)
+ horizontalAlignment: Text.AlignRight
+ font.pixelSize: Style.current.additionalTextSize
+ loading: root.feesLoading
+ customColor: root.enoughFundsForFees ? Theme.palette.directColor1 : Theme.palette.dangerColor1
+ }
+ StatusTextWithLoadingState {
+ objectName: "cryptoFeesText"
+ Layout.alignment: Qt.AlignRight
+ text: "%1 ETH".arg(formatBigNumber(root.cryptoFees))
+ horizontalAlignment: Text.AlignRight
+ font.pixelSize: Style.current.additionalTextSize
+ customColor: root.enoughFundsForFees ? Theme.palette.baseColor1 : Theme.palette.dangerColor1
+ loading: root.feesLoading
+ }
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/ui/imports/shared/popups/walletconnect/RoundImageWithBadge.qml b/ui/imports/shared/popups/walletconnect/RoundImageWithBadge.qml
index e476a3de66..d22969b858 100644
--- a/ui/imports/shared/popups/walletconnect/RoundImageWithBadge.qml
+++ b/ui/imports/shared/popups/walletconnect/RoundImageWithBadge.qml
@@ -39,15 +39,15 @@ Item {
anchors.fill: mainImage
active: !mainImage.visible
sourceComponent: StatusRoundedComponent {
+ id: imageWrapper
color: Theme.palette.primaryColor3
StatusIcon {
- anchors.fill: parent
- anchors.margins: Style.current.padding
+ anchors.fill: imageWrapper
+ anchors.margins: imageWrapper.width / 4.5
color: Theme.palette.primaryColor1
icon: "dapp"
}
- }
- }
+ } }
layer.enabled: true
layer.effect: OpacityMask {
@@ -72,7 +72,7 @@ Item {
StatusRoundIcon {
id: badge
- width: (root.width / 2) - Style.current.padding
+ width: root.width / 3.6
height: width
anchors.bottom: parent.bottom
anchors.right: parent.right
diff --git a/ui/imports/shared/popups/walletconnect/panels/ContentPanel.qml b/ui/imports/shared/popups/walletconnect/panels/ContentPanel.qml
index df35e13b89..b2dd16a73b 100644
--- a/ui/imports/shared/popups/walletconnect/panels/ContentPanel.qml
+++ b/ui/imports/shared/popups/walletconnect/panels/ContentPanel.qml
@@ -1,9 +1,13 @@
import QtQuick 2.15
import QtQuick.Layouts 1.15
+import QtGraphicalEffects 1.15
+import StatusQ.Controls 0.1
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
+import utils 1.0
+
Rectangle {
id: root
@@ -14,40 +18,60 @@ Rectangle {
color: "transparent"
radius: 8
- implicitHeight: contentScrollView.implicitHeight + (2 * contentText.anchors.margins)
+ implicitHeight: d.expanded ? contentText.implicitHeight + (2 * contentText.anchors.margins) :
+ Math.min(contentText.implicitHeight + (2 * contentText.anchors.margins), 200)
- MouseArea {
- anchors.fill: parent
- cursorShape: contentScrollView.enabled || !enabled ? undefined : Qt.PointingHandCursor
- enabled: contentScrollView.height < contentScrollView.contentHeight
-
- onClicked: {
- contentScrollView.enabled = !contentScrollView.enabled
- }
- z: contentScrollView.z + 1
+ HoverHandler {
+ id: hoverHandler
+ target: root
}
- StatusScrollView {
- id: contentScrollView
+ StatusBaseText {
+ id: contentText
+ objectName: "textContent"
+
anchors.fill: parent
+ anchors.margins: 20
- contentWidth: availableWidth
- contentHeight: contentText.contentHeight
+ text: root.payloadToDisplay
+ font.pixelSize: Style.current.additionalTextSize
+ lineHeightMode: Text.FixedHeight
+ lineHeight: 18
- padding: 0
+ wrapMode: Text.WrapAnywhere
- enabled: false
-
- StatusBaseText {
- id: contentText
- anchors.fill: parent
- anchors.margins: 20
-
- width: contentScrollView.availableWidth
-
- text: root.payloadToDisplay
-
- wrapMode: Text.WrapAnywhere
+ StatusFlatButton {
+ objectName: "expandButton"
+ anchors.top: parent.top
+ anchors.right: parent.right
+ icon.name: d.expanded ? "collapse" : "expand"
+ icon.color: hovered ? Theme.palette.directColor1 : Theme.palette.baseColor1
+ hoverColor: "transparent"
+ visible: d.canExpand && hoverHandler.hovered
+ onClicked: {
+ d.expanded = !d.expanded
+ }
}
+
+ layer.enabled: !d.expanded && d.canExpand
+ layer.effect: OpacityMask {
+ maskSource: Rectangle {
+ width: root.width
+ height: root.height
+ gradient: Gradient {
+ orientation: Gradient.Vertical
+ GradientStop { position: 0.0 }
+ GradientStop { position: (root.height - 60) / root.height }
+ GradientStop { position: 1; color: "transparent" }
+ }
+ }
+ }
+ }
+
+ QtObject {
+ id: d
+ readonly property int maxContentHeight: 350
+ property bool expanded: false
+ property bool canExpand: contentText.implicitHeight > maxContentHeight
}
}
diff --git a/ui/imports/shared/popups/walletconnect/qmldir b/ui/imports/shared/popups/walletconnect/qmldir
index db9ca02679..8086638a23 100644
--- a/ui/imports/shared/popups/walletconnect/qmldir
+++ b/ui/imports/shared/popups/walletconnect/qmldir
@@ -2,6 +2,6 @@ PairWCModal 1.0 PairWCModal.qml
DAppsListPopup 1.0 DAppsListPopup.qml
ConnectDAppModal 1.0 ConnectDAppModal.qml
ConnectionStatusTag 1.0 ConnectionStatusTag.qml
-DAppRequestModal 1.0 DAppRequestModal.qml
+DAppSignRequestModal 1.0 DAppSignRequestModal.qml
DAppsUriCopyInstructionsPopup 1.0 DAppsUriCopyInstructionsPopup.qml
RoundImageWithBadge 1.0 RoundImageWithBadge.qml
diff --git a/ui/imports/shared/stores/DAppsStore.qml b/ui/imports/shared/stores/DAppsStore.qml
index f2c408a2b8..236b5c8f22 100644
--- a/ui/imports/shared/stores/DAppsStore.qml
+++ b/ui/imports/shared/stores/DAppsStore.qml
@@ -6,7 +6,6 @@ QObject {
id: root
required property var controller
-
/// \c dappsJson serialized from status-go.wallet.GetDapps
signal dappsListReceived(string dappsJson)
signal userAuthenticated(string topic, string id, string password, string pin)