status-desktop/ui/imports/shared/popups/walletconnect/DAppRequestModal.qml

529 lines
16 KiB
QML

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 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
}
ContentPanel {
Layout.fillWidth: true
Layout.maximumHeight: 340
}
// 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 {
}
Item {
width: 20
}
EstimatedTimeDisplay {
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()
}
}
}
}
component MaxFeesDisplay: ColumnLayout {
StatusBaseText {
text: qsTr("Max fees:")
font.pixelSize: 12
color: Theme.palette.directColor1
}
StatusBaseText {
id: maxFeesDisplay
text: root.maxFeesText
visible: !!root.maxFeesText
font.pixelSize: 16
color: root.enoughFunds ? Theme.palette.directColor1 : Theme.palette.dangerColor1
}
StatusBaseText {
text: qsTr("No fees")
visible: !maxFeesDisplay.visible
font.pixelSize: maxFeesDisplay.font.pixelSize
font.weight: maxFeesDisplay.font.weight
}
}
component EstimatedTimeDisplay: ColumnLayout {
StatusBaseText {
text: qsTr("Est. time:")
font.pixelSize: 12
color: Theme.palette.directColor1
}
StatusBaseText {
text: root.estimatedTimeText
font.pixelSize: 16
}
}
component IntentionPanel: ColumnLayout {
spacing: 8
required property string dappName
required property url dappIcon
required property var account
// Icons
Item {
Layout.fillWidth: true
Layout.preferredHeight: 40
Layout.alignment: Qt.AlignHCenter
Layout.bottomMargin: 8
StatusRoundedImage {
id: dappIconComponent
width: height
height: parent.height
anchors.horizontalCenter: parent.horizontalCenter
anchors.horizontalCenterOffset: -16
anchors.verticalCenter: parent.verticalCenter
image.source: root.dappIcon
}
StatusRoundIcon {
anchors.horizontalCenter: parent.horizontalCenter
anchors.horizontalCenterOffset: 16
anchors.verticalCenter: parent.verticalCenter
asset: StatusAssetSettings {
width: 24
height: 24
color: Theme.palette.primaryColor1
bgWidth: 40
bgHeight: 40
bgColor: Theme.palette.desktopBlue10
bgRadius: bgWidth / 2
bgBorderWidth: 2
bgBorderColor: Theme.palette.statusAppLayout.backgroundColor
source: "assets/sign.svg"
}
}
}
// Names and intentions
StatusBaseText {
text: qsTr("%1 wants you to %2 with %3").arg(dappName).arg(d.userDisplayNaming).arg(account.name)
Layout.preferredWidth: 400
Layout.alignment: Qt.AlignHCenter
font.pixelSize: 15
font.weight: Font.DemiBold
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignHCenter
}
// TODO #14762: externalize as a InfoPill and merge base implementation with
// the existing IssuePill reusable component
Rectangle {
Layout.preferredWidth: operationStatusLayout.implicitWidth + 24
Layout.preferredHeight: operationStatusLayout.implicitHeight + 14
Layout.alignment: Qt.AlignHCenter
visible: true
border.color: Theme.palette.successColor2
border.width: 1
color: "transparent"
radius: height / 2
RowLayout {
id: operationStatusLayout
spacing: 8
anchors.centerIn: parent
StatusIcon {
Layout.preferredWidth: 16
Layout.preferredHeight: 16
visible: true
color: Theme.palette.directColor1
icon: "info"
}
StatusBaseText {
text: qsTr("Only sign if you trust the dApp")
font.pixelSize: 12
color: Theme.palette.directColor1
}
}
}
}
component ContentPanel: Rectangle {
id: contentPanelRect
border.width: 1
border.color: Theme.palette.baseColor2
color: "transparent"
radius: 8
implicitHeight: contentScrollView.implicitHeight + (2 * contentText.anchors.margins)
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
}
StatusScrollView {
id: contentScrollView
anchors.fill: parent
contentWidth: availableWidth
contentHeight: contentText.contentHeight
padding: 0
enabled: false
StatusBaseText {
id: contentText
anchors.fill: parent
anchors.margins: 20
width: contentScrollView.availableWidth
text: d.payloadToDisplay
wrapMode: Text.WrapAnywhere
}
}
}
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.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.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
}
}
}
}
}