feat(wallet) run WebEngineView as a service in background
This is required to control the resource consumption in case of no usage of wallet connect Hence we load the WebEngineView only if we have active pairings and such that SDK events are expected from the paired dapps. Also: - Moved the generic WebEngineView communication bridge to StatusQ - Added basic tests for WebEngineLoader - Add a way to know when wallet is loaded (`walletReady`) - Add storybook support for mock of nim sections as context properties Updates: #12639
This commit is contained in:
parent
b30c2992a5
commit
5b9e4faa8a
|
@ -312,6 +312,7 @@ proc checkIfModuleDidLoad(self: Module) =
|
|||
self.notifyFilterChanged()
|
||||
self.moduleLoaded = true
|
||||
self.delegate.walletSectionDidLoad()
|
||||
self.view.setWalletReady()
|
||||
|
||||
method viewDidLoad*(self: Module) =
|
||||
self.checkIfModuleDidLoad()
|
||||
|
|
|
@ -23,6 +23,7 @@ QtObject:
|
|||
isNonArchivalNode: bool
|
||||
keypairOperabilityForObservedAccount: string
|
||||
wcController: wcc.Controller
|
||||
walletReady: bool
|
||||
|
||||
proc setup(self: View) =
|
||||
self.QObject.setup
|
||||
|
@ -212,3 +213,17 @@ QtObject:
|
|||
return newQVariant(self.wcController)
|
||||
QtProperty[QVariant] walletConnectController:
|
||||
read = getWalletConnectController
|
||||
|
||||
proc walletReadyChanged*(self: View) {.signal.}
|
||||
|
||||
proc getWalletReady*(self: View): bool {.slot.} =
|
||||
return self.walletReady
|
||||
|
||||
proc setWalletReady*(self: View) =
|
||||
if not self.walletReady:
|
||||
self.walletReady = true
|
||||
self.walletReadyChanged()
|
||||
|
||||
QtProperty[bool] walletReady:
|
||||
read = getWalletReady
|
||||
notify = walletReadyChanged
|
|
@ -1,4 +1,4 @@
|
|||
import NimQml, strutils, logging, json
|
||||
import NimQml, strutils, logging, json, options
|
||||
|
||||
import backend/wallet_connect as backend
|
||||
|
||||
|
@ -20,6 +20,7 @@ QtObject:
|
|||
Controller* = ref object of QObject
|
||||
events: EventEmitter
|
||||
sessionRequestJson: JsonNode
|
||||
hasActivePairings: Option[bool]
|
||||
|
||||
## Forward declarations
|
||||
proc onDataSigned(self: Controller, keyUid: string, path: string, r: string, s: string, v: string, pin: string)
|
||||
|
@ -68,6 +69,19 @@ QtObject:
|
|||
let supportedNamespacesJson = if res.hasKey("supportedNamespaces"): $res["supportedNamespaces"] else: ""
|
||||
self.proposeUserPair(sessionProposalJson, supportedNamespacesJson)
|
||||
|
||||
proc recordSuccessfulPairing(self: Controller, sessionProposalJson: string) {.slot.} =
|
||||
if backend.recordSuccessfulPairing(sessionProposalJson):
|
||||
if not self.hasActivePairings.get(false):
|
||||
self.hasActivePairings = some(true)
|
||||
|
||||
proc getHasActivePairings*(self: Controller): bool {.slot.} =
|
||||
if self.hasActivePairings.isNone:
|
||||
self.hasActivePairings = some(backend.hasActivePairings())
|
||||
return self.hasActivePairings.get(false)
|
||||
|
||||
QtProperty[bool] hasActivePairings:
|
||||
read = getHasActivePairings
|
||||
|
||||
proc respondSessionRequest*(self: Controller, sessionRequestJson: string, signedJson: string, error: bool) {.signal.}
|
||||
|
||||
proc sendTransactionAndRespond(self: Controller, signature: string) =
|
||||
|
|
|
@ -6,7 +6,7 @@ from gen import rpc
|
|||
import backend
|
||||
|
||||
# Declared in services/wallet/walletconnect/walletconnect.go
|
||||
#const eventWCTODO*: string = "wallet-wc-todo"
|
||||
const eventWCProposeUserPair*: string = "WalletConnectProposeUserPair"
|
||||
|
||||
# Declared in services/wallet/walletconnect/walletconnect.go
|
||||
const ErrorChainsNotSupported*: string = "chains not supported"
|
||||
|
@ -28,11 +28,23 @@ rpc(wCSendTransactionWithSignature, "wallet"):
|
|||
rpc(wCPairSessionProposal, "wallet"):
|
||||
sessionProposalJson: string
|
||||
|
||||
rpc(wCRecordSuccessfulPairing, "wallet"):
|
||||
sessionProposalJson: string
|
||||
|
||||
rpc(wCHasActivePairings, "wallet"):
|
||||
discard
|
||||
|
||||
rpc(wCSessionRequest, "wallet"):
|
||||
sessionRequestJson: string
|
||||
|
||||
proc prepareResponse(res: var JsonNode, rpcResponse: RpcResponse[JsonNode]): string =
|
||||
|
||||
proc isErrorResponse(rpcResponse: RpcResponse[JsonNode]): bool =
|
||||
if not rpcResponse.error.isNil:
|
||||
return true
|
||||
return false
|
||||
|
||||
proc prepareResponse(res: var JsonNode, rpcResponse: RpcResponse[JsonNode]): string =
|
||||
if isErrorResponse(rpcResponse):
|
||||
return rpcResponse.error.message
|
||||
if rpcResponse.result.isNil:
|
||||
return "no result"
|
||||
|
@ -79,6 +91,24 @@ proc pair*(res: var JsonNode, sessionProposalJson: string): string =
|
|||
warn e.msg
|
||||
return e.msg
|
||||
|
||||
proc recordSuccessfulPairing*(sessionProposalJson: string): bool =
|
||||
try:
|
||||
let response = wCRecordSuccessfulPairing(sessionProposalJson)
|
||||
return not isErrorResponse(response)
|
||||
except Exception as e:
|
||||
warn e.msg
|
||||
return false
|
||||
|
||||
proc hasActivePairings*(): bool =
|
||||
try:
|
||||
let response = wCHasActivePairings()
|
||||
if isErrorResponse(response):
|
||||
return false
|
||||
return response.result.getBool()
|
||||
except Exception as e:
|
||||
warn e.msg
|
||||
return false
|
||||
|
||||
proc sessionRequest*(res: var JsonNode, sessionRequestJson: string): string =
|
||||
try:
|
||||
let response = wCSessionRequest(sessionRequestJson)
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
#include <QGuiApplication>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QQmlContext>
|
||||
#include <QQmlComponent>
|
||||
#include <QDirIterator>
|
||||
|
||||
#include <QtWebView>
|
||||
|
||||
|
||||
#include "cachecleaner.h"
|
||||
#include "directorieswatcher.h"
|
||||
#include "figmalinks.h"
|
||||
|
@ -12,11 +15,15 @@
|
|||
#include "testsrunner.h"
|
||||
#include "systemutils.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
struct PagesModelInitialized : public PagesModel {
|
||||
explicit PagesModelInitialized(QObject *parent = nullptr)
|
||||
: PagesModel(QML_IMPORT_ROOT + QStringLiteral("/pages"), parent) {}
|
||||
};
|
||||
|
||||
void loadContextPropertiesMocks(const char* storybookRoot, QQmlApplicationEngine& engine);
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Required by the WalletConnectSDK view
|
||||
|
@ -90,6 +97,7 @@ int main(int argc, char *argv[])
|
|||
return new SystemUtils;
|
||||
});
|
||||
|
||||
loadContextPropertiesMocks(QML_IMPORT_ROOT, engine);
|
||||
#ifdef Q_OS_WIN
|
||||
const QUrl url(QUrl::fromLocalFile(QML_IMPORT_ROOT + QStringLiteral("/main.qml")));
|
||||
#else
|
||||
|
@ -104,3 +112,34 @@ int main(int argc, char *argv[])
|
|||
|
||||
return QGuiApplication::exec();
|
||||
}
|
||||
|
||||
void loadContextPropertiesMocks(const char* storybookRoot, QQmlApplicationEngine& engine) {
|
||||
QDirIterator it(QML_IMPORT_ROOT + QStringLiteral("/stubs/nim/sectionmocks"), QDirIterator::Subdirectories);
|
||||
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
if (it.fileInfo().isFile() && it.fileInfo().suffix() == QStringLiteral("qml")) {
|
||||
auto component = std::make_unique<QQmlComponent>(&engine, QUrl::fromLocalFile(it.filePath()));
|
||||
if (component->status() != QQmlComponent::Ready) {
|
||||
qWarning() << "Failed to load mock for" << it.filePath() << component->errorString();
|
||||
continue;
|
||||
}
|
||||
|
||||
auto objPtr = std::unique_ptr<QObject>(component->create());
|
||||
if(!objPtr) {
|
||||
qWarning() << "Failed to create mock for" << it.filePath();
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!objPtr->property("contextPropertyName").isValid()) {
|
||||
qInfo() << "Not a mock, missing property name \"contextPropertyName\"";
|
||||
continue;
|
||||
}
|
||||
|
||||
auto contextPropertyName = objPtr->property("contextPropertyName").toString();
|
||||
auto obj = objPtr.release();
|
||||
obj->setParent(&engine);
|
||||
engine.rootContext()->setContextProperty(contextPropertyName, obj);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -323,12 +323,12 @@ ApplicationWindow {
|
|||
modal: true
|
||||
|
||||
contentItem: Label {
|
||||
text: `
|
||||
Tips:
|
||||
• For inline components use naming convention of adding
|
||||
"Custom" at the begining (like Custom${root.currentPage})
|
||||
• For popups set closePolicy to "Popup.NoAutoClose""
|
||||
`
|
||||
text: '
|
||||
Tips:\n\
|
||||
• For inline components use naming convention of adding\n\
|
||||
"Custom" at the begining (like Custom'+root.currentPage+')\n\
|
||||
• For popups set closePolicy to "Popup.NoAutoClose"\n\
|
||||
'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ 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 StatusQ.Core 0.1
|
||||
import StatusQ.Core.Utils 0.1
|
||||
|
@ -24,16 +25,12 @@ import nim 1.0
|
|||
Item {
|
||||
id: root
|
||||
|
||||
// qml Splitter
|
||||
SplitView {
|
||||
anchors.fill: parent
|
||||
|
||||
WalletConnect {
|
||||
id: walletConnect
|
||||
id: wc
|
||||
|
||||
SplitView.fillWidth: true
|
||||
|
||||
backgroundColor: Theme.palette.statusAppLayout.backgroundColor
|
||||
anchors.top: parent.bottom
|
||||
anchors.left: parent.left
|
||||
url: `${pagesFolder}/../stubs/AppLayouts/Wallet/views/walletconnect/src/index.html`
|
||||
|
||||
controller: WalletConnectController {
|
||||
pairSessionProposal: function(sessionProposalJson) {
|
||||
|
@ -45,34 +42,68 @@ Item {
|
|||
this.respondSessionRequest(sessionRequestJson, signedJson, respondError.checked)
|
||||
}
|
||||
|
||||
hasActivePairings: settings.hasActivePairings
|
||||
projectId: "87815d72a81d739d2a7ce15c2cfdefb3"
|
||||
}
|
||||
|
||||
clip: true
|
||||
}
|
||||
|
||||
// qml Splitter
|
||||
SplitView {
|
||||
anchors.fill: parent
|
||||
|
||||
ColumnLayout {
|
||||
SplitView.fillWidth: true
|
||||
|
||||
StatusButton {
|
||||
id: openModalButton
|
||||
text: "OpenModal"
|
||||
onClicked: {
|
||||
wc.modal.open()
|
||||
}
|
||||
}
|
||||
ColumnLayout {}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: optionsSpace
|
||||
|
||||
StatusRadioButton {
|
||||
text: "WebEngine running"
|
||||
checked: wc.sdk.webEngineLoader.active
|
||||
enabled: false
|
||||
}
|
||||
RowLayout {
|
||||
id: optionsHeader
|
||||
|
||||
Text { text: "projectId" }
|
||||
Text {
|
||||
readonly property string projectId: walletConnect.controller.projectId
|
||||
readonly property string projectId: wc.controller.projectId
|
||||
text: projectId.substring(0, 3) + "..." + projectId.substring(projectId.length - 3)
|
||||
font.bold: true
|
||||
}
|
||||
}
|
||||
CheckBox {
|
||||
StatusCheckBox {
|
||||
id: hasActivePairingsCheckBox
|
||||
text: "Has active pairings"
|
||||
checked: settings.hasActivePairings
|
||||
}
|
||||
StatusCheckBox {
|
||||
id: respondError
|
||||
text: "Respond Error"
|
||||
checked: false
|
||||
}
|
||||
|
||||
// spacer
|
||||
ColumnLayout {}
|
||||
}
|
||||
}
|
||||
|
||||
Settings {
|
||||
id: settings
|
||||
property bool hasActivePairings: hasActivePairingsCheckBox.checked
|
||||
}
|
||||
}
|
||||
|
||||
// category: Popups
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<!-- Keep in sync with mock of: ui/app/AppLayouts/Wallet/views/walletconnect/sdk/src/index.html to avoid trying to load from app qrc -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="qrc:/StatusQ/Components/private/qwebchannel/qwebchannel.js" defer></script>
|
||||
<script src="qrc:/StatusQ/Components/private/qwebchannel/helpers.js" defer></script>
|
||||
<script src="../../../../../../../ui/app/AppLayouts/Wallet/views/walletconnect/sdk/generated/bundle.js" defer></script>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
|
@ -17,5 +17,6 @@ Item {
|
|||
required property var sessionRequest
|
||||
|
||||
|
||||
required property bool hasActivePairings
|
||||
required property string projectId
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
// Mock of src/app/modules/main/wallet_section/networks/view.nim
|
||||
import QtQuick 2.15
|
||||
|
||||
QtObject {
|
||||
readonly property string contextPropertyName: "networksModule"
|
||||
|
||||
//
|
||||
// Silence warnings
|
||||
readonly property ListModel layer1: ListModel {}
|
||||
readonly property ListModel layer2: ListModel {}
|
||||
readonly property ListModel enabled: ListModel {}
|
||||
readonly property ListModel all: ListModel {}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
import QtQuick 2.15
|
||||
|
||||
// Required mock of: src/app/modules/main/wallet_section/view.nim
|
||||
|
||||
Item {
|
||||
readonly property string contextPropertyName: "walletSection"
|
||||
|
||||
// Required
|
||||
//
|
||||
readonly property bool walletReady: true
|
||||
|
||||
//
|
||||
// Silence warnings
|
||||
readonly property QtObject overview: QtObject {
|
||||
readonly property string mixedcaseAddress: ""
|
||||
}
|
||||
readonly property ListModel mixedcaseAddress: ListModel {}
|
||||
|
||||
signal walletAccountRemoved(string address)
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
// Mock of src/app/modules/main/wallet_section/buy_sell_crypto/view.nim
|
||||
import QtQuick 2.15
|
||||
|
||||
QtObject {
|
||||
readonly property string contextPropertyName: "walletSectionBuySellCrypto"
|
||||
|
||||
// Silence warnings
|
||||
readonly property ListModel model: ListModel {}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import QtQuick 2.15
|
||||
|
||||
// Required mock of: src/app/modules/main/wallet_section/overview/view.nim
|
||||
|
||||
Item {
|
||||
readonly property string contextPropertyName: "walletSectionOverview"
|
||||
|
||||
//
|
||||
// Silence warnings
|
||||
readonly property string mixedcaseAddress: ""
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import QtQuick 2.15
|
||||
|
||||
// Required mock of: src/app/modules/main/wallet_section/send/view.nim
|
||||
|
||||
Item {
|
||||
readonly property string contextPropertyName: "walletSectionSend"
|
||||
|
||||
// Silence warnings
|
||||
readonly property ListModel accounts: ListModel {}
|
||||
readonly property QtObject selectedReceiveAccount: QtObject {}
|
||||
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
import QtQuick 2.15
|
||||
|
||||
import QtWebEngine 1.10
|
||||
import QtWebChannel 1.15
|
||||
|
||||
// Helper to load and setup an instance of \c WebEngineView
|
||||
//
|
||||
// The \c webChannelObjects property is used to register specific objects
|
||||
//
|
||||
// Loading qrc:/StatusQ/Components/private/qwebchannel/qwebchannel.js and
|
||||
// qrc:/StatusQ/Components/private/qwebchannel/helpers.js will provide
|
||||
// access to window.statusq APIs used to exchange data between the internal
|
||||
// web engine and the QML application
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property url url
|
||||
required property var webChannelObjects
|
||||
|
||||
property alias active: loader.active
|
||||
property alias instance: loader.item
|
||||
|
||||
signal engineLoaded(WebEngineView instance)
|
||||
signal engineUnloaded()
|
||||
signal pageLoaded()
|
||||
signal pageLoadingError(string errorString)
|
||||
|
||||
Loader {
|
||||
id: loader
|
||||
|
||||
active: false
|
||||
|
||||
onStatusChanged: function() {
|
||||
if (status === Loader.Ready) {
|
||||
root.engineLoaded(loader.item)
|
||||
} else if (status === Loader.Null) {
|
||||
root.engineUnloaded()
|
||||
}
|
||||
}
|
||||
|
||||
sourceComponent: WebEngineView {
|
||||
id: webEngineView
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
visible: false
|
||||
|
||||
url: root.url
|
||||
webChannel: statusChannel
|
||||
|
||||
onLoadingChanged: function(loadRequest) {
|
||||
switch(loadRequest.status) {
|
||||
case WebEngineView.LoadSucceededStatus:
|
||||
root.pageLoaded()
|
||||
break
|
||||
case WebEngineView.LoadFailedStatus:
|
||||
root.pageLoadingError(loadRequest.errorString)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
WebChannel {
|
||||
id: statusChannel
|
||||
registeredObjects: root.webChannelObjects
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
// Helper functions for instantiating QWebChannel
|
||||
// Requires loading of qwebchannel.js first
|
||||
function initializeWebChannel() {
|
||||
if (window.statusq && window.statusq.channel) {
|
||||
console.error("WebChannel already initialized");
|
||||
window.statusq.error = "WebChannel already initialized";
|
||||
return;
|
||||
}
|
||||
|
||||
window.statusq = {error: ""}
|
||||
try {
|
||||
window.statusq.channel = new QWebChannel(qt.webChannelTransport);
|
||||
} catch (e) {
|
||||
console.error("Unable to initialize WebChannel", e);
|
||||
window.statusq.error = "initialize WebChannel fail: " + e.message;
|
||||
}
|
||||
}
|
||||
|
||||
initializeWebChannel();
|
|
@ -67,3 +67,4 @@ StatusToastMessage 0.1 StatusToastMessage.qml
|
|||
StatusToolBar 0.1 StatusToolBar.qml
|
||||
StatusVideo 0.1 StatusVideo.qml
|
||||
StatusWizardStepper 0.1 StatusWizardStepper.qml
|
||||
WebEngineLoader 0.1 WebEngineLoader.qml
|
|
@ -228,5 +228,8 @@
|
|||
<file>StatusQ/Controls/StatusWarningBox.qml</file>
|
||||
<file>StatusQ/Core/Utils/Subscription.qml</file>
|
||||
<file>StatusQ/Core/Utils/SubscriptionBroker.qml</file>
|
||||
<file>StatusQ/Components/WebEngineLoader.qml</file>
|
||||
<file>StatusQ/Components/private/qwebchannel/qwebchannel.js</file>
|
||||
<file>StatusQ/Components/private/qwebchannel/helpers.js</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
|
@ -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 WebChannel WebEngine REQUIRED)
|
||||
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS QuickTest Qml Quick WebChannel WebEngine REQUIRED)
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
|
@ -31,6 +31,8 @@ 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}::WebChannel
|
||||
Qt${QT_VERSION_MAJOR}::WebEngine
|
||||
StatusQ
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test Page</title>
|
||||
<script src="../../../src/StatusQ/Components/private/qwebchannel/qwebchannel.js" defer></script>
|
||||
<script src="../../../src/StatusQ/Components/private/qwebchannel/helpers.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Test Page</h1>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,110 @@
|
|||
import QtQuick 2.15
|
||||
import QtTest 1.0
|
||||
|
||||
import QtWebEngine 1.10
|
||||
import QtWebChannel 1.15
|
||||
|
||||
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: "TestWebEngineLoader"
|
||||
|
||||
QtObject {
|
||||
id: testObject
|
||||
|
||||
WebChannel.id: "testObject"
|
||||
|
||||
signal webChannelInitOk()
|
||||
signal webChannelError()
|
||||
function signalWebChannelInitResult(error) {
|
||||
if(error) {
|
||||
webChannelError()
|
||||
} else {
|
||||
webChannelInitOk()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: loader
|
||||
|
||||
active: false
|
||||
|
||||
sourceComponent: WebEngineLoader {
|
||||
url: "./WebEngineLoader/test.html"
|
||||
webChannelObjects: [testObject]
|
||||
}
|
||||
}
|
||||
SignalSpy { id: loadedSpy; target: loader; signalName: "loaded" }
|
||||
|
||||
SignalSpy { id: webEngineLoadedSpy; target: loader.item; signalName: "engineLoaded" }
|
||||
SignalSpy { id: pageLoadedSpy; target: loader.item; signalName: "pageLoaded" }
|
||||
SignalSpy { id: engineUnloadedSpy; target: loader.item; signalName: "engineUnloaded" }
|
||||
SignalSpy { id: pageLoadingErrorSpy; target: loader.item; signalName: "onPageLoadingError" }
|
||||
|
||||
function init() {
|
||||
for (var i = 0; i < root.children.length; i++) {
|
||||
const child = root.children[i]
|
||||
if(child.hasOwnProperty("signalName")) {
|
||||
child.clear()
|
||||
}
|
||||
}
|
||||
loader.active = true
|
||||
loadedSpy.wait(1000);
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
loader.active = false
|
||||
}
|
||||
|
||||
function test_loadUnload() {
|
||||
const webEngine = loader.item
|
||||
compare(webEngine.instance, null, "By default the engine is not loaded")
|
||||
webEngine.active = true
|
||||
|
||||
webEngineLoadedSpy.wait(1000);
|
||||
verify(webEngine.instance !== null , "The WebEngineView should be available")
|
||||
|
||||
if (Qt.platform.os === "linux") {
|
||||
skip("fails to load page on linux")
|
||||
}
|
||||
pageLoadedSpy.wait(1000);
|
||||
webEngine.active = false
|
||||
engineUnloadedSpy.wait(1000);
|
||||
|
||||
verify(webEngine.instance === null , "The WebEngineView should be unavailable")
|
||||
}
|
||||
|
||||
SignalSpy { id: wcInitOkSpy; target: testObject; signalName: "webChannelInitOk" }
|
||||
SignalSpy { id: wcInitErrorSpy; target: testObject; signalName: "webChannelError" }
|
||||
function test_executeCode() {
|
||||
if (Qt.platform.os === "linux") {
|
||||
skip("fails to load page on linux")
|
||||
}
|
||||
|
||||
const webEngine = loader.item
|
||||
webEngine.active = true
|
||||
pageLoadedSpy.wait(1000);
|
||||
|
||||
let errorResult = null
|
||||
webEngine.instance.runJavaScript(`
|
||||
window.testError = window.statusq.error;
|
||||
try {
|
||||
window.statusq.channel.objects.testObject.signalWebChannelInitResult("");
|
||||
} catch (e) {
|
||||
window.testError = e.message;
|
||||
}
|
||||
window.testError
|
||||
`, function(result) {
|
||||
errorResult = result
|
||||
})
|
||||
|
||||
wcInitOkSpy.wait(1000);
|
||||
compare(errorResult, "", "Expected empty error string if all good")
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
---
|
|
@ -1,3 +0,0 @@
|
|||
Start testing: Apr 18 18:49 CEST
|
||||
----------------------------------------------------------
|
||||
End testing: Apr 18 18:49 CEST
|
|
@ -1,8 +1,20 @@
|
|||
#include <QtQuickTest/quicktest.h>
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include <QtWebEngine>
|
||||
|
||||
#include "TestHelpers/MonitorQtOutput.h"
|
||||
|
||||
class RunBeforeQApplicationIsInitialized {
|
||||
public:
|
||||
RunBeforeQApplicationIsInitialized()
|
||||
{
|
||||
QtWebEngine::initialize();
|
||||
}
|
||||
};
|
||||
|
||||
static RunBeforeQApplicationIsInitialized runBeforeQApplicationIsInitialized;
|
||||
|
||||
class TestSetup : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
|
|
@ -163,45 +163,7 @@ SettingsContentBase {
|
|||
visible: root.advancedStore.isDebugEnabled
|
||||
|
||||
onClicked: {
|
||||
wcLoader.active = true
|
||||
}
|
||||
|
||||
Component {
|
||||
id: wcDialogComponent
|
||||
StatusDialog {
|
||||
id: wcDialog
|
||||
|
||||
onOpenedChanged: {
|
||||
if (!opened) {
|
||||
wcLoader.active = false
|
||||
}
|
||||
}
|
||||
|
||||
WalletConnect {
|
||||
SplitView.preferredWidth: 400
|
||||
SplitView.preferredHeight: 600
|
||||
|
||||
backgroundColor: wcDialog.backgroundColor
|
||||
|
||||
controller: WalletStores.RootStore.walletConnectController
|
||||
}
|
||||
|
||||
clip: true
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: wcLoader
|
||||
|
||||
active: false
|
||||
|
||||
onStatusChanged: {
|
||||
if (status === Loader.Ready) {
|
||||
wcLoader.item.open()
|
||||
}
|
||||
}
|
||||
|
||||
sourceComponent: wcDialogComponent
|
||||
Global.popupWalletConnect()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -1,295 +1,32 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Utils 0.1 as SQUtils
|
||||
import AppLayouts.Wallet.stores 1.0 as WalletStores
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
implicitWidth: Math.min(mainLayout.implicitWidth, 400)
|
||||
implicitHeight: Math.min(mainLayout.implicitHeight, 700)
|
||||
|
||||
required property color backgroundColor
|
||||
|
||||
property bool sdkReady: state === d.sdkReadyState
|
||||
|
||||
// wallet_connect.Controller \see wallet_section/wallet_connect/controller.nim
|
||||
required property var controller
|
||||
|
||||
ColumnLayout {
|
||||
id: mainLayout
|
||||
property alias modal: modal
|
||||
property alias sdk: sdk
|
||||
property alias url: sdk.url
|
||||
|
||||
anchors.fill: parent
|
||||
WalletConnectModal {
|
||||
id: modal
|
||||
|
||||
StatusBaseText {
|
||||
text: qsTr("Debugging UX until design is ready")
|
||||
}
|
||||
|
||||
StatusInput {
|
||||
id: pairLinkInput
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
placeholderText: "Insert pair link"
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
StatusButton {
|
||||
text: "Pair"
|
||||
onClicked: {
|
||||
statusText.text = "Pairing..."
|
||||
sdkView.pair(pairLinkInput.text)
|
||||
}
|
||||
enabled: pairLinkInput.text.length > 0 && sdkView.sdkReady
|
||||
}
|
||||
|
||||
StatusButton {
|
||||
text: "Auth"
|
||||
onClicked: {
|
||||
statusText.text = "Authenticating..."
|
||||
sdkView.auth()
|
||||
}
|
||||
enabled: false && pairLinkInput.text.length > 0 && sdkView.sdkReady
|
||||
}
|
||||
|
||||
StatusButton {
|
||||
text: "Accept"
|
||||
onClicked: {
|
||||
sdkView.approvePairSession(d.sessionProposal, d.supportedNamespaces)
|
||||
}
|
||||
visible: root.state === d.waitingPairState
|
||||
}
|
||||
StatusButton {
|
||||
text: "Reject"
|
||||
onClicked: {
|
||||
sdkView.rejectPairSession(d.sessionProposal.id)
|
||||
}
|
||||
visible: root.state === d.waitingPairState
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
StatusBaseText {
|
||||
id: statusText
|
||||
text: "-"
|
||||
}
|
||||
StatusBaseText {
|
||||
text: "Pairings"
|
||||
visible: sdkView.pairingsModel.count > 0
|
||||
}
|
||||
StatusListView {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: contentHeight
|
||||
Layout.maximumHeight: 200
|
||||
|
||||
model: sdkView.pairingsModel
|
||||
|
||||
delegate: StatusBaseText {
|
||||
text: `${SQUtils.Utils.elideText(topic, 6, 6)} - ${new Date(expiry * 1000).toLocaleString()}`
|
||||
color: active ? "green" : "orange"
|
||||
}
|
||||
}
|
||||
Flickable {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 200
|
||||
Layout.maximumHeight: 400
|
||||
|
||||
contentWidth: detailsText.width
|
||||
contentHeight: detailsText.height
|
||||
|
||||
StatusBaseText {
|
||||
id: detailsText
|
||||
text: ""
|
||||
visible: text.length > 0
|
||||
|
||||
color: "#FF00FF"
|
||||
}
|
||||
|
||||
ScrollBar.vertical: ScrollBar {}
|
||||
|
||||
clip: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
StatusButton {
|
||||
text: "Accept"
|
||||
onClicked: {
|
||||
root.controller.sessionRequest(JSON.stringify(d.sessionRequest), passwordInput.text)
|
||||
}
|
||||
visible: root.state === d.waitingUserResponseToSessionRequest
|
||||
}
|
||||
StatusButton {
|
||||
text: "Reject"
|
||||
onClicked: {
|
||||
sdkView.rejectSessionRequest(d.sessionRequest.topic, d.sessionRequest.id, false)
|
||||
}
|
||||
visible: root.state === d.waitingUserResponseToSessionRequest
|
||||
}
|
||||
StatusInput {
|
||||
id: passwordInput
|
||||
|
||||
text: "1234567890"
|
||||
placeholderText: "Insert account password"
|
||||
visible: root.state === d.waitingUserResponseToSessionRequest
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout { /* spacer */ }
|
||||
}
|
||||
|
||||
// Separator
|
||||
ColumnLayout {}
|
||||
controller: root.controller
|
||||
sdk: sdk
|
||||
}
|
||||
|
||||
WalletConnectSDK {
|
||||
id: sdkView
|
||||
|
||||
// SDK runs fine if WebEngineView is not visible
|
||||
visible: false
|
||||
anchors.top: parent.bottom
|
||||
anchors.left: parent.left
|
||||
width: 100
|
||||
height: 100
|
||||
id: sdk
|
||||
|
||||
projectId: controller.projectId
|
||||
|
||||
onSdkInit: function(success, info) {
|
||||
d.setDetailsText(info)
|
||||
if (success) {
|
||||
d.setStatusText("Ready to pair or auth")
|
||||
root.state = d.sdkReadyState
|
||||
} else {
|
||||
d.setStatusText("SDK Error", "red")
|
||||
root.state = ""
|
||||
}
|
||||
}
|
||||
active: WalletStores.RootStore.walletSectionInst.walletReady && (controller.hasActivePairings || modal.opened)
|
||||
|
||||
onPairSessionProposal: function(success, sessionProposal) {
|
||||
d.setDetailsText(sessionProposal)
|
||||
if (success) {
|
||||
d.setStatusText("Pair ID: " + sessionProposal.id + "; Topic: " + sessionProposal.params.pairingTopic)
|
||||
root.controller.pairSessionProposal(JSON.stringify(sessionProposal))
|
||||
// Expecting signal onProposeUserPair from controller
|
||||
} else {
|
||||
d.setStatusText("Pairing error", "red")
|
||||
}
|
||||
}
|
||||
|
||||
onPairAcceptedResult: function(success, result) {
|
||||
d.setDetailsText(result)
|
||||
if (success) {
|
||||
d.setStatusText("Pairing OK")
|
||||
root.state = d.pairedState
|
||||
} else {
|
||||
d.setStatusText("Pairing error", "red")
|
||||
root.state = d.sdkReadyState
|
||||
}
|
||||
}
|
||||
|
||||
onPairRejectedResult: function(success, result) {
|
||||
d.setDetailsText(result)
|
||||
root.state = d.sdkReadyState
|
||||
if (success) {
|
||||
d.setStatusText("Pairing rejected")
|
||||
} else {
|
||||
d.setStatusText("Rejecting pairing error", "red")
|
||||
}
|
||||
}
|
||||
|
||||
onSessionRequestUserAnswerResult: function(accept, error) {
|
||||
if (error) {
|
||||
d.setStatusText(`Session Request ${accept ? "Accept" : "Reject"} error`, "red")
|
||||
return
|
||||
}
|
||||
root.state = d.pairedState
|
||||
if (accept) {
|
||||
d.setStatusText(`Session Request accepted`)
|
||||
} else {
|
||||
d.setStatusText(`Session Request rejected`)
|
||||
}
|
||||
}
|
||||
|
||||
onSessionRequestEvent: function(sessionRequest) {
|
||||
d.setStatusText("Approve session request")
|
||||
d.setDetailsText(JSON.stringify(sessionRequest, null, 2))
|
||||
d.sessionRequest = sessionRequest
|
||||
root.state = d.waitingUserResponseToSessionRequest
|
||||
}
|
||||
|
||||
onPairSessionProposalExpired: {
|
||||
d.setStatusText(`Timeout waiting for response. Reusing URI?`, "red")
|
||||
}
|
||||
|
||||
onStatusChanged: function(message) {
|
||||
statusText.text = message
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
||||
property var sessionProposal: null
|
||||
property var supportedNamespaces: null
|
||||
|
||||
property var sessionRequest: null
|
||||
property var signedData: null
|
||||
|
||||
readonly property string sdkReadyState: "sdk_ready"
|
||||
readonly property string waitingPairState: "waiting_pairing"
|
||||
readonly property string waitingUserResponseToSessionRequest: "waiting_user_response_to_session_request"
|
||||
readonly property string pairedState: "paired"
|
||||
|
||||
function setStatusText(message, textColor) {
|
||||
statusText.text = message
|
||||
if (textColor === undefined) {
|
||||
textColor = "green"
|
||||
}
|
||||
statusText.color = textColor
|
||||
}
|
||||
function setDetailsText(message) {
|
||||
if (message === undefined) {
|
||||
message = "undefined"
|
||||
} else if (typeof message !== "string") {
|
||||
message = JSON.stringify(message, null, 2)
|
||||
}
|
||||
detailsText.text = message
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.controller
|
||||
|
||||
function onProposeUserPair(sessionProposalJson, supportedNamespacesJson) {
|
||||
d.setStatusText("Waiting user accept")
|
||||
|
||||
d.sessionProposal = JSON.parse(sessionProposalJson)
|
||||
d.supportedNamespaces = JSON.parse(supportedNamespacesJson)
|
||||
|
||||
d.setDetailsText(JSON.stringify(d.supportedNamespaces, null, 2))
|
||||
|
||||
root.state = d.waitingPairState
|
||||
}
|
||||
|
||||
function onRespondSessionRequest(sessionRequestJson, signedData, error) {
|
||||
console.log("@dd respondSessionRequest", sessionRequestJson, " signedData", signedData, " error: ", error)
|
||||
if (error) {
|
||||
d.setStatusText("Session Request error", "red")
|
||||
sdkView.rejectSessionRequest(d.sessionRequest.topic, d.sessionRequest.id, true)
|
||||
return
|
||||
}
|
||||
|
||||
d.sessionRequest = JSON.parse(sessionRequestJson)
|
||||
d.signedData = signedData
|
||||
|
||||
sdkView.acceptSessionRequest(d.sessionRequest.topic, d.sessionRequest.id, d.signedData)
|
||||
|
||||
d.setStatusText("Session Request accepted")
|
||||
d.setDetailsText(d.signedData)
|
||||
onSessionRequestEvent: (details) => {
|
||||
modal.openWithSessionRequestEvent(details)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,308 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Utils 0.1 as SQUtils
|
||||
import StatusQ.Popups 0.1
|
||||
|
||||
Popup {
|
||||
id: root
|
||||
|
||||
implicitWidth: Math.min(mainLayout.implicitWidth, 400)
|
||||
implicitHeight: Math.min(mainLayout.implicitHeight, 700)
|
||||
|
||||
required property WalletConnectSDK sdk
|
||||
|
||||
parent: Overlay.overlay
|
||||
anchors.centerIn: parent
|
||||
|
||||
clip: true
|
||||
|
||||
property bool sdkReady: d.state === d.sdkReadyState
|
||||
|
||||
// wallet_connect.Controller \see wallet_section/wallet_connect/controller.nim
|
||||
required property var controller
|
||||
|
||||
function openWithSessionRequestEvent(sessionRequest) {
|
||||
d.setStatusText("Approve session request")
|
||||
d.setDetailsText(JSON.stringify(sessionRequest, null, 2))
|
||||
d.sessionRequest = sessionRequest
|
||||
d.state = d.waitingUserResponseToSessionRequest
|
||||
root.open()
|
||||
}
|
||||
|
||||
Flickable {
|
||||
id: flickable
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
contentWidth: mainLayout.width
|
||||
contentHeight: mainLayout.height
|
||||
|
||||
ColumnLayout {
|
||||
id: mainLayout
|
||||
|
||||
StatusBaseText {
|
||||
text: qsTr("Debugging UX until design is ready")
|
||||
}
|
||||
|
||||
StatusInput {
|
||||
id: pairLinkInput
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
placeholderText: "Insert pair link"
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
StatusButton {
|
||||
text: "Pair"
|
||||
onClicked: {
|
||||
statusText.text = "Pairing..."
|
||||
sdk.pair(pairLinkInput.text)
|
||||
}
|
||||
enabled: pairLinkInput.text.length > 0 && sdk.sdkReady
|
||||
}
|
||||
|
||||
StatusButton {
|
||||
text: "Auth"
|
||||
onClicked: {
|
||||
statusText.text = "Authenticating..."
|
||||
sdk.auth()
|
||||
}
|
||||
enabled: false && pairLinkInput.text.length > 0 && sdk.sdkReady
|
||||
}
|
||||
|
||||
StatusButton {
|
||||
text: "Accept"
|
||||
onClicked: {
|
||||
sdk.approvePairSession(d.sessionProposal, d.supportedNamespaces)
|
||||
}
|
||||
visible: d.state === d.waitingPairState
|
||||
}
|
||||
StatusButton {
|
||||
text: "Reject"
|
||||
onClicked: {
|
||||
sdk.rejectPairSession(d.sessionProposal.id)
|
||||
}
|
||||
visible: d.state === d.waitingPairState
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
StatusBaseText {
|
||||
id: statusText
|
||||
text: "-"
|
||||
}
|
||||
StatusBaseText {
|
||||
text: "Pairings"
|
||||
visible: sdk.pairingsModel.count > 0
|
||||
}
|
||||
StatusListView {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: contentHeight
|
||||
Layout.maximumHeight: 200
|
||||
|
||||
model: sdk.pairingsModel
|
||||
|
||||
delegate: StatusBaseText {
|
||||
text: `${SQUtils.Utils.elideText(topic, 6, 6)} - ${new Date(expiry * 1000).toLocaleString()}`
|
||||
color: active ? "green" : "orange"
|
||||
}
|
||||
}
|
||||
Flickable {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 200
|
||||
Layout.maximumHeight: 400
|
||||
|
||||
contentWidth: detailsText.width
|
||||
contentHeight: detailsText.height
|
||||
|
||||
StatusBaseText {
|
||||
id: detailsText
|
||||
text: ""
|
||||
visible: text.length > 0
|
||||
|
||||
color: "#FF00FF"
|
||||
}
|
||||
|
||||
ScrollBar.vertical: ScrollBar {}
|
||||
|
||||
clip: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
StatusButton {
|
||||
text: "Accept"
|
||||
onClicked: {
|
||||
root.controller.sessionRequest(JSON.stringify(d.sessionRequest), passwordInput.text)
|
||||
}
|
||||
visible: d.state === d.waitingUserResponseToSessionRequest
|
||||
}
|
||||
StatusButton {
|
||||
text: "Reject"
|
||||
onClicked: {
|
||||
sdk.rejectSessionRequest(d.sessionRequest.topic, d.sessionRequest.id, false)
|
||||
}
|
||||
visible: d.state === d.waitingUserResponseToSessionRequest
|
||||
}
|
||||
StatusInput {
|
||||
id: passwordInput
|
||||
|
||||
text: "1234567890"
|
||||
placeholderText: "Insert account password"
|
||||
visible: d.state === d.waitingUserResponseToSessionRequest
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout { /* spacer */ }
|
||||
}
|
||||
|
||||
// Separator
|
||||
ColumnLayout {}
|
||||
}
|
||||
|
||||
ScrollBar.vertical: ScrollBar {}
|
||||
|
||||
clip: true
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.sdk
|
||||
|
||||
function onSdkInit(success, info) {
|
||||
d.setDetailsText(info)
|
||||
if (success) {
|
||||
d.setStatusText("Ready to pair or auth")
|
||||
d.state = d.sdkReadyState
|
||||
} else {
|
||||
d.setStatusText("SDK Error", "red")
|
||||
d.state = ""
|
||||
}
|
||||
}
|
||||
|
||||
function onPairSessionProposal(success, sessionProposal) {
|
||||
d.setDetailsText(sessionProposal)
|
||||
if (success) {
|
||||
d.setStatusText("Pair ID: " + sessionProposal.id + "; Topic: " + sessionProposal.params.pairingTopic)
|
||||
root.controller.pairSessionProposal(JSON.stringify(sessionProposal))
|
||||
// Expecting signal onProposeUserPair from controller
|
||||
} else {
|
||||
d.setStatusText("Pairing error", "red")
|
||||
}
|
||||
}
|
||||
|
||||
function onPairAcceptedResult(sessionProposal, success, result) {
|
||||
d.setDetailsText(result)
|
||||
if (success) {
|
||||
d.setStatusText("Pairing OK")
|
||||
d.state = d.pairedState
|
||||
root.controller.recordSuccessfulPairing(JSON.stringify(sessionProposal))
|
||||
} else {
|
||||
d.setStatusText("Pairing error", "red")
|
||||
d.state = d.sdkReadyState
|
||||
}
|
||||
}
|
||||
|
||||
function onPairRejectedResult(success, result) {
|
||||
d.setDetailsText(result)
|
||||
d.state = d.sdkReadyState
|
||||
if (success) {
|
||||
d.setStatusText("Pairing rejected")
|
||||
} else {
|
||||
d.setStatusText("Rejecting pairing error", "red")
|
||||
}
|
||||
}
|
||||
|
||||
function onSessionRequestUserAnswerResult(accept, error) {
|
||||
if (error) {
|
||||
d.setStatusText(`Session Request ${accept ? "Accept" : "Reject"} error`, "red")
|
||||
return
|
||||
}
|
||||
d.state = d.pairedState
|
||||
if (accept) {
|
||||
d.setStatusText(`Session Request accepted`)
|
||||
} else {
|
||||
d.setStatusText(`Session Request rejected`)
|
||||
}
|
||||
}
|
||||
|
||||
function onPairSessionProposalExpired() {
|
||||
d.setStatusText(`Timeout waiting for response. Reusing URI?`, "red")
|
||||
}
|
||||
|
||||
function onStatusChanged(message) {
|
||||
statusText.text = message
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
||||
property var sessionProposal: null
|
||||
property var supportedNamespaces: null
|
||||
|
||||
property var sessionRequest: null
|
||||
property var signedData: null
|
||||
|
||||
property string state: ""
|
||||
readonly property string sdkReadyState: "sdk_ready"
|
||||
readonly property string waitingPairState: "waiting_pairing"
|
||||
readonly property string waitingUserResponseToSessionRequest: "waiting_user_response_to_session_request"
|
||||
readonly property string pairedState: "paired"
|
||||
|
||||
function setStatusText(message, textColor) {
|
||||
statusText.text = message
|
||||
if (textColor === undefined) {
|
||||
textColor = "green"
|
||||
}
|
||||
statusText.color = textColor
|
||||
}
|
||||
function setDetailsText(message) {
|
||||
if (message === undefined) {
|
||||
message = "undefined"
|
||||
} else if (typeof message !== "string") {
|
||||
message = JSON.stringify(message, null, 2)
|
||||
}
|
||||
detailsText.text = message
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.controller
|
||||
|
||||
function onProposeUserPair(sessionProposalJson, supportedNamespacesJson) {
|
||||
d.setStatusText("Waiting user accept")
|
||||
|
||||
d.sessionProposal = JSON.parse(sessionProposalJson)
|
||||
d.supportedNamespaces = JSON.parse(supportedNamespacesJson)
|
||||
|
||||
d.setDetailsText(JSON.stringify(d.supportedNamespaces, null, 2))
|
||||
|
||||
d.state = d.waitingPairState
|
||||
}
|
||||
|
||||
function onRespondSessionRequest(sessionRequestJson, signedData, error) {
|
||||
console.log("WC respondSessionRequest", sessionRequestJson, " signedData", signedData, " error: ", error)
|
||||
if (error) {
|
||||
d.setStatusText("Session Request error", "red")
|
||||
sdk.rejectSessionRequest(d.sessionRequest.topic, d.sessionRequest.id, true)
|
||||
return
|
||||
}
|
||||
|
||||
d.sessionRequest = JSON.parse(sessionRequestJson)
|
||||
d.signedData = signedData
|
||||
|
||||
sdk.acceptSessionRequest(d.sessionRequest.topic, d.sessionRequest.id, d.signedData)
|
||||
|
||||
d.state = d.pairedState
|
||||
|
||||
d.setStatusText("Session Request accepted")
|
||||
d.setDetailsText(d.signedData)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +1,12 @@
|
|||
import QtQuick 2.15
|
||||
import QtWebEngine 1.10
|
||||
import QtWebChannel 1.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import QtWebEngine 1.10
|
||||
import QtWebChannel 1.15
|
||||
|
||||
import StatusQ.Core.Utils 0.1 as SQUtils
|
||||
import StatusQ.Components 0.1
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
@ -12,6 +14,10 @@ Item {
|
|||
required property string projectId
|
||||
readonly property alias sdkReady: d.sdkReady
|
||||
readonly property alias pairingsModel: d.pairingsModel
|
||||
readonly property alias webEngineLoader: loader
|
||||
|
||||
property alias active: loader.active
|
||||
property alias url: loader.url
|
||||
|
||||
implicitWidth: 1
|
||||
implicitHeight: 1
|
||||
|
@ -20,7 +26,7 @@ Item {
|
|||
signal sdkInit(bool success, var result)
|
||||
signal pairSessionProposal(bool success, var sessionProposal)
|
||||
signal pairSessionProposalExpired()
|
||||
signal pairAcceptedResult(bool success, var sessionType)
|
||||
signal pairAcceptedResult(var sessionProposal, bool success, var sessionType)
|
||||
signal pairRejectedResult(bool success, var result)
|
||||
signal sessionRequestEvent(var sessionRequest)
|
||||
signal sessionRequestUserAnswerResult(bool accept, string error)
|
||||
|
@ -54,6 +60,8 @@ Item {
|
|||
property bool sdkReady: false
|
||||
property ListModel pairingsModel: pairings
|
||||
|
||||
property WebEngineView engine: loader.instance
|
||||
|
||||
onSdkReadyChanged: {
|
||||
if (sdkReady)
|
||||
{
|
||||
|
@ -61,7 +69,7 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
function resetPairingsModel()
|
||||
function resetPairingsModel(entryCallback)
|
||||
{
|
||||
pairings.clear();
|
||||
|
||||
|
@ -72,6 +80,9 @@ Item {
|
|||
topic: pairList[i].topic,
|
||||
expiry: pairList[i].expiry
|
||||
});
|
||||
if (entryCallback) {
|
||||
entryCallback(pairList[i])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -95,11 +106,11 @@ Item {
|
|||
id: wcCalls
|
||||
|
||||
function init() {
|
||||
console.debug(`@dd WalletConnectSDK.wcCall.init; root.projectId: ${root.projectId}`)
|
||||
console.debug(`WC WalletConnectSDK.wcCall.init; root.projectId: ${root.projectId}`)
|
||||
|
||||
webEngineView.runJavaScript(`wc.init("${root.projectId}")`, function(result) {
|
||||
d.engine.runJavaScript(`wc.init("${root.projectId}")`, function(result) {
|
||||
|
||||
console.debug(`@dd WalletConnectSDK.wcCall.init; response: ${JSON.stringify(result, null, 2)}`)
|
||||
console.debug(`WC WalletConnectSDK.wcCall.init; response: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
if (result && !!result.error)
|
||||
{
|
||||
|
@ -109,11 +120,11 @@ Item {
|
|||
}
|
||||
|
||||
function getPairings(callback) {
|
||||
console.debug(`@dd WalletConnectSDK.wcCall.getPairings;`)
|
||||
console.debug(`WC WalletConnectSDK.wcCall.getPairings;`)
|
||||
|
||||
webEngineView.runJavaScript(`wc.getPairings()`, function(result) {
|
||||
d.engine.runJavaScript(`wc.getPairings()`, function(result) {
|
||||
|
||||
console.debug(`@dd WalletConnectSDK.wcCall.getPairings; response: ${JSON.stringify(result, null, 2)}`)
|
||||
console.debug(`WC WalletConnectSDK.wcCall.getPairings; response: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
if (result)
|
||||
{
|
||||
|
@ -129,11 +140,11 @@ Item {
|
|||
}
|
||||
|
||||
function pair(pairLink) {
|
||||
console.debug(`@dd WalletConnectSDK.wcCall.pair; pairLink: ${pairLink}`)
|
||||
console.debug(`WC WalletConnectSDK.wcCall.pair; pairLink: ${pairLink}`)
|
||||
|
||||
wcCalls.getPairings((allPairings) => {
|
||||
|
||||
console.debug(`@dd WalletConnectSDK.wcCall.pair; response: ${JSON.stringify(allPairings, null, 2)}`)
|
||||
console.debug(`WC WalletConnectSDK.wcCall.pair; response: ${JSON.stringify(allPairings, null, 2)}`)
|
||||
|
||||
let pairingTopic = d.getPairingTopicFromPairingUrl(pairLink);
|
||||
|
||||
|
@ -147,7 +158,7 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
webEngineView.runJavaScript(`wc.pair("${pairLink}")`, function(result) {
|
||||
d.engine.runJavaScript(`wc.pair("${pairLink}")`, function(result) {
|
||||
if (result && !!result.error)
|
||||
{
|
||||
console.error("pair: ", result.error)
|
||||
|
@ -158,32 +169,38 @@ Item {
|
|||
}
|
||||
|
||||
function approvePairSession(sessionProposal, supportedNamespaces) {
|
||||
console.debug(`@dd WalletConnectSDK.wcCall.approvePairSession; sessionProposal: ${JSON.stringify(sessionProposal)}, supportedNamespaces: ${JSON.stringify(supportedNamespaces)}`)
|
||||
console.debug(`WC WalletConnectSDK.wcCall.approvePairSession; sessionProposal: ${JSON.stringify(sessionProposal)}, supportedNamespaces: ${JSON.stringify(supportedNamespaces)}`)
|
||||
|
||||
webEngineView.runJavaScript(`wc.approvePairSession(${JSON.stringify(sessionProposal)}, ${JSON.stringify(supportedNamespaces)})`, function(result) {
|
||||
d.engine.runJavaScript(`wc.approvePairSession(${JSON.stringify(sessionProposal)}, ${JSON.stringify(supportedNamespaces)})`, function(result) {
|
||||
|
||||
console.debug(`@dd WalletConnectSDK.wcCall.approvePairSession; response: ${JSON.stringify(result, null, 2)}`)
|
||||
console.debug(`WC WalletConnectSDK.wcCall.approvePairSession; response: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
if (result) {
|
||||
if (!!result.error)
|
||||
{
|
||||
console.error("approvePairSession: ", result.error)
|
||||
root.pairAcceptedResult(false, result.error)
|
||||
root.pairAcceptedResult(sessionProposal, false, result.error)
|
||||
return
|
||||
}
|
||||
root.pairAcceptedResult(true, result.error)
|
||||
// Update the temporary expiry with the one from the pairing
|
||||
d.resetPairingsModel((pairing) => {
|
||||
if (pairing.topic === sessionProposal.params.pairingTopic) {
|
||||
sessionProposal.params.expiry = pairing.expiry
|
||||
root.pairAcceptedResult(sessionProposal, true, result.error)
|
||||
}
|
||||
})
|
||||
}
|
||||
d.resetPairingsModel()
|
||||
})
|
||||
}
|
||||
|
||||
function rejectPairSession(id) {
|
||||
console.debug(`@dd WalletConnectSDK.wcCall.rejectPairSession; id: ${id}`)
|
||||
console.debug(`WC WalletConnectSDK.wcCall.rejectPairSession; id: ${id}`)
|
||||
|
||||
webEngineView.runJavaScript(`wc.rejectPairSession(${id})`, function(result) {
|
||||
d.engine.runJavaScript(`wc.rejectPairSession(${id})`, function(result) {
|
||||
|
||||
console.debug(`@dd WalletConnectSDK.wcCall.rejectPairSession; response: ${JSON.stringify(result, null, 2)}`)
|
||||
console.debug(`WC WalletConnectSDK.wcCall.rejectPairSession; response: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
d.resetPairingsModel()
|
||||
if (result) {
|
||||
if (!!result.error)
|
||||
{
|
||||
|
@ -193,16 +210,15 @@ Item {
|
|||
}
|
||||
root.pairRejectedResult(true, result.error)
|
||||
}
|
||||
d.resetPairingsModel()
|
||||
})
|
||||
}
|
||||
|
||||
function acceptSessionRequest(topic, id, signature) {
|
||||
console.debug(`@dd WalletConnectSDK.wcCall.acceptSessionRequest; topic: "${topic}", id: ${id}, signature: "${signature}"`)
|
||||
console.debug(`WC WalletConnectSDK.wcCall.acceptSessionRequest; topic: "${topic}", id: ${id}, signature: "${signature}"`)
|
||||
|
||||
webEngineView.runJavaScript(`wc.respondSessionRequest("${topic}", ${id}, "${signature}")`, function(result) {
|
||||
d.engine.runJavaScript(`wc.respondSessionRequest("${topic}", ${id}, "${signature}")`, function(result) {
|
||||
|
||||
console.debug(`@dd WalletConnectSDK.wcCall.acceptSessionRequest; response: ${JSON.stringify(allPairings, null, 2)}`)
|
||||
console.debug(`WC WalletConnectSDK.wcCall.acceptSessionRequest; response: ${JSON.stringify(allPairings, null, 2)}`)
|
||||
|
||||
if (result) {
|
||||
if (!!result.error)
|
||||
|
@ -218,11 +234,11 @@ Item {
|
|||
}
|
||||
|
||||
function rejectSessionRequest(topic, id, error) {
|
||||
console.debug(`@dd WalletConnectSDK.wcCall.rejectSessionRequest; topic: "${topic}", id: ${id}, error: "${error}"`)
|
||||
console.debug(`WC WalletConnectSDK.wcCall.rejectSessionRequest; topic: "${topic}", id: ${id}, error: "${error}"`)
|
||||
|
||||
webEngineView.runJavaScript(`wc.rejectSessionRequest("${topic}", ${id}, "${error}")`, function(result) {
|
||||
d.engine.runJavaScript(`wc.rejectSessionRequest("${topic}", ${id}, "${error}")`, function(result) {
|
||||
|
||||
console.debug(`@dd WalletConnectSDK.wcCall.rejectSessionRequest; response: ${JSON.stringify(result, null, 2)}`)
|
||||
console.debug(`WC WalletConnectSDK.wcCall.rejectSessionRequest; response: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
if (result) {
|
||||
if (!!result.error)
|
||||
|
@ -263,54 +279,54 @@ Item {
|
|||
|
||||
function onSessionProposal(details)
|
||||
{
|
||||
console.debug(`@dd WalletConnectSDK.onSessionProposal; details: ${JSON.stringify(details, null, 2)}`)
|
||||
console.debug(`WC WalletConnectSDK.onSessionProposal; details: ${JSON.stringify(details, null, 2)}`)
|
||||
root.pairSessionProposal(true, details)
|
||||
}
|
||||
|
||||
function onSessionUpdate(details)
|
||||
{
|
||||
console.debug(`@dd TODO WalletConnectSDK.onSessionUpdate; details: ${JSON.stringify(details, null, 2)}`)
|
||||
console.debug(`WC TODO WalletConnectSDK.onSessionUpdate; details: ${JSON.stringify(details, null, 2)}`)
|
||||
}
|
||||
|
||||
function onSessionExtend(details)
|
||||
{
|
||||
console.debug(`@dd TODO WalletConnectSDK.onSessionExtend; details: ${JSON.stringify(details, null, 2)}`)
|
||||
console.debug(`WC TODO WalletConnectSDK.onSessionExtend; details: ${JSON.stringify(details, null, 2)}`)
|
||||
}
|
||||
|
||||
function onSessionPing(details)
|
||||
{
|
||||
console.debug(`@dd TODO WalletConnectSDK.onSessionPing; details: ${JSON.stringify(details, null, 2)}`)
|
||||
console.debug(`WC TODO WalletConnectSDK.onSessionPing; details: ${JSON.stringify(details, null, 2)}`)
|
||||
}
|
||||
|
||||
function onSessionDelete(details)
|
||||
{
|
||||
console.debug(`@dd TODO WalletConnectSDK.onSessionDelete; details: ${JSON.stringify(details, null, 2)}`)
|
||||
console.debug(`WC TODO WalletConnectSDK.onSessionDelete; details: ${JSON.stringify(details, null, 2)}`)
|
||||
}
|
||||
|
||||
function onSessionExpire(details)
|
||||
{
|
||||
console.debug(`@dd TODO WalletConnectSDK.onSessionExpire; details: ${JSON.stringify(details, null, 2)}`)
|
||||
console.debug(`WC TODO WalletConnectSDK.onSessionExpire; details: ${JSON.stringify(details, null, 2)}`)
|
||||
}
|
||||
|
||||
function onSessionRequest(details)
|
||||
{
|
||||
console.debug(`@dd WalletConnectSDK.onSessionRequest; details: ${JSON.stringify(details, null, 2)}`)
|
||||
console.debug(`WC WalletConnectSDK.onSessionRequest; details: ${JSON.stringify(details, null, 2)}`)
|
||||
root.sessionRequestEvent(details)
|
||||
}
|
||||
|
||||
function onSessionRequestSent(details)
|
||||
{
|
||||
console.debug(`@dd TODO WalletConnectSDK.onSessionRequestSent; details: ${JSON.stringify(details, null, 2)}`)
|
||||
console.debug(`WC TODO WalletConnectSDK.onSessionRequestSent; details: ${JSON.stringify(details, null, 2)}`)
|
||||
}
|
||||
|
||||
function onSessionEvent(details)
|
||||
{
|
||||
console.debug(`@dd TODO WalletConnectSDK.onSessionEvent; details: ${JSON.stringify(details, null, 2)}`)
|
||||
console.debug(`WC TODO WalletConnectSDK.onSessionEvent; details: ${JSON.stringify(details, null, 2)}`)
|
||||
}
|
||||
|
||||
function onProposalExpire(details)
|
||||
{
|
||||
console.debug(`@dd WalletConnectSDK.onProposalExpire; details: ${JSON.stringify(details, null, 2)}`)
|
||||
console.debug(`WC WalletConnectSDK.onProposalExpire; details: ${JSON.stringify(details, null, 2)}`)
|
||||
root.pairSessionProposalExpired()
|
||||
}
|
||||
}
|
||||
|
@ -319,36 +335,19 @@ Item {
|
|||
id: pairings
|
||||
}
|
||||
|
||||
WebChannel {
|
||||
id: statusChannel
|
||||
registeredObjects: [statusObject]
|
||||
}
|
||||
|
||||
WebEngineView {
|
||||
id: webEngineView
|
||||
WebEngineLoader {
|
||||
id: loader
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
Component.onCompleted: {
|
||||
console.debug(`@dd WalletConnectSDK.WebEngineView.onCompleted; url: ${url}; debug? ${SQUtils.isDebug()};`)
|
||||
}
|
||||
|
||||
url: "qrc:/app/AppLayouts/Wallet/views/walletconnect/sdk/src/index.html"
|
||||
webChannel: statusChannel
|
||||
webChannelObjects: [ statusObject ]
|
||||
|
||||
onLoadingChanged: function(loadRequest) {
|
||||
console.debug(`@dd WalletConnectSDK.onLoadingChanged; status: ${loadRequest.status}; error: ${loadRequest.errorString}`)
|
||||
switch(loadRequest.status) {
|
||||
case WebEngineView.LoadSucceededStatus:
|
||||
onPageLoaded: function() {
|
||||
wcCalls.init()
|
||||
break
|
||||
case WebEngineView.LoadFailedStatus:
|
||||
root.statusChanged(`<font color="red">Failed loading SDK JS code; error: "${loadRequest.errorString}"</font>`)
|
||||
break
|
||||
case WebEngineView.LoadStartedStatus:
|
||||
root.statusChanged(`<font color="blue">Loading SDK JS code</font>`)
|
||||
break
|
||||
}
|
||||
}
|
||||
onPageLoadingError: function(error) {
|
||||
console.error("WebEngineLoader.onPageLoadingError: ", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
WalletConnect 1.0 WalletConnect.qml
|
||||
WalletConnectModal 1.0 WalletConnectModal.qml
|
||||
WalletConnectSDK 1.0 WalletConnectSDK.qml
|
|
@ -28,33 +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
|
||||
|
||||
## Dev - to be removed
|
||||
|
||||
To test SDK loading add the following to `ui/app/mainui/AppMain.qml`
|
||||
|
||||
```qml
|
||||
import AppLayouts.Wallet.stores 1.0 as WalletStores
|
||||
import AppLayouts.Wallet.views.walletconnect 1.0
|
||||
|
||||
// ...
|
||||
|
||||
StatusDialog {
|
||||
id: wcHelperDialog
|
||||
visible: true
|
||||
|
||||
WalletConnect {
|
||||
SplitView.preferredWidth: 400
|
||||
SplitView.preferredHeight: 600
|
||||
|
||||
backgroundColor: wcHelperDialog.backgroundColor
|
||||
|
||||
controller: WalletStores.RootStore.walletConnectController
|
||||
}
|
||||
|
||||
clip: true
|
||||
}
|
||||
```
|
||||
|
||||
## Log
|
||||
|
||||
Initial setup
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,7 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="qrc:/app/AppLayouts/Wallet/views/walletconnect/sdk/generated/bundle.js"></script>
|
||||
<script src="qrc:/StatusQ/Components/private/qwebchannel/qwebchannel.js" defer></script>
|
||||
<script src="qrc:/StatusQ/Components/private/qwebchannel/helpers.js" defer></script>
|
||||
<script src="qrc:/app/AppLayouts/Wallet/views/walletconnect/sdk/generated/bundle.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
|
|
|
@ -3,8 +3,6 @@ import { Web3Wallet } from "@walletconnect/web3wallet";
|
|||
|
||||
import AuthClient from '@walletconnect/auth-client'
|
||||
|
||||
import { QWebChannel } from './qwebchannel';
|
||||
|
||||
// import the builder util
|
||||
import { buildApprovedNamespaces, getSdkError } from "@walletconnect/utils";
|
||||
import { formatJsonRpcResult, formatJsonRpcError } from "@walletconnect/jsonrpc-utils";
|
||||
|
@ -17,11 +15,20 @@ window.wc = {
|
|||
|
||||
init: function (projectId) {
|
||||
(async () => {
|
||||
try {
|
||||
await createWebChannel();
|
||||
} catch (error) {
|
||||
wc.statusObject.sdkInitialized(error);
|
||||
return
|
||||
if (!window.statusq) {
|
||||
console.error('missing window.statusq! Forgot to execute "ui/StatusQ/src/StatusQ/Components/private/qwebchannel/helpers.js" first?');
|
||||
return;
|
||||
}
|
||||
|
||||
if (window.statusq.error) {
|
||||
console.error("Failed initializing WebChannel: " + window.statusq.error);
|
||||
return;
|
||||
}
|
||||
|
||||
wc.statusObject = window.statusq.channel.objects.statusObject;
|
||||
if (!wc.statusObject) {
|
||||
console.error("Failed initializing WebChannel or initialization not run");
|
||||
return;
|
||||
}
|
||||
|
||||
window.wc.core = new Core({
|
||||
|
@ -208,18 +215,3 @@ window.wc = {
|
|||
};
|
||||
},
|
||||
};
|
||||
|
||||
function createWebChannel(projectId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
window.wc.channel = new QWebChannel(qt.webChannelTransport, function (channel) {
|
||||
let statusObject = channel.objects.statusObject;
|
||||
|
||||
if (!statusObject) {
|
||||
reject(new Error("Unable to resolve statusObject"));
|
||||
} else {
|
||||
window.wc.statusObject = statusObject;
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ import AppLayouts.stores 1.0
|
|||
import AppLayouts.Chat.stores 1.0 as ChatStores
|
||||
import AppLayouts.Communities.stores 1.0
|
||||
import AppLayouts.Wallet.stores 1.0 as WalletStore
|
||||
import AppLayouts.Wallet.views.walletconnect 1.0
|
||||
|
||||
import mainui.activitycenter.stores 1.0
|
||||
import mainui.activitycenter.popups 1.0
|
||||
|
@ -1674,4 +1675,20 @@ Item {
|
|||
onClosed: userAgreementLoader.active = false
|
||||
}
|
||||
}
|
||||
|
||||
WalletConnect {
|
||||
id: walletConnect
|
||||
anchors.top: parent.bottom
|
||||
width: 100
|
||||
height: 100
|
||||
|
||||
controller: WalletStore.RootStore.walletConnectController
|
||||
|
||||
Connections {
|
||||
target: Global
|
||||
function onPopupWalletConnect() {
|
||||
walletConnect.modal.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,6 +79,8 @@ QtObject {
|
|||
|
||||
signal openTestnetPopup()
|
||||
|
||||
signal popupWalletConnect()
|
||||
|
||||
function openProfilePopup(publicKey, parentPopup, cb) {
|
||||
root.openProfilePopupRequested(publicKey, parentPopup, cb)
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ continueUserActivity:(NSUserActivity *)userActivity
|
|||
if (!url)
|
||||
return FALSE;
|
||||
QUrl deeplink = QUrl::fromNSURL(url);
|
||||
|
||||
// TODO #12434: Check if WalletConnect link and redirect the workflow to Pair or Authenticate
|
||||
|
||||
// TODO #12245: set it to nim
|
||||
|
|
Loading…
Reference in New Issue