diff --git a/storybook/pages/DAppsWorkflowPage.qml b/storybook/pages/DAppsWorkflowPage.qml index 7533a06c04..020bd34a02 100644 --- a/storybook/pages/DAppsWorkflowPage.qml +++ b/storybook/pages/DAppsWorkflowPage.qml @@ -53,6 +53,14 @@ Item { spacing: 8 wcService: walletConnectService + + onDisplayToastMessage: (message, isErr) => { + if(isErr) { + console.log(`Storybook.displayToastMessage(${message}, "", "warning", false, Constants.ephemeralNotificationType.danger, "")`) + return + } + console.log(`Storybook.displayToastMessage(${message}, "", "checkmark-circle", false, Constants.ephemeralNotificationType.success, "")`) + } } } ColumnLayout {} @@ -83,6 +91,15 @@ Item { // spacer ColumnLayout {} + CheckBox { + + text: "Enable SDK" + checked: settings.enableSDK + onCheckedChanged: { + settings.enableSDK = checked + } + } + RowLayout { Text { text: "URI" } TextField { @@ -170,7 +187,7 @@ Item { id: walletConnectService wcSDK: WalletConnectSDK { - active: true + active: settings.enableSDK projectId: projectIdText.projectId } @@ -246,6 +263,7 @@ Item { property int testCase: d.noTestCase property string pairUri: "" property bool testNetworks: false + property bool enableSDK: true } } diff --git a/storybook/qmlTests/tests/helpers/wallet_connect.js b/storybook/qmlTests/tests/helpers/wallet_connect.js new file mode 100644 index 0000000000..063a09c1a2 --- /dev/null +++ b/storybook/qmlTests/tests/helpers/wallet_connect.js @@ -0,0 +1,134 @@ + +const requiredEventsJsonString = `["chainChanged", "accountsChanged"]` +const requiredMethodsJsonString = `["eth_sendTransaction","personal_sign"]` +const optionalNamespacesJsonString = `{ + "eip155": { + "chains": [ + "eip155:1", + "eip155:10" + ], + "events": [ + "chainChanged", + "accountsChanged", + "message", + "disconnect", + "connect" + ], + "methods": [ + "eth_sign", + "eth_signTransaction", + "eth_signTypedData", + "eth_signTypedData_v3", + "eth_signTypedData_v4", + "eth_sendTransaction" + ], + "rpcMap": { + "1": "https://cloudflare-eth.com", + "10": "https://mainnet.optimism.io/" + } + } +}` + +const requiredNamespacesJsonString = `{ + "eip155": { + "chains": [ + "eip155:1" + ], + "events": ${requiredEventsJsonString}, + "methods": ${requiredMethodsJsonString}, + "rpcMap": { + "1": "https://mainnet.io/123" + } + } +}` + +const dappMetadataJsonString = `{ + "description": "Test dApp description", + "icons": [ + "https://test.com/icon.png" + ], + "name": "TestApp", + "url": "https://app.test.org" +}` + +function formatSessionProposal() { + return `{ + "id": 1715976881734096, + "params": { + "expiryTimestamp": 1715977219, + "id": 1715976881734096, + "optionalNamespaces": ${optionalNamespacesJsonString}, + "pairingTopic": "50fba141cdb5c015493c2907c46bacf9f7cbd7c8e3d4e97df891f18dddcff69c", + "proposer": { + "metadata": ${dappMetadataJsonString}, + "publicKey": "095d9992ca0eb6081cabed26faf48919162fd70cc66d639f118a60507ae0463d" + }, + "relays": [ + { + "protocol": "irn" + } + ], + "requiredNamespaces": ${requiredNamespacesJsonString} + }, + "verifyContext": { + "verified": { + "origin": "https://app.test.org", + "validation": "UNKNOWN", + "verifyUrl": "https://verify.walletconnect.com" + } + } + }` +} + +function formatBuildApprovedNamespacesResult(networksArray, accountsArray) { + let requiredChainsStr = networksArray.map(chainId => `"eip155:${chainId}"`).join(',') + let requiredAccountsStr = accountsArray.map(address => networksArray.map(chainId => `"eip155:${chainId}:${address}"`).join(',')).join(',') + return `{ + "eip155": { + "chains": [${requiredChainsStr}], + "accounts": [${requiredAccountsStr}], + "events": ${requiredEventsJsonString}, + "methods": ${requiredMethodsJsonString} + } + }` +} + +function formatApproveSessionResponse(networksArray, accountsArray) { + let chainsStr = networksArray.map(chainId => `"eip155:${chainId}"`).join(',') + let accountsStr = accountsArray.map(address => networksArray.map(chainId => `"eip155:${chainId}:${address}"`).join(',')).join(',') + return `{ + "acknowledged": true, + "controller": "da4a87d5f0f54951afe870ebf020cf03f8a3522fbd219398c3fa159a37e16d54", + "expiry": 1716581732, + "namespaces": { + "eip155": { + "accounts": [${accountsStr}], + "chains": [${chainsStr}], + "events": ${requiredEventsJsonString}, + "methods": ${requiredMethodsJsonString} + } + }, + "optionalNamespaces": ${optionalNamespacesJsonString}, + "pairingTopic": "50fba141cdb5c015493c2907c46bacf9f7cbd7c8e3d4e97df891f18dddcff69c", + "peer": { + "metadata": ${dappMetadataJsonString}, + "publicKey": "095d9992ca0eb6081cabed26faf48919162fd70cc66d639f118a60507ae0463d" + }, + "relay": { + "protocol": "irn" + }, + "requiredNamespaces": ${requiredNamespacesJsonString}, + "self": { + "metadata": { + "description": "Status Wallet", + "icons": [ + "https://status.im/img/status-footer-logo.svg" + ], + "name": "Status", + "url": "http://localhost" + }, + "publicKey": "da4a87d5f0f54951afe870ebf020cf03f8a3522fbd219398c3fa159a37e16d54" + }, + "topic": "e39e1f435a46b5ee6b31484d1751cfbc35be1275653af2ea340974a7592f1a19" + }` +} \ No newline at end of file diff --git a/storybook/qmlTests/tests/tst_DAppsWorkflow.qml b/storybook/qmlTests/tests/tst_DAppsWorkflow.qml index 83d9ab49d7..6720a5d89f 100644 --- a/storybook/qmlTests/tests/tst_DAppsWorkflow.qml +++ b/storybook/qmlTests/tests/tst_DAppsWorkflow.qml @@ -2,56 +2,286 @@ import QtQuick 2.15 import QtTest 1.15 import StatusQ 0.1 // See #10218 +import StatusQ.Core.Utils 0.1 // See #10218 import QtQuick.Controls 2.15 import Storybook 1.0 -//import AppLayouts.Wallet.panels 1.0 - import AppLayouts.Wallet.services.dapps 1.0 +import AppLayouts.Profile.stores 1.0 +import shared.stores 1.0 -import QtQml.Models 2.15 +import AppLayouts.Wallet.panels 1.0 + +import "helpers/wallet_connect.js" as Testing Item { id: root width: 600 height: 400 - // TODO: mock WalletConnectSDK - // Component { - // id: componentUnderTest - // DAppsWorkflow { - // } - // } - // TestCase { - // name: "DAppsWorkflow" - // when: windowShown + Component { + id: sdkComponent - // property DAppsWorkflow controlUnderTest: null + WalletConnectSDKBase { + property bool sdkReady: true - // function init() { - // controlUnderTest = createTemporaryObject(componentUnderTest, root) - // } + property int pairCalled: 0 - // function test_ClickToOpenAndClosePopup() { - // verify(!!controlUnderTest) - // waitForRendering(controlUnderTest) + getActiveSessions: function() { + return [] + } + pair: function() { + pairCalled++ + } - // mouseClick(controlUnderTest, Qt.LeftButton) - // waitForRendering(controlUnderTest) + property var buildApprovedNamespacesCalls: [] + buildApprovedNamespaces: function(params, supportedNamespaces) { + buildApprovedNamespacesCalls.push({params, supportedNamespaces}) + } - // let popup = findChild(controlUnderTest, "dappsPopup") - // verify(!!popup) - // verify(popup.opened) + property var approveSessionCalls: [] + approveSession: function(sessionProposalJson, approvedNamespaces) { + approveSessionCalls.push({sessionProposalJson, approvedNamespaces}) + } + } + } - // mouseClick(Overlay.overlay, Qt.LeftButton) - // waitForRendering(controlUnderTest) + Component { + id: serviceComponent - // verify(!popup.opened) - // } - // } + WalletConnectService { + property var onApproveSessionResultTriggers: [] + onApproveSessionResult: function(session, error) { + onApproveSessionResultTriggers.push({session, error}) + } + + property var onDisplayToastMessageTriggers: [] + onDisplayToastMessage: function(message, error) { + onDisplayToastMessageTriggers.push({message, error}) + } + } + } + + Component { + id: dappsStoreComponent + + DAppsStore { + signal dappsListReceived(string dappsJson) + + // By default, return no dapps in store + function getDapps() { + dappsListReceived('[]') + return true + } + + property var addWalletConnectSessionCalls: [] + function addWalletConnectSession(sessionJson) { + addWalletConnectSessionCalls.push({sessionJson}) + } + } + } + + Component { + id: walletStoreComponent + + WalletStore { + readonly property ListModel flatNetworks: ListModel { + ListElement { chainId: 1 } + ListElement { chainId: 2 } + } + + readonly property ListModel accounts: ListModel { + ListElement { address: "0x1" } + ListElement { address: "0x2" } + } + } + } + + TestCase { + id: walletConnectServiceTest + name: "WalletConnectService" + + property WalletConnectService service: null + + SignalSpy { + id: connectDAppSpy + target: walletConnectServiceTest.service + signalName: "connectDApp" + + property var argPos: { + "dappChains": 0, + "sessionProposalJson": 1, + "availableNamespaces": 0 + } + } + + function init() { + let walletStore = createTemporaryObject(walletStoreComponent, root) + verify(!!walletStore) + let sdk = createTemporaryObject(sdkComponent, root, { projectId: "12ab" }) + verify(!!sdk) + let store = createTemporaryObject(dappsStoreComponent, root) + verify(!!store) + service = createTemporaryObject(serviceComponent, root, {wcSDK: sdk, store: store, walletStore: walletStore}) + verify(!!service) + } + + function cleanup() { + service.destroy() + connectDAppSpy.clear() + } + + function test_TestPairing() { + // All calls to SDK are expected as events to be made by the wallet connect SDK + let sdk = service.wcSDK + let walletStore = service.walletStore + let store = service.store + + service.pair("wc:12ab@1?bridge=https%3A%2F%2Fbridge.walletconnect.org&key=12ab") + compare(sdk.pairCalled, 1, "expected a call to sdk.pair") + + sdk.sessionProposal(JSON.parse(Testing.formatSessionProposal())) + compare(sdk.buildApprovedNamespacesCalls.length, 1, "expected a call to sdk.buildApprovedNamespaces") + var args = sdk.buildApprovedNamespacesCalls[0] + verify(!!args.supportedNamespaces, "expected supportedNamespaces to be set") + let chainsForApproval = args.supportedNamespaces.eip155.chains + let networksArray = ModelUtils.modelToArray(walletStore.flatNetworks).map(entry => entry.chainId) + verify(networksArray.every(chainId => chainsForApproval.some(eip155Chain => eip155Chain === `eip155:${chainId}`)), + "expect all the networks to be present") + // We test here all accounts for one chain only, we have separate tests to validate that all accounts are present + let allAccountsForApproval = args.supportedNamespaces.eip155.accounts + let accountsArray = ModelUtils.modelToArray(walletStore.accounts).map(entry => entry.address) + verify(accountsArray.every(address => allAccountsForApproval.some(eip155Address => eip155Address === `eip155:${networksArray[0]}:${address}`)), + "expect at least all accounts for the first chain to be present" + ) + + let allApprovedNamespaces = JSON.parse(Testing.formatBuildApprovedNamespacesResult(networksArray, accountsArray)) + sdk.buildApprovedNamespacesResult(allApprovedNamespaces, "") + compare(connectDAppSpy.count, 1, "expected a call to service.connectDApp") + let connectArgs = connectDAppSpy.signalArguments[0] + compare(connectArgs[connectDAppSpy.argPos.dappChains], networksArray, "expected all provided networks (walletStore.flatNetworks) for the dappChains") + verify(!!connectArgs[connectDAppSpy.argPos.sessionProposalJson], "expected sessionProposalJson to be set") + verify(!!connectArgs[connectDAppSpy.argPos.availableNamespaces], "expected availableNamespaces to be set") + + let selectedAccount = walletStore.accounts.get(1) + service.approvePairSession(connectArgs[connectDAppSpy.argPos.sessionProposalJson], connectArgs[connectDAppSpy.argPos.dappChains], selectedAccount) + compare(sdk.buildApprovedNamespacesCalls.length, 2, "expected a call to sdk.buildApprovedNamespaces") + args = sdk.buildApprovedNamespacesCalls[1] + verify(!!args.supportedNamespaces, "expected supportedNamespaces to be set") + // We test here that only one account for all chains is provided + let accountsForApproval = args.supportedNamespaces.eip155.accounts + compare(accountsForApproval.length, networksArray.length, "expect only one account per chain") + compare(accountsForApproval[0], `eip155:${networksArray[0]}:${selectedAccount.address}`) + compare(accountsForApproval[1], `eip155:${networksArray[1]}:${selectedAccount.address}`) + + let approvedNamespaces = JSON.parse(Testing.formatBuildApprovedNamespacesResult(networksArray, [selectedAccount.address])) + sdk.buildApprovedNamespacesResult(approvedNamespaces, "") + + compare(sdk.approveSessionCalls.length, 1, "expected a call to sdk.approveSession") + verify(!!sdk.approveSessionCalls[0].sessionProposalJson, "expected sessionProposalJson to be set") + verify(!!sdk.approveSessionCalls[0].approvedNamespaces, "expected approvedNamespaces to be set") + + let finalApprovedNamespaces = JSON.parse(Testing.formatApproveSessionResponse(networksArray, [selectedAccount.address])) + sdk.approveSessionResult(finalApprovedNamespaces, "") + verify(store.addWalletConnectSessionCalls.length === 1) + verify(store.addWalletConnectSessionCalls[0].sessionJson, "expected sessionJson to be set") + + verify(service.onApproveSessionResultTriggers.length === 1) + verify(service.onApproveSessionResultTriggers[0].session, "expected session to be set") + + compare(service.onDisplayToastMessageTriggers.length, 1, "expected a success message to be displayed") + verify(!service.onDisplayToastMessageTriggers[0].error, "expected no error") + verify(service.onDisplayToastMessageTriggers[0].message, "expected message to be set") + } + } + + Component { + id: componentUnderTest + DAppsWorkflow { + } + } + + TestCase { + id: dappsWorkflowTest + name: "DAppsWorkflow" + when: windowShown + + property DAppsWorkflow controlUnderTest: null + + SignalSpy { + id: dappsListReadySpy + target: dappsWorkflowTest.controlUnderTest + signalName: "dappsListReady" + } + + SignalSpy { + id: pairWCReadySpy + target: dappsWorkflowTest.controlUnderTest + signalName: "pairWCReady" + } + + function init() { + let walletStore = createTemporaryObject(walletStoreComponent, root) + verify(!!walletStore) + let sdk = createTemporaryObject(sdkComponent, root, { projectId: "12ab" }) + verify(!!sdk) + let store = createTemporaryObject(dappsStoreComponent, root) + verify(!!store) + let service = createTemporaryObject(serviceComponent, root, {wcSDK: sdk, store: store, walletStore: walletStore}) + verify(!!service) + controlUnderTest = createTemporaryObject(componentUnderTest, root, {wcService: service}) + verify(!!controlUnderTest) + } + + function cleanup() { + controlUnderTest.destroy() + dappsListReadySpy.reset() + pairWCReadySpy.reset() + } + + function test_OpenAndCloseDappList() { + waitForRendering(controlUnderTest) + + compare(dappsListReadySpy.count, 0, "expected NO dappsListReady signal to be emitted") + mouseClick(controlUnderTest, Qt.LeftButton) + waitForRendering(controlUnderTest) + compare(dappsListReadySpy.count, 1, "expected dappsListReady signal to be emitted") + + let popup = findChild(controlUnderTest, "dappsPopup") + verify(!!popup) + verify(popup.opened) + + mouseClick(Overlay.overlay, Qt.LeftButton) + waitForRendering(controlUnderTest) + + verify(!popup.opened) + } + + function test_OpenPairModal() { + waitForRendering(controlUnderTest) + + mouseClick(controlUnderTest, Qt.LeftButton) + waitForRendering(controlUnderTest) + + let popup = findChild(controlUnderTest, "dappsPopup") + verify(!!popup) + verify(popup.opened) + + let connectButton = findChild(popup, "connectDappButton") + verify(!!connectButton) + + verify(pairWCReadySpy.count === 0, "expected NO pairWCReady signal to be emitted") + mouseClick(connectButton, Qt.LeftButton) + waitForRendering(controlUnderTest) + verify(pairWCReadySpy.count === 1, "expected pairWCReady signal to be emitted") + + let pairWCModal = findChild(controlUnderTest, "pairWCModal") + verify(!!pairWCModal) + } + } TestCase { name: "ServiceHelpers" diff --git a/ui/app/AppLayouts/Wallet/panels/DAppsWorkflow.qml b/ui/app/AppLayouts/Wallet/panels/DAppsWorkflow.qml index d599444e62..4fb947053f 100644 --- a/ui/app/AppLayouts/Wallet/panels/DAppsWorkflow.qml +++ b/ui/app/AppLayouts/Wallet/panels/DAppsWorkflow.qml @@ -8,6 +8,7 @@ import shared.popups.walletconnect 1.0 import AppLayouts.Wallet.services.dapps 1.0 import shared.stores 1.0 +import utils 1.0 ConnectedDappsButton { id: root @@ -16,6 +17,7 @@ ConnectedDappsButton { signal dappsListReady() signal pairWCReady() + signal displayToastMessage(string message, bool error) onClicked: { dappsListLoader.active = true @@ -138,5 +140,9 @@ ConnectedDappsButton { } } } + + function onDisplayToastMessage(message, err) { + root.displayToastMessage(message, err) + } } } diff --git a/ui/app/AppLayouts/Wallet/panels/WalletHeader.qml b/ui/app/AppLayouts/Wallet/panels/WalletHeader.qml index 6c1fe580ef..727c44785f 100644 --- a/ui/app/AppLayouts/Wallet/panels/WalletHeader.qml +++ b/ui/app/AppLayouts/Wallet/panels/WalletHeader.qml @@ -83,6 +83,16 @@ Item { enabled: !!Global.walletConnectService wcService: Global.walletConnectService + + onDisplayToastMessage: (message, isErr) => { + if (isErr) { + Global.displayToastMessage(message, "", "warning", false, + Constants.ephemeralNotificationType.danger, "") + } else { + Global.displayToastMessage(message, "", "checkmark-circle", false, + Constants.ephemeralNotificationType.success, "") + } + } } StatusButton { diff --git a/ui/app/AppLayouts/Wallet/services/dapps/DAppsListProvider.qml b/ui/app/AppLayouts/Wallet/services/dapps/DAppsListProvider.qml index 5815e62ad9..9dbb47078d 100644 --- a/ui/app/AppLayouts/Wallet/services/dapps/DAppsListProvider.qml +++ b/ui/app/AppLayouts/Wallet/services/dapps/DAppsListProvider.qml @@ -9,7 +9,7 @@ import utils 1.0 QObject { id: root - required property WalletConnectSDK sdk + required property WalletConnectSDKBase sdk required property DAppsStore store readonly property alias dappsModel: d.dappsModel diff --git a/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectSDK.qml b/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectSDK.qml index 2da7e532bb..f2317e8eb1 100644 --- a/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectSDK.qml +++ b/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectSDK.qml @@ -8,10 +8,9 @@ import QtWebChannel 1.15 import StatusQ.Core.Utils 0.1 as SQUtils import StatusQ.Components 0.1 -Item { +WalletConnectSDKBase { id: root - required property string projectId readonly property alias sdkReady: d.sdkReady readonly property alias webEngineLoader: loader @@ -21,66 +20,49 @@ Item { implicitWidth: 1 implicitHeight: 1 - signal statusChanged(string message) - signal sdkInit(bool success, var result) - signal pairResponse(bool success) - signal sessionProposal(var sessionProposal) - signal sessionProposalExpired() - signal buildApprovedNamespacesResult(var session, string error) - signal approveSessionResult(var approvedNamespaces, string error) - signal rejectSessionResult(string error) - signal sessionRequestEvent(var sessionRequest) - signal sessionRequestUserAnswerResult(bool accept, string error) - - signal authRequest(var request) - signal authMessageFormated(string formatedMessage, string address) - signal authRequestUserAnswerResult(bool accept, string error) - - signal sessionDelete(var topic, string error) - /// Generates \c pairResponse signal and expects to receive /// a \c sessionProposal signal with the sessionProposal object - function pair(pairLink) { + pair: function(pairLink) { wcCalls.pair(pairLink) } - function getPairings(callback) { + getPairings: function(callback) { wcCalls.getPairings(callback) } - function getActiveSessions(callback) { + getActiveSessions: function(callback) { wcCalls.getActiveSessions(callback) } - function disconnectSession(topic) { + disconnectSession: function(topic) { wcCalls.disconnectSession(topic) } - function disconnectPairing(topic) { + disconnectPairing: function(topic) { wcCalls.disconnectPairing(topic) } - function ping(topic) { + ping: function(topic) { wcCalls.ping(topic) } - function buildApprovedNamespaces(params, supportedNamespaces) { + buildApprovedNamespaces: function(params, supportedNamespaces) { wcCalls.buildApprovedNamespaces(params, supportedNamespaces) } - function approveSession(sessionProposal, supportedNamespaces) { + approveSession: function(sessionProposal, supportedNamespaces) { wcCalls.approveSession(sessionProposal, supportedNamespaces) } - function rejectSession(id) { + rejectSession: function(id) { wcCalls.rejectSession(id) } - function acceptSessionRequest(topic, id, signature) { + acceptSessionRequest: function(topic, id, signature) { wcCalls.acceptSessionRequest(topic, id, signature) } - function rejectSessionRequest(topic, id, error) { + rejectSessionRequest: function(topic, id, error) { wcCalls.rejectSessionRequest(topic, id, error) } diff --git a/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectSDKBase.qml b/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectSDKBase.qml new file mode 100644 index 0000000000..22270df913 --- /dev/null +++ b/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectSDKBase.qml @@ -0,0 +1,62 @@ +import QtQuick 2.15 + +/// SDK requires a visible parent to embed WebEngineView +Item { + required property string projectId + + signal statusChanged(string message) + signal sdkInit(bool success, var result) + signal pairResponse(bool success) + signal sessionProposal(var sessionProposal) + signal sessionProposalExpired() + signal buildApprovedNamespacesResult(var session, string error) + signal approveSessionResult(var approvedNamespaces, string error) + signal rejectSessionResult(string error) + signal sessionRequestEvent(var sessionRequest) + signal sessionRequestUserAnswerResult(bool accept, string error) + + signal authRequest(var request) + signal authMessageFormated(string formatedMessage, string address) + signal authRequestUserAnswerResult(bool accept, string error) + + signal sessionDelete(var topic, string error) + + property var pair: function(pairLink) { + console.error("pair not implemented") + } + property var getPairings: function(callback) { + console.error("getPairings not implemented") + } + property var getActiveSessions: function(callback) { + console.error("getActiveSessions not implemented") + } + property var disconnectSession: function(topic) { + console.error("disconnectSession not implemented") + } + property var disconnectPairing: function(topic) { + console.error("disconnectPairing not implemented") + } + + property var ping: function(topic) { + console.error("ping not implemented") + } + + property var buildApprovedNamespaces: function(params, supportedNamespaces) { + console.error("buildApprovedNamespaces not implemented") + } + property var approveSession: function(sessionProposal, supportedNamespaces) { + console.error("approveSession not implemented") + } + + property var rejectSession: function(id) { + console.error("rejectSession not implemented") + } + + property var acceptSessionRequest: function(topic, id, signature) { + console.error("acceptSessionRequest not implemented") + } + + property var rejectSessionRequest: function(topic, id, error) { + console.error("rejectSessionRequest not implemented") + } +} diff --git a/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml b/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml index cbd7e43182..0c76ff4267 100644 --- a/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml +++ b/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectService.qml @@ -14,11 +14,11 @@ import utils 1.0 QObject { id: root - required property WalletConnectSDK wcSDK + required property WalletConnectSDKBase wcSDK required property DAppsStore store required property WalletStore walletStore - readonly property alias dappsModel: d.dappsProvider.dappsModel + readonly property alias dappsModel: dappsProvider.dappsModel readonly property var validAccounts: SortFilterProxyModel { sourceModel: walletStore.accounts @@ -51,6 +51,7 @@ QObject { signal connectDApp(var dappChains, var sessionProposal, var approvedNamespaces) signal approveSessionResult(var session, var error) + signal displayToastMessage(string message, bool error) readonly property Connections sdkConnections: Connections { target: wcSDK @@ -78,9 +79,6 @@ QObject { } function onApproveSessionResult(session, err) { - // Notify client - root.approveSessionResult(session, err) - if (err) { // TODO #14676: handle the error console.error("Failed to approve session", err) @@ -89,44 +87,42 @@ QObject { // TODO #14754: implement custom dApp notification let app_url = d.currentSessionProposal ? d.currentSessionProposal.params.proposer.metadata.url : "-" - Global.displayToastMessage(qsTr("Connected to %1 via WalletConnect").arg(app_url), "", "checkmark-circle", false, Constants.ephemeralNotificationType.success, "") + root.displayToastMessage(qsTr("Connected to %1 via WalletConnect").arg(app_url), false) // Persist session store.addWalletConnectSession(JSON.stringify(session)) - d.dappsProvider.updateDapps() + // Notify client + root.approveSessionResult(session, err) + + dappsProvider.updateDapps() } function onRejectSessionResult(err) { let app_url = d.currentSessionProposal ? d.currentSessionProposal.params.proposer.metadata.url : "-" if(err) { - Global.displayToastMessage(qsTr("Failed to reject connection request for %1").arg(app_url), "", "warning", false, Constants.ephemeralNotificationType.danger, "") + root.displayToastMessage(qsTr("Failed to reject connection request for %1").arg(app_url), true) } else { - Global.displayToastMessage(qsTr("Connection request for %1 was rejected").arg(app_url), "", "checkmark-circle", false, Constants.ephemeralNotificationType.success, "") + root.displayToastMessage(qsTr("Connection request for %1 was rejected").arg(app_url), false) } } function onSessionDelete(topic, err) { let app_url = d.currentSessionProposal ? d.currentSessionProposal.params.proposer.metadata.url : "-" if(err) { - Global.displayToastMessage(qsTr("Failed to disconnect from %1").arg(app_url), "", "warning", false, Constants.ephemeralNotificationType.danger, "") + root.displayToastMessage(qsTr("Failed to disconnect from %1").arg(app_url), true) } else { - Global.displayToastMessage(qsTr("Disconnected from %1").arg(app_url), "", "checkmark-circle", false, Constants.ephemeralNotificationType.success, "") + root.displayToastMessage(qsTr("Disconnected from %1").arg(app_url), false) } } } - QtObject { + QObject { id: d property var currentSessionProposal: null property var acceptedSessionProposal: null - readonly property DAppsListProvider dappsProvider: DAppsListProvider { - sdk: root.wcSDK - store: root.store - } - // TODO #14676: use it to check if already paired function getPairingTopicFromPairingUrl(url) { @@ -146,6 +142,13 @@ QObject { } Component.onCompleted: { - d.dappsProvider.updateDapps() + dappsProvider.updateDapps() + } + + DAppsListProvider { + id: dappsProvider + + sdk: root.wcSDK + store: root.store } } \ No newline at end of file diff --git a/ui/app/AppLayouts/Wallet/services/dapps/qmldir b/ui/app/AppLayouts/Wallet/services/dapps/qmldir index e99ace38b5..b974d8fb9a 100644 --- a/ui/app/AppLayouts/Wallet/services/dapps/qmldir +++ b/ui/app/AppLayouts/Wallet/services/dapps/qmldir @@ -1,3 +1,4 @@ +WalletConnectSDKBase 1.0 WalletConnectSDKBase.qml WalletConnectSDK 1.0 WalletConnectSDK.qml WalletConnectService 1.0 WalletConnectService.qml DAppsListProvider 1.0 DAppsListProvider.qml diff --git a/ui/imports/shared/popups/walletconnect/DAppsListPopup.qml b/ui/imports/shared/popups/walletconnect/DAppsListPopup.qml index 83ea5cde80..a22d7ced49 100644 --- a/ui/imports/shared/popups/walletconnect/DAppsListPopup.qml +++ b/ui/imports/shared/popups/walletconnect/DAppsListPopup.qml @@ -95,6 +95,7 @@ Popup { } StatusButton { + objectName: "connectDappButton" Layout.fillWidth: true Layout.preferredHeight: implicitHeight diff --git a/ui/imports/shared/popups/walletconnect/PairWCModal.qml b/ui/imports/shared/popups/walletconnect/PairWCModal.qml index c80804d1fc..12bac9a20d 100644 --- a/ui/imports/shared/popups/walletconnect/PairWCModal.qml +++ b/ui/imports/shared/popups/walletconnect/PairWCModal.qml @@ -14,6 +14,8 @@ import "PairWCModal" StatusDialog { id: root + objectName: "pairWCModal" + width: 480 implicitHeight: 633