feat(wallet) Wallet Connect integration prototype
Implement a prototype of integrating [WalletConnect Web SDK]() - integrate WalletConnect Web SDK using Node.js and packing it using [webpack](https://webpack.js.org/guides/getting-started/) - this way, we achieve the same versioning strategy for the SDK - add WalletConnectSDK view - it is used to load the web SDK via a WebView (validated working on Mac and Windows) - add new app dependency of WebView QT - also update vendor packages `Dotherside` and `nimqml` to add required WebView::initialize API used to initialize the WebView integration at the app start - add WalletConnectPage to Storybook for quick prototyping - Also add dependency for WebView Qt lib - index.js is the wrapper used to provide a simple stateful interface with the WC SDK - Entry in ui/generate-rcc.go ensures the node_modules cache is excluded from the resource file Notes: - Added `com.apple.security.cs.allow-jit` entitlement when signing the app package. This allows Execution of JIT-compiled Code Entitlement required by the fast-path of the JavaScriptCore framework on MacOS platforms. - Keep some debugging entries expected to help debugging Linux package - Removed outdated `DerivationPathInputRegressionTests` qml test Closes #12301
This commit is contained in:
parent
fd0e4eff43
commit
ccd8c5b65f
2
Makefile
2
Makefile
|
@ -186,7 +186,7 @@ ifneq ($(detected_OS),Windows)
|
||||||
endif
|
endif
|
||||||
DOTHERSIDE_LIBFILE := vendor/DOtherSide/build/lib/libDOtherSideStatic.a
|
DOTHERSIDE_LIBFILE := vendor/DOtherSide/build/lib/libDOtherSideStatic.a
|
||||||
# order matters here, due to "-Wl,-as-needed"
|
# order matters here, due to "-Wl,-as-needed"
|
||||||
NIM_PARAMS += --passL:"$(DOTHERSIDE_LIBFILE)" --passL:"$(shell PKG_CONFIG_PATH="$(QT5_PCFILEDIR)" pkg-config --libs Qt5Core Qt5Qml Qt5Gui Qt5Quick Qt5QuickControls2 Qt5Widgets Qt5Svg Qt5Multimedia)"
|
NIM_PARAMS += --passL:"$(DOTHERSIDE_LIBFILE)" --passL:"$(shell PKG_CONFIG_PATH="$(QT5_PCFILEDIR)" pkg-config --libs Qt5Core Qt5Qml Qt5Gui Qt5Quick Qt5QuickControls2 Qt5Widgets Qt5Svg Qt5Multimedia Qt5WebView)"
|
||||||
else
|
else
|
||||||
NIM_EXTRA_PARAMS := --passL:"-lsetupapi -lhid"
|
NIM_EXTRA_PARAMS := --passL:"-lsetupapi -lhid"
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -10,5 +10,9 @@
|
||||||
<string>applinks:status.app</string>
|
<string>applinks:status.app</string>
|
||||||
</array>
|
</array>
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
<!-- Required by WalletConnect web SDK to run in WebView -->
|
||||||
|
<key>com.apple.security.cs.allow-jit</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
@ -132,6 +132,9 @@ proc mainProc() =
|
||||||
|
|
||||||
let app = newQGuiApplication()
|
let app = newQGuiApplication()
|
||||||
|
|
||||||
|
# Required by the WalletConnectSDK view right after creating the QGuiApplication instance
|
||||||
|
initializeWebView()
|
||||||
|
|
||||||
let singleInstance = newSingleInstance($toMD5(DATADIR), openUri)
|
let singleInstance = newSingleInstance($toMD5(DATADIR), openUri)
|
||||||
let urlSchemeEvent = newStatusUrlSchemeEventObject()
|
let urlSchemeEvent = newStatusUrlSchemeEventObject()
|
||||||
# init url manager before app controller
|
# init url manager before app controller
|
||||||
|
|
|
@ -20,7 +20,7 @@ endif()
|
||||||
|
|
||||||
find_package(
|
find_package(
|
||||||
Qt5
|
Qt5
|
||||||
COMPONENTS Core Gui Quick QuickControls2 Test QuickTest Qml
|
COMPONENTS Core Gui Quick QuickControls2 Test QuickTest Qml WebView
|
||||||
REQUIRED)
|
REQUIRED)
|
||||||
|
|
||||||
set(STATUSQ_BUILD_SANDBOX OFF)
|
set(STATUSQ_BUILD_SANDBOX OFF)
|
||||||
|
@ -53,6 +53,7 @@ add_library(${PROJECT_LIB}
|
||||||
pagesmodel.h pagesmodel.cpp
|
pagesmodel.h pagesmodel.cpp
|
||||||
sectionsdecoratormodel.cpp sectionsdecoratormodel.h
|
sectionsdecoratormodel.cpp sectionsdecoratormodel.h
|
||||||
testsrunner.h testsrunner.cpp
|
testsrunner.h testsrunner.cpp
|
||||||
|
systemutils.cpp systemutils.h
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(
|
add_executable(
|
||||||
|
@ -72,7 +73,7 @@ target_compile_definitions(${PROJECT_NAME} PRIVATE
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
${PROJECT_LIB} PUBLIC Qt5::Core Qt5::Gui Qt5::Quick Qt5::QuickControls2)
|
${PROJECT_LIB} PUBLIC Qt5::Core Qt5::Gui Qt5::Quick Qt5::QuickControls2 Qt5::WebView)
|
||||||
|
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
${PROJECT_NAME} PRIVATE ${PROJECT_LIB})
|
${PROJECT_NAME} PRIVATE ${PROJECT_LIB})
|
||||||
|
@ -87,7 +88,7 @@ add_executable(
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
PagesValidator PUBLIC Qt5::Core Qt5::Gui Qt5::Quick Qt5::QuickControls2)
|
PagesValidator PUBLIC Qt5::Core Qt5::Gui Qt5::Quick Qt5::QuickControls2 Qt5::WebView)
|
||||||
|
|
||||||
add_dependencies(PagesValidator StatusQ)
|
add_dependencies(PagesValidator StatusQ)
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,15 @@
|
||||||
#include <QQmlApplicationEngine>
|
#include <QQmlApplicationEngine>
|
||||||
#include <QQmlContext>
|
#include <QQmlContext>
|
||||||
|
|
||||||
|
#include <QtWebView>
|
||||||
|
|
||||||
#include "cachecleaner.h"
|
#include "cachecleaner.h"
|
||||||
#include "directorieswatcher.h"
|
#include "directorieswatcher.h"
|
||||||
#include "figmalinks.h"
|
#include "figmalinks.h"
|
||||||
#include "pagesmodel.h"
|
#include "pagesmodel.h"
|
||||||
#include "sectionsdecoratormodel.h"
|
#include "sectionsdecoratormodel.h"
|
||||||
#include "testsrunner.h"
|
#include "testsrunner.h"
|
||||||
|
#include "systemutils.h"
|
||||||
|
|
||||||
struct PagesModelInitialized : public PagesModel {
|
struct PagesModelInitialized : public PagesModel {
|
||||||
explicit PagesModelInitialized(QObject *parent = nullptr)
|
explicit PagesModelInitialized(QObject *parent = nullptr)
|
||||||
|
@ -16,6 +19,9 @@ struct PagesModelInitialized : public PagesModel {
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
// Required by the WalletConnectSDK view
|
||||||
|
QtWebView::initialize();
|
||||||
|
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||||
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||||
#endif
|
#endif
|
||||||
|
@ -79,6 +85,11 @@ int main(int argc, char *argv[])
|
||||||
qmlRegisterSingletonType<TestsRunner>(
|
qmlRegisterSingletonType<TestsRunner>(
|
||||||
"Storybook", 1, 0, "TestsRunner", runnerFactory);
|
"Storybook", 1, 0, "TestsRunner", runnerFactory);
|
||||||
|
|
||||||
|
qmlRegisterSingletonType<SystemUtils>(
|
||||||
|
"Storybook", 1, 0, "SystemUtils", [](QQmlEngine*, QJSEngine*) {
|
||||||
|
return new SystemUtils;
|
||||||
|
});
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
const QUrl url(QUrl::fromLocalFile(QML_IMPORT_ROOT + QStringLiteral("/main.qml")));
|
const QUrl url(QUrl::fromLocalFile(QML_IMPORT_ROOT + QStringLiteral("/main.qml")));
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
import QtQml 2.15
|
||||||
|
|
||||||
|
import StatusQ.Core 0.1
|
||||||
|
import StatusQ.Core.Utils 0.1
|
||||||
|
import StatusQ.Controls 0.1
|
||||||
|
import StatusQ.Components 0.1
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
import StatusQ.Popups.Dialog 0.1
|
||||||
|
|
||||||
|
import Models 1.0
|
||||||
|
import Storybook 1.0
|
||||||
|
|
||||||
|
import AppLayouts.Wallet.views.walletconnect 1.0
|
||||||
|
|
||||||
|
import SortFilterProxyModel 0.2
|
||||||
|
|
||||||
|
import utils 1.0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
// qml Splitter
|
||||||
|
SplitView {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
WalletConnect {
|
||||||
|
id: walletConnect
|
||||||
|
|
||||||
|
SplitView.preferredWidth: 400
|
||||||
|
|
||||||
|
projectId: SystemUtils.getEnvVar("WALLET_CONNECT_PROJECT_ID")
|
||||||
|
backgroundColor: Theme.palette.statusAppLayout.backgroundColor
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: optionsSpace
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: optionsHeader
|
||||||
|
|
||||||
|
Text { text: "projectId" }
|
||||||
|
Text {
|
||||||
|
text: walletConnect.projectId.substring(0, 3) + "..." + walletConnect.projectId.substring(walletConnect.projectId.length - 3)
|
||||||
|
font.bold: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// spacer
|
||||||
|
ColumnLayout {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// category: Popups
|
|
@ -161,24 +161,4 @@ Item {
|
||||||
verify(/^<style>(?:\.[a-zA-Z]{[a-zA-Z0-9#:;]+})+<\/style>(?:<span\sclass=["']\w["']>[\/\d'm]+<\/span>)+$/.test(res), `The generated html is valid and optimum (no extra spaces or CSS long names) - "${res}"`)
|
verify(/^<style>(?:\.[a-zA-Z]{[a-zA-Z0-9#:;]+})+<\/style>(?:<span\sclass=["']\w["']>[\/\d'm]+<\/span>)+$/.test(res), `The generated html is valid and optimum (no extra spaces or CSS long names) - "${res}"`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TestCase {
|
|
||||||
name: "DerivationPathInputRegressionTests"
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: regressionControlComponent
|
|
||||||
|
|
||||||
DerivationPathInput {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
property DerivationPathInput controller: null
|
|
||||||
|
|
||||||
// Controller.Component.onCompleted was initializing Component.d.referenceElements after DerivationPathInput.onCompleted was processing the DerivationPathInput.initialDerivationPath, hence the output was wrong (m/44'/60001)
|
|
||||||
function test_successfulInitializationOfControllerBeforeItem() {
|
|
||||||
skip("Failing test. Needs to be fixed or updated")
|
|
||||||
const control = createTemporaryObject(regressionControlComponent, root)
|
|
||||||
compare("m/44'/60'/0'/0/1", control.derivationPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
#include "systemutils.h"
|
||||||
|
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
SystemUtils::SystemUtils(QObject *parent) : QObject(parent) {}
|
||||||
|
|
||||||
|
QString SystemUtils::getEnvVar(const QString &varName) {
|
||||||
|
return qEnvironmentVariable(varName.toUtf8().constData(), "");
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
class SystemUtils : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit SystemUtils(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
Q_INVOKABLE QString getEnvVar(const QString &varName);
|
||||||
|
};
|
|
@ -16,6 +16,18 @@ QString StringUtilsInternal::escapeHtml(const QString& unsafe) const
|
||||||
return unsafe.toHtmlEscaped();
|
return unsafe.toHtmlEscaped();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString resolveFileUsingQmlImportPaths(QQmlEngine *engine, const QString &relativeFilePath) {
|
||||||
|
QStringList importPaths = engine->importPathList();
|
||||||
|
for (const auto &path : importPaths) {
|
||||||
|
QString fullPath = path + "/" + relativeFilePath;
|
||||||
|
QFile file(fullPath);
|
||||||
|
if (file.exists()) {
|
||||||
|
return fullPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
QString StringUtilsInternal::readTextFile(const QString& filePath) const
|
QString StringUtilsInternal::readTextFile(const QString& filePath) const
|
||||||
{
|
{
|
||||||
auto selector = QQmlFileSelector::get(m_engine);
|
auto selector = QQmlFileSelector::get(m_engine);
|
||||||
|
@ -28,9 +40,17 @@ QString StringUtilsInternal::readTextFile(const QString& filePath) const
|
||||||
|
|
||||||
QFile file(resolvedFilePath);
|
QFile file(resolvedFilePath);
|
||||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
qWarning() << Q_FUNC_INFO << "Error opening" << resolvedFilePath << "for reading";
|
auto fileUrl = resolveFileUsingQmlImportPaths(m_engine, filePath);
|
||||||
|
if (fileUrl.isEmpty()) {
|
||||||
|
qWarning() << Q_FUNC_INFO << "Can't find file in QML import paths" << filePath;
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
file.setFileName(fileUrl);
|
||||||
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
|
qWarning() << Q_FUNC_INFO << "Error opening existing file" << fileUrl << "for reading";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return file.readAll();
|
return file.readAll();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,141 @@
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtWebView 1.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
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
implicitWidth: Math.min(mainLayout.implicitWidth, 400)
|
||||||
|
implicitHeight: Math.min(mainLayout.implicitHeight, 700)
|
||||||
|
|
||||||
|
|
||||||
|
required property string projectId
|
||||||
|
required property color backgroundColor
|
||||||
|
|
||||||
|
property alias optionalSdkPath: sdkView.optionalSdkPath
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: mainLayout
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
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.state === sdkView.disconnectedState
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusButton {
|
||||||
|
text: "Auth"
|
||||||
|
onClicked: {
|
||||||
|
statusText.text = "Authenticating..."
|
||||||
|
sdkView.auth()
|
||||||
|
}
|
||||||
|
enabled: false && pairLinkInput.text.length > 0 && sdkView.state === sdkView.disconnectedState
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusButton {
|
||||||
|
text: "Accept"
|
||||||
|
onClicked: {
|
||||||
|
sdkView.acceptPairing()
|
||||||
|
}
|
||||||
|
visible: sdkView.state === sdkView.waitingPairState
|
||||||
|
}
|
||||||
|
StatusButton {
|
||||||
|
text: "Reject"
|
||||||
|
onClicked: {
|
||||||
|
sdkView.rejectPairing()
|
||||||
|
}
|
||||||
|
visible: sdkView.state === sdkView.waitingPairState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
StatusBaseText {
|
||||||
|
id: statusText
|
||||||
|
text: "-"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: DEBUG JS Loading in DMG
|
||||||
|
// RowLayout {
|
||||||
|
// TextField {
|
||||||
|
// id: urlInput
|
||||||
|
|
||||||
|
// Layout.fillWidth: true
|
||||||
|
|
||||||
|
// placeholderText: "Insert URL here"
|
||||||
|
// }
|
||||||
|
// Button {
|
||||||
|
// text: "Set URL"
|
||||||
|
// onClicked: {
|
||||||
|
// sdkView.url = urlInput.text
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Button {
|
||||||
|
// text: "Set HTML"
|
||||||
|
// onClicked: {
|
||||||
|
// sdkView.loadHtml(htmlContent.text, "http://status.im")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// StatusInput {
|
||||||
|
// id: htmlContent
|
||||||
|
|
||||||
|
// Layout.fillWidth: true
|
||||||
|
// Layout.minimumHeight: 200
|
||||||
|
// Layout.maximumHeight: 300
|
||||||
|
|
||||||
|
// text: `<!DOCTYPE html><html><head><title>TODO: Test</title>\n<!--<script src="http://127.0.0.1:8080/bundle.js" defer></script>-->\n<script type='text/javascript'>\n console.log("@dd loaded dummy script!")\n</script>\n</head><body style='background-color: ${root.backgroundColor.toString()};'></body></html>`
|
||||||
|
|
||||||
|
// multiline: true
|
||||||
|
// minimumHeight: Layout.minimumHeight
|
||||||
|
// maximumHeight: Layout.maximumHeight
|
||||||
|
|
||||||
|
// }
|
||||||
|
// END DEBUGGING
|
||||||
|
|
||||||
|
// Separator
|
||||||
|
ColumnLayout {}
|
||||||
|
|
||||||
|
WalletConnectSDK {
|
||||||
|
id: sdkView
|
||||||
|
|
||||||
|
projectId: root.projectId
|
||||||
|
backgroundColor: root.backgroundColor
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
// Note that a too smaller height might cause the webview to generate rendering errors
|
||||||
|
Layout.preferredHeight: 10
|
||||||
|
|
||||||
|
onStatusChanged: function(message) {
|
||||||
|
statusText.text = message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,208 @@
|
||||||
|
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 string notReadyState: "not-ready"
|
||||||
|
readonly property string disconnectedState: "disconnected"
|
||||||
|
readonly property string waitingPairState: "waiting_pairing"
|
||||||
|
readonly property string pairedState: "paired"
|
||||||
|
|
||||||
|
state: root.notReadyState
|
||||||
|
|
||||||
|
property string optionalSdkPath: ""
|
||||||
|
|
||||||
|
// TODO: proper report
|
||||||
|
signal statusChanged(string message)
|
||||||
|
|
||||||
|
function pair(pairLink) {
|
||||||
|
d.requestSdk(
|
||||||
|
"wcResult = {error: null}; try { wc.pair(\"" + pairLink + "\").then((sessionProposal) => { wcResult = {state: \"" + root.waitingPairState + "\", error: null, sessionProposal: sessionProposal}; }).catch((error) => { wcResult = {error: error}; }); } catch (e) { wcResult = {error: \"Exception: \" + e.message}; }; wcResult"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function acceptPairing() {
|
||||||
|
d.acceptPair(d.sessionProposal)
|
||||||
|
}
|
||||||
|
|
||||||
|
function rejectPairing() {
|
||||||
|
d.rejectPair(d.sessionProposal.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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(`<font color="red">Failed loading SDK JS code; error: "${loadRequest.errorString}"</font>`)
|
||||||
|
break
|
||||||
|
case WebView.LoadStartedStatus:
|
||||||
|
// case WebEngineView.LoadStartedStatus:
|
||||||
|
root.statusChanged(`<font color="blue">Loading SDK JS code</font>`)
|
||||||
|
break
|
||||||
|
// case WebEngineView.LoadStoppedStatus:
|
||||||
|
// root.statusChanged(`<font color="pink">STOPPED loading SDK JS code</font>`)
|
||||||
|
// 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 = `<!DOCTYPE html><html><head><!--<title>TODO: Test</title>--><script type='text/javascript'>${scriptSrc}</script></head><body style='background-color: ${root.backgroundColor.toString()};'></body></html>`
|
||||||
|
|
||||||
|
console.debug(`@dd WalletConnectSDK.loadHtml; htmlSrc len: ${htmlSrc.length}`)
|
||||||
|
root.loadHtml(htmlSrc, "http://status.im")
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: timer
|
||||||
|
|
||||||
|
interval: 100
|
||||||
|
repeat: true
|
||||||
|
running: false
|
||||||
|
triggeredOnStart: true
|
||||||
|
|
||||||
|
property int errorCount: 0
|
||||||
|
|
||||||
|
onTriggered: {
|
||||||
|
root.runJavaScript(
|
||||||
|
"wcResult",
|
||||||
|
function(result) {
|
||||||
|
if (!result) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let done = false
|
||||||
|
if (result.error) {
|
||||||
|
done = true
|
||||||
|
if (root.state === root.notReadyState) {
|
||||||
|
root.statusChanged(`<font color="red">[${timer.errorCount++}] Failed SDK init; error: ${result.error}</font>`)
|
||||||
|
} else {
|
||||||
|
root.state = root.disconnectedState
|
||||||
|
root.statusChanged(`<font color="red">[${timer.errorCount++}] Operation error: ${result.error}</font>`)
|
||||||
|
}
|
||||||
|
} else if (result.state) {
|
||||||
|
switch (result.state) {
|
||||||
|
case root.disconnectedState: {
|
||||||
|
root.statusChanged(`<font color="green">Ready to pair or auth</font>`)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case root.waitingPairState: {
|
||||||
|
d.sessionProposal = result.sessionProposal
|
||||||
|
root.statusChanged("Pair ID: " + result.sessionProposal.id + "; Topic: " + result.sessionProposal.params.pairingTopic)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case root.pairedState: {
|
||||||
|
d.sessionType = result.sessionType
|
||||||
|
root.statusChanged(`<font color="blue">Paired: ${JSON.stringify(result.sessionType)}</font>`)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case root.disconnectedState: {
|
||||||
|
root.statusChanged(`<font color="orange">User rejected PairID ${d.sessionProposal.id}</font>`)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
root.statusChanged(`<font color="red">[${timer.errorCount++}] Unknown state: ${result.state}</font>`)
|
||||||
|
result.state = root.disconnectedState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
root.state = result.state
|
||||||
|
done = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (done) {
|
||||||
|
timer.stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: responseTimeoutTimer
|
||||||
|
|
||||||
|
interval: 10000
|
||||||
|
repeat: false
|
||||||
|
running: timer.running
|
||||||
|
|
||||||
|
onTriggered: {
|
||||||
|
timer.stop()
|
||||||
|
root.state = root.disconnectedState
|
||||||
|
root.statusChanged(`<font color="red">Timeout waiting for response. The pairing might have been already attempted for the URI.</font>`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: d
|
||||||
|
|
||||||
|
property var sessionProposal: null
|
||||||
|
property var sessionType: null
|
||||||
|
|
||||||
|
function isWaitingForSdk() {
|
||||||
|
return timer.running
|
||||||
|
}
|
||||||
|
|
||||||
|
function requestSdk(jsCode) {
|
||||||
|
console.debug(`@dd WalletConnectSDK.requestSdk; jsCode: ${jsCode}`)
|
||||||
|
root.runJavaScript(jsCode,
|
||||||
|
function(result) {
|
||||||
|
console.debug(`@dd WalletConnectSDK.requestSdk; result: ${JSON.stringify(result)}`)
|
||||||
|
timer.restart()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function init(projectId) {
|
||||||
|
d.requestSdk(
|
||||||
|
"wcResult = {error: null}; try { wc.init(\"" + projectId + "\").then((wc) => { wcResult = {state: \"" + root.disconnectedState + "\", error: null}; }).catch((error) => { wcResult = {error: error}; }); } catch (e) { wcResult = {error: \"Exception: \" + e.message}; }; wcResult"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function acceptPair(sessionProposal) {
|
||||||
|
d.requestSdk(
|
||||||
|
"wcResult = {error: null}; try { wc.approveSession(" + JSON.stringify(sessionProposal) + ").then((sessionType) => { wcResult = {state: \"" + root.pairedState + "\", error: null, sessionType: sessionType}; }).catch((error) => { wcResult = {error: error}; }); } catch (e) { wcResult = {error: \"Exception: \" + e.message}; }; wcResult"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function rejectPair(id) {
|
||||||
|
d.requestSdk(
|
||||||
|
"wcResult = {error: null}; try { wc.rejectSession(" + JSON.stringify(id) + ").then(() => { wcResult = {state: \"" + root.disconnectedState + "\", error: null}; }).catch((error) => { wcResult = {error: error}; }); } catch (e) { wcResult = {error: \"Exception: \" + e.message}; }; wcResult"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
WalletConnect 1.0 WalletConnect.qml
|
|
@ -0,0 +1,58 @@
|
||||||
|
# Wallet Connect Integration
|
||||||
|
|
||||||
|
## WalletConnect SDK management
|
||||||
|
|
||||||
|
Install dependencies steps by executing commands in this directory:
|
||||||
|
|
||||||
|
- update the [`package.json`](./package.json) versions and run `npm install`
|
||||||
|
- alternatively
|
||||||
|
- use the command `npm install <package-name>@<version/latest> --save` for individual packages
|
||||||
|
- or to update to the latest run `npm update` in here
|
||||||
|
- these commands will also create or update a `package-lock.json` file and populate the `node_modules` directory
|
||||||
|
- update the [`bundle.js`](./dist/main.js) file by running `npm run build`
|
||||||
|
- the result will be embedded with the app and loaded by [`WalletConnectSDK.qml`](../WalletConnectSDK.qml) component
|
||||||
|
- add the newly generated files to index `git add --update .` to include in the commit
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
Use the web demo test client https://react-app.walletconnect.com/ for wallet pairing and https://react-auth-dapp.walletconnect.com/ for authentication
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
- [ ] test namespaces implementation https://se-sdk-dapp.vercel.app/
|
||||||
|
|
||||||
|
## 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
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dev - to be removed
|
||||||
|
|
||||||
|
To test SDK loading add the following to `ui/app/mainui/AppMain.qml`
|
||||||
|
|
||||||
|
```qml
|
||||||
|
import AppLayouts.Wallet.views.walletconnect 1.0
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
StatusDialog {
|
||||||
|
id: wcHelperDialog
|
||||||
|
visible: true
|
||||||
|
|
||||||
|
WalletConnect {
|
||||||
|
SplitView.preferredWidth: 400
|
||||||
|
SplitView.preferredHeight: 600
|
||||||
|
|
||||||
|
projectId: "<Project ID>"
|
||||||
|
backgroundColor: wcHelperDialog.backgroundColor
|
||||||
|
}
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
}
|
||||||
|
```
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,23 @@
|
||||||
|
/*! *****************************************************************************
|
||||||
|
Copyright (c) Microsoft Corporation.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
***************************************************************************** */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [js-sha3]{@link https://github.com/emn178/js-sha3}
|
||||||
|
*
|
||||||
|
* @version 0.8.0
|
||||||
|
* @author Chen, Yi-Cyuan [emn178@gmail.com]
|
||||||
|
* @copyright Chen, Yi-Cyuan 2015-2018
|
||||||
|
* @license MIT
|
||||||
|
*/
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"name": "wallet_connect_integration",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Wallet Connect Integration for status-desktop",
|
||||||
|
"private": true,
|
||||||
|
"devDependencies": {
|
||||||
|
"webpack": "^5.88.2",
|
||||||
|
"webpack-cli": "^5.1.4",
|
||||||
|
"webpack-dev-server": "^4.15.1"
|
||||||
|
},
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"build": "webpack"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@walletconnect/auth-client": "^2.1.2",
|
||||||
|
"@walletconnect/web3wallet": "^1.9.2"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,197 @@
|
||||||
|
import { Core } from "@walletconnect/core";
|
||||||
|
import { Web3Wallet } from "@walletconnect/web3wallet";
|
||||||
|
|
||||||
|
import AuthClient from '@walletconnect/auth-client'
|
||||||
|
|
||||||
|
// import the builder util
|
||||||
|
import { buildApprovedNamespaces, getSdkError } from "@walletconnect/utils";
|
||||||
|
|
||||||
|
// "Export" API to window
|
||||||
|
// Workaround, tried using export via output.module: true in webpack.config.js, but it didn't work
|
||||||
|
window.wc = {
|
||||||
|
core: null,
|
||||||
|
web3wallet: null,
|
||||||
|
authClient: null,
|
||||||
|
|
||||||
|
init: function (projectId) {
|
||||||
|
return new Promise(async (resolve) => {
|
||||||
|
window.wc.core = new Core({
|
||||||
|
projectId: projectId,
|
||||||
|
});
|
||||||
|
|
||||||
|
window.wc.web3wallet = await Web3Wallet.init({
|
||||||
|
core: window.wc.core, // <- pass the shared `core` instance
|
||||||
|
metadata: {
|
||||||
|
// TODO: what values should be here?
|
||||||
|
name: "Prototype",
|
||||||
|
description: "Prototype Wallet/Peer",
|
||||||
|
url: "https://github.com/status-im/status-desktop",
|
||||||
|
icons: ['https://status.im/img/status-footer-logo.svg'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
window.wc.authClient = await AuthClient.init({
|
||||||
|
projectId: projectId,
|
||||||
|
metadata: window.wc.web3wallet.metadata,
|
||||||
|
})
|
||||||
|
|
||||||
|
resolve(window.wc)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
alreadyPaired: new Error("Already paired"),
|
||||||
|
waitingForApproval: new Error("Waiting for approval"),
|
||||||
|
// TODO: there is a corner case when attempting to pair with a link that is already paired or was rejected won't trigger any event back
|
||||||
|
pair: function (uri) {
|
||||||
|
let pairingTopic = getPairingTopicFromPairingUrl(uri);
|
||||||
|
|
||||||
|
let pairPromise = window.wc.web3wallet
|
||||||
|
.pair({ uri: uri })
|
||||||
|
.catch((error) => console.error(error));
|
||||||
|
|
||||||
|
const pairings = window.wc.core.pairing.getPairings();
|
||||||
|
// Find pairing by topic
|
||||||
|
const pairing = pairings.find((p) => p.topic === pairingTopic);
|
||||||
|
if (pairing) {
|
||||||
|
if (pairing.active) {
|
||||||
|
return new Promise((_, reject) => {
|
||||||
|
reject(window.wc.alreadyPaired);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
pairPromise
|
||||||
|
.then(() => {
|
||||||
|
window.wc.web3wallet.on("session_proposal", async (sessionProposal) => {
|
||||||
|
resolve(sessionProposal);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
registerForSessionRequest: function (callback) {
|
||||||
|
window.wc.web3wallet.on("session_request", callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
// TODO: ensure if session requests only one account we don't provide all accounts
|
||||||
|
approveSession: function (sessionProposal) {
|
||||||
|
const { id, params } = sessionProposal;
|
||||||
|
|
||||||
|
// ------- namespaces builder util ------------ //
|
||||||
|
const approvedNamespaces = buildApprovedNamespaces({
|
||||||
|
proposal: params,
|
||||||
|
// TODO: source this from wallet
|
||||||
|
supportedNamespaces: {
|
||||||
|
eip155: {
|
||||||
|
chains: ["eip155:1", "eip155:5"],
|
||||||
|
methods: ["eth_sendTransaction", "personal_sign"],
|
||||||
|
events: ["accountsChanged", "chainChanged"],
|
||||||
|
accounts: [
|
||||||
|
"eip155:1:0x0000000000000000000000000000000000000001",
|
||||||
|
"eip155:5:0xe74E17D586227691Cb7b64ed78b1b7B14828B034",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// ------- end namespaces builder util ------------ //
|
||||||
|
|
||||||
|
const session = window.wc.web3wallet.approveSession({
|
||||||
|
id,
|
||||||
|
namespaces: approvedNamespaces,
|
||||||
|
});
|
||||||
|
|
||||||
|
return session;
|
||||||
|
},
|
||||||
|
rejectSession: function (id) {
|
||||||
|
return window.wc.web3wallet.rejectSession({
|
||||||
|
id: id,
|
||||||
|
reason: getSdkError("USER_REJECTED"), // TODO USER_REJECTED_METHODS, USER_REJECTED_CHAINS, USER_REJECTED_EVENTS
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
auth: function (uri) {
|
||||||
|
let pairingTopic = getPairingTopicFromPairingUrl(uri);
|
||||||
|
|
||||||
|
let pairPromise = window.wc.authClient.core.pairing
|
||||||
|
.pair({ uri })
|
||||||
|
.catch((error) => console.error(error));
|
||||||
|
|
||||||
|
const pairings = window.wc.core.pairing.getPairings();
|
||||||
|
// Find pairing by topic
|
||||||
|
const pairing = pairings.find((p) => p.topic === pairingTopic);
|
||||||
|
if (pairing) {
|
||||||
|
if (pairing.active) {
|
||||||
|
return new Promise((_, reject) => {
|
||||||
|
reject(window.wc.alreadyPaired);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
pairPromise
|
||||||
|
.then(() => {
|
||||||
|
// TODO: check if we can separate using the URI info
|
||||||
|
window.wc.authClient.on("auth_request", async (authProposal) => {
|
||||||
|
resolve(authProposal);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
approveAuth: function (authProposal) {
|
||||||
|
const { id, params } = authProposal;
|
||||||
|
|
||||||
|
// TODO: source user’s address
|
||||||
|
const iss = `did:pkh:eip155:1:${"0x0123456789"}`;
|
||||||
|
|
||||||
|
// format the cacao payload with the user’s address
|
||||||
|
const message = window.wc.authClient.formatMessage(params.cacaoPayload, iss);
|
||||||
|
|
||||||
|
// TODO: signature
|
||||||
|
const signature = "0x123456789"
|
||||||
|
|
||||||
|
return window.wc.authClient.respond(
|
||||||
|
{
|
||||||
|
id: id,
|
||||||
|
signature: {
|
||||||
|
s: signature,
|
||||||
|
t: "eip191",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
iss
|
||||||
|
);
|
||||||
|
},
|
||||||
|
rejectAuth: function (id) {
|
||||||
|
return window.wc.authClient.reject(id);
|
||||||
|
},
|
||||||
|
|
||||||
|
respondSessionRequest: function (topic, response) {
|
||||||
|
window.wc.web3wallet.respondSessionRequest({ topic, response });
|
||||||
|
},
|
||||||
|
|
||||||
|
disconnectAll: function () {
|
||||||
|
const pairings = window.wc.core.pairing.getPairings();
|
||||||
|
pairings.forEach((p) => {
|
||||||
|
window.wc.core.pairing.disconnect({ topic: p.topic });
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns null if not a pairing url
|
||||||
|
function getPairingTopicFromPairingUrl(url) {
|
||||||
|
if (!url.startsWith("wc:")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const atIndex = url.indexOf("@");
|
||||||
|
if (atIndex < 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return url.slice(3, atIndex);
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry: './src/index.js',
|
||||||
|
output: {
|
||||||
|
filename: 'bundle.js',
|
||||||
|
path: path.resolve(__dirname, 'generated'),
|
||||||
|
module: true,
|
||||||
|
//clean: true,
|
||||||
|
},
|
||||||
|
devServer: {
|
||||||
|
static: path.join(__dirname, "."),
|
||||||
|
compress: true,
|
||||||
|
port: 9000,
|
||||||
|
client: {
|
||||||
|
overlay: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
experiments: {
|
||||||
|
outputModule: true,
|
||||||
|
},
|
||||||
|
};
|
|
@ -53,7 +53,7 @@ func main() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if info.IsDir() && (info.Name() == "vendor" || info.Name() == "tests" || info.Name() == "StatusQ") {
|
if info.IsDir() && (info.Name() == "vendor" || info.Name() == "tests" || info.Name() == "StatusQ" || info.Name() == "node_modules") {
|
||||||
return filepath.SkipDir
|
return filepath.SkipDir
|
||||||
}
|
}
|
||||||
if !info.IsDir() {
|
if !info.IsDir() {
|
||||||
|
|
|
@ -11,7 +11,7 @@ endif()
|
||||||
|
|
||||||
# Macro for merging common code between static and shared
|
# Macro for merging common code between static and shared
|
||||||
macro(add_target name type)
|
macro(add_target name type)
|
||||||
find_package(Qt5 COMPONENTS Core Qml Gui Quick QuickControls2 Widgets Network Multimedia REQUIRED)
|
find_package(Qt5 COMPONENTS Core Qml Gui Quick QuickControls2 Widgets Network Multimedia WebView REQUIRED)
|
||||||
|
|
||||||
file(GLOB HEADERS include/DOtherSide/*.h include/DOtherSide/Status/*.h)
|
file(GLOB HEADERS include/DOtherSide/*.h include/DOtherSide/Status/*.h)
|
||||||
file(GLOB SOURCES src/*.cpp src/Status/*.cpp)
|
file(GLOB SOURCES src/*.cpp src/Status/*.cpp)
|
||||||
|
@ -35,7 +35,7 @@ macro(add_target name type)
|
||||||
|
|
||||||
target_include_directories(${name} PUBLIC include include/Qt)
|
target_include_directories(${name} PUBLIC include include/Qt)
|
||||||
|
|
||||||
target_link_libraries(${name} PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Qml Qt5::Quick Qt5::Network Qt5::Multimedia)
|
target_link_libraries(${name} PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Qml Qt5::Quick Qt5::Network Qt5::Multimedia Qt5::WebView)
|
||||||
|
|
||||||
target_compile_definitions(${name} PRIVATE $<$<CONFIG:Debug>:QT_QML_DEBUG>)
|
target_compile_definitions(${name} PRIVATE $<$<CONFIG:Debug>:QT_QML_DEBUG>)
|
||||||
if(DEFINED QML_DEBUG_PORT)
|
if(DEFINED QML_DEBUG_PORT)
|
||||||
|
@ -58,7 +58,7 @@ macro(add_target name type)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# for DOtherSide.pc
|
# for DOtherSide.pc
|
||||||
set(PC_REQUIRES "Qt5Core, Qt5Gui, Qt5Widgets, Qt5Qml, Qt5Quick, Qt5Network, Qt5DBus, Qt5Multimedia")
|
set(PC_REQUIRES "Qt5Core, Qt5Gui, Qt5Widgets, Qt5Qml, Qt5Quick, Qt5Network, Qt5DBus, Qt5Multimedia, Qt5WebView")
|
||||||
if (${Qt5QuickControls2_FOUND})
|
if (${Qt5QuickControls2_FOUND})
|
||||||
target_link_libraries(${name} PRIVATE Qt5::QuickControls2)
|
target_link_libraries(${name} PRIVATE Qt5::QuickControls2)
|
||||||
set(PC_REQUIRES "${PC_REQUIRES}, Qt5QuickControls2")
|
set(PC_REQUIRES "${PC_REQUIRES}, Qt5QuickControls2")
|
||||||
|
|
|
@ -60,6 +60,8 @@ DOS_API void DOS_CALL dos_qguiapplication_enable_hdpi(const char *uiScaleFilePat
|
||||||
|
|
||||||
DOS_API void DOS_CALL dos_qguiapplication_initialize_opengl(void);
|
DOS_API void DOS_CALL dos_qguiapplication_initialize_opengl(void);
|
||||||
|
|
||||||
|
DOS_API void DOS_CALL dos_qtwebview_initialize(void);
|
||||||
|
|
||||||
DOS_API void DOS_CALL dos_qguiapplication_try_enable_threaded_renderer();
|
DOS_API void DOS_CALL dos_qguiapplication_try_enable_threaded_renderer();
|
||||||
|
|
||||||
/// \brief Create a QGuiApplication
|
/// \brief Create a QGuiApplication
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
lib_version = '0.6.3'
|
lib_version = '0.6.3'
|
||||||
lib_dependencies = dependency('qt5', modules : ['Core', 'Gui', 'Widgets', 'Quick', 'Qml'])
|
lib_dependencies = dependency('qt5', modules : ['Core', 'Gui', 'Widgets', 'Quick', 'Qml', 'WebView'])
|
||||||
lib_sources = [
|
lib_sources = [
|
||||||
'src/DOtherSide.cpp',
|
'src/DOtherSide.cpp',
|
||||||
'src/DOtherSideTypesCpp.cpp',
|
'src/DOtherSideTypesCpp.cpp',
|
||||||
|
|
|
@ -56,6 +56,7 @@
|
||||||
#ifdef QT_QUICKCONTROLS2_LIB
|
#ifdef QT_QUICKCONTROLS2_LIB
|
||||||
#include <QtQuickControls2/QQuickStyle>
|
#include <QtQuickControls2/QQuickStyle>
|
||||||
#endif
|
#endif
|
||||||
|
#include <QtWebView>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
@ -175,6 +176,11 @@ void dos_qguiapplication_initialize_opengl()
|
||||||
QGuiApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
|
QGuiApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dos_qtwebview_initialize()
|
||||||
|
{
|
||||||
|
QtWebView::initialize();
|
||||||
|
}
|
||||||
|
|
||||||
void dos_qguiapplication_try_enable_threaded_renderer()
|
void dos_qguiapplication_try_enable_threaded_renderer()
|
||||||
{
|
{
|
||||||
if(QSysInfo::kernelType() == "darwin" && QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
if(QSysInfo::kernelType() == "darwin" && QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||||
|
|
|
@ -22,7 +22,10 @@ continueUserActivity:(NSUserActivity *)userActivity
|
||||||
if (!url)
|
if (!url)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
QUrl deeplink = QUrl::fromNSURL(url);
|
QUrl deeplink = QUrl::fromNSURL(url);
|
||||||
// set it to nim
|
// TODO #12434: Check if WalletConnect link and redirect the workflow
|
||||||
|
qDebug() << "@dd deeplink " << deeplink;
|
||||||
|
|
||||||
|
// TODO #12245: set it to nim
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 90f19ac030c28bbb4f89bf014afba344ca2941cb
|
Subproject commit df74bf1e65d0c185740f17cb31e6e59d4bf8ad4a
|
Loading…
Reference in New Issue