From 1f60410db894a84c216585282523eb97d9c900db Mon Sep 17 00:00:00 2001 From: Stefan Date: Wed, 1 May 2024 18:15:42 +0200 Subject: [PATCH] feat(dapps) implement wallet connect URI input Other changes - start Wallet Connect SDK using web engine after wallet is ready - source WC project ID from the constants in nim - close list when opening the `ConnectDappModal` - remove the old POC modal instantiation - rename ConnectDappPopup to Modal for consistency - move specific dapps related user settings to their place in the component - add DAppsStore to be used for dapps abstraction Updates #14556 --- src/app/global/local_app_settings.nim | 8 +- .../src/StatusQ/Controls/StatusBaseButton.qml | 2 + .../src/StatusQ/Core/Theme/ThemePalette.qml | 2 + .../Wallet/controls/ConnectedDappsButton.qml | 1 + .../AppLayouts/Wallet/panels/WalletHeader.qml | 23 +++- .../pocwalletconnect/POCWalletConnect.qml | 45 ------- ui/app/mainui/AppMain.qml | 37 ++---- .../popups/walletconnect/ConnectDappModal.qml | 67 ++++++++++ .../ConnectDappModal/WCUriInput.qml | 120 ++++++++++++++++++ .../popups/walletconnect/ConnectDappPopup.qml | 57 --------- ui/imports/shared/popups/walletconnect/qmldir | 2 +- ui/imports/shared/stores/DAppsStore.qml | 11 ++ ui/imports/shared/stores/qmldir | 1 + ui/imports/utils/Constants.qml | 5 - 14 files changed, 247 insertions(+), 134 deletions(-) delete mode 100644 ui/app/AppLayouts/Wallet/views/pocwalletconnect/POCWalletConnect.qml create mode 100644 ui/imports/shared/popups/walletconnect/ConnectDappModal.qml create mode 100644 ui/imports/shared/popups/walletconnect/ConnectDappModal/WCUriInput.qml delete mode 100644 ui/imports/shared/popups/walletconnect/ConnectDappPopup.qml create mode 100644 ui/imports/shared/stores/DAppsStore.qml diff --git a/src/app/global/local_app_settings.nim b/src/app/global/local_app_settings.nim index 594d264401..fe6fbde9a1 100644 --- a/src/app/global/local_app_settings.nim +++ b/src/app/global/local_app_settings.nim @@ -1,6 +1,6 @@ import NimQml, strutils, os -import ../../constants +import constants # Local App Settings keys: const LAS_KEY_LANGUAGE* = "global/language" @@ -207,3 +207,9 @@ QtObject: of LAS_KEY_FAKE_LOADING_SCREEN_ENABLED: self.fakeLoadingScreenEnabledChanged() of LAS_KEY_SHARDED_COMMUNITIES_ENABLED: self.wakuV2ShardedCommunitiesEnabledChanged() of LAS_KEY_TRANSLATIONS_ENABLED: self.translationsEnabledChanged() + + proc getWalletConnectProjectID*(self: LocalAppSettings): string {.slot.} = + return constants.WALLET_CONNECT_PROJECT_ID + + QtProperty[string] walletConnectProjectID: + read = getWalletConnectProjectID \ No newline at end of file diff --git a/ui/StatusQ/src/StatusQ/Controls/StatusBaseButton.qml b/ui/StatusQ/src/StatusQ/Controls/StatusBaseButton.qml index df88c04314..09f900c42a 100644 --- a/ui/StatusQ/src/StatusQ/Controls/StatusBaseButton.qml +++ b/ui/StatusQ/src/StatusQ/Controls/StatusBaseButton.qml @@ -46,6 +46,7 @@ Button { property color textHoverColor: textColor property color disabledTextColor property color borderColor: "transparent" + property int borderWidth: 0 property bool textFillWidth: false property int radius: size === StatusBaseButton.Size.Tiny ? 6 : 8 @@ -126,6 +127,7 @@ Button { background: Rectangle { radius: root.radius border.color: root.borderColor + border.width: root.borderWidth color: { if (!root.enabled || !root.interactive) return disabledColor diff --git a/ui/StatusQ/src/StatusQ/Core/Theme/ThemePalette.qml b/ui/StatusQ/src/StatusQ/Core/Theme/ThemePalette.qml index 4664558ff1..aef2035498 100644 --- a/ui/StatusQ/src/StatusQ/Core/Theme/ThemePalette.qml +++ b/ui/StatusQ/src/StatusQ/Core/Theme/ThemePalette.qml @@ -104,6 +104,8 @@ QtObject { property color white: getColor('white') property color transparent: "#00000000" + property color green: getColor('green') + property color blue: getColor('blue') property color darkBlue: getColor('blue2') diff --git a/ui/app/AppLayouts/Wallet/controls/ConnectedDappsButton.qml b/ui/app/AppLayouts/Wallet/controls/ConnectedDappsButton.qml index e46c6020db..6e31cc83d0 100644 --- a/ui/app/AppLayouts/Wallet/controls/ConnectedDappsButton.qml +++ b/ui/app/AppLayouts/Wallet/controls/ConnectedDappsButton.qml @@ -90,6 +90,7 @@ StatusButton { text: qsTr("Connect a dApp via WalletConnect") onClicked: { root.connectDapp() + popup.close() } } } diff --git a/ui/app/AppLayouts/Wallet/panels/WalletHeader.qml b/ui/app/AppLayouts/Wallet/panels/WalletHeader.qml index 9a70460a70..69bdcb2bc6 100644 --- a/ui/app/AppLayouts/Wallet/panels/WalletHeader.qml +++ b/ui/app/AppLayouts/Wallet/panels/WalletHeader.qml @@ -11,6 +11,8 @@ import StatusQ.Popups 0.1 import SortFilterProxyModel 0.2 +import shared.popups.walletconnect 1.0 + import utils 1.0 import "../controls" @@ -81,7 +83,26 @@ Item { visible: !root.walletStore.showSavedAddresses && Global.featureFlags.dappsEnabled onConnectDapp: { - console.warn("TODO: run ConnectDappPopup...") + connectDappLoader.active = true + } + + Loader { + id: connectDappLoader + + active: false + + onLoaded: item.open() + + sourceComponent: ConnectDappModal { + visible: true + + onClosed: connectDappLoader.active = false + + onPair: (uri) => { + this.close() + console.debug(`TODO(#14556): ConnectionRequestDappModal with ${uri}`) + } + } } } diff --git a/ui/app/AppLayouts/Wallet/views/pocwalletconnect/POCWalletConnect.qml b/ui/app/AppLayouts/Wallet/views/pocwalletconnect/POCWalletConnect.qml deleted file mode 100644 index 37f38c668d..0000000000 --- a/ui/app/AppLayouts/Wallet/views/pocwalletconnect/POCWalletConnect.qml +++ /dev/null @@ -1,45 +0,0 @@ -///////////////////////////////////////////////////// -// WalletConnect POC - to remove this file -///////////////////////////////////////////////////// - -import QtQuick 2.15 - -import AppLayouts.Wallet.stores 1.0 as WalletStores - -import shared.popups.walletconnect 1.0 - -Item { - id: root - - required property var controller - - property alias modal: modal - property alias sdk: sdk - property alias url: sdk.url - - POCWalletConnectModal { - id: modal - - controller: root.controller - sdk: sdk - } - - WalletConnectSDK { - id: sdk - - projectId: controller.projectId - - active: WalletStores.RootStore.walletSectionInst.walletReady && (controller.hasActivePairings || modal.opened) - - onSessionRequestEvent: (details) => { - modal.openWithSessionRequestEvent(details) - } - } - - Connections { - target: root.controller - function onRequestOpenWalletConnectPopup(uri) { - modal.openWithUri(uri) - } - } -} diff --git a/ui/app/mainui/AppMain.qml b/ui/app/mainui/AppMain.qml index b150c5c98b..65763e2242 100644 --- a/ui/app/mainui/AppMain.qml +++ b/ui/app/mainui/AppMain.qml @@ -27,6 +27,7 @@ import shared.stores 1.0 import shared.popups.send 1.0 import shared.popups.send.views 1.0 import shared.stores.send 1.0 +import shared.popups.walletconnect 1.0 import StatusQ.Core.Theme 0.1 import StatusQ.Components 0.1 @@ -43,11 +44,6 @@ import AppLayouts.Communities.stores 1.0 import AppLayouts.Wallet.stores 1.0 as WalletStore import AppLayouts.Wallet.popups 1.0 as WalletPopups -///////////////////////////////////////////////////// -// WalletConnect POC - to remove -import AppLayouts.Wallet.views.pocwalletconnect 1.0 -///////////////////////////////////////////////////// - import mainui.activitycenter.stores 1.0 import mainui.activitycenter.popups 1.0 @@ -82,6 +78,18 @@ Item { walletAssetStore: appMain.walletAssetsStore tokensStore: appMain.tokensStore } + readonly property DAppsStore dappsStore: DAppsStore { + wCSDK: WalletConnectSDK { + active: WalletStore.RootStore.walletSectionInst.walletReady + + projectId: WalletStore.RootStore.appSettings.walletConnectProjectID + + onSessionRequestEvent: (details) => { + // TODO #14556 + console.debug(`@dd onSessionRequestEvent: ${JSON.stringify(details)}`) + } + } + } // set from main.qml property var sysPalette @@ -2032,23 +2040,4 @@ Item { onClosed: userAgreementLoader.active = false } } - - ///////////////////////////////////////////////////// - // WalletConnect POC - to remove - POCWalletConnect { - id: walletConnect - anchors.top: parent.bottom - width: 100 - height: 100 - - controller: WalletStore.RootStore.walletSectionInst.walletConnectController - - Connections { - target: Global - function onPopupWalletConnect() { - walletConnect.modal.open() - } - } - } - ///////////////////////////////////////////////////// } diff --git a/ui/imports/shared/popups/walletconnect/ConnectDappModal.qml b/ui/imports/shared/popups/walletconnect/ConnectDappModal.qml new file mode 100644 index 0000000000..0231ef6d38 --- /dev/null +++ b/ui/imports/shared/popups/walletconnect/ConnectDappModal.qml @@ -0,0 +1,67 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 +import QtQml.Models 2.14 + +import StatusQ.Core 0.1 +import StatusQ.Popups.Dialog 0.1 +import StatusQ.Controls 0.1 + +import utils 1.0 + +import "ConnectDappModal" + +StatusDialog { + id: root + + signal pair(string uri) + + width: 480 + implicitHeight: 633 + + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside + + title: qsTr("Connect a dApp via WalletConnect") + + padding: 20 + + contentItem: ColumnLayout { + StatusBaseText { + text: "WalletConnect URI" + } + + WCUriInput { + id: uriInput + } + + // Spacer + ColumnLayout {} + + StatusLinkText { + text: qsTr("How to copy the dApp URI") + + Layout.alignment: Qt.AlignHCenter + Layout.margins: 18 + + normalColor: linkColor + + onClicked: { + console.warn("TODO: open help...") + } + } + } + + footer: StatusDialogFooter { + id: footer + rightButtons: ObjectModel { + StatusButton { + height: 44 + text: qsTr("Done") + + enabled: uriInput.valid && uriInput.text.length > 0 + + onClicked: root.pair(uriInput.text) + } + } + } +} diff --git a/ui/imports/shared/popups/walletconnect/ConnectDappModal/WCUriInput.qml b/ui/imports/shared/popups/walletconnect/ConnectDappModal/WCUriInput.qml new file mode 100644 index 0000000000..ffbe71451e --- /dev/null +++ b/ui/imports/shared/popups/walletconnect/ConnectDappModal/WCUriInput.qml @@ -0,0 +1,120 @@ +import QtQuick 2.15 +import QtQml 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 + +import StatusQ.Core 0.1 +import StatusQ.Popups 0.1 +import StatusQ.Controls 0.1 +import StatusQ.Core.Theme 0.1 + +ColumnLayout { + id: root + + readonly property bool valid: input.valid && input.text.length > 0 + readonly property alias text: input.text + + StatusBaseInput { + id: input + + Layout.fillWidth: true + Layout.preferredHeight: 132 + + placeholderText: qsTr("Paste URI") + + verticalAlignment: TextInput.AlignTop + + valid: { + let uri = input.text + + if(uri.length === 0) { + errorText.text = "" + return true + } + + if(containsOnlyEmoji(uri)) { + errorText.text = qsTr("WalletConnect URI too cool") + return false + } else if(!validURI(uri)) { + errorText.text = qsTr("WalletConnect URI invalid") + return false + } else if(wcUriAlreadyUsed(uri)) { + errorText.text = qsTr("WalletConnect URI already used") + return false + } else if(wcUriExpired(uri)) { + errorText.text = qsTr("WalletConnect URI has expired") + return false + } + + errorText.text = "" + return true + } + + function validURI(uri) { + var regex = /^wc:[0-9a-fA-F-]*@([1-9][0-9]*)(\?([a-zA-Z-]+=[^&]+)(&[a-zA-Z-]+=[^&]+)*)?$/ + return regex.test(uri) + } + + function containsOnlyEmoji(uri) { + var emojiRegex = new RegExp("[\\u203C-\\u3299\\u1F000-\\u1F644]"); + return !emojiRegex.test(uri); + } + + function wcUriAlreadyUsed(uri) { + // TODO: Check if URI is already used + return false + } + + function wcUriExpired(uri) { + // TODO: Check if URI is expired + return false + } + + rightComponent: Item { + width: pasteButton.implicitWidth + height: pasteButton.implicitHeight + + readonly property bool showIcon: input.valid && input.text.length > 0 + + StatusIcon { + anchors.centerIn: parent + + icon: "tiny/tiny-checkmark" + color: Theme.palette.green + visible: showIcon + } + + StatusButton { + id: pasteButton + + text: qsTr("Paste") + + size: StatusBaseButton.Size.Small + + visible: !showIcon + + borderWidth: enabled ? 1 : 0 + borderColor: textColor + + enabled: input.edit.canPaste + + onClicked: { + input.edit.paste() + input.edit.focus = true + } + } + } + + multiline: true + } + + StatusBaseText { + id: errorText + + visible: !input.valid && input.text.length !== 0 + + Layout.alignment: Qt.AlignRight + + color: Theme.palette.dangerColor1 + } +} diff --git a/ui/imports/shared/popups/walletconnect/ConnectDappPopup.qml b/ui/imports/shared/popups/walletconnect/ConnectDappPopup.qml deleted file mode 100644 index a6aca1e02a..0000000000 --- a/ui/imports/shared/popups/walletconnect/ConnectDappPopup.qml +++ /dev/null @@ -1,57 +0,0 @@ -import QtQuick 2.15 - -import StatusQ.Core 0.1 -import StatusQ.Popups 0.1 -import StatusQ.Controls 0.1 - -import utils 1.0 - -StatusModal { - id: root - - width: Constants.dapps.connectDappPopupWidth - - closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside - - headerSettings.title: qsTr("Connect a Dapp via WalletConnect") - - StatusScrollView { - id: scrollView - - anchors.fill: parent - padding: 0 - contentWidth: availableWidth - - Item { - id: content - - implicitWidth: loader.implicitWidth - implicitHeight: loader.implicitHeight - width: scrollView.availableWidth - - Loader { - id: loader - width: parent.width - sourceComponent: { - // TODO - - return undefined - } - } - } - } - - rightButtons: [ - StatusButton { - id: primaryButton - height: Constants.dapps.footerButtonsHeight - text: qsTr("Done") - visible: text !== "" - enabled: root.store.primaryPopupButtonEnabled - - onClicked: { - console.warn("TODO: done...") - } - } - ] -} diff --git a/ui/imports/shared/popups/walletconnect/qmldir b/ui/imports/shared/popups/walletconnect/qmldir index 4e770088fa..e807421262 100644 --- a/ui/imports/shared/popups/walletconnect/qmldir +++ b/ui/imports/shared/popups/walletconnect/qmldir @@ -1,2 +1,2 @@ WalletConnectSDK 1.0 WalletConnectSDK.qml -ConnectDappPopup 1.0 ConnectDappPopup.qml +ConnectDappModal 1.0 ConnectDappModal.qml \ No newline at end of file diff --git a/ui/imports/shared/stores/DAppsStore.qml b/ui/imports/shared/stores/DAppsStore.qml new file mode 100644 index 0000000000..16991155ac --- /dev/null +++ b/ui/imports/shared/stores/DAppsStore.qml @@ -0,0 +1,11 @@ +import QtQuick 2.15 + +import shared.popups.walletconnect 1.0 + +QtObject { + id: root + + required property WalletConnectSDK wCSDK + + // Here we will have business logic calls and expose connections history models +} \ No newline at end of file diff --git a/ui/imports/shared/stores/qmldir b/ui/imports/shared/stores/qmldir index 938574bd0e..4a4d78ba72 100644 --- a/ui/imports/shared/stores/qmldir +++ b/ui/imports/shared/stores/qmldir @@ -7,3 +7,4 @@ PermissionsStore 1.0 PermissionsStore.qml TokenBalanceHistoryStore 1.0 TokenBalanceHistoryStore.qml TokenMarketValuesStore 1.0 TokenMarketValuesStore.qml NetworkConnectionStore 1.0 NetworkConnectionStore.qml +DAppsStore 1.0 DAppsStore.qml \ No newline at end of file diff --git a/ui/imports/utils/Constants.qml b/ui/imports/utils/Constants.qml index c1549183c1..821ae9583d 100644 --- a/ui/imports/utils/Constants.qml +++ b/ui/imports/utils/Constants.qml @@ -788,11 +788,6 @@ QtObject { } } - readonly property QtObject dapps: QtObject { - readonly property int connectDappPopupWidth: 480 - readonly property int footerButtonsHeight: 44 - } - readonly property QtObject localPairingAction: QtObject { readonly property int actionUnknown: 0 readonly property int actionConnect: 1