import QtQuick 2.15 import QtWebView 1.15 // TODO #12434: remove debugging WebEngineView code // import QtWebEngine 1.10 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 import StatusQ.Core.Utils 0.1 as SQUtils // Control used to instantiate and run the the WalletConnect web SDK // The view is not used to draw anything, but has to be visible to be able to run JS code // Use the \c backgroundColor property to blend in with the background // \warning A too smaller height might cause rendering errors // TODO #12434: remove debugging WebEngineView code WebView { //WebEngineView { id: root implicitWidth: 1 implicitHeight: 1 required property string projectId required property color backgroundColor readonly property alias sdkReady: d.sdkReady readonly property alias pairingsModel: d.pairingsModel signal sdkInit(bool success, var result) signal pairSessionProposal(bool success, var sessionProposal) signal pairAcceptedResult(bool success, var sessionType) signal pairRejectedResult(bool success, var result) signal sessionRequestEvent(var sessionRequest) signal sessionRequestUserAnswerResult(bool accept, string error) signal responseTimeout() // TODO: proper report signal statusChanged(string message) function pair(pairLink) { let callStr = d.generateSdkCall("pair", `"${pairLink}"`, RequestCodes.PairSuccess, RequestCodes.PairError) d.requestSdkAsync(callStr) } function approvePairSession(sessionProposal, supportedNamespaces) { let callStr = d.generateSdkCall("approvePairSession", `${JSON.stringify(sessionProposal)}, ${JSON.stringify(supportedNamespaces)}`, RequestCodes.ApprovePairSuccess, RequestCodes.ApprovePairSuccess) d.requestSdkAsync(callStr) } function rejectPairSession(id) { let callStr = d.generateSdkCall("rejectPairSession", id, RequestCodes.RejectPairSuccess, RequestCodes.RejectPairError) d.requestSdkAsync(callStr) } function acceptSessionRequest(topic, id, signature) { let callStr = d.generateSdkCall("respondSessionRequest", `"${topic}", ${id}, "${signature}"`, RequestCodes.AcceptSessionSuccess, RequestCodes.AcceptSessionError) d.requestSdkAsync(callStr) } function rejectSessionRequest(topic, id, error) { let callStr = d.generateSdkCall("rejectSessionRequest", `"${topic}", ${id}, ${error}`, RequestCodes.RejectSessionSuccess, RequestCodes.RejectSessionError) d.requestSdkAsync(callStr) } // TODO #12434: remove debugging WebEngineView code onLoadingChanged: function(loadRequest) { console.debug(`@dd WalletConnectSDK.onLoadingChanged; status: ${loadRequest.status}; error: ${loadRequest.errorString}`) switch(loadRequest.status) { case WebView.LoadSucceededStatus: // case WebEngineView.LoadSucceededStatus: d.init(root.projectId) break case WebView.LoadFailedStatus: // case WebEngineView.LoadFailedStatus: root.statusChanged(`Failed loading SDK JS code; error: "${loadRequest.errorString}"`) break case WebView.LoadStartedStatus: // case WebEngineView.LoadStartedStatus: root.statusChanged(`Loading SDK JS code`) break } } Component.onCompleted: { console.debug(`@dd WalletConnectSDK onCompleted`) var scriptSrc = SQUtils.StringUtils.readTextFile(":/app/AppLayouts/Wallet/views/walletconnect/sdk/generated/bundle.js") // Load bundle from disk if not found in resources (Storybook) if (scriptSrc === "") { scriptSrc = SQUtils.StringUtils.readTextFile("./AppLayouts/Wallet/views/walletconnect/sdk/generated/bundle.js") if (scriptSrc === "") { console.error("Failed to read WalletConnect SDK bundle") return } } let htmlSrc = `
` console.debug(`@dd WalletConnectSDK.loadHtml; htmlSrc len: ${htmlSrc.length}`) root.loadHtml(htmlSrc, "https://status.app") } Timer { id: timer interval: 100 repeat: true running: false triggeredOnStart: true property int errorCount: 0 onTriggered: { root.runJavaScript( "wcResult", function(wcResult) { if (!wcResult) { return } let done = false if (wcResult.error) { console.debug(`WC JS error response - ${JSON.stringify(wcResult)}`) done = true if (!d.sdkReady) { root.statusChanged(`[${timer.errorCount++}] Failed SDK init; error: ${wcResult.error}`) } else { root.statusChanged(`[${timer.errorCount++}] Operation error: ${wcResult.error}`) } } if (wcResult.state !== undefined) { switch (wcResult.state) { case RequestCodes.SdkInitSuccess: d.sdkReady = true root.sdkInit(true, "") d.startListeningForEvents() break case RequestCodes.SdkInitError: d.sdkReady = false root.sdkInit(false, wcResult.error) break case RequestCodes.PairSuccess: root.pairSessionProposal(true, wcResult.result) d.getPairings() break case RequestCodes.PairError: root.pairSessionProposal(false, wcResult.error) break case RequestCodes.ApprovePairSuccess: root.pairAcceptedResult(true, "") d.getPairings() break case RequestCodes.ApprovePairError: root.pairAcceptedResult(false, wcResult.error) d.getPairings() break case RequestCodes.RejectPairSuccess: root.pairRejectedResult(true, "") break case RequestCodes.RejectPairError: root.pairRejectedResult(false, wcResult.error) break case RequestCodes.AcceptSessionSuccess: root.sessionRequestUserAnswerResult(true, "") break case RequestCodes.AcceptSessionError: root.sessionRequestUserAnswerResult(true, wcResult.error) break case RequestCodes.RejectSessionSuccess: root.sessionRequestUserAnswerResult(false, "") break case RequestCodes.RejectSessionError: root.sessionRequestUserAnswerResult(false, wcResult.error) break case RequestCodes.GetPairings: d.populatePairingsModel(wcResult.result) break case RequestCodes.GetPairingsError: console.error(`WalletConnectSDK - getPairings error: ${wcResult.error}`) break default: { root.statusChanged(`[${timer.errorCount++}] Unknown state: ${wcResult.state}`) } } done = true } if (done) { timer.stop() } } ) } } Timer { id: responseTimeoutTimer interval: 10000 repeat: false running: timer.running onTriggered: { timer.stop() root.responseTimeout() } } Timer { id: eventsTimer interval: 100 repeat: true running: false onTriggered: { root.runJavaScript("window.wcEventResult ? window.wcEventResult.shift() : null", function(event) { if (event) { switch(event.name) { case "session_request": root.sessionRequestEvent(event.payload) break default: console.error("WC unknown event type: ", event.type) break } } }) } } QtObject { id: d property var sessionProposal: null property var sessionType: null property bool sdkReady: false property ListModel pairingsModel: pairings onSdkReadyChanged: { if (sdkReady) { d.getPairings() } } function populatePairingsModel(pairList) { pairings.clear(); for (let i = 0; i < pairList.length; i++) { pairings.append({ active: pairList[i].active, topic: pairList[i].topic, expiry: pairList[i].expiry }); } } function isWaitingForSdk() { return timer.running } function generateSdkCall(methodName, paramsStr, successState, errorState) { return "wcResult = {}; try { wc." + methodName + "(" + paramsStr + ").then((callRes) => { wcResult = {state: " + successState + ", error: null, result: callRes}; }).catch((error) => { wcResult = {state: " + errorState + ", error: error}; }); } catch (e) { wcResult = {state: " + errorState + ", error: \"Exception: \" + e.message}; }; wcResult" } function requestSdkAsync(jsCode) { root.runJavaScript(jsCode, function(result) { timer.restart() } ) } function requestSdk(methodName, paramsStr, successState, errorState) { const jsCode = "wcResult = {}; try { const callRes = wc." + methodName + "(" + (paramsStr ? (paramsStr) : "") + "); wcResult = {state: " + successState + ", error: null, result: callRes}; } catch (e) { wcResult = {state: " + errorState + ", error: \"Exception: \" + e.message}; }; wcResult" root.runJavaScript(jsCode, function(result) { timer.restart() } ) } function startListeningForEvents() { const jsCode = " try { function processWCEvents() { window.wcEventResult = []; window.wcEventError = null window.wc.registerForSessionRequest((event) => { window.wcEventResult.push({name: 'session_request', payload: event}); }); } processWCEvents(); } catch (e) { window.wcEventError = e } window.wcEventError" root.runJavaScript(jsCode, function(result) { if (result) { console.error("startListeningForEvents: processWCEvents error", result) return } } ) eventsTimer.start() } function init(projectId) { d.requestSdkAsync(generateSdkCall("init", `"${projectId}"`, RequestCodes.SdkInitSuccess, RequestCodes.SdkInitError)) } function getPairings(projectId) { d.requestSdk("getPairings", `null`, RequestCodes.GetPairings, RequestCodes.GetPairingsError) } } ListModel { id: pairings } }