feat(dapps) implement basic DAppRequestModal

I added it to storybook for testing. There is not integration with with
the app yet.

Updates: #14762
This commit is contained in:
Stefan 2024-05-27 22:50:16 +03:00 committed by Stefan Dunca
parent 6973ccef6b
commit 1618e6ce0a
10 changed files with 558 additions and 3 deletions

View File

@ -72,8 +72,7 @@ Item {
spacing: 8
accounts: WalletAccountsModel{
}
accounts: d.selectedAccount
flatNetworks: SortFilterProxyModel {
sourceModel: NetworksModel.flatNetworks

View File

@ -0,0 +1,164 @@
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 utils 1.0
import shared.stores 1.0
Item {
id: root
function openModal() {
modal.openWith()
}
// 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
signContent: JSON.stringify(d.signTestContent, null, 2)
maxFeesText: "1.82 EUR"
estimatedTimeText: "3-5 mins"
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
}
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"
}
QtObject {
id: d
readonly property var accountsModel: WalletAccountsModel{}
readonly property var selectedAccount: accountsModel.data[0]
readonly property var selectedNetwork: NetworksModel.flatNetworks.get(0)
readonly property var signTestContent: {
"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/"
}
}
}
}
}
// category: Wallet

View File

@ -83,5 +83,8 @@ QtObject {
'mossHovered': '#1E857B',
'brownHovered': '#6F2727',
'brown2Hovered': '#7C6926',
'lightDesktopBlue10': '#ECEFFB',
'darkDesktopBlue10': '#273251',
}
}

View File

@ -74,6 +74,8 @@ ThemePalette {
messageHighlightColor: getColor('blue4', 0.2)
desktopBlue10: getColor('darkDesktopBlue10')
userCustomizationColors: [
"#AAC6FF",
"#887AF9",

View File

@ -72,6 +72,8 @@ ThemePalette {
messageHighlightColor: getColor('blue', 0.2)
desktopBlue10: getColor('lightDesktopBlue10')
userCustomizationColors: [
"#2946C4",
"#887AF9",

View File

@ -183,6 +183,8 @@ QtObject {
property color messageHighlightColor
property color desktopBlue10
property var userCustomizationColors: []
property var identiconRingColors: []

View File

@ -4,3 +4,4 @@ ShowcaseDelegate 1.0 ShowcaseDelegate.qml
StaticSocialLinkInput 1.0 StaticSocialLinkInput.qml
WalletAccountDelegate 1.0 WalletAccountDelegate.qml
WalletKeyPairDelegate 1.0 WalletKeyPairDelegate.qml
WalletAccountDetailsKeypairItem 1.0 WalletAccountDetailsKeypairItem.qml

View File

@ -0,0 +1,376 @@
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
StatusDialog {
id: root
implicitWidth: 480
required property string dappName
required property string dappUrl
required property url dappIcon
required property string signContent
required property string maxFeesText
required property string estimatedTimeText
required property var account
property var network: null
signal sign()
signal reject()
function openWith() {
root.open()
}
title: qsTr("Sign request")
padding: 20
contentItem: ColumnLayout {
spacing: 20
clip: true
IntentionPanel {
Layout.fillWidth: true
dappName: root.dappName
dappIcon: root.dappIcon
account: root.account
signContent: root.signContent
}
ContentPanel {
Layout.fillWidth: true
Layout.preferredHeight: 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
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 }
}
}
// 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
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 }
}
}
}
}
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 {}
}
rightButtons: ObjectModel {
StatusButton {
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 {
text: root.maxFeesText
font.pixelSize: 16
font.weight: Font.DemiBold
}
}
component EstimatedTimeDisplay: ColumnLayout {
StatusBaseText {
text: qsTr("Est. time:")
font.pixelSize: 12
color: Theme.palette.directColor1
}
StatusBaseText {
text: root.estimatedTimeText
font.pixelSize: 16
font.weight: Font.DemiBold
}
}
component IntentionPanel: ColumnLayout {
spacing: 8
required property string dappName
required property url dappIcon
required property var account
required property string signContent
// 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 sign this transaction with %2").arg(dappName).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
StatusScrollView {
id: contentScrollView
anchors.fill: parent
contentWidth: availableWidth
contentHeight: contentText.implicitHeight
StatusBaseText {
id: contentText
anchors.fill: parent
anchors.margins: 20
width: contentScrollView.availableWidth
text: signContent
wrapMode: Text.WrapAnywhere
}
}
}
}

View File

@ -0,0 +1,5 @@
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Feature / Sign">
<path id="Vector 32" d="M3.30026 5.87295C2.67249 5.56241 1.93965 5.00104 1.46227 4.33462C0.711792 3.28696 0.491562 2.00625 1.53098 1.23526C2.3099 0.657513 3.61233 1.37874 4.10757 2.49686C4.7675 3.98668 4.6573 6.28428 3.42906 8.2263C2.8116 9.2026 1.86004 9.10424 1.53098 8.54312C1.07672 7.76851 1.54754 6.07397 3.70877 4.99336C5.59513 4.05019 6.12334 4.52806 6.01429 5.03985C5.9758 5.22047 5.55049 5.95884 6.11735 5.95884C6.60595 5.95884 7.02642 5.354 7.42285 5.03985C7.92791 4.63959 8.4535 4.51593 8.38977 5.26933C8.36595 5.55093 8.48785 6.15638 9.17496 5.81283" stroke="white" stroke-width="0.833333" stroke-linecap="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 759 B

View File

@ -1,3 +1,4 @@
PairWCModal 1.0 PairWCModal.qml
DAppsListPopup 1.0 DAppsListPopup.qml
ConnectDAppModal 1.0 ConnectDAppModal.qml
ConnectDAppModal 1.0 ConnectDAppModal.qml
DAppRequestModal 1.0 DAppRequestModal.qml