diff --git a/Makefile b/Makefile
index 38f67cb158..809f4645f8 100644
--- a/Makefile
+++ b/Makefile
@@ -312,6 +312,7 @@ statusq-tests:
-DSTATUSQ_BUILD_SANDBOX=OFF \
-DSTATUSQ_BUILD_SANITY_CHECKER=OFF \
-DSTATUSQ_BUILD_TESTS=ON \
+ -DSTATUSQ_SHADOW_BUILD=OFF \
-B $(STATUSQ_BUILD_PATH) \
-S $(STATUSQ_SOURCE_PATH) \
$(HANDLE_OUTPUT)
diff --git a/ui/StatusQ/src/StatusQ/Components/StatusWebView.qml b/ui/StatusQ/src/StatusQ/Components/StatusWebView.qml
new file mode 100644
index 0000000000..80d76b6244
--- /dev/null
+++ b/ui/StatusQ/src/StatusQ/Components/StatusWebView.qml
@@ -0,0 +1,191 @@
+import QtQuick 2.15
+import QtWebView 1.15
+
+// Specialization of \c WebView that provides a bridge between QML and JS code in the web page
+// The bridge uses a polling mechanism for handing async responses
+// TODO: stop timer when there is nothing to poll to
+// TODO: simpler events
+WebView {
+ id: root
+
+ // object name under the window object that will be used to cache internal runtime state
+ property string globalObjectName: "statusq"
+
+ signal contentReady();
+ signal contentFailedLoading(string errorString);
+
+ function asyncCall(callName, paramsStr) {
+ return d.call(callName, paramsStr, d.callTypeAsync)
+ }
+
+ function call(callName, paramsStr) {
+ return d.call(callName, paramsStr, d.callTypeSync)
+ }
+
+ // callback of type (result, error) => {}
+ function onCallback(callName, callback) {
+ d.call(callName, null, d.callTypeCallback).then(function(result) {
+ callback(result, false)
+ }).error(function(error) {
+ callback(error, true)
+ });
+ }
+
+ Timer {
+ id: timer
+ interval: 100
+ repeat: true
+ running: false
+
+ onTriggered: {
+ root.runJavaScript(`${d.ctx}.popCalls = ${d.ctx}.calls; ${d.ctx}.calls = null; ${d.ctx}.popCalls`, function(results) {
+ if (!results) {
+ return;
+ }
+ d.pendingResults = d.pendingResults.concat(results);
+
+ d.processPendingResults();
+ });
+ }
+ }
+
+ QtObject {
+ id: d
+
+ readonly property int successState: 1
+ readonly property int errorState: 2
+ readonly property int exceptionState: 3
+
+ readonly property int callTypeAsync: 1
+ readonly property int callTypeSync: 2
+ readonly property int callTypeCallback: 3
+
+ readonly property string ctx: `window.${root.globalObjectName}`
+
+ property int nextCallIndex: 0
+ property var callbacks: ({})
+ property var pendingResults: []
+
+ function call(callName, paramsStr, callType) {
+ const currentCallIndex = d.nextCallIndex++;
+ var jsCode = `
+ if (!${d.ctx}) {
+ ${d.ctx} = {};
+ }
+ function reportCallResult(callIndex, state, result) {
+ if (!${d.ctx}.calls) {
+ ${d.ctx}.calls = [];
+ }
+ const callRes = {state: state, result: result, callIndex: callIndex};
+ ${d.ctx}.calls.push(callRes);
+ }
+
+ try {
+ switch(${callType}) {
+ case ${d.callTypeAsync}:
+ ${callName}(${paramsStr}).then((callRes) => {
+ reportCallResult(${currentCallIndex}, ${d.successState}, callRes);
+ }).catch((error) => {
+ reportCallResult(${currentCallIndex}, ${d.errorState}, error);
+ });
+ break;
+ case ${d.callTypeSync}:
+ const callRes = ${callName}(${paramsStr});
+ reportCallResult(${currentCallIndex}, ${d.successState}, callRes);
+ break;
+ case ${d.callTypeCallback}:
+ ${callName}(${paramsStr}, function(callRes) {
+ reportCallResult(${currentCallIndex}, ${d.successState}, callRes);
+ });
+ }
+ } catch (e) {
+ ${d.ctx}.errorRes = {state: ${d.exceptionState}, result: e.message, callIndex: ${currentCallIndex}};
+ }
+ ${d.ctx}.errorRes
+ `;
+
+ let promise = promiseComponent.createObject(null, {callIndex: currentCallIndex})
+ root.runJavaScript(jsCode, function(result) {
+ d.callbacks[currentCallIndex] = promise;
+ if (!result) {
+ timer.restart();
+ return;
+ }
+
+ // Process it now
+ d.pendingResults = d.pendingResults.concat(result);
+ d.processPendingResults();
+ });
+
+ return promise;
+ }
+
+ function processPendingResults() {
+ while(pendingResults.length != 0) {
+ const res = pendingResults[0]
+ if(d.callbacks[res.callIndex]) {
+ const callback = d.callbacks[res.callIndex]
+ if (res.state === d.successState && callback.hasSuccess()) {
+ callback.successCallback(res.result)
+ } else if (res.state !== d.successState && callback.hasError()) {
+ callback.errorCallback(res.result)
+ } else {
+ callback.result = res
+ }
+ d.callbacks[res.callIndex] = null
+ }
+
+ pendingResults.splice(0, 1)
+ }
+ }
+ }
+
+ Component {
+ id: promiseComponent
+
+ QtObject {
+ id: callbackObj
+
+ function then(callback) {
+ successCallback = callback;
+ // If the callback is set after the result is available
+ if (result && result.state === d.successState) {
+ successCallback(result.result);
+ }
+ return this;
+ }
+
+ function error(callback) {
+ errorCallback = callback;
+ // If the callback is set after the result is available
+ if (result && result.state !== d.successState) {
+ successCallback(result.result);
+ }
+ return this;
+ }
+
+ function hasSuccess() {
+ return successCallback !== null
+ }
+ function hasError() {
+ return errorCallback !== null
+ }
+
+ property var successCallback: null
+ property var errorCallback: null
+ property int callIndex: -1
+ property var result: null
+ }
+ }
+
+ onLoadingChanged: function(loadRequest) {
+ switch(loadRequest.status) {
+ case WebView.LoadSucceededStatus:
+ root.contentReady();
+ break
+ case WebView.LoadFailedStatus:
+ root.contentFailedLoading(loadRequest.errorString);
+ break
+ }
+ }
+}
\ No newline at end of file
diff --git a/ui/StatusQ/src/StatusQ/Components/qmldir b/ui/StatusQ/src/StatusQ/Components/qmldir
index 617d87678b..6804c74ee6 100644
--- a/ui/StatusQ/src/StatusQ/Components/qmldir
+++ b/ui/StatusQ/src/StatusQ/Components/qmldir
@@ -66,3 +66,4 @@ StatusToastMessage 0.1 StatusToastMessage.qml
StatusToolBar 0.1 StatusToolBar.qml
StatusVideo 0.1 StatusVideo.qml
StatusWizardStepper 0.1 StatusWizardStepper.qml
+StatusWebView 0.1 StatusWebView.qml
\ No newline at end of file
diff --git a/ui/StatusQ/src/statusq.qrc b/ui/StatusQ/src/statusq.qrc
index 079e87408e..5c30cbd804 100644
--- a/ui/StatusQ/src/statusq.qrc
+++ b/ui/StatusQ/src/statusq.qrc
@@ -60,6 +60,7 @@
StatusQ/Components/StatusVideo.qml
StatusQ/Components/StatusWizardStepper.qml
StatusQ/Components/StatusSortableColumnHeader.qml
+ StatusQ/Components/StatusWebView.qml
StatusQ/Controls/Validators/qmldir
StatusQ/Controls/Validators/StatusAddressOrEnsValidator.qml
StatusQ/Controls/Validators/StatusAddressValidator.qml
diff --git a/ui/StatusQ/tests/CMakeLists.txt b/ui/StatusQ/tests/CMakeLists.txt
index 9bdb83f08f..bbe72290ba 100644
--- a/ui/StatusQ/tests/CMakeLists.txt
+++ b/ui/StatusQ/tests/CMakeLists.txt
@@ -6,8 +6,8 @@ enable_testing()
set(CMAKE_AUTOMOC ON)
-find_package(QT NAMES Qt6 Qt5 COMPONENTS QuickTest Qml Quick REQUIRED)
-find_package(Qt${QT_VERSION_MAJOR} COMPONENTS QuickTest Qml Quick REQUIRED)
+find_package(QT NAMES Qt6 Qt5 COMPONENTS QuickTest Qml Quick WebView REQUIRED)
+find_package(Qt${QT_VERSION_MAJOR} COMPONENTS QuickTest Qml Quick WebView REQUIRED)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
@@ -34,6 +34,7 @@ target_link_libraries(${PROJECT_NAME} PRIVATE
Qt${QT_VERSION_MAJOR}::QuickTest
Qt${QT_VERSION_MAJOR}::Qml
Qt${QT_VERSION_MAJOR}::Quick
+ Qt${QT_VERSION_MAJOR}::WebView
)
target_compile_definitions(${PROJECT_NAME} PRIVATE
diff --git a/ui/StatusQ/tests/TestComponents/StatusWebView/test.html b/ui/StatusQ/tests/TestComponents/StatusWebView/test.html
new file mode 100644
index 0000000000..641cbd1e65
--- /dev/null
+++ b/ui/StatusQ/tests/TestComponents/StatusWebView/test.html
@@ -0,0 +1,39 @@
+
+
+
+ Test Page
+
+
+
+ Test Page
+
+
diff --git a/ui/StatusQ/tests/TestComponents/tst_test-StatusWebView.qml b/ui/StatusQ/tests/TestComponents/tst_test-StatusWebView.qml
new file mode 100644
index 0000000000..fbfc29464b
--- /dev/null
+++ b/ui/StatusQ/tests/TestComponents/tst_test-StatusWebView.qml
@@ -0,0 +1,210 @@
+import QtQuick 2.15
+import QtTest 1.0
+
+//import StatusQ 0.1 // https://github.com/status-im/status-desktop/issues/10218
+
+import StatusQ.Components 0.1
+
+import StatusQ.TestHelpers 0.1
+
+TestCase {
+ id: root
+ name: "StatusWebView"
+
+ Component {
+ id: webViewComponent
+ StatusWebView {
+ id: webView
+ url: "StatusWebView/test.html"
+
+ anchors.fill: parent
+ }
+ }
+
+ SignalSpy {
+ id: spyLoaded
+ target: webView
+ signalName: "contentReady"
+ }
+
+ Component {
+ id: promiseResultComponent
+ QtObject {
+ property var result: null
+ property bool success: false
+ property bool completed: false
+ }
+ }
+
+ property StatusWebView webView: null
+
+ function init() {
+ webView = webViewComponent.createObject(root);
+ }
+
+ function cleanup() {
+ webView.destroy();
+ }
+
+ function test_asyncFunction() {
+ spyLoaded.wait(1000);
+ const callbackInfo = promiseResultComponent.createObject(null);
+ var promise = webView.asyncCall("window.asyncFunction", "'asyncFunctionParam'");
+
+ promise.then(function(result) {
+ callbackInfo.result = result;
+ callbackInfo.completed = true;
+ callbackInfo.success = true;
+ }).error(function(error) {
+ callbackInfo.result = error;
+ callbackInfo.completed = true;
+ callbackInfo.success = false;
+ });
+ tryCompare(callbackInfo, "completed", true, 1000, "The promise should complete");
+ compare(callbackInfo.result, "asyncFunctionParam", "The promise should return the success result")
+ compare(callbackInfo.success, true, "The promise should succeed")
+ }
+
+ function test_rejectAsyncFunction() {
+ spyLoaded.wait(1000);
+ const callbackInfo = promiseResultComponent.createObject(null);
+ var promise = webView.asyncCall("window.asyncRejectFunction", "'asyncRejectFunctionParam'");
+
+ promise.then(function(result) {
+ callbackInfo.result = result;
+ callbackInfo.completed = true;
+ callbackInfo.success = true;
+ }).error(function(error) {
+ callbackInfo.result = error;
+ callbackInfo.completed = true;
+ callbackInfo.success = false;
+ });
+ tryCompare(callbackInfo, "completed", true, 1000, "The promise should complete");
+ compare(callbackInfo.result, "asyncRejectFunctionParam", "The promise should return the success result")
+ compare(callbackInfo.success, false, "The promise should fail")
+ }
+
+ function test_asyncCallMissing() {
+ spyLoaded.wait(1000);
+ const callbackInfo = promiseResultComponent.createObject(null);
+ var promise = webView.asyncCall("window.missingFunction", "'exceptionAsyncFunctionParam'");
+
+ promise.then(function(result) {
+ callbackInfo.result = result;
+ callbackInfo.completed = true;
+ callbackInfo.success = true;
+ }).error(function(error) {
+ callbackInfo.result = error;
+ callbackInfo.completed = true;
+ callbackInfo.success = false;
+ });
+ tryCompare(callbackInfo, "completed", true, 1000, "The promise should complete");
+ compare(callbackInfo.result, "window.missingFunction is not a function", "The promise should return a specific error message")
+ compare(callbackInfo.success, false, "The promise should fail")
+ }
+
+ function test_registerLate() {
+ spyLoaded.wait(1000);
+ let asyncFinalized = {done: false}
+ var promise = webView.call("window.syncFunction", "'syncFunctionParam'");
+ var asyncPromise = webView.asyncCall("window.asyncFunction", "'asyncFunctionParam'");
+
+ asyncPromise.then(function(result) {
+ asyncFinalized.done = true;
+ });
+ tryCompare(asyncFinalized, "done", true, 1000, "The promise should complete");
+
+ const callbackInfo = promiseResultComponent.createObject(null);
+ promise.then(function(result) {
+ callbackInfo.result = result;
+ callbackInfo.completed = true;
+ callbackInfo.success = true
+ }).error(function(error) {
+ callbackInfo.result = error;
+ callbackInfo.completed = true;
+ callbackInfo.success = false;
+ })
+ compare(callbackInfo.completed, true, "The synchronous promise should complete serially");
+ compare(callbackInfo.result, "syncFunctionParam", ".then should report the passed param")
+ compare(callbackInfo.success, true, "The promise should succeed")
+ }
+
+ function test_synchronousCall() {
+ spyLoaded.wait(1000);
+
+ const callbackInfo = promiseResultComponent.createObject(null);
+
+ var promise = webView.call("window.syncFunction", "'syncFunctionParam'");
+
+ promise.then(function(result) {
+ callbackInfo.result = result;
+ callbackInfo.completed = true;
+ callbackInfo.success = true
+ }).error(function(error) {
+ callbackInfo.result = error;
+ callbackInfo.completed = true;
+ callbackInfo.success = false;
+ })
+ tryCompare(callbackInfo, "completed", true, 1000, "The synchronous promise should complete");
+ compare(callbackInfo.result, "syncFunctionParam", ".then should report the passed param")
+ compare(callbackInfo.success, true, "The promise should succeed")
+ }
+
+ function test_synchronousCallThrows() {
+ spyLoaded.wait(1000);
+
+ const callbackInfo = promiseResultComponent.createObject(null);
+
+ var promise = webView.call("window.syncFunctionThrows", "'syncFunctionThrowsParam'");
+
+ promise.then(function(result) {
+ callbackInfo.result = result;
+ callbackInfo.completed = true;
+ callbackInfo.success = true
+ }).error(function(error) {
+ callbackInfo.result = error;
+ callbackInfo.completed = true;
+ callbackInfo.success = false;
+ })
+ tryCompare(callbackInfo, "completed", true, 1000, "The synchronous promise should complete");
+ compare(callbackInfo.result, "Test exception", ".error should report a specific exception message")
+ compare(callbackInfo.success, false, "The promise should succeed")
+ }
+
+ function test_synchronousCallMissing() {
+ spyLoaded.wait(1000);
+
+ const callbackInfo = promiseResultComponent.createObject(null);
+
+ var promise = webView.call("window.missingFunction", "'missingFunctionParam'");
+
+ promise.then(function(result) {
+ callbackInfo.completed = true;
+ callbackInfo.success = true
+ }).error(function(error) {
+ callbackInfo.result = error;
+ callbackInfo.completed = true;
+ callbackInfo.success = false;
+ })
+ tryCompare(callbackInfo, "completed", true, 1000, "The synchronous promise should complete");
+ compare(callbackInfo.success, false, "The promise should succeed")
+ compare(callbackInfo.result, "window.missingFunction is not a function", ".error should report a specific error message")
+ }
+
+ // function test_onCallback() {
+ // spyLoaded.wait(1000);
+
+ // const callbackInfo = promiseResultComponent.createObject(null);
+
+ // let testInfo = {counter: 0}
+
+ // webView.onCallback("document.addEventListener", `"testEvent"`, function(payload, error) {
+ // compare(payload, "testDetail", "The payload should be passed to the callback")
+ // testInfo.counter++;
+ // })
+
+ // webView.call("window.sendEvent", "'testDetail'");
+
+ // tryCompare(testInfo, "counter", 2, 1000, "Expects a specific number of calls");
+ // }
+}
diff --git a/ui/StatusQ/tests/Testing/Temporary/CTestCostData.txt b/ui/StatusQ/tests/Testing/Temporary/CTestCostData.txt
deleted file mode 100644
index ed97d539c0..0000000000
--- a/ui/StatusQ/tests/Testing/Temporary/CTestCostData.txt
+++ /dev/null
@@ -1 +0,0 @@
----
diff --git a/ui/StatusQ/tests/Testing/Temporary/LastTest.log b/ui/StatusQ/tests/Testing/Temporary/LastTest.log
deleted file mode 100644
index b3246465d5..0000000000
--- a/ui/StatusQ/tests/Testing/Temporary/LastTest.log
+++ /dev/null
@@ -1,3 +0,0 @@
-Start testing: Apr 18 18:49 CEST
-----------------------------------------------------------
-End testing: Apr 18 18:49 CEST
diff --git a/ui/StatusQ/tests/main.cpp b/ui/StatusQ/tests/main.cpp
index 1204b6fec6..d520539b6e 100644
--- a/ui/StatusQ/tests/main.cpp
+++ b/ui/StatusQ/tests/main.cpp
@@ -3,6 +3,18 @@
#include "TestHelpers/MonitorQtOutput.h"
+#include
+
+class RunBeforeQApplicationIsInitialized {
+public:
+ RunBeforeQApplicationIsInitialized()
+ {
+ QtWebView::initialize();
+ }
+};
+
+static RunBeforeQApplicationIsInitialized runBeforeQApplicationIsInitialized;
+
class TestSetup : public QObject
{
Q_OBJECT
diff --git a/ui/app/AppLayouts/Wallet/views/walletconnect/RequestCodes.qml b/ui/app/AppLayouts/Wallet/views/walletconnect/RequestCodes.qml
deleted file mode 100644
index bb0eaca7c8..0000000000
--- a/ui/app/AppLayouts/Wallet/views/walletconnect/RequestCodes.qml
+++ /dev/null
@@ -1,23 +0,0 @@
-import QtQuick 2.15
-
-QtObject {
- enum RequestCodes {
- SdkInitSuccess,
- SdkInitError,
-
- PairSuccess,
- PairError,
- ApprovePairSuccess,
- ApprovePairError,
- RejectPairSuccess,
- RejectPairError,
-
- AcceptSessionSuccess,
- AcceptSessionError,
- RejectSessionSuccess,
- RejectSessionError,
-
- GetPairings,
- GetPairingsError
- }
-}
\ No newline at end of file
diff --git a/ui/app/AppLayouts/Wallet/views/walletconnect/WalletConnect.qml b/ui/app/AppLayouts/Wallet/views/walletconnect/WalletConnect.qml
index 6d061acf38..0a88b419be 100644
--- a/ui/app/AppLayouts/Wallet/views/walletconnect/WalletConnect.qml
+++ b/ui/app/AppLayouts/Wallet/views/walletconnect/WalletConnect.qml
@@ -148,46 +148,6 @@ Item {
ColumnLayout { /* spacer */ }
}
- // TODO: DEBUG JS Loading in DMG
- // RowLayout {
- // TextField {
- // id: urlInput
-
- // Layout.fillWidth: true
-
- // placeholderText: "Insert URL here"
- // }
- // Button {
- // text: "Set URL"
- // onClicked: {
- // d.sdkView.url = urlInput.text
- // }
- // }
- // }
-
- // Button {
- // text: "Set HTML"
- // onClicked: {
- // d.sdkView.loadHtml(htmlContent.text, "http://status.im")
- // }
- // }
-
- // StatusInput {
- // id: htmlContent
-
- // Layout.fillWidth: true
- // Layout.minimumHeight: 200
- // Layout.maximumHeight: 300
-
- // text: `TODO: Test\n\n\n`
-
- // multiline: true
- // minimumHeight: Layout.minimumHeight
- // maximumHeight: Layout.maximumHeight
-
- // }
- // END DEBUGGING
-
// Separator
ColumnLayout {}
@@ -332,7 +292,6 @@ Item {
target: root.controller
function onRespondSessionRequest(sessionRequestJson, signedJson, error) {
- console.log("@dd respondSessionRequest", sessionRequestJson, signedJson, error)
if (error) {
d.setStatusText("Session Request error", "red")
d.sdkView.rejectSessionRequest(d.sessionRequest.topic, d.sessionRequest.id, true)
diff --git a/ui/app/AppLayouts/Wallet/views/walletconnect/WalletConnectSDK.qml b/ui/app/AppLayouts/Wallet/views/walletconnect/WalletConnectSDK.qml
index 890ad188aa..7cd62a728a 100644
--- a/ui/app/AppLayouts/Wallet/views/walletconnect/WalletConnectSDK.qml
+++ b/ui/app/AppLayouts/Wallet/views/walletconnect/WalletConnectSDK.qml
@@ -1,19 +1,16 @@
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
+import StatusQ.Components 0.1
// 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 {
+StatusWebView {
id: root
implicitWidth: 1
@@ -33,59 +30,68 @@ WebView {
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)
+ root.asyncCall("wc.pair", `"${pairLink}"`).then((result) => {
+ root.pairSessionProposal(true, result)
+ d.getPairings()
+ }).error((error) => {
+ root.pairSessionProposal(false, error)
+ })
}
function approvePairSession(sessionProposal, supportedNamespaces) {
- let callStr = d.generateSdkCall("approvePairSession", `${JSON.stringify(sessionProposal)}, ${JSON.stringify(supportedNamespaces)}`, RequestCodes.ApprovePairSuccess, RequestCodes.ApprovePairSuccess)
-
- d.requestSdkAsync(callStr)
+ root.asyncCall("wc.approvePairSession", `${JSON.stringify(sessionProposal)}, ${JSON.stringify(supportedNamespaces)}`).then((result) => {
+ root.pairAcceptedResult(true, "")
+ d.getPairings()
+ }).error((error) => {
+ root.pairAcceptedResult(false, error)
+ d.getPairings()
+ })
}
function rejectPairSession(id) {
- let callStr = d.generateSdkCall("rejectPairSession", id, RequestCodes.RejectPairSuccess, RequestCodes.RejectPairError)
-
- d.requestSdkAsync(callStr)
+ root.asyncCall("wc.rejectPairSession", id).then((result) => {
+ root.pairRejectedResult(true, "")
+ }).error((error) => {
+ root.pairRejectedResult(false, error)
+ })
}
function acceptSessionRequest(topic, id, signature) {
- let callStr = d.generateSdkCall("respondSessionRequest", `"${topic}", ${id}, "${signature}"`, RequestCodes.AcceptSessionSuccess, RequestCodes.AcceptSessionError)
-
- d.requestSdkAsync(callStr)
+ root.asyncCall("wc.respondSessionRequest", `"${topic}", ${id}, "${signature}"`).then((result) => {
+ root.sessionRequestUserAnswerResult(true, "")
+ }).error((error) => {
+ root.sessionRequestUserAnswerResult(true, error)
+ })
}
function rejectSessionRequest(topic, id, error) {
- let callStr = d.generateSdkCall("rejectSessionRequest", `"${topic}", ${id}, ${error}`, RequestCodes.RejectSessionSuccess, RequestCodes.RejectSessionError)
-
- d.requestSdkAsync(callStr)
+ root.asyncCall("wc.rejectSessionRequest", `"${topic}", ${id}, ${error}`).then((result) => {
+ root.sessionRequestUserAnswerResult(false, "")
+ }).error((error) => {
+ root.sessionRequestUserAnswerResult(false, error)
+ })
}
- // 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
- }
+ onContentReady: {
+ root.asyncCall("wc.init", `"${projectId}"`).then((result) => {
+ d.sdkReady = true
+ root.sdkInit(true, "")
+ d.startListeningForEvents()
+ d.getPairings()
+ }).error((error) => {
+ d.sdkReady = false
+ root.sdkInit(false, error)
+ })
+ }
+
+ onContentFailedLoading: (errorString) => {
+ root.statusChanged(`Failed loading SDK JS code; error: "${errorString}"`)
}
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 === "") {
@@ -96,142 +102,11 @@ WebView {
}
}
- let htmlSrc = ``
+ 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
@@ -242,12 +117,6 @@ WebView {
property ListModel pairingsModel: pairings
- onSdkReadyChanged: {
- if (sdkReady) {
- d.getPairings()
- }
- }
-
function populatePairingsModel(pairList) {
pairings.clear();
for (let i = 0; i < pairList.length; i++) {
@@ -259,31 +128,6 @@ WebView {
}
}
-
- 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 {
@@ -308,15 +152,14 @@ WebView {
}
}
)
- 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)
+ root.call("wc.getPairings", "").then((result) => {
+ d.populatePairingsModel(result)
+ }).error((error) => {
+ console.error(`WalletConnectSDK - getPairings error: ${error}`)
+ })
}
}
diff --git a/ui/app/AppLayouts/Wallet/views/walletconnect/sdk/README.md b/ui/app/AppLayouts/Wallet/views/walletconnect/sdk/README.md
index 274f695b0b..97ab334a4a 100644
--- a/ui/app/AppLayouts/Wallet/views/walletconnect/sdk/README.md
+++ b/ui/app/AppLayouts/Wallet/views/walletconnect/sdk/README.md
@@ -6,16 +6,7 @@
### Design questions
-- [ ] Do we report all chains and all accounts combination or let user select?
- - Wallet Connect require to report all chainIDs that were requested
- - Show error to user workflow.
-- [ ] Can't respond to sign messages if the wallet-connect dialog/view is closed (app is minimized)
- - Only apps that use deep links are expected to work seamlessly
- [ ] Do we report **disabled chains**? **Update session** in case of enabled/disabled chains?
-- [ ] Allow user to **disconnect session**? Manage sessions?
-- [ ] Support update session if one account is added/removed?
-- [ ] User awareness of session expiration?
- - Support extend session?
- [ ] User error workflow: retry?
- [ ] Check the `Auth` request for verifyContext
- [ ] What `description` and `icons` to use for the app? See `metadata` parameter in `Web3Wallet.init` call
@@ -37,18 +28,6 @@ Install dependencies steps by executing commands in this directory:
Use the web demo test client https://react-app.walletconnect.com/ for wallet pairing and https://react-auth-dapp.walletconnect.com/ for authentication
-## Log
-
-Initial setup
-
-```sh
-npm init -y
-npm install --save-dev webpack webpack-cli webpack-dev-server
-npm install --save @walletconnect/web3wallet
-npm run build
-# npm run build:dev # for development
-```
-
## Dev - to be removed
To test SDK loading add the following to `ui/app/mainui/AppMain.qml`
@@ -75,3 +54,29 @@ StatusDialog {
clip: true
}
```
+
+## Log
+
+Initial setup
+
+```sh
+npm init -y
+npm install --save-dev webpack webpack-cli webpack-dev-server
+npm install --save @walletconnect/web3wallet
+npm run build
+# npm run build:dev # for development
+```
+
+- [x] Do we report all chains and all accounts combination or let user select?
+ - Wallet Connect require to report all chainIDs that were requested
+ - Answer: We only report the available chains for the current account. We will look into adding others to he same session instead of requiring a new link
+- [x] Can't respond to sign messages if the wallet-connect dialog/view is closed (app is minimized)
+ - Only apps that use deep links are expected to work seamlessly
+ - Also the main workflow will be driven by user
+- [x] Allow user to **disconnect session**? Manage sessions?
+ - Yes, in settings
+- [x] Support update session if one account is added/removed?
+ - Not at first
+- [X] User awareness of session expiration?
+ - Support extend session?
+ - Yes
diff --git a/vendor/DOtherSide/lib/src/Status/AppDelegate.mm b/vendor/DOtherSide/lib/src/Status/AppDelegate.mm
index 4101c0002c..1f1ac31d93 100644
--- a/vendor/DOtherSide/lib/src/Status/AppDelegate.mm
+++ b/vendor/DOtherSide/lib/src/Status/AppDelegate.mm
@@ -22,8 +22,7 @@ continueUserActivity:(NSUserActivity *)userActivity
if (!url)
return FALSE;
QUrl deeplink = QUrl::fromNSURL(url);
- // TODO #12434: Check if WalletConnect link and redirect the workflow
- qDebug() << "@dd deeplink " << deeplink;
+ // TODO #12434: Check if WalletConnect link and redirect the workflow
// TODO #12245: set it to nim
return TRUE;