├── vendor ├── QtQmlModels │ ├── .gitignore │ ├── QtQmlModels.pro │ ├── QtQmlModels.pri │ ├── .gitlab-ci.yml │ ├── LICENSE.md │ ├── README.md │ ├── QtQmlTricksPlugin_SmartDataModels.h │ ├── QtQmlModels.qbs │ ├── QQmlVariantListModel.h │ ├── QQmlObjectListModel.cpp │ ├── QQmlVariantListModel.cpp │ └── QQmlObjectListModel.h └── QuickPromise │ ├── QuickPromise │ ├── qmldir │ ├── Q.qml │ ├── combinator.js │ ├── Promise.qml │ └── promise.js │ ├── tests │ ├── unittests │ │ ├── TestSuite.qml │ │ ├── tst_timer.qml │ │ ├── tst_promise_rejectwhen_signal.qml │ │ ├── tst_promise_resolvewhen_signal.qml │ │ ├── unittests.pro │ │ ├── tst_promise_resolvewhen_all_signal.qml │ │ ├── tst_promisejs_resolve_qt_binding.qml │ │ ├── main.cpp │ │ ├── tst_promisejs_all_signal.qml │ │ ├── tst_promise_resolvewhen_all_promise.qml │ │ ├── tst_promise_resolvewhen_promise.qml │ │ ├── tst_promisejs_all_promise_item.qml │ │ ├── tst_promise_resolve_signal.qml │ │ └── tst_aplus_spec.qml │ └── promises-aplus-tests │ │ ├── run.js │ │ ├── package.json │ │ └── adapter.js │ ├── quickpromise.pri │ ├── quickpromise.qrc │ ├── .gitignore │ ├── qptimer.h │ ├── qpm.json │ ├── .travis.yml │ ├── qptimer.cpp │ ├── README.md │ └── LICENSE ├── qml.qrc ├── AccountKeys.cpp ├── KeyDelegate.qml ├── TransactionFoundry.hpp ├── qbsmodules └── modules │ └── steem │ └── steem.qbs ├── .gitignore ├── Promise.hpp ├── QmlJsonRpcProvider.hpp ├── LICENSE ├── AccountKeys.hpp ├── Pressure.qbs ├── AccountDelegate.qml ├── ShadowedPopup.qml ├── main.cpp ├── FadeOnVisible.qml ├── KeyStore.hpp ├── EditKeysPage.qml ├── AddAccountPopup.qml ├── Promise.cpp ├── README.md ├── EditKeysForm.ui.qml ├── QmlJsonRpcProvider.cpp ├── TransactionFoundry.cpp ├── MyKeysForm.ui.qml ├── KeyPair.hpp ├── main.qml ├── res └── steem.svg ├── KeyStore.cpp ├── KeyPair.cpp └── MyKeysPage.qml /vendor/QtQmlModels/.gitignore: -------------------------------------------------------------------------------- 1 | *.user* 2 | -------------------------------------------------------------------------------- /vendor/QuickPromise/QuickPromise/qmldir: -------------------------------------------------------------------------------- 1 | module QuickPromise 2 | singleton Q 1.0 Q.qml 3 | Promise 1.0 Promise.qml 4 | 5 | -------------------------------------------------------------------------------- /vendor/QtQmlModels/QtQmlModels.pro: -------------------------------------------------------------------------------- 1 | 2 | TARGET = QtQmlModels 3 | 4 | TEMPLATE = lib 5 | 6 | CONFIG += static 7 | 8 | include ($$PWD/QtQmlModels.pri) 9 | -------------------------------------------------------------------------------- /vendor/QuickPromise/tests/unittests/TestSuite.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtTest 1.0 3 | import QuickPromise 1.0 4 | 5 | TestCase { 6 | 7 | function tick() { 8 | wait(0); 9 | wait(0); 10 | wait(0); 11 | } 12 | 13 | 14 | } 15 | -------------------------------------------------------------------------------- /vendor/QuickPromise/tests/promises-aplus-tests/run.js: -------------------------------------------------------------------------------- 1 | var promisesAplusTests = require("promises-aplus-tests"); 2 | var adapter = require("./adapter.js"); 3 | 4 | promisesAplusTests(adapter, function (err) { 5 | // All done; output is in the console. Or check `err` for number of failures. 6 | }); -------------------------------------------------------------------------------- /vendor/QtQmlModels/QtQmlModels.pri: -------------------------------------------------------------------------------- 1 | 2 | # Qt QML Models 3 | 4 | QT += core qml 5 | 6 | INCLUDEPATH += $$PWD 7 | 8 | HEADERS += \ 9 | $$PWD/QQmlObjectListModel.h \ 10 | $$PWD/QQmlVariantListModel.h 11 | 12 | SOURCES += \ 13 | $$PWD/QQmlObjectListModel.cpp \ 14 | $$PWD/QQmlVariantListModel.cpp 15 | 16 | -------------------------------------------------------------------------------- /vendor/QuickPromise/quickpromise.pri: -------------------------------------------------------------------------------- 1 | QT += qml 2 | 3 | QML_IMPORT_PATH += $$PWD 4 | 5 | INCLUDEPATH += $$PWD 6 | 7 | RESOURCES += \ 8 | $$PWD/quickpromise.qrc 9 | 10 | HEADERS += \ 11 | $$PWD/qptimer.h 12 | 13 | SOURCES += \ 14 | $$PWD/qptimer.cpp 15 | 16 | DISTFILES += \ 17 | $$PWD/README.md 18 | -------------------------------------------------------------------------------- /vendor/QuickPromise/quickpromise.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | QuickPromise/qmldir 4 | QuickPromise/Promise.qml 5 | QuickPromise/promise.js 6 | QuickPromise/Q.qml 7 | QuickPromise/combinator.js 8 | 9 | 10 | -------------------------------------------------------------------------------- /vendor/QtQmlModels/.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | { 2 | "before_script": [], 3 | "stages": [ 4 | "build", 5 | ], 6 | "build_on_linux_gcc_qt5.2": { 7 | "stage": "build", 8 | "script": [ 9 | "qbs build release" 10 | ], 11 | "tags": [ 12 | "linux", 13 | "gcc", 14 | "qt5.2", 15 | "qbs", 16 | ] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /vendor/QuickPromise/tests/unittests/tst_timer.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtTest 1.0 3 | import QuickPromise 1.0 4 | 5 | TestSuite { 6 | name : "Timer" 7 | 8 | function test_exception() { 9 | // It should print exception. 10 | Q.setTimeout(function() { 11 | global = 1; 12 | },0); 13 | wait(10); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /vendor/QuickPromise/tests/promises-aplus-tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aplus-tests", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "promises-aplus-tests": "^2.1.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /vendor/QuickPromise/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | 30 | *.pro.* 31 | build-* 32 | node_modules 33 | -------------------------------------------------------------------------------- /qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | EditKeysForm.ui.qml 5 | EditKeysPage.qml 6 | MyKeysPage.qml 7 | MyKeysForm.ui.qml 8 | FadeOnVisible.qml 9 | AddAccountPopup.qml 10 | ShadowedPopup.qml 11 | res/steem.svg 12 | KeyDelegate.qml 13 | AccountDelegate.qml 14 | 15 | 16 | -------------------------------------------------------------------------------- /vendor/QuickPromise/qptimer.h: -------------------------------------------------------------------------------- 1 | #ifndef QPTIMER_H 2 | #define QPTIMER_H 3 | 4 | #include 5 | #include 6 | 7 | /// QuickPromise's timer utility 8 | 9 | class QPTimer : public QObject 10 | { 11 | Q_OBJECT 12 | public: 13 | explicit QPTimer(QObject *parent = 0); 14 | ~QPTimer(); 15 | 16 | /// Implement the setTimeout function by C++. 17 | Q_INVOKABLE void setTimeout(QJSValue func,int interval); 18 | 19 | private: 20 | 21 | Q_INVOKABLE void onTriggered(); 22 | 23 | }; 24 | 25 | #endif // QPTIMER_H 26 | -------------------------------------------------------------------------------- /vendor/QuickPromise/qpm.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.github.benlau.quickpromise", 3 | "description": "Promise library for QML", 4 | "author": { 5 | "name": "Ben Lau", 6 | "email": "xbenlau@gmail.com" 7 | }, 8 | "repository": { 9 | "type": "GITHUB", 10 | "url": "https://github.com/benlau/quickpromise.git" 11 | }, 12 | "version": { 13 | "label": "1.0.3", 14 | "revision": "1", 15 | "fingerprint": "" 16 | }, 17 | "dependencies": [ 18 | ], 19 | "license": "APACHE_2_0", 20 | "pri_filename": "quickpromise.pri", 21 | "webpage": "https://github.com/benlau/quickpromise" 22 | } 23 | -------------------------------------------------------------------------------- /AccountKeys.cpp: -------------------------------------------------------------------------------- 1 | #include "AccountKeys.hpp" 2 | 3 | AccountKeys::AccountKeys(QObject *parent) : QObject(parent) { 4 | connect(this, &AccountKeys::nameChanged, this, &AccountKeys::updated); 5 | connect(m_ownerKey, &KeyPair::updated, this, &AccountKeys::updated); 6 | connect(m_activeKey, &KeyPair::updated, this, &AccountKeys::updated); 7 | connect(m_postingKey, &KeyPair::updated, this, &AccountKeys::updated); 8 | connect(m_memoKey, &KeyPair::updated, this, &AccountKeys::updated); 9 | } 10 | 11 | void AccountKeys::setName(QString name) 12 | { 13 | if (m_name == name) 14 | return; 15 | 16 | m_name = name; 17 | emit nameChanged(name); 18 | } 19 | -------------------------------------------------------------------------------- /KeyDelegate.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | import QtQuick.Controls 2.0 3 | import QtQuick.Layouts 1.1 4 | import Qt.WebSockets 1.0 5 | import com.nathanhourt.steem.crypto 1.0 6 | import com.nathanhourt.rpc 1.0 7 | 8 | RowLayout { 9 | property string keyName 10 | property KeyPair key 11 | 12 | property alias editButton: editButton 13 | 14 | Label { 15 | text: keyName + ":" 16 | } 17 | Label { 18 | text: key.keyType !== KeyPair.NullKey? key.publicKey : "Unset" 19 | horizontalAlignment: Label.AlignRight 20 | elide: Label.ElideMiddle 21 | Layout.fillWidth: true 22 | } 23 | Button { 24 | id: editButton 25 | text: qsTr("Edit") 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /vendor/QuickPromise/.travis.yml: -------------------------------------------------------------------------------- 1 | language : cpp 2 | env: 3 | - DISPLAY=:99.0 4 | compiler: 5 | - gcc 6 | before_install: 7 | - sudo add-apt-repository --yes ppa:beineri/opt-qt532 8 | - sudo add-apt-repository --yes ppa:chris-lea/node.js 9 | - sudo apt-get update -qq 10 | - apt-cache search qt5 11 | - sudo apt-get install -qq qt53quickcontrols qt53declarative qt53script qt53graphicaleffects 12 | - sudo apt-get install nodejs 13 | - sh -e /etc/init.d/xvfb start 14 | script: 15 | - source /opt/qt53/bin/qt53-env.sh 16 | - cd tests/unittests 17 | - qmake 18 | - make 19 | - ls 20 | - ./unittests 21 | - cd ../promises-aplus-tests/ 22 | - npm install . 23 | - node ./run.js 24 | 25 | -------------------------------------------------------------------------------- /vendor/QuickPromise/QuickPromise/Q.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import "promise.js" as PromiseJS 3 | import "combinator.js" as Combinator 4 | import QuickPromise 1.0 5 | pragma Singleton 6 | 7 | QtObject { 8 | id : component 9 | 10 | function setTimeout(callback,interval) { 11 | QPTimer.setTimeout(callback,interval); 12 | } 13 | 14 | function promise() { 15 | return PromiseJS.promise(); 16 | } 17 | 18 | function all(promises) { 19 | return Combinator.all(promises); 20 | } 21 | 22 | function allSettled(promises) { 23 | return Combinator.allSettled(promises) 24 | } 25 | 26 | function instanceOfPromise(promise) { 27 | return PromiseJS.instanceOfPromise(promise); 28 | } 29 | 30 | } 31 | 32 | -------------------------------------------------------------------------------- /vendor/QuickPromise/tests/unittests/tst_promise_rejectwhen_signal.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtTest 1.0 3 | import QuickPromise 1.0 4 | 5 | TestCase { 6 | name : "Promise_RejectWhen_Signal" 7 | 8 | Timer { 9 | id: timer; 10 | repeat: true 11 | interval : 50 12 | } 13 | 14 | Promise { 15 | id : promise 16 | rejectWhen: timer.onTriggered 17 | } 18 | 19 | function test_rejectwhen_signal() { 20 | compare(promise.isRejected,false); 21 | wait(10); 22 | compare(promise.isRejected,false); 23 | timer.start(); 24 | wait(10); 25 | compare(promise.isRejected,false); 26 | wait(50); 27 | compare(promise.isRejected,true); 28 | 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /vendor/QuickPromise/tests/unittests/tst_promise_resolvewhen_signal.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtTest 1.0 3 | import QuickPromise 1.0 4 | 5 | TestCase { 6 | name : "Promise_ResolveWhen_Signal" 7 | 8 | Timer { 9 | id: timer; 10 | repeat: true 11 | interval : 50 12 | } 13 | 14 | Promise { 15 | id : promise 16 | resolveWhen: timer.onTriggered 17 | } 18 | 19 | function test_resolvewhen_signal() { 20 | compare(promise.isFulfilled,false); 21 | wait(10); 22 | compare(promise.isFulfilled,false); 23 | timer.start(); 24 | wait(10); 25 | compare(promise.isFulfilled,false); 26 | wait(50); 27 | compare(promise.isFulfilled,true); 28 | 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /vendor/QtQmlModels/LICENSE.md: -------------------------------------------------------------------------------- 1 | How can I use this ? 2 | 3 | Well, license here is pretty "super-open", not even copy-left or copy-right, 4 | basically its close to the WTFPL, just use it as you want, 5 | as it's the most practical to you : 6 | 7 | * if you want to use it as GIT submodule and compile it in your app, do it ; 8 | * if you prefer separate project as a shared library, do it ; 9 | * if you need to modify the project to be able to integrate in you app (opensource or not), do it ; 10 | * if you want to share you work on the library, thanks a lot, but if you don't, no problem ; 11 | * if you think about some weird case I didn't talk about, well, do whatever you want, I don't need to know about it ; 12 | * if you like it so much you want to spread the word, thank you, you're welcome. 13 | 14 | Enjoy ! -------------------------------------------------------------------------------- /TransactionFoundry.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TRANSACTIONFOUNDRY_HPP 2 | #define TRANSACTIONFOUNDRY_HPP 3 | 4 | #include "KeyStore.hpp" 5 | 6 | #include 7 | 8 | class TransactionFoundry : public QObject { 9 | Q_OBJECT 10 | Q_PROPERTY(KeyStore* keyStore READ keyStore WRITE setKeyStore NOTIFY keyStoreChanged) 11 | 12 | KeyStore* m_keyStore; 13 | 14 | public: 15 | explicit TransactionFoundry(QObject *parent = 0); 16 | 17 | Q_INVOKABLE QVariantMap keyUpdateTransaction(QString accountName, QString authorityLevel, KeyPair* newKey, QString referenceBlockId); 18 | 19 | KeyStore* keyStore() const { return m_keyStore; } 20 | 21 | public slots: 22 | void setKeyStore(KeyStore* keyStore); 23 | 24 | signals: 25 | void keyStoreChanged(KeyStore* keyStore); 26 | }; 27 | 28 | #endif // TRANSACTIONFOUNDRY_HPP 29 | -------------------------------------------------------------------------------- /vendor/QtQmlModels/README.md: -------------------------------------------------------------------------------- 1 | Qt QML Models 2 | ============= 3 | 4 | Additional data models aimed to bring more power to QML applications by using useful C++ models in back-end. 5 | 6 | * `QQmlObjectListModel` : a much nicer way to expose C++ list to QML than the quick & dirty `QList` property . Supports all the strong model features of `QAbstractListModel` while showing the simple and well know API of QList. 7 | 8 | * `QQmlVariantListModel` : a dead-simple way to create a dynamic C++ list of any type and expose it to QML, way better than using a `QVariantList` property. 9 | 10 | > NOTE : If you want to donate, use this link : [![Flattr this](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=thebootroo&url=http://gitlab.unique-conception.org/qt-qml-tricks/qt-qml-models) 11 | -------------------------------------------------------------------------------- /vendor/QtQmlModels/QtQmlTricksPlugin_SmartDataModels.h: -------------------------------------------------------------------------------- 1 | #ifndef QTQMLTRICKSPLUGIN_SMARTDATAMODELS_H 2 | #define QTQMLTRICKSPLUGIN_SMARTDATAMODELS_H 3 | 4 | #include 5 | #include 6 | 7 | #include "QQmlObjectListModel.h" 8 | #include "QQmlVariantListModel.h" 9 | 10 | static void registerQtQmlTricksSmartDataModel (QQmlEngine * engine) { 11 | Q_UNUSED (engine) 12 | 13 | const char * uri = "QtQmlTricks.SmartDataModels"; // @uri QtQmlTricks.SmartDataModels 14 | const int maj = 2; 15 | const int min = 0; 16 | const char * msg = "!!!"; 17 | 18 | qmlRegisterUncreatableType (uri, maj, min, "ObjectListModel", msg); 19 | qmlRegisterUncreatableType (uri, maj, min, "VariantListModel", msg); 20 | } 21 | 22 | #endif // QTQMLTRICKSPLUGIN_SMARTDATAMODELS_H 23 | -------------------------------------------------------------------------------- /vendor/QuickPromise/tests/unittests/unittests.pro: -------------------------------------------------------------------------------- 1 | QT += core 2 | QT += testlib gui quick qml qmltest 3 | 4 | TARGET = unittests 5 | CONFIG += console 6 | CONFIG -= app_bundle 7 | 8 | TEMPLATE = app 9 | 10 | SOURCES += main.cpp 11 | 12 | include(../../quickpromise.pri) 13 | 14 | DEFINES += SRCDIR=\\\"$$PWD/\\\" BASEDIR=\\\"$$PWD/..\\\" 15 | 16 | DISTFILES += \ 17 | tst_promise_resolve_signal.qml \ 18 | tst_promise_resolvewhen_signal.qml \ 19 | tst_promise_rejectwhen_signal.qml \ 20 | tst_aplus_spec.qml \ 21 | tst_promisejs_all_signal.qml \ 22 | tst_promisejs_all_promise_item.qml \ 23 | tst_promise_resolvewhen_promise.qml \ 24 | TestSuite.qml \ 25 | tst_promise_resolvewhen_all_signal.qml \ 26 | tst_promise_resolvewhen_all_promise.qml \ 27 | tst_promisejs_resolve_qt_binding.qml \ 28 | tst_timer.qml 29 | -------------------------------------------------------------------------------- /vendor/QuickPromise/tests/unittests/tst_promise_resolvewhen_all_signal.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtTest 1.0 3 | import QuickPromise 1.0 4 | 5 | TestSuite { 6 | name : "Promise_ResolveWhen_Signal_All" 7 | 8 | Promise { 9 | id : promise 10 | signal triggered1 11 | signal triggered2 12 | resolveWhen: Q.all([promise.triggered1,promise.triggered2]) 13 | } 14 | 15 | function test_resolve() { 16 | compare(promise.isSettled,false); 17 | tick(); 18 | compare(promise.isSettled,false); 19 | 20 | promise.triggered1(); 21 | compare(promise.isSettled,false); 22 | tick(); 23 | compare(promise.isSettled,false); 24 | 25 | promise.triggered2(); 26 | compare(promise.isSettled,false); 27 | 28 | tick(); 29 | compare(promise.isSettled,true); 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /qbsmodules/modules/steem/steem.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | import qbs.File 3 | import qbs.Environment 4 | 5 | Module { 6 | Depends { name: "cpp" } 7 | property string steemPath: Environment.getEnv("STEEM_PATH") 8 | property bool found: File.exists(steemPath+"/lib/libsteem_app.a") 9 | cpp.includePaths: steemPath+"/include" 10 | cpp.libraryPaths: [steemPath+"/lib", steemPath+"/lib/cryptonomex"] 11 | cpp.staticLibraries: [ 12 | "boost_system", 13 | "boost_filesystem", 14 | "boost_program_options", 15 | "boost_chrono", 16 | "boost_date_time", 17 | "boost_coroutine", 18 | qbs.hostOS.contains("osx")? "boost_context-mt" : "boost_context", 19 | qbs.hostOS.contains("osx")? "boost_thread-mt" : "boost_thread", 20 | "ssl", 21 | "crypto", 22 | "fc", 23 | "steemit_chain", 24 | "secp256k1", 25 | "z" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /vendor/QuickPromise/tests/promises-aplus-tests/adapter.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | var fileData = fs.readFileSync('../../QuickPromise/promise.js', 4 | 'utf8'); 5 | 6 | var res = fileData.replace(/.pragma library/i,"") 7 | .replace(/.import.*/i,"") 8 | .replace(/QP.QPTimer./g,""); 9 | 10 | eval(res); 11 | 12 | var adapter = { 13 | deferred: function() { 14 | var p = promise(); 15 | return { 16 | promise: p, 17 | resolve: function(x) {p.resolve(x)}, 18 | reject: function(x) {p.reject(x)} 19 | } 20 | }, 21 | rejected: function(reason) { 22 | var p = promise(); 23 | p.reject(reason); 24 | return p; 25 | }, 26 | resolved: function(value) { 27 | var p = promise(); 28 | p.resolve(value); 29 | return p; 30 | } 31 | } 32 | 33 | module.exports = adapter; 34 | -------------------------------------------------------------------------------- /vendor/QuickPromise/tests/unittests/tst_promisejs_resolve_qt_binding.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtTest 1.0 3 | import QuickPromise 1.0 4 | 5 | TestSuite { 6 | name : "PromiseJS_Resolve_Qt_Binding" 7 | 8 | Item { 9 | id: item 10 | property int value1 : 0 11 | property int value2 : 0 12 | } 13 | 14 | function test_resolve_qt_binding() { 15 | var binding = Qt.binding(function() { 16 | return item.value1; 17 | }); 18 | var exceptionOccurred = false; 19 | 20 | item.value2 = binding; 21 | item.value1 = 13; 22 | compare(item.value2,13); 23 | 24 | item.value1 = 0; 25 | 26 | var promise = Q.promise(); 27 | 28 | // Don't pass the result of Qt.binding() to resolve(). 29 | // It will throw exception. 30 | try { 31 | promise.resolve(binding); 32 | } catch (e) { 33 | exceptionOccurred = true; 34 | } 35 | 36 | compare(exceptionOccurred,true); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # This file is used to ignore files which are generated 2 | # ---------------------------------------------------------------------------- 3 | 4 | *~ 5 | *.autosave 6 | *.a 7 | *.core 8 | *.moc 9 | *.o 10 | *.obj 11 | *.orig 12 | *.rej 13 | *.so 14 | *.so.* 15 | *_pch.h.cpp 16 | *_resource.rc 17 | *.qm 18 | .#* 19 | *.*# 20 | core 21 | !core/ 22 | tags 23 | .DS_Store 24 | .directory 25 | *.debug 26 | Makefile* 27 | *.prl 28 | *.app 29 | moc_*.cpp 30 | ui_*.h 31 | qrc_*.cpp 32 | Thumbs.db 33 | *.res 34 | *.rc 35 | /.qmake.cache 36 | /.qmake.stash 37 | 38 | # qtcreator generated files 39 | *.pro.user* 40 | *.qbs.user* 41 | 42 | # xemacs temporary files 43 | *.flc 44 | 45 | # Vim temporary files 46 | .*.swp 47 | 48 | # Visual Studio generated files 49 | *.ib_pdb_index 50 | *.idb 51 | *.ilk 52 | *.pdb 53 | *.sln 54 | *.suo 55 | *.vcproj 56 | *vcproj.*.*.user 57 | *.ncb 58 | *.sdf 59 | *.opensdf 60 | *.vcxproj 61 | *vcxproj.* 62 | 63 | # MinGW generated files 64 | *.Debug 65 | *.Release 66 | 67 | # Python byte code 68 | *.pyc 69 | 70 | # Binaries 71 | # -------- 72 | *.dll 73 | *.exe 74 | build 75 | -------------------------------------------------------------------------------- /Promise.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PROMISE_HPP 2 | #define PROMISE_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class QmlPromise : public QObject 9 | { 10 | Q_OBJECT 11 | Q_PROPERTY(bool isFulfilled READ isFulfilled NOTIFY settled) 12 | Q_PROPERTY(bool isRejected READ isRejected NOTIFY settled) 13 | Q_PROPERTY(bool isSettled READ isSettled NOTIFY settled) 14 | QObject* internalPromise; 15 | bool wasFulfilled = false; 16 | bool wasRejected = false; 17 | 18 | public: 19 | QmlPromise(QObject* parent); 20 | virtual ~QmlPromise(); 21 | 22 | bool isFulfilled(); 23 | bool isRejected(); 24 | bool isSettled(); 25 | /// Returns true if the javascript promise was already destroyed; false otherwise 26 | bool wasForgotten() { return internalPromise; } 27 | 28 | operator QJSValue(); 29 | 30 | public slots: 31 | void resolve(QVariant args = QVariant()); 32 | void reject(QVariant reason = QVariant()); 33 | 34 | signals: 35 | void fulfilled(QVariant); 36 | void rejected(QVariant); 37 | void settled(); 38 | }; 39 | 40 | #endif // PROMISE_HPP 41 | -------------------------------------------------------------------------------- /QmlJsonRpcProvider.hpp: -------------------------------------------------------------------------------- 1 | #ifndef JSONRPCPROVIDER_HPP 2 | #define JSONRPCPROVIDER_HPP 3 | 4 | #include "Promise.hpp" 5 | 6 | #include 7 | 8 | #include 9 | 10 | class QmlJsonRpcProvider : public QObject { 11 | Q_OBJECT 12 | Q_PROPERTY(QObject* socket READ socket WRITE setSocket NOTIFY socketChanged) 13 | 14 | int64_t nextQueryId = 0; 15 | std::map> pendingRequests; 16 | 17 | QObject* m_socket; 18 | 19 | public: 20 | QmlJsonRpcProvider(QObject* parent = nullptr); 21 | 22 | Q_INVOKABLE QJSValue call(QString method, QVariantList params); 23 | 24 | QObject* socket() const { return m_socket; } 25 | 26 | public slots: 27 | /// Set the socket to communicate RPC over. Technically, this can be any object with a textMessageReceived signal 28 | /// and a sendTextMessage slot, each having a single QString argument. 29 | void setSocket(QObject* socket); 30 | 31 | protected slots: 32 | void messageReceived(QString message); 33 | 34 | signals: 35 | void socketChanged(QObject* socket); 36 | }; 37 | 38 | #endif // JSONRPCPROVIDER_HPP 39 | -------------------------------------------------------------------------------- /vendor/QuickPromise/tests/unittests/main.cpp: -------------------------------------------------------------------------------- 1 | // Author: Ben Lau (https://github.com/benlau) 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(int argc, char *argv[]) 9 | { 10 | QApplication a(argc, argv); 11 | QString importPath = "qrc:///"; 12 | QStringList args = a.arguments(); 13 | QString executable = args.takeAt(0); 14 | 15 | char **s = (char**) malloc(sizeof(char*) * 10 ); 16 | int idx = 0; 17 | s[idx++] = executable.toUtf8().data(); 18 | s[idx++] = "-import"; 19 | s[idx++] = strdup(SRCDIR); 20 | s[idx++] = "-import"; 21 | s[idx++] = strdup(importPath.toLocal8Bit().data()); 22 | 23 | foreach( QString arg,args) { 24 | s[idx++] = strdup(arg.toUtf8().data()); 25 | } 26 | 27 | s[idx++] = 0; 28 | 29 | 30 | const char *name = "QuickPromise"; 31 | const char *source = SRCDIR; 32 | 33 | bool error = quick_test_main( idx-1,s,name,source); 34 | 35 | if (error){ 36 | qWarning() << "Error found!"; 37 | return -1; 38 | } 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Nathan Hourt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/QuickPromise/tests/unittests/tst_promisejs_all_signal.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtTest 1.0 3 | import QuickPromise 1.0 4 | 5 | TestCase { 6 | name : "PromiseJS_All_Signal" 7 | 8 | function tick() { 9 | wait(0); 10 | wait(0); 11 | wait(0); 12 | } 13 | 14 | Item { 15 | id: item1 16 | signal triggered; 17 | } 18 | 19 | Item { 20 | id: item2 21 | signal triggered; 22 | } 23 | 24 | function test_all_single_signal() { 25 | var promise = Q.all([item1.triggered]); 26 | compare(promise.state , "pending"); 27 | item1.triggered(); 28 | compare(promise.state , "pending"); 29 | tick(); 30 | compare(promise.state , "fulfilled"); 31 | } 32 | 33 | function test_all_multiple_signal() { 34 | var promise = Q.all([item1.triggered,item2.triggered]); 35 | 36 | compare(promise.state , "pending"); 37 | item2.triggered(); 38 | compare(promise.state , "pending"); 39 | tick(); 40 | compare(promise.state , "pending"); 41 | 42 | item1.triggered(); 43 | compare(promise.state , "pending"); 44 | tick(); 45 | compare(promise.state , "fulfilled"); 46 | } 47 | } 48 | 49 | -------------------------------------------------------------------------------- /AccountKeys.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ACCOUNTKEYS_HPP 2 | #define ACCOUNTKEYS_HPP 3 | 4 | #include "KeyPair.hpp" 5 | 6 | #include 7 | 8 | class AccountKeys : public QObject 9 | { 10 | Q_OBJECT 11 | Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) 12 | Q_PROPERTY(KeyPair* ownerKey READ ownerKey CONSTANT) 13 | Q_PROPERTY(KeyPair* activeKey READ activeKey CONSTANT) 14 | Q_PROPERTY(KeyPair* postingKey READ postingKey CONSTANT) 15 | Q_PROPERTY(KeyPair* memoKey READ memoKey CONSTANT) 16 | 17 | QString m_name; 18 | KeyPair* m_ownerKey = new KeyPair(this); 19 | KeyPair* m_activeKey = new KeyPair(this); 20 | KeyPair* m_postingKey = new KeyPair(this); 21 | KeyPair* m_memoKey = new KeyPair(this); 22 | 23 | public: 24 | explicit AccountKeys(QObject *parent = 0); 25 | 26 | QString name() const { return m_name; } 27 | KeyPair* ownerKey() const { return m_ownerKey; } 28 | KeyPair* activeKey() const { return m_activeKey; } 29 | KeyPair* postingKey() const { return m_postingKey; } 30 | KeyPair* memoKey() const { return m_memoKey; } 31 | 32 | signals: 33 | void nameChanged(QString name); 34 | void updated(); 35 | 36 | public slots: 37 | void setName(QString name); 38 | }; 39 | 40 | #endif // ACCOUNTKEYS_HPP 41 | -------------------------------------------------------------------------------- /vendor/QuickPromise/tests/unittests/tst_promise_resolvewhen_all_promise.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtTest 1.0 3 | import QuickPromise 1.0 4 | 5 | TestSuite { 6 | name : "Promise_ResolveWhen_All_Promise" 7 | 8 | Component { 9 | id: promiseCreator; 10 | Promise { 11 | id : promise 12 | signal triggered 13 | property alias promise1 : promiseItem1 14 | property alias promise2 : promiseItem2 15 | resolveWhen: Q.all([promise1,promise2,promise.triggered]) 16 | Promise { 17 | id: promiseItem1 18 | } 19 | Promise { 20 | id: promiseItem2 21 | } 22 | } 23 | } 24 | 25 | function test_resolve() { 26 | var promise = promiseCreator.createObject(); 27 | compare(promise.isSettled,false); 28 | tick(); 29 | compare(promise.isSettled,false); 30 | 31 | promise.triggered(); 32 | compare(promise.isSettled,false); 33 | tick(); 34 | compare(promise.isSettled,false); 35 | 36 | promise.promise1.resolve(); 37 | promise.promise2.resolve(); 38 | compare(promise.isSettled,false); 39 | 40 | tick(); 41 | compare(promise.isSettled,true); 42 | } 43 | } 44 | 45 | -------------------------------------------------------------------------------- /vendor/QuickPromise/tests/unittests/tst_promise_resolvewhen_promise.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtTest 1.0 3 | import QuickPromise 1.0 4 | 5 | TestSuite { 6 | name : "Promise_ResolveWhen_Promise" 7 | 8 | Component { 9 | id: promiseItem 10 | Promise { 11 | property alias source : sourcePromise 12 | resolveWhen : Promise { 13 | id : sourcePromise 14 | } 15 | } 16 | } 17 | 18 | function test_resolve() { 19 | var promise = promiseItem.createObject(); 20 | compare(promise.isSettled,false); 21 | tick(); 22 | compare(promise.isSettled,false); 23 | 24 | promise.source.resolve(); 25 | compare(promise.isSettled,false); 26 | tick(); 27 | compare(promise.isSettled,true); 28 | compare(promise.isFulfilled,true); 29 | 30 | } 31 | 32 | function test_reject() { 33 | var promise = promiseItem.createObject(); 34 | compare(promise.isSettled,false); 35 | tick(); 36 | compare(promise.isSettled,false); 37 | 38 | promise.source.reject(); 39 | compare(promise.isSettled,false); 40 | tick(); 41 | compare(promise.isSettled,true); 42 | compare(promise.isRejected,true); 43 | } 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /vendor/QtQmlModels/QtQmlModels.qbs: -------------------------------------------------------------------------------- 1 | import qbs; 2 | 3 | Project { 4 | name: "Qt QML Models"; 5 | 6 | Product { 7 | name: "libqtqmltricks-qtqmlmodels"; 8 | type: "staticlibrary"; 9 | targetName: "QtQmlModels"; 10 | cpp.cxxLanguageVersion: "c++11" 11 | 12 | Export { 13 | cpp.includePaths: "."; 14 | cpp.cxxLanguageVersion: "c++11" 15 | 16 | Depends { name: "cpp"; } 17 | Depends { 18 | name: "Qt"; 19 | submodules: ["core", "qml"]; 20 | } 21 | } 22 | Depends { name: "cpp"; } 23 | Depends { 24 | name: "Qt"; 25 | submodules: ["core", "qml"]; 26 | } 27 | Group { 28 | name: "C++ sources"; 29 | files: [ 30 | "QQmlObjectListModel.cpp", 31 | "QQmlVariantListModel.cpp", 32 | ] 33 | } 34 | Group { 35 | name: "C++ headers"; 36 | files: [ 37 | "QQmlObjectListModel.h", 38 | "QQmlVariantListModel.h", 39 | "QtQmlTricksPlugin_SmartDataModels.h", 40 | ] 41 | } 42 | Group { 43 | qbs.install: (product.type === "dynamiclibrary"); 44 | fileTagsFilter: product.type; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Pressure.qbs: -------------------------------------------------------------------------------- 1 | import qbs 2 | 3 | Project { 4 | Application { 5 | name: "Pressure" 6 | 7 | qbsSearchPaths: "qbsmodules" 8 | 9 | Depends { name: "Qt"; submodules: ["quick", "network", "websockets"] } 10 | Depends { name: "cpp" } 11 | Depends { name: "steem" } 12 | Depends { name: "libqtqmltricks-qtqmlmodels" } 13 | 14 | cpp.cxxLanguageVersion: "c++14" 15 | cpp.includePaths: "vendor/QuickPromise" 16 | cpp.rpaths: qbs.hostOS.contains("osx")? ["@executable_path/../Frameworks"] : [] 17 | 18 | files: [ 19 | "AccountKeys.cpp", 20 | "AccountKeys.hpp", 21 | "KeyPair.cpp", 22 | "KeyPair.hpp", 23 | "KeyStore.cpp", 24 | "KeyStore.hpp", 25 | "Promise.cpp", 26 | "Promise.hpp", 27 | "QmlJsonRpcProvider.cpp", 28 | "QmlJsonRpcProvider.hpp", 29 | "TransactionFoundry.cpp", 30 | "TransactionFoundry.hpp", 31 | "main.cpp", 32 | "qml.qrc", 33 | "vendor/QuickPromise/qptimer.h", 34 | "vendor/QuickPromise/qptimer.cpp", 35 | "vendor/QuickPromise/quickpromise.qrc", 36 | ] 37 | } 38 | 39 | SubProject { 40 | filePath: "vendor/QtQmlModels/QtQmlModels.qbs" 41 | Properties { 42 | name: "QtQmlModels" 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /AccountDelegate.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | import QtQuick.Controls 2.0 3 | import QtQuick.Layouts 1.1 4 | import Qt.WebSockets 1.0 5 | import com.nathanhourt.steem.accounts 1.0 6 | import com.nathanhourt.steem.crypto 1.0 7 | import com.nathanhourt.rpc 1.0 8 | 9 | ItemDelegate { 10 | onClicked: ListView.view.currentIndex = index 11 | 12 | signal editKey(string authorityLevel, KeyPair key) 13 | 14 | contentItem: Column { 15 | spacing: 4 16 | Label { 17 | font.bold: true 18 | text: name 19 | } 20 | KeyDelegate { 21 | keyName: qsTr("Owner key") 22 | key: ownerKey 23 | width: parent.width 24 | editButton.onClicked: editKey("owner", ownerKey) 25 | } 26 | KeyDelegate { 27 | keyName: qsTr("Active key") 28 | key: activeKey 29 | width: parent.width 30 | editButton.onClicked: editKey("active", activeKey) 31 | } 32 | KeyDelegate { 33 | keyName: qsTr("Posting key") 34 | key: postingKey 35 | width: parent.width 36 | editButton.onClicked: editKey("posting", postingKey) 37 | } 38 | KeyDelegate { 39 | keyName: qsTr("Memo key") 40 | key: memoKey 41 | width: parent.width 42 | editButton.onClicked: editKey("memo_key", memoKey) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ShadowedPopup.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQuick.Controls 2.0 3 | import QtQuick.Layouts 1.3 4 | import QtGraphicalEffects 1.0 5 | 6 | Popup { 7 | id: addAccountPopup 8 | width: 375 9 | height: 200 10 | background: Rectangle { 11 | layer.enabled: true 12 | layer.effect: DropShadow { 13 | color: "#77000000" 14 | transparentBorder: true 15 | radius: 50 16 | samples: 1 + radius * 2 17 | verticalOffset: 10 18 | } 19 | } 20 | modal: true 21 | dim: false 22 | z: 3 23 | 24 | default property alias contents: clientArea.data 25 | property alias cancelButton: cancelButton 26 | property alias acceptButton: acceptButton 27 | property alias showButtons: buttonRow.visible 28 | 29 | ColumnLayout { 30 | anchors.fill: parent 31 | 32 | Item { 33 | id: clientArea 34 | Layout.fillWidth: true 35 | Layout.fillHeight: true 36 | } 37 | Row { 38 | id: buttonRow 39 | anchors.right: parent.right 40 | anchors.bottom: parent.bottom 41 | spacing: 10 42 | 43 | Button { 44 | id: cancelButton 45 | text: qsTr("Cancel") 46 | } 47 | Button { 48 | id: acceptButton 49 | text: qsTr("Accept") 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "KeyPair.hpp" 2 | #include "KeyStore.hpp" 3 | #include "AccountKeys.hpp" 4 | #include "QmlJsonRpcProvider.hpp" 5 | #include "TransactionFoundry.hpp" 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | int main(int argc, char *argv[]) 13 | { 14 | QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); 15 | QGuiApplication app(argc, argv); 16 | app.setOrganizationName("Nathan Hourt"); 17 | app.setOrganizationDomain("nathanhourt.com"); 18 | #ifdef NDEBUG 19 | app.setApplicationName("Steem Pressure"); 20 | #else 21 | app.setApplicationName("Steem Pressure Debug"); 22 | #endif 23 | app.setApplicationVersion("1.0 Beta"); 24 | 25 | QQmlApplicationEngine engine; 26 | 27 | // For benlau's quickpromise library 28 | engine.addImportPath("qrc:/"); 29 | 30 | registerQtQmlTricksSmartDataModel(&engine); 31 | 32 | qmlRegisterType("com.nathanhourt.steem.crypto", 1, 0, "TransactionFoundry"); 33 | qmlRegisterType("com.nathanhourt.steem.crypto", 1, 0, "KeyPair"); 34 | qmlRegisterType("com.nathanhourt.steem.accounts", 1, 0, "KeyStore"); 35 | qmlRegisterType("com.nathanhourt.steem.accounts", 1, 0, "AccountKeys"); 36 | qmlRegisterType("com.nathanhourt.rpc", 1, 0, "JsonRpcProvider"); 37 | 38 | engine.load(QUrl(QLatin1String("qrc:/main.qml"))); 39 | 40 | return app.exec(); 41 | } 42 | -------------------------------------------------------------------------------- /FadeOnVisible.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | Item { 4 | states: [ 5 | State { 6 | name: "VISIBLE" 7 | when: accountList.count === 0 8 | PropertyChanges { 9 | target: emptyAccountListPlaceHolder 10 | visible: true 11 | } 12 | }, 13 | State { 14 | name: "INVISIBLE" 15 | when: accountList.count > 0 16 | PropertyChanges { 17 | target: emptyAccountListPlaceHolder 18 | visible: false 19 | } 20 | } 21 | ] 22 | transitions: [ 23 | Transition { 24 | from: "VISIBLE"; to: "INVISIBLE" 25 | SequentialAnimation { 26 | PropertyAnimation { 27 | target: emptyAccountListPlaceHolder 28 | property: "opacity" 29 | from: 1; to: 0 30 | } 31 | PropertyAction { 32 | property: "visible" 33 | } 34 | } 35 | }, 36 | Transition { 37 | from: "INVISIBLE"; to: "VISIBLE" 38 | SequentialAnimation { 39 | PropertyAction { 40 | property: "visible" 41 | } 42 | PropertyAnimation { 43 | target: emptyAccountListPlaceHolder 44 | property: "opacity" 45 | from: 0; to: 1 46 | } 47 | } 48 | } 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /KeyStore.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KEYSTORE_HPP 2 | #define KEYSTORE_HPP 3 | 4 | #include "AccountKeys.hpp" 5 | 6 | #include 7 | 8 | #include 9 | 10 | class KeyStore : public QObject 11 | { 12 | Q_OBJECT 13 | QML_OBJMODEL_PROPERTY(AccountKeys, accountList) 14 | Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged) 15 | Q_PROPERTY(bool hasPersistedData READ hasPersistedData NOTIFY hasPersistedDataChanged) 16 | 17 | QString m_password; 18 | 19 | public: 20 | explicit KeyStore(QObject *parent = 0); 21 | 22 | /// Determines whether the provided account is supported or not. If unsupported, the human-readable reason is 23 | /// returned. If supported, the null string is returned. 24 | Q_INVOKABLE QString accountUnsupportedReason(QVariantMap account); 25 | 26 | Q_INVOKABLE AccountKeys* findAccount(QString accountName); 27 | Q_INVOKABLE AccountKeys* addAccount(QVariantMap account); 28 | 29 | Q_INVOKABLE bool hasPersistedData(); 30 | Q_INVOKABLE bool restore(); 31 | 32 | /// Because QML can't make them on its own... 33 | Q_INVOKABLE static KeyPair* makeKeyPair() { return new KeyPair(); } 34 | 35 | QString password() const { return m_password; } 36 | 37 | public slots: 38 | void persist(); 39 | void resetPersistence(); 40 | void setPassword(QString password); 41 | 42 | signals: 43 | void passwordChanged(QString password); 44 | void hasPersistedDataChanged(bool); 45 | void restored(); 46 | }; 47 | 48 | 49 | 50 | #endif // KEYSTORE_HPP 51 | -------------------------------------------------------------------------------- /EditKeysPage.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | import com.nathanhourt.steem.crypto 1.0 3 | import QuickPromise 1.0 4 | 5 | EditKeysForm { 6 | property KeyPair keyPair 7 | property KeyPair modifiedKeypair 8 | property alias modifiedKeyPromise: modifiedKeyPromise 9 | 10 | Component.onCompleted: modifiedKeypair = keyPair.deepCopy() 11 | 12 | Promise { 13 | id: modifiedKeyPromise 14 | } 15 | 16 | Keys.onEscapePressed: modifiedKeyPromise.reject() 17 | 18 | seedField.placeholderText: qsTr("Key recovery phrase") 19 | publicKeyField.text: modifiedKeypair.keyType === KeyPair.NullKey? "" : modifiedKeypair.publicKey 20 | publicKeyField.placeholderText: qsTr("Public Key") 21 | privateKeyField.text: modifiedKeypair.keyType === KeyPair.NullKey? "" : modifiedKeypair.wifKey 22 | privateKeyField.placeholderText: qsTr("Private Key") 23 | 24 | seedField.onAccepted: recoverButton.clicked() 25 | recoverButton.onClicked: modifiedKeypair.generateFromSeed(seedField.text) 26 | generateButton.onClicked: modifiedKeypair.generateRandomly() 27 | publicKeyField.onAccepted: modifiedKeypair.fromPublicKey(publicKeyField.text) 28 | privateKeyField.onAccepted: modifiedKeypair.fromWifKey(privateKeyField.text) 29 | 30 | cancelButton.onClicked: modifiedKeyPromise.reject() 31 | saveButton.onClicked: { 32 | if (privateKeyField.text !== modifiedKeypair.wifKey) 33 | privateKeyField.accepted() 34 | else if (publicKeyField.text !== modifiedKeypair.publicKey) 35 | publicKeyField.accepted() 36 | 37 | modifiedKeyPromise.resolve(modifiedKeypair) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /AddAccountPopup.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.4 2 | import QtQuick.Controls 2.0 3 | import QtQuick.Layouts 1.3 4 | import QtGraphicalEffects 1.0 5 | 6 | ShadowedPopup { 7 | onOpened: newKeyField.forceActiveFocus() 8 | onClosed: newKeyField.text = "" 9 | acceptButton.text: qsTr("Add Account") 10 | cancelButton.text: qsTr("Don't Add") 11 | 12 | property alias newKeyField: newKeyField 13 | property alias infoLabel: infoLabel 14 | 15 | TextField { 16 | id: newKeyField 17 | anchors.centerIn: parent 18 | placeholderText: qsTr("Private key or account password") 19 | onAccepted: { 20 | if (acceptButton.enabled) 21 | acceptButton.clicked() 22 | } 23 | } 24 | Label { 25 | id: infoLabel 26 | wrapMode: Label.WrapAtWordBoundaryOrAnywhere 27 | anchors.top: newKeyField.bottom 28 | anchors.topMargin: 4 29 | anchors.left: newKeyField.left 30 | anchors.right: parent.right 31 | anchors.rightMargin: 4 32 | Behavior on text { 33 | SequentialAnimation { 34 | PropertyAnimation { 35 | target: infoLabel 36 | property: "opacity" 37 | from: 1; to: 0 38 | duration: 100 39 | } 40 | PropertyAction { target: infoLabel; property: "text" } 41 | PropertyAnimation { 42 | target: infoLabel 43 | property: "opacity" 44 | from: 0; to: 1 45 | duration: 100 46 | } 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /vendor/QuickPromise/tests/unittests/tst_promisejs_all_promise_item.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtTest 1.0 3 | import QuickPromise 1.0 4 | 5 | TestCase { 6 | name : "PromiseJS_All_PromiseItem" 7 | 8 | function tick() { 9 | wait(0); 10 | wait(0); 11 | wait(0); 12 | } 13 | 14 | Component { 15 | id : promiseItem 16 | Promise { 17 | } 18 | } 19 | 20 | function test_single() { 21 | var source = promiseItem.createObject(); 22 | var promise = Q.all([source]); 23 | compare(promise.state , "pending"); 24 | tick(); 25 | compare(promise.state , "pending"); 26 | 27 | source.resolve(); 28 | compare(promise.state , "pending"); 29 | tick(); 30 | compare(promise.state , "fulfilled"); 31 | } 32 | 33 | function test_rejected() { 34 | var source1 = promiseItem.createObject(); 35 | var source2 = promiseItem.createObject(); 36 | var promise = Q.all([source1,source2]); 37 | compare(promise.state , "pending"); 38 | tick(); 39 | compare(promise.state , "pending"); 40 | 41 | source2.reject(); 42 | compare(promise.state , "pending"); 43 | 44 | tick(); 45 | compare(promise.state , "rejected"); 46 | } 47 | 48 | function test_already_rejected() { 49 | var source1 = promiseItem.createObject(); 50 | var source2 = promiseItem.createObject(); 51 | source1.reject(); 52 | 53 | var promise = Q.all([source1,source2]); 54 | compare(promise.state , "pending"); 55 | tick(); 56 | compare(promise.state , "rejected"); 57 | } 58 | 59 | } 60 | 61 | -------------------------------------------------------------------------------- /vendor/QtQmlModels/QQmlVariantListModel.h: -------------------------------------------------------------------------------- 1 | #ifndef QQMLVARIANTLISTMODEL_H 2 | #define QQMLVARIANTLISTMODEL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class QQmlVariantListModel : public QAbstractListModel { 10 | Q_OBJECT 11 | Q_PROPERTY (int count READ count NOTIFY countChanged) 12 | 13 | public: 14 | explicit QQmlVariantListModel (QObject * parent = Q_NULLPTR); 15 | ~QQmlVariantListModel (void); 16 | 17 | public: // QAbstractItemModel interface reimplemented 18 | int rowCount (const QModelIndex & parent = QModelIndex ()) const; 19 | bool setData (const QModelIndex & index, const QVariant & value, int role); 20 | QVariant data (const QModelIndex & index, int role) const; 21 | QHash roleNames (void) const; 22 | 23 | public slots: // public API 24 | void clear (void); 25 | int count (void) const; 26 | bool isEmpty (void) const; 27 | void append (const QVariant & item); 28 | void prepend (const QVariant & item); 29 | void insert (int idx, const QVariant & item); 30 | void appendList (const QVariantList & itemList); 31 | void prependList (const QVariantList & itemList); 32 | void replace (int pos, const QVariant & item); 33 | void insertList (int idx, const QVariantList & itemList); 34 | void move (int idx, int pos); 35 | void remove (int idx); 36 | QVariant get (int idx) const; 37 | QVariantList list (void) const; 38 | 39 | signals: // notifiers 40 | void countChanged (int count); 41 | 42 | protected: 43 | void updateCounter (void); 44 | 45 | private: 46 | int m_count; 47 | QVariantList m_items; 48 | QHash m_roles; 49 | }; 50 | 51 | #endif // QQMLVARIANTLISTMODEL_H 52 | -------------------------------------------------------------------------------- /vendor/QuickPromise/qptimer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "qptimer.h" 5 | 6 | QPTimer::QPTimer(QObject *parent) : QObject(parent) 7 | { 8 | 9 | } 10 | 11 | QPTimer::~QPTimer() 12 | { 13 | 14 | } 15 | 16 | void QPTimer::setTimeout(QJSValue func, int interval) 17 | { 18 | // It can't use the Timer from Quick Component to implement the function. 19 | // Because setTimeout(0) could not be executed in next tick with < Qt 5.4 20 | 21 | QTimer *timer = new QTimer(this); 22 | 23 | connect(timer,SIGNAL(timeout()), 24 | this,SLOT(onTriggered()),Qt::QueuedConnection); 25 | 26 | QVariant v = QVariant::fromValue(func); 27 | 28 | timer->setProperty("__quick_promise_func___",v); 29 | timer->setInterval(interval); 30 | timer->setSingleShot(true); 31 | timer->start(); 32 | } 33 | 34 | void QPTimer::onTriggered() 35 | { 36 | QTimer* timer = qobject_cast(sender()); 37 | 38 | QJSValue func = timer->property("__quick_promise_func___").value(); 39 | 40 | QJSValue res = func.call(); 41 | 42 | if (res.isError()) { 43 | qDebug() << "Q.setTimeout() - Uncaught exception at line" 44 | << res.property("lineNumber").toInt() 45 | << ":" << res.toString(); 46 | } 47 | 48 | timer->deleteLater(); 49 | } 50 | 51 | static QJSValue provider(QQmlEngine* engine , QJSEngine *scriptEngine) { 52 | Q_UNUSED(engine); 53 | 54 | QPTimer* timer = new QPTimer(); 55 | 56 | QJSValue value = scriptEngine->newQObject(timer); 57 | return value; 58 | } 59 | 60 | class QPTimerRegisterHelper { 61 | 62 | public: 63 | QPTimerRegisterHelper() { 64 | qmlRegisterSingletonType("QuickPromise", 1, 0, "QPTimer", provider); 65 | } 66 | }; 67 | 68 | static QPTimerRegisterHelper registerHelper; 69 | 70 | -------------------------------------------------------------------------------- /Promise.cpp: -------------------------------------------------------------------------------- 1 | #include "Promise.hpp" 2 | 3 | #include 4 | #include 5 | 6 | QmlPromise::QmlPromise(QObject* parent) 7 | : QObject(parent) { 8 | QQmlComponent promiserComponent(qmlEngine(parent)); 9 | promiserComponent.setData("import QuickPromise 1.0\nPromise {}", QUrl()); 10 | internalPromise = promiserComponent.create(); 11 | QQmlEngine::setObjectOwnership(internalPromise, QQmlEngine::JavaScriptOwnership); 12 | 13 | connect(internalPromise, &QObject::destroyed, this, [this] { internalPromise = nullptr; }); 14 | connect(internalPromise, SIGNAL(fulfilled(QVariant)), this, SIGNAL(fulfilled(QVariant))); 15 | connect(internalPromise, SIGNAL(rejected(QVariant)), this, SIGNAL(rejected(QVariant))); 16 | connect(internalPromise, SIGNAL(settled(QVariant)), this, SIGNAL(settled())); 17 | connect(this, &QmlPromise::fulfilled, [this] { wasFulfilled = true; }); 18 | connect(this, &QmlPromise::rejected, [this] { wasRejected = true; }); 19 | } 20 | 21 | QmlPromise::~QmlPromise() { 22 | } 23 | 24 | bool QmlPromise::isFulfilled() { 25 | return internalPromise? internalPromise->property("isFulfilled").toBool() : wasFulfilled; 26 | } 27 | 28 | bool QmlPromise::isRejected() { 29 | return internalPromise? internalPromise->property("isRejected").toBool() : wasRejected; 30 | } 31 | 32 | bool QmlPromise::isSettled() { 33 | return internalPromise? internalPromise->property("isSettled").toBool() : wasFulfilled || wasRejected; 34 | } 35 | 36 | QmlPromise::operator QJSValue() { 37 | return qmlEngine(internalPromise)->toScriptValue(internalPromise); 38 | } 39 | 40 | void QmlPromise::resolve(QVariant args) { 41 | if (internalPromise) 42 | QMetaObject::invokeMethod(internalPromise, "resolve", Q_ARG(QVariant, args)); 43 | } 44 | 45 | void QmlPromise::reject(QVariant reason) { 46 | if (internalPromise) 47 | QMetaObject::invokeMethod(internalPromise, "reject", Q_ARG(QVariant, reason)); 48 | } 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Steem Pressure 2 | --- 3 | 4 | ### Keeping your STEEM locked safely inside 5 | 6 | Steem Pressure is a simple application which lets you store your Steem private keys outside of the browser. This keeps them safe against most web-based attacks such as the XSS attack which recently affected Steemit.com. If a Steemian affected by the XSS attack had used Steem Pressure to keep his owner and active keys outside the browser, the most the attacker could have gained was his posting key allowing the attacker to vote and make posts on the victim's account, but not steal the account itself or the funds within it. 7 | 8 | Please note that at this point, Steem Pressure is considered a beta, and while I believe the keys it stores are safe from theft (unless the password which encrypts them is compromised), it is possible that a bug in the software may cause it to forget keys. I recommend having safe backups of keys (or their recovery phrases) stored in Steem Pressure. 9 | 10 | I would like to provide binaries for Windows and Mac; however, I am unable to build Steem on Windows and thus cannot build Steem Pressure. Others have succeeded in building Steem on Windows, so I would appreciate it if someone could give me some pointers on how to do it. Mac binaries are available in the Releases section. 11 | 12 | ## Build Instructions 13 | Steem Pressure is designed to be light on dependencies, and thus the only externally required dependencies are Qt 5.7+ (with qbs) and Steem. As Steem is linked statically, it is a build-time dependency only. Steem Pressure uses qbs as its build system, and as such, does not require a configure step. Ensure the `STEEM_PATH` environment variable is set to the path Steem is installed to, then simply run `qbs` to build. 14 | 15 | **Notes** 16 | - Some Linux distributions, such as Ubuntu, do not ship recent versions of Qt, thus it will be necessary to download an up-to-date copy from https://qt.io. 17 | - Steemit's repo for Steem has broken installation. This is fixed in [my fork](https://github.com/nathanhourt/steem), so for now it will be easiest to build/install Steem from my fork 18 | -------------------------------------------------------------------------------- /EditKeysForm.ui.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | import QtQuick.Controls 2.0 3 | import QtQuick.Layouts 1.0 4 | 5 | Page { 6 | id: item1 7 | property alias recoverButton: recoverButton 8 | property alias seedField: seedField 9 | property alias publicKeyField: publicKeyField 10 | property alias privateKeyField: privateKeyField 11 | property alias generateButton: generateButton 12 | property alias cancelButton: cancelButton 13 | property alias saveButton: saveButton 14 | ColumnLayout { 15 | id: columnLayout1 16 | x: 270 17 | y: 190 18 | width: 599 19 | height: 175 20 | anchors.horizontalCenter: parent.horizontalCenter 21 | anchors.verticalCenter: parent.verticalCenter 22 | 23 | RowLayout { 24 | id: rowLayout1 25 | width: 100 26 | height: 100 27 | spacing: 8 28 | 29 | TextField { 30 | id: seedField 31 | Layout.fillWidth: true 32 | } 33 | 34 | Button { 35 | id: recoverButton 36 | text: qsTr("Recover") 37 | } 38 | } 39 | 40 | Button { 41 | id: generateButton 42 | text: qsTr("Generate") 43 | Layout.fillWidth: true 44 | } 45 | 46 | TextField { 47 | id: publicKeyField 48 | text: qsTr("") 49 | Layout.fillWidth: true 50 | } 51 | 52 | TextField { 53 | id: privateKeyField 54 | text: qsTr("") 55 | Layout.fillWidth: true 56 | } 57 | } 58 | 59 | Row { 60 | id: row1 61 | spacing: 8 62 | anchors.right: parent.right 63 | anchors.rightMargin: 20 64 | anchors.bottom: parent.bottom 65 | anchors.bottomMargin: 20 66 | 67 | Button { 68 | id: cancelButton 69 | text: qsTr("Cancel") 70 | } 71 | 72 | Button { 73 | id: saveButton 74 | text: qsTr("Update Key") 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /QmlJsonRpcProvider.cpp: -------------------------------------------------------------------------------- 1 | #include "QmlJsonRpcProvider.hpp" 2 | 3 | #include 4 | 5 | QmlJsonRpcProvider::QmlJsonRpcProvider(QObject* parent) 6 | : QObject(parent) {} 7 | 8 | QJSValue QmlJsonRpcProvider::call(QString method, QVariantList params) { 9 | if (!m_socket) 10 | return QJSValue::NullValue; 11 | 12 | QJsonObject call { 13 | {"jsonrpc", "2.0"}, 14 | {"method", method}, 15 | {"params", QJsonValue::fromVariant(params)}, 16 | {"id", qint64(nextQueryId)} 17 | }; 18 | QString jsonCall = QJsonDocument(call).toJson(QJsonDocument::JsonFormat::Compact); 19 | qDebug() << "Making call:" << jsonCall; 20 | QMetaObject::invokeMethod(m_socket, "sendTextMessage", Q_ARG(QString, jsonCall)); 21 | 22 | return *pendingRequests.emplace(std::make_pair(nextQueryId++, 23 | std::unique_ptr(new QmlPromise(this)))).first->second; 24 | } 25 | 26 | void QmlJsonRpcProvider::messageReceived(QString message) { 27 | qDebug() << "Got response:" << message; 28 | QJsonParseError error; 29 | auto response = QJsonDocument::fromJson(message.toLocal8Bit(), &error).object(); 30 | 31 | if (error.error != QJsonParseError::ParseError::NoError || !response.contains("id") || 32 | (!response.contains("error") && !response.contains("result"))) { 33 | qWarning() << "Got unrecognizeable message back from Bitshares wallet" << message; 34 | return; 35 | } 36 | 37 | auto itr = pendingRequests.find(response["id"].toVariant().toLongLong()); 38 | if (itr == pendingRequests.end()) { 39 | qWarning() << "Got response from Bitshares wallet, but the ID doesn't match any outstanding call:" << message; 40 | return; 41 | } 42 | 43 | if (response.contains("result")) 44 | itr->second->resolve(response["result"].toVariant()); 45 | else 46 | itr->second->reject(response["error"].toVariant()); 47 | pendingRequests.erase(itr); 48 | } 49 | 50 | void QmlJsonRpcProvider::setSocket(QObject* socket) { 51 | if (m_socket == socket) 52 | return; 53 | 54 | m_socket = socket; 55 | if (m_socket) 56 | connect(m_socket, SIGNAL(textMessageReceived(QString)), this, SLOT(messageReceived(QString))); 57 | emit socketChanged(socket); 58 | } 59 | -------------------------------------------------------------------------------- /TransactionFoundry.cpp: -------------------------------------------------------------------------------- 1 | #include "TransactionFoundry.hpp" 2 | #include "AccountKeys.hpp" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | TransactionFoundry::TransactionFoundry(QObject *parent) : QObject(parent) { 14 | } 15 | 16 | QVariantMap TransactionFoundry::keyUpdateTransaction(QString accountName, QString authorityLevel, 17 | KeyPair* newKey, QString referenceBlockId) { 18 | if (m_keyStore == nullptr || newKey == nullptr) 19 | return {}; 20 | auto account = m_keyStore->findAccount(accountName); 21 | fc::ecc::private_key signingKey; 22 | if (authorityLevel == "owner") 23 | signingKey = account->ownerKey()->privateKey(); 24 | else 25 | signingKey = account->activeKey()->privateKey(); 26 | if (account == nullptr || signingKey == fc::ecc::private_key()) 27 | return {}; 28 | 29 | namespace sch = steemit::chain; 30 | sch::account_update_operation op; 31 | op.account = accountName.toStdString(); 32 | 33 | auto makeAuth = [](KeyPair* k) { 34 | using fc::json; 35 | return json::from_string(QJsonDocument::fromVariant(k->toAuthority()).toJson().data()).as(); 36 | }; 37 | 38 | if (authorityLevel == "owner") 39 | op.owner = makeAuth(newKey); 40 | else if (authorityLevel == "active") 41 | op.active = makeAuth(newKey); 42 | else if (authorityLevel == "posting") 43 | op.posting = makeAuth(newKey); 44 | else if (authorityLevel == "memo_key") 45 | op.memo_key = sch::public_key_type(newKey->publicKey().toStdString()); 46 | else return {}; 47 | 48 | steemit::chain::signed_transaction trx; 49 | trx.operations = {op}; 50 | trx.set_expiration(fc::time_point::now() + fc::minutes(1)); 51 | trx.set_reference_block(sch::block_id_type(referenceBlockId.toStdString())); 52 | trx.sign(signingKey, STEEMIT_CHAIN_ID); 53 | 54 | return QJsonDocument::fromJson(fc::json::to_string(trx).c_str()).toVariant().toMap(); 55 | } 56 | 57 | void TransactionFoundry::setKeyStore(KeyStore* keyStore) { 58 | if (m_keyStore == keyStore) 59 | return; 60 | 61 | m_keyStore = keyStore; 62 | emit keyStoreChanged(keyStore); 63 | } 64 | -------------------------------------------------------------------------------- /MyKeysForm.ui.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.4 2 | import QtQuick.Controls 2.0 3 | import QtQuick.Layouts 1.3 4 | import QtGraphicalEffects 1.0 5 | 6 | Page { 7 | property alias accountList: accountList 8 | property alias newAccountButton: newAccountButton 9 | property alias deleteAccountButton: deleteAccountButton 10 | property alias emptyAccountListLabel: emptyAccountListLabel 11 | property alias emptyAccountListPlaceHolder: emptyAccountListPlaceHolder 12 | 13 | ColumnLayout { 14 | id: columnLayout1 15 | anchors.rightMargin: 20 16 | anchors.leftMargin: 20 17 | anchors.bottomMargin: 20 18 | anchors.topMargin: 20 19 | anchors.fill: parent 20 | 21 | ListView { 22 | id: accountList 23 | width: 400 24 | height: 160 25 | clip: true 26 | Layout.fillHeight: true 27 | Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter 28 | 29 | FadeOnVisible { 30 | id: emptyAccountListPlaceHolder 31 | anchors.fill: parent 32 | 33 | Image { 34 | id: image1 35 | opacity: .1 36 | sourceSize.height: 100 37 | height: 100 38 | width: 100 39 | anchors.centerIn: parent 40 | source: "qrc:/res/steem.svg" 41 | fillMode: Image.PreserveAspectFit 42 | smooth: true 43 | layer.enabled: true 44 | layer.effect: Colorize { 45 | saturation: 0 46 | } 47 | } 48 | Label { 49 | id: emptyAccountListLabel 50 | text: qsTr("No accounts yet... Add one!") 51 | anchors.centerIn: parent 52 | } 53 | } 54 | } 55 | 56 | RowLayout { 57 | id: rowLayout1 58 | width: 100 59 | height: 100 60 | spacing: 20 61 | Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter 62 | 63 | Button { 64 | id: newAccountButton 65 | text: qsTr("New Account") 66 | } 67 | 68 | Button { 69 | id: deleteAccountButton 70 | text: qsTr("Delete Account") 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /KeyPair.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KEYPAIR_HPP 2 | #define KEYPAIR_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | class KeyPair : public QObject 11 | { 12 | Q_OBJECT 13 | Q_PROPERTY(KeyType keyType READ keyType NOTIFY keyTypeChanged) 14 | Q_PROPERTY(QString publicKey READ publicKey NOTIFY publicKeyChanged) 15 | Q_PROPERTY(QString wifKey READ wifKey NOTIFY wifKeyChanged) 16 | 17 | // If type is bool, no key is set. Value is irrelevant. 18 | public: 19 | using KeyStore = fc::static_variant; 20 | private: 21 | KeyStore key; 22 | void setKey(KeyStore newKey); 23 | 24 | public: 25 | enum KeyType { 26 | NullKey, 27 | PublicKey, 28 | PrivateKey 29 | }; 30 | Q_ENUM(KeyType) 31 | 32 | const static QString KeyPrefix; 33 | 34 | explicit KeyPair(QObject *parent = 0); 35 | KeyPair(const KeyPair& other) { key = other.key; } 36 | KeyPair(KeyPair&& other) { key = std::move(other.key); } 37 | KeyPair& operator=(const KeyPair& other); 38 | KeyPair& operator=(KeyPair&& other); 39 | 40 | Q_INVOKABLE void generateFromSeed(QString seed); 41 | Q_INVOKABLE void generateRandomly(); 42 | Q_INVOKABLE void fromPublicKey(QString publicKey); 43 | Q_INVOKABLE void fromWifKey(QString wifKey); 44 | Q_INVOKABLE void fromAuthority(QVariantMap authority); 45 | void fromKeyStore(KeyStore store) { setKey(store); } 46 | 47 | /// Makes a deep copy of this keypair. Caller takes ownership of returned KeyPair. 48 | Q_INVOKABLE KeyPair* deepCopy() const { return new KeyPair(*this); } 49 | /// Overwrites this key with other 50 | Q_INVOKABLE KeyPair* replaceWith(const KeyPair* other); 51 | /// Because QML is stupid 52 | Q_INVOKABLE KeyPair* replaceWith(KeyPair* other) { return replaceWith((const KeyPair*)other); } 53 | 54 | /// Compare this key to other, since javascript doesn't support == operator overloading 55 | /// @note Returns true if one operand is a public key and the other is a private key, but they are the same key 56 | Q_INVOKABLE bool equals(const KeyPair* other); 57 | Q_INVOKABLE bool equals(KeyPair* other) { return equals((const KeyPair*)other); } 58 | 59 | Q_INVOKABLE QString publicKey() const; 60 | Q_INVOKABLE QString wifKey() const; 61 | Q_INVOKABLE QVariantMap toAuthority() const; 62 | 63 | fc::ecc::private_key privateKey() const { 64 | if (keyType() == PrivateKey) 65 | return key.get(); 66 | return {}; 67 | } 68 | KeyStore keyStore() const { 69 | return key; 70 | } 71 | 72 | KeyType keyType() const; 73 | 74 | static bool isSupportedAuthority(QVariantMap authority); 75 | 76 | signals: 77 | void keyTypeChanged(KeyType); 78 | void publicKeyChanged(QString); 79 | void wifKeyChanged(QString); 80 | void updated(); 81 | 82 | public slots: 83 | }; 84 | 85 | #endif // KEYPAIR_HPP 86 | -------------------------------------------------------------------------------- /main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | import QtQuick.Controls 2.0 3 | import QtQuick.Layouts 1.0 4 | 5 | ApplicationWindow { 6 | id: window 7 | visible: true 8 | width: 640 9 | height: 480 10 | title: qsTr("Steem Pressure") 11 | 12 | ShadowedPopup { 13 | id: passwordEntry 14 | modal: true 15 | dim: true 16 | width: 400 17 | height: 300 18 | x: window.width / 2 - width / 2 19 | y: window.height / 2 - height / 2 20 | visible: true 21 | showButtons: false 22 | closePolicy: Popup.NoAutoClose 23 | 24 | Column { 25 | anchors.verticalCenter: parent.verticalCenter 26 | width: parent.width 27 | spacing: 8 28 | 29 | Label { 30 | id: welcomeLabel 31 | text: keysPage.keyStore.hasPersistedData? qsTr("Welcome back! Enter your password to continue") 32 | : qsTr("Welcome to Steem Pressure! Set a password " + 33 | "below") 34 | 35 | } 36 | RowLayout { 37 | width: parent.width 38 | 39 | TextField { 40 | id: passwordTextField 41 | placeholderText: qsTr("Enter password here") 42 | echoMode: showPasswordButton.pressed? TextField.Normal : TextField.Password 43 | Layout.fillWidth: true 44 | onAccepted: { 45 | keysPage.keyStore.password = text 46 | if (keysPage.keyStore.hasPersistedData) { 47 | if (keysPage.keyStore.restore()) 48 | passwordEntry.close() 49 | else { 50 | passwordLabel.text = qsTr("That password's no good. Try again?") 51 | keysPage.keyStore.password = "" 52 | } 53 | } else { 54 | keysPage.keyStore.persist() 55 | passwordEntry.close() 56 | } 57 | } 58 | Component.onCompleted: forceActiveFocus() 59 | } 60 | Button { 61 | id: showPasswordButton 62 | text: qsTr("Reveal") 63 | onReleased: passwordTextField.forceActiveFocus() 64 | onDoubleClicked: { 65 | if (passwordTextField.text === "I want to reset all my data") { 66 | keysPage.keyStore.resetPersistence() 67 | passwordTextField.text = "" 68 | } 69 | } 70 | } 71 | } 72 | Label { 73 | id: passwordLabel 74 | text: qsTr("This password is used to encrypt your keys") 75 | } 76 | } 77 | } 78 | 79 | StackView { 80 | id: mainStack 81 | anchors.fill: parent 82 | 83 | initialItem: MyKeysPage { 84 | id: keysPage 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /vendor/QuickPromise/tests/unittests/tst_promise_resolve_signal.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtTest 1.0 3 | import QuickPromise 1.0 4 | 5 | TestCase { 6 | name : "Promise_Resolve_Signal" 7 | 8 | Item { 9 | id: customItem 10 | signal emitted() 11 | } 12 | 13 | Timer { 14 | id: builtInItem 15 | repeat: false; 16 | interval: 50 17 | } 18 | 19 | function test_type() { 20 | compare(typeof customItem.emitted,"function"); 21 | compare(typeof customItem.emitted.connect,"function"); 22 | compare(typeof customItem.emitted.disconnect,"function"); 23 | compare(typeof customItem.emitted.hasOwnProperty,"function"); 24 | 25 | compare(typeof customItem.onEmitted,"object"); // That is the differnet between custom type and built-in type 26 | compare(typeof customItem.onEmitted.connect,"function"); 27 | compare(typeof customItem.onEmitted.disconnect,"function"); 28 | compare(typeof customItem.onEmitted.hasOwnProperty,"function") 29 | 30 | compare(typeof builtInItem.triggered,"function"); 31 | compare(typeof builtInItem.triggered.connect,"function"); 32 | compare(typeof builtInItem.triggered.disconnect,"function"); 33 | compare(typeof builtInItem.triggered.hasOwnProperty,"function"); 34 | 35 | compare(typeof builtInItem.onTriggered,"function"); 36 | compare(typeof builtInItem.onTriggered.connect,"function"); 37 | compare(typeof builtInItem.onTriggered.disconnect,"function"); 38 | compare(typeof builtInItem.onTriggered.hasOwnProperty,"function"); 39 | } 40 | 41 | Promise { 42 | id : promise 43 | } 44 | 45 | function test_instanceOfSignal() { 46 | compare(promise._instanceOfSignal(0),false); 47 | compare(promise._instanceOfSignal(3),false); 48 | compare(promise._instanceOfSignal({}),false); 49 | compare(promise._instanceOfSignal(""),false); 50 | 51 | compare(promise._instanceOfSignal(customItem.emitted),true); 52 | compare(promise._instanceOfSignal(customItem.onEmitted),true); 53 | 54 | compare(promise._instanceOfSignal(builtInItem.triggered),true); 55 | compare(promise._instanceOfSignal(builtInItem.onTriggered),true); 56 | } 57 | 58 | Component { 59 | id : promiseCreator 60 | Promise { 61 | 62 | } 63 | } 64 | 65 | function run(sig,resolve,timeout) { 66 | var promise = promiseCreator.createObject(); 67 | compare(promise._instanceOfSignal(sig),true); 68 | compare(promise.isFulfilled,false); 69 | promise.resolve(sig); 70 | compare(promise.isFulfilled,false); 71 | wait(timeout); 72 | compare(promise.isFulfilled,false); 73 | 74 | resolve(); 75 | 76 | wait(timeout); 77 | compare(promise.isFulfilled,true); 78 | } 79 | 80 | function test_resolve_signal() { 81 | run(customItem.emitted,function() {customItem.emitted();},50); 82 | run(customItem.onEmitted,function() {customItem.emitted();},50); 83 | run(builtInItem.triggered,function() {builtInItem.start();},100); 84 | run(builtInItem.onTriggered,function() {builtInItem.start();},100); 85 | } 86 | 87 | 88 | } 89 | 90 | -------------------------------------------------------------------------------- /vendor/QuickPromise/QuickPromise/combinator.js: -------------------------------------------------------------------------------- 1 | .pragma library 2 | .import "./promise.js" as PromiseJS 3 | 4 | // Combinate a list of promises into a single promise object. 5 | function Combinator(promises,allSettled) { 6 | this._combined = new PromiseJS.Promise(); 7 | this._allSettled = allSettled === undefined ? false : allSettled; 8 | this._promises = []; 9 | this._results = []; 10 | this._count = 0; 11 | 12 | // Any promise rejected? 13 | this._rejected = false; 14 | this._rejectReason = undefined; 15 | 16 | this.add(promises); 17 | 18 | this._settle(); 19 | } 20 | 21 | Combinator.prototype.add = function(promises) { 22 | if (PromiseJS.instanceOfPromise(promises)) { 23 | this._addPromise(promises); 24 | } else { 25 | this._addPromises(promises); 26 | } 27 | return this.combined; 28 | } 29 | 30 | Combinator.prototype._addPromises = function(promises) { 31 | for (var i = 0 ; i < promises.length;i++) { 32 | this._addPromise(promises[i]); 33 | } 34 | } 35 | 36 | Combinator.prototype._addPromise = function(promise) { 37 | if (PromiseJS._instanceOfSignal(promise)) { 38 | var delegate = new PromiseJS.Promise(); 39 | delegate.resolve(promise); 40 | this._addCheckedPromise(delegate); 41 | } else if (promise.isSettled) { 42 | if (promise.isRejected) { 43 | this._reject(promise._result); 44 | } 45 | // calling `resolve` after adding resolved promises is covered 46 | // by the call to `_settle` in constructor 47 | } else { 48 | this._addCheckedPromise(promise); 49 | } 50 | } 51 | 52 | Combinator.prototype._addCheckedPromise = function(promise) { 53 | var combinator = this; 54 | 55 | var promiseIndex = this._promises.length; 56 | combinator._promises.push(promise); 57 | combinator._results.push(undefined); 58 | combinator._count++; 59 | 60 | promise.then(function(value) { 61 | combinator._count--; 62 | combinator._results[promiseIndex] = value; 63 | combinator._settle(); 64 | },function(reason) { 65 | combinator._count--; 66 | combinator._reject(reason); 67 | }); 68 | } 69 | 70 | Combinator.prototype._reject = function(reason) { 71 | if (this._allSettled) { 72 | this._rejected = true; 73 | this._rejectReason = reason; 74 | } else { 75 | this._combined.reject(reason); 76 | } 77 | } 78 | 79 | /// Check it match the settle condition, it will call resolve / reject on the combined promise. 80 | Combinator.prototype._settle = function() { 81 | if (this._count !== 0) { 82 | return; 83 | } 84 | 85 | if (this._rejected) { 86 | this._combined.reject(this._rejectedReason); 87 | } else { 88 | this._combined.resolve(this._results); 89 | } 90 | } 91 | 92 | function create(promises,allSettled) { 93 | return new Combinator(promises,allSettled); 94 | } 95 | 96 | function all(promises) { 97 | var combinator = new Combinator(promises,false); 98 | 99 | return combinator._combined; 100 | } 101 | 102 | /// Combine multiple promises into one. It will wait for all of the promises to either be fulfilled or rejected. 103 | function allSettled(promises) { 104 | var combinator = new Combinator(promises,true); 105 | 106 | return combinator._combined; 107 | } 108 | 109 | -------------------------------------------------------------------------------- /vendor/QuickPromise/QuickPromise/Promise.qml: -------------------------------------------------------------------------------- 1 | // Author: Ben Lau (https://github.com/benlau) 2 | import QtQuick 2.0 3 | import QtQml 2.2 4 | import QuickPromise 1.0 5 | import "promise.js" as PromiseJS 6 | import "combinator.js" as Combinator 7 | 8 | QtObject { 9 | id : promise 10 | 11 | default property alias data: promise.__data 12 | property list __data: [QtObject{}] 13 | 14 | property bool isFulfilled : false 15 | 16 | property bool isRejected : false 17 | 18 | property bool isSettled : isFulfilled || isRejected 19 | 20 | /// An expression that will trigger resolve() if the value become true or another promise object got resolved. 21 | property var resolveWhen 22 | 23 | /// An expression that will trigger reject() if the value become true. Don't assign another promise object here. 24 | property var rejectWhen 25 | 26 | signal fulfilled(var value) 27 | signal rejected(var reason) 28 | signal settled(var value) 29 | 30 | property var _promise; 31 | 32 | property var _result 33 | 34 | /// Random signature for type checking 35 | property var ___promiseQmlSignature71237___ 36 | 37 | function setTimeout(callback,interval) { 38 | QPTimer.setTimeout(callback,interval); 39 | } 40 | 41 | function then(onFulfilled,onRejected) { 42 | return _promise.then(onFulfilled,onRejected); 43 | } 44 | 45 | function resolve(value) { 46 | if (instanceOfPromise(value)) { 47 | value._init(); 48 | value = value._promise; 49 | } 50 | 51 | _promise.resolve(value); 52 | } 53 | 54 | function reject(reason) { 55 | _promise.reject(reason); 56 | } 57 | 58 | /// Combine multiple promises into a single promise. 59 | function all(promises) { 60 | return Combinator.all(promises); 61 | } 62 | 63 | /// Combine multiple promises into a single promise. It will wait for all of the promises to either be fulfilled or rejected. 64 | function allSettled(promises) { 65 | return Combinator.allSettled(promises); 66 | } 67 | 68 | function instanceOfPromise(object) { 69 | return typeof object === "object" && object.hasOwnProperty("___promiseQmlSignature71237___"); 70 | } 71 | 72 | function _instanceOfSignal(object) { 73 | return PromiseJS._instanceOfSignal(object); 74 | } 75 | 76 | function _init() { 77 | if (!_promise) 78 | _promise = PromiseJS.promise(); 79 | } 80 | 81 | onResolveWhenChanged: { 82 | _init(); 83 | 84 | if (resolveWhen === true) { 85 | resolve(resolveWhen); 86 | } else if (instanceOfPromise(resolveWhen) || 87 | PromiseJS.instanceOfPromiseJS(resolveWhen) || 88 | _instanceOfSignal(resolveWhen)) { 89 | resolve(resolveWhen); 90 | } 91 | } 92 | 93 | onRejectWhenChanged: { 94 | _init(); 95 | if (rejectWhen === true) { 96 | reject(true); 97 | } else if (_instanceOfSignal(rejectWhen)) { 98 | reject(rejectWhen); 99 | } 100 | } 101 | 102 | Component.onCompleted: { 103 | _init(); 104 | 105 | _promise.then(function(value) { 106 | isFulfilled = true; 107 | fulfilled(value); 108 | settled(value); 109 | },function(reason) { 110 | isRejected = true; 111 | rejected(reason); 112 | settled(reason); 113 | }); 114 | } 115 | 116 | } 117 | 118 | -------------------------------------------------------------------------------- /res/steem.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 60 | 64 | 66 | 69 | 71 | 73 | 78 | 83 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /KeyStore.cpp: -------------------------------------------------------------------------------- 1 | #include "KeyStore.hpp" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | KeyStore::KeyStore(QObject *parent) 9 | : QObject(parent), 10 | m_accountList(new QQmlObjectListModel(this)){ 11 | connect(m_accountList, &QQmlObjectListModel::rowsInserted, this, &KeyStore::persist); 12 | connect(m_accountList, &QQmlObjectListModel::rowsRemoved, this, &KeyStore::persist); 13 | } 14 | 15 | QString KeyStore::accountUnsupportedReason(QVariantMap account) { 16 | if (!(account.contains("name") && account.contains("owner") && account.contains("active") && 17 | account.contains("posting") && account.contains("memo_key"))) 18 | return tr("Account is malformed"); 19 | if (account["name"].toString().isEmpty()) 20 | return tr("Account has no name"); 21 | if (!(KeyPair::isSupportedAuthority(account["owner"].toMap()) && 22 | KeyPair::isSupportedAuthority(account["active"].toMap()) && 23 | KeyPair::isSupportedAuthority(account["posting"].toMap()))) 24 | return tr("Account contains multisig authorities"); 25 | return QString::null; 26 | } 27 | 28 | AccountKeys* KeyStore::findAccount(QString accountName) { 29 | auto itr = std::find_if(m_accountList->begin(), m_accountList->end(), [accountName](const AccountKeys* keys) { 30 | return keys->name() == accountName; 31 | }); 32 | 33 | if (itr == m_accountList->end()) 34 | return nullptr; 35 | return *itr; 36 | } 37 | 38 | AccountKeys* KeyStore::addAccount(QVariantMap account) { 39 | if (!accountUnsupportedReason(account).isEmpty()) 40 | return nullptr; 41 | 42 | AccountKeys* accountKeys(findAccount(account["name"].toString())); 43 | if (accountKeys == nullptr) { 44 | accountKeys = new AccountKeys(this); 45 | connect(accountKeys, &AccountKeys::updated, this, &KeyStore::persist); 46 | m_accountList->append(accountKeys); 47 | } 48 | 49 | accountKeys->setName(account["name"].toString()); 50 | accountKeys->ownerKey()->fromAuthority(account["owner"].toMap()); 51 | accountKeys->activeKey()->fromAuthority(account["active"].toMap()); 52 | accountKeys->postingKey()->fromAuthority(account["posting"].toMap()); 53 | accountKeys->memoKey()->fromPublicKey(account["memo_key"].toString()); 54 | 55 | return accountKeys; 56 | } 57 | 58 | struct PersistenceData { 59 | uint64_t magic = 'nate'; 60 | std::map> accountsAndKeys; 61 | }; 62 | 63 | void KeyStore::persist() { 64 | if (m_password.isEmpty()) 65 | return; 66 | 67 | wdump(()); 68 | PersistenceData data; 69 | std::transform(m_accountList->begin(), m_accountList->end(), std::inserter(data.accountsAndKeys, 70 | data.accountsAndKeys.begin()), 71 | [](const AccountKeys* account) { 72 | return std::make_pair(account->name().toStdString(), 73 | std::vector{ 74 | account->ownerKey()->keyStore(), 75 | account->activeKey()->keyStore(), 76 | account->postingKey()->keyStore(), 77 | account->memoKey()->keyStore() 78 | }); 79 | }); 80 | 81 | auto buffer = fc::raw::pack(data); 82 | buffer = fc::aes_encrypt(fc::sha512::hash(m_password.toStdString()), std::move(buffer)); 83 | if (!hasPersistedData()) 84 | emit hasPersistedDataChanged(true); 85 | QSettings().setValue("storage", QByteArray::fromRawData(buffer.data(), buffer.size())); 86 | } 87 | 88 | void KeyStore::resetPersistence() { 89 | QSettings().remove("storage"); 90 | emit hasPersistedDataChanged(false); 91 | } 92 | 93 | bool KeyStore::hasPersistedData() { 94 | return QSettings().contains("storage"); 95 | } 96 | 97 | bool KeyStore::restore() { 98 | QString passwordBackup; 99 | try { 100 | std::swap(m_password, passwordBackup); 101 | auto storage = QSettings().value("storage"); 102 | if (storage == QVariant()) 103 | return false; 104 | auto bytes = storage.toByteArray(); 105 | auto buffer = fc::aes_decrypt(fc::sha512::hash(passwordBackup.toStdString()), 106 | std::vector(bytes.begin(), bytes.end())); 107 | auto data = fc::raw::unpack(buffer); 108 | if (data.magic != 'nate') 109 | return false; 110 | 111 | for (const auto& account : data.accountsAndKeys) { 112 | idump((account.first)); 113 | auto accountKeys = new AccountKeys(this); 114 | m_accountList->append(accountKeys); 115 | connect(accountKeys, &AccountKeys::updated, this, &KeyStore::persist); 116 | accountKeys->setName(QString::fromStdString(account.first)); 117 | accountKeys->ownerKey()->fromKeyStore(account.second[0]); 118 | accountKeys->activeKey()->fromKeyStore(account.second[1]); 119 | accountKeys->postingKey()->fromKeyStore(account.second[2]); 120 | accountKeys->memoKey()->fromKeyStore(account.second[3]); 121 | } 122 | 123 | std::swap(m_password, passwordBackup); 124 | } catch (fc::exception& e) { 125 | edump((e)); 126 | return false; 127 | } 128 | 129 | emit restored(); 130 | return true; 131 | } 132 | 133 | void KeyStore::setPassword(QString password) { 134 | if (m_password == password) 135 | return; 136 | 137 | m_password = password; 138 | emit passwordChanged(password); 139 | } 140 | 141 | FC_REFLECT(PersistenceData, (accountsAndKeys)) 142 | -------------------------------------------------------------------------------- /KeyPair.cpp: -------------------------------------------------------------------------------- 1 | #include "KeyPair.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | const QString KeyPair::KeyPrefix = QStringLiteral("STM"); 11 | 12 | struct binary_key { 13 | binary_key() {} 14 | uint32_t check = 0; 15 | fc::ecc::public_key_data data; 16 | }; 17 | FC_REFLECT(binary_key, (data)(check)) 18 | 19 | void KeyPair::setKey(KeyStore newKey) { 20 | std::swap(key, newKey); 21 | if (key.which() != newKey.which()) 22 | emit keyTypeChanged(keyType()); 23 | emit publicKeyChanged(publicKey()); 24 | emit wifKeyChanged(wifKey()); 25 | emit updated(); 26 | } 27 | 28 | KeyPair::KeyPair(QObject *parent) 29 | : QObject(parent), key(false) {} 30 | 31 | KeyPair& KeyPair::operator=(KeyPair&& other) { 32 | if (this->equals(&other) && other.keyType() != PrivateKey) 33 | return *this; 34 | 35 | setKey(std::move(other.key)); 36 | return *this; 37 | } 38 | 39 | KeyPair& KeyPair::operator=(const KeyPair& other) { 40 | if (this->equals(&other) && other.keyType() != PrivateKey) 41 | return *this; 42 | 43 | setKey(other.key); 44 | return *this; 45 | } 46 | 47 | void KeyPair::generateFromSeed(QString seed) { 48 | setKey(fc::ecc::private_key::regenerate(fc::sha256::hash(seed.toStdString()))); 49 | } 50 | 51 | void KeyPair::generateRandomly() { 52 | setKey(fc::ecc::private_key::generate()); 53 | } 54 | 55 | void KeyPair::fromPublicKey(QString publicKeyString) { 56 | if (publicKey() == publicKeyString) 57 | return; 58 | 59 | try { 60 | if (!publicKeyString.startsWith(KeyPrefix)) { 61 | qDebug() << "Cannot create KeyPair from public key string due to invalid prefix:" << publicKeyString; 62 | return setKey(false); 63 | } 64 | 65 | auto buffer = fc::from_base58(publicKeyString.mid(KeyPrefix.size()).toStdString()); 66 | auto keyData = fc::raw::unpack(buffer); 67 | if (fc::ripemd160::hash(keyData.data.data, keyData.data.size())._hash[0] != keyData.check) { 68 | qDebug() << "Cannot create KeyPair from public key string due to invalid checksum" << publicKeyString; 69 | return setKey(false); 70 | } 71 | 72 | setKey(fc::ecc::public_key(keyData.data)); 73 | } catch (fc::exception& e) { 74 | qDebug() << "Cannot create KeyPair from public key string due to exception" << publicKeyString 75 | << e.to_detail_string().c_str(); 76 | setKey(false); 77 | } 78 | } 79 | 80 | void KeyPair::fromWifKey(QString wifKey) { 81 | try { 82 | auto buffer = fc::from_base58(wifKey.toStdString()); 83 | if (buffer.size() < 5) { 84 | qDebug() << "Cannot create KeyPair from WIF due to undersized buffer" << buffer.size(); 85 | return setKey(false); 86 | } 87 | if (buffer[0] != '\x80') { 88 | qDebug() << "Cannot create KeyPair from WIF due to invalid prefix" << buffer.size(); 89 | return setKey(false); 90 | } 91 | auto easyBuffer = QByteArray::fromRawData(buffer.data(), buffer.size()); 92 | qDebug() << easyBuffer.toHex(); 93 | auto checksum = easyBuffer.right(4); 94 | easyBuffer.chop(4); 95 | 96 | auto keyHash = QCryptographicHash::hash(easyBuffer, QCryptographicHash::Sha256); 97 | qDebug() << keyHash.toHex(); 98 | auto reHash = QCryptographicHash::hash(keyHash, QCryptographicHash::Sha256); 99 | qDebug() << reHash.toHex(); 100 | 101 | if (keyHash.left(4) == checksum || reHash.left(4) == checksum) { 102 | buffer.resize(buffer.size() - 4); 103 | buffer.erase(buffer.begin()); 104 | return setKey(fc::variant(buffer).as()); 105 | } 106 | 107 | qDebug() << "Cannot create KeyPair from WIF due to invalid checksum"; 108 | setKey(false); 109 | } catch (fc::exception& e) { 110 | qDebug() << "Cannot create KeyPair from WIF due to exception" << e.to_detail_string().c_str(); 111 | setKey(false); 112 | } 113 | } 114 | 115 | void KeyPair::fromAuthority(QVariantMap authority) { 116 | if (!isSupportedAuthority(authority)) { 117 | qDebug() << "Cannot create KeyPair from unsupported authority" << authority; 118 | setKey(false); 119 | } 120 | 121 | auto keyAndWeight = authority["key_auths"].toList().first().toList(); 122 | if (keyAndWeight[1].toInt() < authority["weight_threshold"].toInt()) 123 | setKey(false); 124 | else 125 | fromPublicKey(keyAndWeight[0].toString()); 126 | } 127 | 128 | KeyPair* KeyPair::replaceWith(const KeyPair* other) { 129 | if (other == nullptr) { 130 | setKey(false); 131 | return this; 132 | } 133 | *this = *other; 134 | return this; 135 | } 136 | 137 | bool KeyPair::equals(const KeyPair* other) { 138 | if (keyType() == NullKey && other->keyType() == NullKey) 139 | return true; 140 | if (keyType() == NullKey || other->keyType() == NullKey) 141 | return false; 142 | return publicKey() == other->publicKey(); 143 | } 144 | 145 | QString KeyPair::publicKey() const { 146 | binary_key keyData; 147 | if (keyType() == PublicKey) 148 | keyData.data = key.get(); 149 | else if (keyType() == PrivateKey) 150 | keyData.data = key.get().get_public_key(); 151 | 152 | keyData.check = fc::ripemd160::hash(keyData.data.data, keyData.data.size())._hash[0]; 153 | auto buffer = fc::raw::pack(keyData); 154 | 155 | return QString::fromStdString(fc::to_base58(buffer)).prepend(KeyPrefix); 156 | } 157 | 158 | QString KeyPair::wifKey() const { 159 | std::vector buffer(256/8, 0); 160 | if (keyType() == PrivateKey) 161 | buffer = fc::variant(key.get()).as>(); 162 | else 163 | return tr("Unset"); 164 | 165 | auto easyBuffer = QByteArray::fromRawData(buffer.data(), buffer.size()).prepend('\x80'); 166 | auto checksum = QCryptographicHash::hash(easyBuffer, QCryptographicHash::Sha256); 167 | checksum = QCryptographicHash::hash(checksum, QCryptographicHash::Sha256).left(4); 168 | auto wifBuffer = easyBuffer + checksum; 169 | 170 | return QString::fromStdString(fc::to_base58(wifBuffer.data(), wifBuffer.size())); 171 | } 172 | 173 | QVariantMap KeyPair::toAuthority() const { 174 | return { 175 | {"weight_threshold", 1}, 176 | {"account_auths", QVariantList()}, 177 | {"key_auths", QVariantList { 178 | QVariantList {publicKey(), 1} 179 | } 180 | } 181 | }; 182 | } 183 | 184 | KeyPair::KeyType KeyPair::keyType() const { 185 | if (key.which() == decltype(key)::tag::value) 186 | return NullKey; 187 | if (key.which() == decltype(key)::tag::value) 188 | return PublicKey; 189 | return PrivateKey; 190 | } 191 | 192 | bool KeyPair::isSupportedAuthority(QVariantMap authority) { 193 | if (!authority["account_auths"].toList().isEmpty()) 194 | return false; 195 | if (authority["key_auths"].toList().size() != 1) 196 | return false; 197 | return true; 198 | } 199 | -------------------------------------------------------------------------------- /vendor/QuickPromise/QuickPromise/promise.js: -------------------------------------------------------------------------------- 1 | // Quick Promise Script 2 | // Author: Ben Lau (https://github.com/benlau) 3 | 4 | .pragma library 5 | .import QuickPromise 1.0 as QP 6 | 7 | /* JavaScript implementation of promise object 8 | */ 9 | 10 | function Promise() { 11 | 12 | this.state = "pending"; 13 | 14 | this._onFulfilled = []; 15 | this._onRejected = []; 16 | 17 | // The value of resolved promise or the reason to be rejected. 18 | this._result = undefined; 19 | 20 | this.___promiseSignature___ = true; 21 | 22 | this.isSettled = false; 23 | this.isFulfilled = false; 24 | this.isRejected = false; 25 | } 26 | 27 | function instanceOfPromiseJS(object) { 28 | return typeof object === "object" && 29 | typeof object.hasOwnProperty === "function" && 30 | object.hasOwnProperty("___promiseSignature___"); 31 | } 32 | 33 | function instanceOfPromiseItem(object) { 34 | return typeof object === "object" && 35 | typeof object.hasOwnProperty === "function" && 36 | object.hasOwnProperty("___promiseQmlSignature71237___"); 37 | } 38 | 39 | function instanceOfPromise(object) { 40 | return instanceOfPromiseJS(object) || instanceOfPromiseItem(object) 41 | } 42 | 43 | function _instanceOfSignal(object) { 44 | return (typeof object === "object" || 45 | typeof object === "function") && 46 | typeof object.hasOwnProperty === "function" && 47 | typeof object.connect === "function" && 48 | typeof object.disconnect === "function"; 49 | } 50 | 51 | Promise.prototype.then = function(onFulfilled,onRejected) { 52 | var thenPromise = new Promise(); 53 | 54 | this._onFulfilled.push(function(value) { 55 | if (typeof onFulfilled === "function" ) { 56 | try { 57 | var x = onFulfilled(value); 58 | thenPromise.resolve(x); 59 | } catch (e) { 60 | console.error(e); 61 | console.trace(); 62 | thenPromise.reject(e); 63 | } 64 | 65 | } else { 66 | // 2.2.7.3 67 | thenPromise.resolve(value); 68 | } 69 | }); 70 | 71 | this._onRejected.push(function(reason) { 72 | if (typeof onRejected === "function") { 73 | try { 74 | var x = onRejected(reason); 75 | thenPromise.resolve(x); 76 | } catch (e) { 77 | console.error(e); 78 | console.trace(); 79 | thenPromise.reject(e); 80 | } 81 | } else { 82 | // 2.2.7.4 83 | thenPromise.reject(reason); 84 | } 85 | }); 86 | 87 | return thenPromise; 88 | } 89 | 90 | 91 | Promise.prototype.resolve = function(value) { 92 | if (this.state !== "pending") 93 | return; 94 | 95 | if (this === value) { // 2.3.1 96 | this.reject(new TypeError("Promise.resolve(value) : The value can not be same as the promise object.")); 97 | return; 98 | } 99 | 100 | var promise = this; 101 | 102 | if (value && _instanceOfSignal(value)) { 103 | try { 104 | var newPromise = new Promise(); 105 | value.connect(function() { 106 | newPromise.resolve(); 107 | }); 108 | value = newPromise; 109 | } catch (e) { 110 | console.error(e); 111 | throw new Error("promise.resolve(object): Failed to call object.connect(). Are you passing the result of Qt.binding()? Please use QML Promise and pass it to resolveWhen property."); 112 | } 113 | } 114 | 115 | if (value && 116 | instanceOfPromise(value)) { 117 | 118 | if (value.state === "pending") { 119 | value.then(function(x) { 120 | promise._resolveInTick(x); 121 | },function(x) { 122 | promise.reject(x); 123 | }); 124 | 125 | return; 126 | } else if (value.state === "fulfilled") { 127 | this._resolveInTick(value._result); 128 | return; 129 | } else if (value.state === "rejected") { 130 | this.reject(value._result); 131 | return; 132 | } 133 | } 134 | 135 | if (value !== null && (typeof value === "object" || typeof value === "function")) { 136 | var then; 137 | try { 138 | then = value.then; 139 | } catch (e) { 140 | console.error(e); 141 | console.trace(); 142 | promise.reject(e); 143 | return; 144 | } 145 | 146 | if (typeof then === "function") { 147 | try { 148 | var called = false; 149 | then.call(value,function(y) { 150 | if (called) 151 | return; 152 | called = true; 153 | promise.resolve(y); 154 | },function (y) { 155 | if (called) 156 | return; 157 | 158 | called = true; 159 | promise.reject(y); 160 | }); 161 | } catch (e) { 162 | console.error(e); 163 | console.trace(); 164 | if (!called) { 165 | promise.reject(e); 166 | } 167 | } 168 | return; 169 | } 170 | } 171 | 172 | this._resolveInTick(value); 173 | } 174 | 175 | Promise.prototype._resolveInTick = function(value) { 176 | var promise = this; 177 | 178 | QP.QPTimer.setTimeout(function() { 179 | if (promise.state !== "pending") 180 | return; 181 | 182 | promise._resolveUnsafe(value); 183 | },0); 184 | } 185 | 186 | /* 187 | Resolve without value type checking 188 | */ 189 | 190 | Promise.prototype._resolveUnsafe = function(value) { 191 | 192 | this._emit(this._onFulfilled,value); 193 | 194 | this._result = value; 195 | this._setState("fulfilled"); 196 | } 197 | 198 | Promise.prototype.reject = function(reason) { 199 | if (this.state !== "pending") 200 | return; 201 | 202 | var promise = this; 203 | 204 | if (reason && _instanceOfSignal(reason)) { 205 | reason.connect(function() { 206 | promise.reject(); 207 | }); 208 | return; 209 | } 210 | 211 | QP.QPTimer.setTimeout(function() { 212 | if (promise.state !== "pending") 213 | return; 214 | 215 | promise._rejectUnsafe(reason); 216 | },0); 217 | } 218 | 219 | Promise.prototype._rejectUnsafe = function(reason) { 220 | this._emit(this._onRejected,reason); 221 | 222 | this._result = reason; 223 | this._setState("rejected"); 224 | } 225 | 226 | Promise.prototype._emit = function(arr,value) { 227 | var res = value; 228 | for (var i = 0 ; i < arr.length;i++) { 229 | var func = arr[i]; 230 | if (typeof func !== "function") 231 | continue; 232 | var t = func(value); 233 | if (t) // It is non-defined behaviour in A+ 234 | res = t; 235 | } 236 | return res; 237 | } 238 | 239 | Promise.prototype._setState = function(state) { 240 | if (state === "fulfilled") { 241 | this.isFulfilled = true; 242 | this.isSettled = true; 243 | this._onFullfilled = []; 244 | this._onRejected = []; 245 | } else if (state === "rejected"){ 246 | this.isRejected = true; 247 | this.isSettled = true; 248 | this._onFullfilled = []; 249 | this._onRejected = []; 250 | } 251 | this.state = state; 252 | } 253 | 254 | 255 | function promise() { 256 | return new Promise(); 257 | } 258 | -------------------------------------------------------------------------------- /vendor/QtQmlModels/QQmlObjectListModel.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "QQmlObjectListModel.h" 3 | 4 | /*! 5 | \class QQmlObjectListModel 6 | 7 | \ingroup QT_QML_MODELS 8 | 9 | \brief Provides a generic way to generate a list model from QObject derived class, suitable for QML 10 | 11 | QQmlObjectListModel is a convenience subclass \c QAbstractListModel that makes use of C++ templates 12 | and Qt Meta Object to extract properties from a \c QObject derived class and create according roles 13 | inside the model. 14 | 15 | This is a far better way than to expose directly a \c QList inside a \c QVariant. 16 | 17 | And this is far simpler than doing all Qt model stuff manually : no subclassing or reimplementing need. 18 | 19 | The class was designed so that most of the added API is really common with \c QList one. 20 | 21 | \b Note : Simply needs that the class used for items inherits \c QObject and has Qt Meta Properties. 22 | 23 | \sa QQmlVariantListModel 24 | */ 25 | 26 | /*! 27 | \fn static QQmlObjectListModel * QQmlObjectListModel::create (QObject * parent = Q_NULLPTR) 28 | 29 | \details A factory to create a new model from a class that will be used as item type. 30 | 31 | \tparam ItemType The class to use as item 32 | \param parent The owner object for the model 33 | \return The newly created and configurerd model 34 | 35 | This is a template method, meant to be used like this : 36 | \code 37 | QQmlObjectListModel * myModel = QQmlObjectListModel::create(this); 38 | \endcode 39 | 40 | No other customization in needed after that. 41 | */ 42 | 43 | /*! 44 | \fn ItemType * QQmlObjectListModel::getAs () const 45 | 46 | \details A template method to retreive a given item as a precise \c T* Qt object pointer. 47 | 48 | \tparam ItemType The class to use as return pointer type 49 | \param idx The position of the item in the model 50 | \return The typed pointer to the object, or \c Q_NULLPTR if the type doesn't match 51 | 52 | \sa get(int) const, getByUid(QString) const 53 | */ 54 | 55 | /*! 56 | \fn QList QQmlObjectListModel::listAs () const 57 | 58 | \details A template method to retreive all the items as \c QList typed Qt object pointer list. 59 | 60 | \tparam ItemType The class as object type to use in the returned pointer list 61 | \return A strongly typed \c QList of items Qt object pointers 62 | 63 | \sa list() const 64 | */ 65 | 66 | 67 | /*! 68 | \details Returns the data in a specific index for a given role. 69 | 70 | Reimplemented for QAbstractItemModel. 71 | 72 | \param index The item index (row, column and parent) 73 | \param role The role for property 74 | \return The data in the role 75 | 76 | \b Note : the \c 0 role is a pointer to item object itself. 77 | */ 78 | 79 | /*! 80 | \details Returns the roles available in the model. 81 | 82 | Reimplemented for QAbstractItemModel. 83 | 84 | \return The hash table of role to name matching 85 | 86 | \b Note : an additional \c 'qtObject' role is added for convenience. 87 | */ 88 | 89 | /*! 90 | \details Modifies the data in a specific index for a given role. 91 | 92 | Reimplemented for QAbstractItemModel. 93 | 94 | \param index The item index (row, column and parent) 95 | \param value The data to write 96 | \param role The role for property 97 | \return Weither the modification was done 98 | */ 99 | 100 | /*! 101 | \details Returns the role associated to the given property name. 102 | 103 | \param name The property name inside the item class 104 | \return The matching role, \c -1 if not found 105 | */ 106 | 107 | 108 | /*! 109 | \details Counts the items in the model. 110 | 111 | \return The count of items in the model 112 | */ 113 | 114 | /*! 115 | \details Counts the items in the model. 116 | 117 | \return The count of items in the model 118 | */ 119 | /*! 120 | \details Tests the content of the model. 121 | 122 | \return Whether the model contains no item 123 | */ 124 | 125 | /*! 126 | \details Tests the presence of a given item in the model. 127 | 128 | \param item The pointer to the item 129 | \return Whether the item was found 130 | */ 131 | 132 | /*! 133 | \details Finds the position of given item in the model. 134 | 135 | \param item The pointer to the item 136 | \return The row index of the item, \c -1 if not found 137 | */ 138 | 139 | /*! 140 | \details Delete all the items in the model. 141 | 142 | \b Note : The items objects will be removed from the model but they will be destructed 143 | only if they have no parent (because the model took the ownership). 144 | */ 145 | 146 | 147 | /*! 148 | \details Adds the given item at the end of the model. 149 | 150 | \param item The pointer to the item 151 | 152 | \sa prepend(QObject*), insert(int,QObject*) 153 | */ 154 | 155 | 156 | /*! 157 | \details Adds the given item at the beginning of the model. 158 | 159 | \param item The pointer to the item 160 | 161 | \sa append(QObject*), insert(int,QObject*) 162 | */ 163 | 164 | 165 | /*! 166 | \details Adds the given item at a certain position in the model. 167 | 168 | \param idx The position where the item must be added 169 | \param item The pointer to the item 170 | 171 | \sa append(QObject*), prepend(QObject*) 172 | */ 173 | 174 | 175 | /*! 176 | \details Adds the given list of items at the end of the model. 177 | 178 | \param itemList The list of items 179 | 180 | \sa prepend(QObjectList), insert(int, QObjectList) 181 | */ 182 | 183 | 184 | /*! 185 | \details Adds the given list of items at the beginning of the model. 186 | 187 | \param itemList The list of items 188 | 189 | \sa append(QObjectList), insert(int, QObjectList) 190 | */ 191 | 192 | 193 | /*! 194 | \details Adds the given list of items at a certain position in the model. 195 | 196 | \param idx The position where the items must be added 197 | \param itemList The list of items 198 | 199 | \sa append(QObjectList), prepend(QObjectList) 200 | */ 201 | 202 | /*! 203 | \details Moves an item from the model to another position. 204 | 205 | \param idx The current position of the item 206 | \param pos The position where it willl be after the move 207 | */ 208 | 209 | 210 | /*! 211 | \details Remove an item from the model. 212 | 213 | \param item The pointer to the item object 214 | */ 215 | 216 | 217 | /*! 218 | \details Remove an item from the model. 219 | 220 | \param idx The position of the item in the model 221 | */ 222 | 223 | 224 | /*! 225 | \details Retreives a model item as standard Qt object pointer. 226 | 227 | \param idx The position of the item in the model 228 | \return A pointer to the \c QObject 229 | 230 | \sa getAs(int) const, getByUid(QString) const 231 | */ 232 | 233 | /*! 234 | \details Retreives the first item of the model as standard Qt object pointer. 235 | 236 | \return A pointer to the \c QObject 237 | 238 | \sa last() 239 | */ 240 | 241 | /*! 242 | \details Retreives the last item of the model as standard Qt object pointer. 243 | 244 | \return A pointer to the \c QObject 245 | 246 | \sa first() 247 | */ 248 | 249 | /*! 250 | \details Retreives all the items of the model as a standard Qt object pointer list. 251 | 252 | \return A \c QObjectList containing all the pointers 253 | 254 | \sa listAs() const 255 | */ 256 | 257 | /*! 258 | \details Retreives a model item as standard Qt object pointer using its indexed property. 259 | Works only if setRoleNameForUid() was used correctly at start. 260 | 261 | \param uid The identifier value that points to the item in the index 262 | \return A pointer to the \c QObject 263 | 264 | \sa getAs(int) const, get(int) const 265 | */ 266 | 267 | /*! 268 | \details Sets which property of the items will be used as an index key. 269 | This can be used or not, but if not, getByUid() won't work. 270 | 271 | Ideally, the property used for UID should not change after items are added 272 | to the model, because it could have some side-effects. 273 | 274 | \param name The name of the property / role that is used as the index key 275 | */ 276 | -------------------------------------------------------------------------------- /vendor/QtQmlModels/QQmlVariantListModel.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "QQmlVariantListModel.h" 3 | 4 | #define NO_PARENT QModelIndex () 5 | #define BASE_ROLE Qt::UserRole 6 | #define EMPTY_STR QStringLiteral ("") 7 | #define EMPTY_BA QByteArrayLiteral ("") 8 | 9 | /*! 10 | \class QQmlVariantListModel 11 | 12 | \ingroup QT_QML_MODELS 13 | 14 | \brief Provides a generic way to generate a list model from QVariant, suitable for QML 15 | 16 | QQmlVariantListModel is a convenience subclass \c QAbstractListModel that makes use of the versatile 17 | nature of QVariant to allow creating a list model from every type : 18 | \li Booleans 19 | \li Numbers 20 | \li Strings 21 | \li Lists 22 | \li Maps 23 | \li Object pointers 24 | \li etc... 25 | 26 | This is a far better way than to expose directly a \c QList<____> inside a \c QVariant. 27 | 28 | And this is far simpler than doing all Qt model stuff manually : no subclassing or reimplementing need. 29 | 30 | The class was designed so that most of the added API is really common with \c QList one. 31 | 32 | \b Note : Simply needs that the type items inherits is handled by Qt MetaType system and \c QVariant. 33 | 34 | \sa QQmlObjectListModel 35 | */ 36 | 37 | 38 | /*! 39 | \details Constructs a new model that will hold QVariant as items. 40 | 41 | \param parent The parent object for the model memory management 42 | */ 43 | QQmlVariantListModel::QQmlVariantListModel (QObject * parent) : QAbstractListModel (parent) 44 | , m_count(0) 45 | , m_items() 46 | , m_roles() 47 | { 48 | m_roles.insert (BASE_ROLE, QByteArrayLiteral ("qtVariant")); 49 | } 50 | 51 | /*! 52 | \internal 53 | */ 54 | QQmlVariantListModel::~QQmlVariantListModel (void) { 55 | clear (); 56 | } 57 | 58 | /*! 59 | \internal 60 | */ 61 | int QQmlVariantListModel::rowCount (const QModelIndex & parent) const 62 | { 63 | Q_UNUSED (parent); 64 | return m_items.count (); 65 | } 66 | 67 | /*! 68 | \details Returns the data in a specific index for a given role. 69 | 70 | Reimplemented for QAbstractItemModel. 71 | 72 | \param index The item index (row, column and parent) 73 | \param role The role 74 | \return The data in the role 75 | 76 | \b Note : the \c 0 role contains the QVariant itself. 77 | */ 78 | QVariant QQmlVariantListModel::data (const QModelIndex & index, int role) const 79 | { 80 | QVariant ret; 81 | int idx = index.row (); 82 | if (idx >= 0 && idx < count () && role == BASE_ROLE) { 83 | ret = m_items.value (idx); 84 | } 85 | return ret; 86 | } 87 | 88 | /*! 89 | \details Returns the roles available in the model. 90 | 91 | Reimplemented for QAbstractItemModel. 92 | 93 | \return The hash table of role to name matching 94 | 95 | \b Note : the only role is \c 'qtVariant'. 96 | */ 97 | QHash QQmlVariantListModel::roleNames () const 98 | { 99 | return m_roles; 100 | } 101 | 102 | /*! 103 | \details Modifies the data in a specific index for a given role. 104 | 105 | Reimplemented for QAbstractItemModel. 106 | 107 | \param index The item index (row, column and parent) 108 | \param value The data to write 109 | \param role The role 110 | \return Weither the modification was done 111 | 112 | \b Note : as the only role is \c 0 ('qtVariant'), it replaces the QVariant value 113 | */ 114 | bool QQmlVariantListModel::setData (const QModelIndex & index, const QVariant & value, int role) 115 | { 116 | bool ret = false; 117 | int idx = index.row (); 118 | if (idx >= 0 && idx < count () && role == BASE_ROLE) { 119 | m_items.replace (idx, value); 120 | QModelIndex item = QAbstractListModel::index (idx, 0, NO_PARENT); 121 | emit dataChanged (item, item, QVector (1, role)); 122 | ret = true; 123 | } 124 | return ret; 125 | } 126 | 127 | /*! 128 | \details Counts the items in the model. 129 | 130 | \return The count of items in the model 131 | */ 132 | int QQmlVariantListModel::count () const 133 | { 134 | return m_items.size (); 135 | } 136 | 137 | /*! 138 | \details Tests the content of the model. 139 | 140 | \return Whether the model contains no item 141 | */ 142 | bool QQmlVariantListModel::isEmpty () const 143 | { 144 | return m_items.isEmpty (); 145 | } 146 | 147 | /*! 148 | \details Delete all the items in the model. 149 | */ 150 | void QQmlVariantListModel::clear () 151 | { 152 | if (!m_items.isEmpty ()) { 153 | beginRemoveRows (NO_PARENT, 0, count () -1); 154 | m_items.clear (); 155 | endRemoveRows (); 156 | updateCounter (); 157 | } 158 | } 159 | 160 | /*! 161 | \details Adds the given item at the end of the model. 162 | 163 | \param item The variant value 164 | 165 | \sa prepend(QVariant), insert(int,QVariant) 166 | */ 167 | void QQmlVariantListModel::append (const QVariant & item) 168 | { 169 | int pos = m_items.count (); 170 | beginInsertRows (NO_PARENT, pos, pos); 171 | m_items.append (item); 172 | endInsertRows (); 173 | updateCounter (); 174 | } 175 | 176 | /*! 177 | \details Adds the given item at the beginning of the model. 178 | 179 | \param item The variant value 180 | 181 | \sa append(QVariant), insert(int,QVariant) 182 | */ 183 | void QQmlVariantListModel::prepend (const QVariant & item) 184 | { 185 | beginInsertRows (NO_PARENT, 0, 0); 186 | m_items.prepend (item); 187 | endInsertRows (); 188 | updateCounter (); 189 | } 190 | 191 | /*! 192 | \details Adds the given item at a certain position in the model. 193 | 194 | \param idx The position where the item must be added 195 | \param item The variant value 196 | 197 | \sa append(QVariant), prepend(QVariant) 198 | */ 199 | void QQmlVariantListModel::insert (int idx, const QVariant & item) 200 | { 201 | beginInsertRows (NO_PARENT, idx, idx); 202 | m_items.insert (idx, item); 203 | endInsertRows (); 204 | updateCounter (); 205 | } 206 | 207 | /*! 208 | \details Replace the variant at a certain position in the model with another value. 209 | 210 | \param pos The position where the item must be replaced 211 | \param item The variant value 212 | 213 | \b Note : this is the regular way in C++ to modify the variant value. 214 | */ 215 | void QQmlVariantListModel::replace (int pos, const QVariant & item) 216 | { 217 | if (pos >= 0 && pos < count ()) { 218 | m_items.replace (pos, item); 219 | QModelIndex index = QAbstractListModel::index (pos, 0, NO_PARENT); 220 | emit dataChanged (index, index, QVector (1, BASE_ROLE)); 221 | } 222 | } 223 | 224 | /*! 225 | \details Adds the given list of items at the end of the model. 226 | 227 | \param itemList The list of items 228 | 229 | \sa prepend(QVariantList), insert(int, QVariantList) 230 | */ 231 | void QQmlVariantListModel::appendList (const QVariantList & itemList) 232 | { 233 | if (!itemList.isEmpty ()) { 234 | int pos = m_items.count (); 235 | beginInsertRows (NO_PARENT, pos, pos + itemList.count () -1); 236 | m_items.append (itemList); 237 | endInsertRows (); 238 | updateCounter (); 239 | } 240 | } 241 | 242 | /*! 243 | \details Adds the given list of items at the beginning of the model. 244 | 245 | \param itemList The list of items 246 | 247 | \sa append(QVariantList), insert(int, QVariantList) 248 | */ 249 | void QQmlVariantListModel::prependList (const QVariantList & itemList) 250 | { 251 | if (!itemList.isEmpty ()) { 252 | beginInsertRows (NO_PARENT, 0, itemList.count () -1); 253 | int offset = 0; 254 | foreach (QVariant item, itemList) { 255 | m_items.insert (offset, item); 256 | } 257 | endInsertRows (); 258 | updateCounter (); 259 | } 260 | } 261 | 262 | /*! 263 | \details Adds the given list of items at a certain position in the model. 264 | 265 | \param idx The position where the items must be added 266 | \param itemList The list of items 267 | 268 | \sa append(QVariantList), prepend(QVariantList) 269 | */ 270 | void QQmlVariantListModel::insertList (int idx, const QVariantList & itemList) 271 | { 272 | if (!itemList.isEmpty ()) { 273 | beginInsertRows (NO_PARENT, idx, idx + itemList.count () -1); 274 | int offset = 0; 275 | foreach (QVariant item, itemList) { 276 | m_items.insert (idx + offset, item); 277 | offset++; 278 | } 279 | endInsertRows (); 280 | updateCounter (); 281 | } 282 | } 283 | 284 | /*! 285 | \details Moves an item from the model to another position. 286 | 287 | \param idx The current position of the item 288 | \param pos The position where it willl be after the move 289 | */ 290 | void QQmlVariantListModel::move (int idx, int pos) 291 | { 292 | beginMoveRows (NO_PARENT, idx, idx, NO_PARENT, pos); 293 | m_items.move (idx, pos); 294 | endMoveRows (); 295 | } 296 | 297 | /*! 298 | \details Remove an item from the model. 299 | 300 | \param idx The position of the item in the model 301 | */ 302 | void QQmlVariantListModel::remove (int idx) 303 | { 304 | if (idx >= 0 && idx < m_items.size ()) { 305 | beginRemoveRows (NO_PARENT, idx, idx); 306 | m_items.removeAt (idx); 307 | endRemoveRows (); 308 | updateCounter (); 309 | } 310 | } 311 | 312 | /*! 313 | \details Retreives a model item as a standard Qt variant object. 314 | 315 | \param idx The position of the item in the model 316 | \return A variant containing the item 317 | */ 318 | QVariant QQmlVariantListModel::get (int idx) const 319 | { 320 | QVariant ret; 321 | if (idx >= 0 && idx < m_items.size ()) { 322 | ret = m_items.value (idx); 323 | } 324 | return ret; 325 | } 326 | 327 | /*! 328 | \details Retreives all the items of the model as a standard Qt variant list. 329 | 330 | \return A \c QVariantList containing all the variants 331 | */ 332 | QVariantList QQmlVariantListModel::list () const 333 | { 334 | return m_items; 335 | } 336 | 337 | /*! 338 | \internal 339 | */ 340 | void QQmlVariantListModel::updateCounter () 341 | { 342 | if (m_count != m_items.count ()) { 343 | m_count = m_items.count (); 344 | emit countChanged (m_count); 345 | } 346 | } 347 | -------------------------------------------------------------------------------- /vendor/QuickPromise/README.md: -------------------------------------------------------------------------------- 1 | 2 | Promises/A+ logo 4 | 5 | Quick Promise - QML Promise Library 6 | =================================== 7 | [![Build Status](https://travis-ci.org/benlau/quickpromise.svg?branch=master)](https://travis-ci.org/benlau/quickpromise) 8 | 9 | The Promise object is widely used for deferred and asynchronous computation in Javascript Application. "Quick Promise” is a library that provides Promise object in a QML way. It comes with a Promise component with signal and property. It could be resolved via a binary expression, another promise object, then trigger your callback by QueuedConnection. 10 | 11 | Moreover, it also provides Promise as a Javascript object that don’t need to declare in QML way. The API is fully compliant with [Promises/A+](https://promisesaplus.com/) specification with all the test cases passed and therefore it just works like many other Promise solutions for Javascript application. 12 | 13 | *Example:* 14 | 15 | ``` 16 | import QtQuick 2.0 17 | import QuickPromise 1.0 18 | 19 | Item { 20 | id: item 21 | opacity: 0 22 | 23 | Image { 24 | id : image 25 | asynchronous: true 26 | source : "https://lh3.googleusercontent.com/_yimBU8uLUTqQQ9gl5vODqDufdZ_cJEiKnx6O6uNkX6K9lT63MReAWiEonkAVatiPxvWQu7GDs8=s640-h400-e365-rw"; 27 | } 28 | 29 | Timer { 30 | id: timer 31 | interval: 3000 32 | } 33 | 34 | Promise { 35 | // Resolve when the image is ready 36 | resolveWhen: image.status === Image.Ready 37 | 38 | // Reject when time out. 39 | rejectWhen: timer.triggered 40 | 41 | onFulfilled: { 42 | // If the timer is reached, the image will not be shown even it is ready. 43 | item.opacity = 1; 44 | } 45 | } 46 | } 47 | 48 | ``` 49 | 50 | The code above demonstrated how Promise component could be used in asynchronous workflow for QML application. 51 | The resolveWhen property accepts a boolean expression, another Promise object and signal. 52 | Once the result of expression becomes truth, 53 | it will trigger the “onFulfilled” slot via QueuedConnection. *The slot will be executed for once only*. 54 | 55 | Remarks: The QML Promise component is not fully compliant with Promises/A+ specification. 56 | 57 | Related artciles: 58 | 1. [JavaScript Promises with ArcGIS Runtime SDK for Qt | GeoNet](https://geonet.esri.com/community/developers/native-app-developers/arcgis-runtime-sdk-for-qt/blog/2016/07/05/javascript-promises-with-arcgis-runtime-sdk-for-qt) 59 | 60 | Feature List 61 | ------------ 62 | 63 | 1. Promise in a QML way 64 | 1. Trigger resolve()/reject() via binary expression, signal from resloveWhen / rejectWhen property 65 | 2. isFulfilled / isRejected / isSettled properties for data binding. 66 | 3. fulfulled , rejected , settled signals 67 | 2. Promise in a Javascript way 68 | 1. Unlike QML component, it don’t need to declare before use it. 69 | 2. The API interface is fully compatible with [Promises/A+](https://promisesaplus.com/) specification. It is easy to get started. 70 | 3. Extra API 71 | 1. Q.setTimeout() - An implementation of setTimeout() function for QML. 72 | 2. all()/allSettled() - Create a promise object from an array of promises 73 | 74 | Installation Instruction (qpm) 75 | ============================== 76 | 77 | For user who are already using qpm from [qpm.io](https://qpm.io) 78 | 79 | 80 | 1) Run `qpm install` 81 | 82 | ``` 83 | $ qpm install com.github.benlau.quickpromise 84 | ``` 85 | 86 | 2) Include vendor/vendor.pri in your .pro file 87 | 88 | You may skip this step if you are already using qpm. 89 | 90 | ``` 91 | include(vendor/vendor.pri) 92 | ``` 93 | 94 | 3) Add "qrc://" to your QML import path 95 | 96 | ``` 97 | engine.addImportPath("qrc:///"); // QQmlEngine 98 | ``` 99 | 100 | 4) Add import statement in your QML file 101 | 102 | ``` 103 | import QuickPromise 1.0 104 | ``` 105 | 106 | Installation Instruction 107 | ======================== 108 | 109 | 1) Clone this repository or download release to a folder within your source tree. 110 | 111 | 2) Add this line to your profile file(.pro): 112 | 113 | include(quickpromise/quickpromise.pri) # You should modify the path by yourself 114 | 115 | 3) Add "qrc://" to your QML import path 116 | 117 | ``` 118 | engine.addImportPath("qrc:///"); // QQmlEngine 119 | ``` 120 | 121 | 4) Add import statement in your QML file 122 | 123 | ``` 124 | import QuickPromise 1.0 125 | ``` 126 | 127 | What is Promise and how to use it? 128 | ========================== 129 | 130 | Please refer to this site for core concept : [Promises](https://www.promisejs.org/) 131 | 132 | Promise QML Componnet 133 | ===================== 134 | 135 | ``` 136 | Promise { 137 | property bool isFulfilled 138 | 139 | property bool isRejected 140 | 141 | property bool isSettled : isFulfilled || isRejected 142 | 143 | /// An expression that will trigger resolve() if the value become true or another promise object got resolved. 144 | property var resolveWhen 145 | 146 | /// An expression that will trigger reject() if the value become true. Don't assign another promise object here. 147 | property var rejectWhen 148 | 149 | signal fulfilled(var value) 150 | 151 | signal rejected(var reason) 152 | 153 | signal settled(var value) 154 | 155 | function setTimeout(func,timeout) { ... } 156 | 157 | function then(onFulfilled,onRejected) { ... } 158 | 159 | function resolve(value) { ... } 160 | 161 | function reject(reason) { ... } 162 | 163 | function all(promises) { ... } 164 | 165 | function allSettled(promises) { ... } 166 | } 167 | ``` 168 | 169 | **isFullfilled** 170 | It is true if resolve() has been called on that promise object 171 | 172 | **isRejected** 173 | It is true if reject() has been called on that promise object 174 | 175 | **isSettled** 176 | It is true if either of isFullfilled or isRejected has been set. 177 | 178 | 179 | **resolveWhen** 180 | 181 | resolveWhen property is an alternative method to call resolve() in a QML way. You may bind a binary expression, another promise, signal to the "resolveWhen" property. That will trigger the resolve() depend on its type and value. 182 | 183 | **resolveWhen: binary expression** 184 | 185 | Once the expression become true, it will trigger resolve(true). 186 | 187 | **resolveWhen: signal** 188 | 189 | Listen the signal, once it is triggered, it will call resolve(). 190 | 191 | Example: 192 | ``` 193 | Timer { 194 | id: timer; 195 | repeat: true 196 | interval : 50 197 | } 198 | 199 | Promise { 200 | id : promise 201 | resolveWhen: timer.onTriggered 202 | } 203 | ``` 204 | 205 | **resolveWhen: promise** 206 | 207 | It is equivalent to resolve(promise). It will adopt the state from the input promise object. 208 | 209 | Let x be the input promise. 210 | 211 | If x is fulfilled, call resolve(). 212 | 213 | If x is rejected, call reject(). 214 | 215 | If x is not settled, listen its state change. Once it is fulfilled/rejected, repeat the above steps. 216 | 217 | **rejectWhen** 218 | 219 | _rejectWhen_ property is an alternative method to call reject() in a QML way. You may bind a binary expression, signal to this property. It may trigger the reject() depend on its type and value. 220 | 221 | Remarks: _rejectWhen_ can not take promise as parameter. 222 | 223 | **rejectWhen: binary expression** 224 | 225 | Once the expression become true, it will trigger reject(true). 226 | 227 | **rejectWhen: signal** 228 | 229 | Listen the signal, once it is triggered, it will call reject(). 230 | 231 | 232 | Q.promise() 233 | =========== 234 | 235 | Q.promise() is the creator function of Promise object in a Javascript way. You won't need to declare a QML component before use it. As it is fully compliant with Promise/A+ specification, it is very easy to get started. But it don't support property binding (resolveWhen , rejectWhen) like the Promise QML component. 236 | 237 | However, you may still pass a signal object to resolve()/reject(). In this case, the promise will not change its state until the signal is triggered. If multiple signal call are made, the first signal call takes precedence, and any further calls are ignored. 238 | 239 | But it don't support to resolve by the result of Qt.binding(). It will just throw exception. In this case, you should use a QML Promise and pass it to resolveWhen property. 240 | 241 | *API* 242 | 243 | ``` 244 | Promise.prototype.then = function(onFulfilled,onRejected) { ... } 245 | Promise.prototype.resolve = function(value) { ... } 246 | Promise.prototype.reject = function(reason) { ... } 247 | ``` 248 | 249 | Instruction of using then/resolve/reject: [Promises](https://www.promisejs.org/) 250 | 251 | 252 | Extra API 253 | --------- 254 | 255 | **Q.setTimeout(func,milliseconds)** 256 | 257 | The setTimeout() method will wait the specified number of milliseconds, and then execute the specified function. If the milliseconds is equal to zero, the behaviour will be same as triggering a signal via QueuedConnection. 258 | 259 | 260 | **Q.all(promises)** 261 | 262 | Given an array of promise / signal , it will create a promise object that will be fulfilled once all the input promises are fulfilled. And it will be rejected if any one of the input promises is rejected. 263 | 264 | 265 | **Q.allSettled(promises)** 266 | 267 | Given an array of promise / signal , it will create a promise object that will be fulfilled once all the input promises are fulfilled. And it will be rejected if any one of the input promises is rejected. It won't change the state until all the input promises are settled. 268 | 269 | Advanced Usage 270 | ============== 271 | 272 | 1. Resolve by multiple signals. 273 | ------------------------------- 274 | 275 | 276 | ``` 277 | 278 | Promise { 279 | resolveWhen: Q.all([timer.triggered, loader.loaded]); 280 | } 281 | 282 | ``` 283 | 284 | 2. Resolve by signal and binary expression 285 | ------------------------------------------ 286 | 287 | ``` 288 | Promise { 289 | resolveWhen: Q.all([timer.triggered, promise2]); 290 | 291 | Promise { 292 | id : promise2 293 | resolveWhen: image.status === Image.Ready 294 | } 295 | } 296 | 297 | ``` 298 | 299 | -------------------------------------------------------------------------------- /vendor/QuickPromise/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /MyKeysPage.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | import QtQuick.Controls 2.0 3 | import QtQuick.Layouts 1.1 4 | import Qt.WebSockets 1.0 5 | import com.nathanhourt.steem.accounts 1.0 6 | import com.nathanhourt.steem.crypto 1.0 7 | import com.nathanhourt.rpc 1.0 8 | import QuickPromise 1.0 9 | 10 | MyKeysForm { 11 | id: keysForm 12 | property alias keyStore: store 13 | 14 | KeyStore { 15 | id: store 16 | onRestored: { 17 | rpc.refreshAccounts(accountList.toVarArray().map(function(account) { return account.name })) 18 | } 19 | } 20 | TransactionFoundry { 21 | id: foundry 22 | keyStore: store 23 | } 24 | 25 | JsonRpcProvider { 26 | id: rpc 27 | property int networkApi 28 | socket: WebSocket { 29 | id: socket 30 | active: true 31 | url: "wss://steemit.com/wspa" 32 | onStatusChanged: { 33 | console.log(status, errorString) 34 | if (status === WebSocket.Error) { 35 | active = false 36 | active = true 37 | } 38 | if (status === WebSocket.Open) { 39 | rpc.call("call", [1, "login", ["",""]]).then(function() { 40 | rpc.call("call", [1, "get_api_by_name", ["network_broadcast_api"]]).then(function(id) { 41 | rpc.networkApi = id 42 | }) 43 | }) 44 | } 45 | } 46 | } 47 | 48 | function refreshAccounts(accounts) { 49 | if (accounts) 50 | return rpc.call("get_accounts", [accounts]).then(function(accounts) { 51 | store.addAccount(accounts[0]) 52 | }) 53 | } 54 | } 55 | 56 | Popup { 57 | id: changeKeySnackbar 58 | modal: false 59 | x: 0 60 | y: ApplicationWindow.window.height - height 61 | z: 2 62 | implicitHeight: 48 63 | implicitWidth: keysForm.width 64 | margins: 0 65 | closePolicy: Popup.NoAutoClose 66 | 67 | function openWithMessage(message, buttonText) { 68 | changeKeySnackbarLabel.text = message? message : "" 69 | changeKeySnackbarButton.text = buttonText? buttonText : "" 70 | keyUpdateTimer.restart() 71 | open() 72 | } 73 | 74 | enter: Transition { 75 | PropertyAnimation { 76 | target: changeKeySnackbar 77 | property: "implicitHeight" 78 | from: 0; to: 48 79 | easing.type: Easing.InOutQuad 80 | } 81 | } 82 | exit: Transition { 83 | PropertyAnimation { 84 | target: changeKeySnackbar 85 | property: "implicitHeight" 86 | from: 48; to: 0 87 | } 88 | } 89 | 90 | signal cancelClicked 91 | 92 | background: Rectangle { 93 | color: "#323232" 94 | } 95 | 96 | Timer { 97 | id: keyUpdateTimer 98 | interval: 5000 99 | repeat: false 100 | onTriggered: changeKeySnackbar.close() 101 | } 102 | RowLayout { 103 | anchors.top: parent.top 104 | width: parent.width 105 | spacing: 24 106 | 107 | Label { 108 | id: changeKeySnackbarLabel 109 | anchors.verticalCenter: parent.verticalCenter 110 | Layout.fillWidth: true 111 | color: "white" 112 | } 113 | 114 | Button { 115 | id: changeKeySnackbarButton 116 | font.bold: true 117 | anchors.verticalCenter: parent.verticalCenter 118 | onClicked: { 119 | changeKeySnackbar.cancelClicked() 120 | keyUpdateTimer.stop() 121 | changeKeySnackbar.close() 122 | } 123 | background: Item{} 124 | contentItem: Text { text: changeKeySnackbarButton.text; font: changeKeySnackbarButton.font; horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter; color: "white" } 125 | } 126 | } 127 | } 128 | 129 | AddAccountPopup { 130 | id: addAccountPopup 131 | x: parent.width / 2 - width / 2 132 | y: parent.height / 2 - height / 2 133 | 134 | property var accounts: [] 135 | property var accountKey: store.makeKeyPair() 136 | property string importMode 137 | property string accountPassword 138 | 139 | onClosed: { 140 | accounts = [] 141 | accountKey.generateFromSeed("") 142 | importMode = "" 143 | accountPassword = "" 144 | newKeyField.placeholderText = qsTr("Private key or account password") 145 | } 146 | 147 | function importByPrivateKey() { 148 | rpc.call("get_key_references", [[accountKey.publicKey]]).then(function(stuff) { 149 | var accounts = stuff.reduce(function(prev, next) { return prev.concat(next) }, []) 150 | return rpc.call("get_accounts", [accounts]) 151 | }).then(function(accounts) { 152 | accounts = accounts.filter(function(account) { 153 | return store.accountUnsupportedReason(account) === "" 154 | }) 155 | if (accounts.length) { 156 | addAccountPopup.accounts = accounts 157 | var accountsString = "" 158 | for (var i = 0; i < accounts.length; ++i) 159 | if (accountsString) 160 | accountsString += ", " + accounts[i].name 161 | else 162 | accountsString = accounts[i].name 163 | addAccountPopup.infoLabel.text = qsTr("Importing ") + accountsString 164 | } else { 165 | addAccountPopup.infoLabel.text = qsTr("That key doesn't belong to any supported accounts :(") 166 | } 167 | }) 168 | } 169 | function importByPassword() { 170 | rpc.call("get_accounts", [[addAccountPopup.newKeyField.text]]).then(function(accounts) { 171 | if (!accounts || accounts.length !== 1) { 172 | addAccountPopup.infoLabel.text = qsTr("I can't find an account by that name...") 173 | addAccountPopup.accounts = [] 174 | return 175 | } 176 | if (store.accountUnsupportedReason(accounts[0]) !== "") { 177 | addAccountPopup.infoLabel.text = qsTr("Sorry, that account isn't supported:\n") + 178 | store.accountUnsupportedReason(accounts[0]) 179 | addAccountPopup.accounts = [] 180 | return 181 | } 182 | 183 | var matches = 0 184 | var check = function(level) { 185 | accountKey.generateFromSeed(accounts[0].name + level + accountPassword) 186 | if ((accounts[0].hasOwnProperty(level) && accounts[0][level].key_auths[0] === accountKey.publicKey) 187 | || accounts[0].memo_key === accountKey.publicKey) 188 | ++matches 189 | } 190 | check("owner") 191 | check("active") 192 | check("posting") 193 | check("memo") 194 | 195 | if (matches) { 196 | addAccountPopup.accounts = accounts 197 | addAccountPopup.infoLabel.text = qsTr("Good to go!") 198 | } else { 199 | addAccountPopup.accounts = [] 200 | addAccountPopup.infoLabel.text = qsTr("The password is incorrect for that account") 201 | } 202 | }) 203 | } 204 | 205 | Timer { 206 | id: accountLookupTimer 207 | interval: 200 208 | repeat: false 209 | 210 | onTriggered: { 211 | if (addAccountPopup.newKeyField.text) { 212 | var accountKey = addAccountPopup.accountKey 213 | accountKey.fromWifKey(addAccountPopup.newKeyField.text) 214 | if (accountKey.keyType !== KeyPair.PrivateKey) { 215 | if (addAccountPopup.accountPassword) { 216 | return addAccountPopup.importByPassword() 217 | } 218 | 219 | addAccountPopup.infoLabel.text = qsTr("Enter account password above") 220 | addAccountPopup.importMode = "password" 221 | return 222 | } 223 | 224 | addAccountPopup.infoLabel.text = qsTr("Tis loadin'") 225 | addAccountPopup.importMode = "key" 226 | addAccountPopup.importByPrivateKey() 227 | } else { 228 | addAccountPopup.infoLabel.text = "" 229 | } 230 | } 231 | } 232 | 233 | newKeyField.onTextChanged: accountLookupTimer.restart() 234 | acceptButton.enabled: (importMode === "key" && accounts && accounts.length) || 235 | (importMode === "password" && accountPassword === "") || 236 | (importMode === "password" && accounts && accounts.length === 1) 237 | acceptButton.onClicked: { 238 | if (importMode === "key") { 239 | for (var i = 0; i < accounts.length; ++i) { 240 | var account = store.addAccount(accounts[i]) 241 | if (account.ownerKey.equals(accountKey)) 242 | account.ownerKey.replaceWith(accountKey) 243 | if (account.activeKey.equals(accountKey)) 244 | account.activeKey.replaceWith(accountKey) 245 | if (account.postingKey.equals(accountKey)) 246 | account.postingKey.replaceWith(accountKey) 247 | if (account.memoKey.equals(accountKey)) 248 | account.memoKey.replaceWith(accountKey) 249 | } 250 | 251 | close() 252 | } else { 253 | if (accountPassword) { 254 | account = store.addAccount(accounts[0]) 255 | accountKey.generateFromSeed(account.name + "owner" + accountPassword) 256 | if (account.ownerKey.equals(accountKey)) 257 | account.ownerKey.replaceWith(accountKey) 258 | accountKey.generateFromSeed(account.name + "active" + accountPassword) 259 | if (account.activeKey.equals(accountKey)) 260 | account.activeKey.replaceWith(accountKey) 261 | accountKey.generateFromSeed(account.name + "posting" + accountPassword) 262 | if (account.postingKey.equals(accountKey)) 263 | account.postingKey.replaceWith(accountKey) 264 | accountKey.generateFromSeed(account.name + "memo" + accountPassword) 265 | if (account.memoKey.equals(accountKey)) 266 | account.memoKey.replaceWith(accountKey) 267 | 268 | close() 269 | } else { 270 | accountPassword = newKeyField.text 271 | newKeyField.placeholderText = "Account name" 272 | newKeyField.text = "" 273 | infoLabel.text = qsTr("Now enter account name above") 274 | accountLookupTimer.stop() 275 | } 276 | } 277 | } 278 | cancelButton.onClicked: close() 279 | } 280 | 281 | emptyAccountListPlaceHolder.visible: store.accountList.count === 0 282 | accountList.model: store.accountList 283 | accountList.delegate: AccountDelegate { 284 | highlighted: ListView.isCurrentItem 285 | width: parent.width 286 | 287 | function saveKey(key) { 288 | if (key.equals(ownerKey)) 289 | ownerKey.replaceWith(key) 290 | if (key.equals(activeKey)) 291 | activeKey.replaceWith(key) 292 | if (key.equals(postingKey)) 293 | postingKey.replaceWith(key) 294 | if (key.equals(memoKey)) 295 | memoKey.replaceWith(key) 296 | } 297 | 298 | onEditKey: { 299 | var backupKey = key.deepCopy() 300 | var page = keysForm.StackView.view.push(Qt.resolvedUrl("EditKeysPage.qml"), {keyPair: key}) 301 | page.modifiedKeyPromise.then(function(newKey) { 302 | keysForm.StackView.view.pop(keysForm) 303 | 304 | if (newKey.keyType === KeyPair.NullKey) { 305 | changeKeySnackbar.openWithMessage(qsTr("I'm sorry, Dave. I'm afraid I can't do that"), 306 | qsTr("Dismiss")) 307 | return "pass" 308 | } 309 | if (key.equals(newKey)) { 310 | if (key.keyType === KeyPair.PublicKey && newKey.keyType === KeyPair.PrivateKey) { 311 | saveKey(newKey) 312 | changeKeySnackbar.openWithMessage(qsTr("Private key saved"), qsTr("Dismiss")) 313 | } else 314 | changeKeySnackbar.openWithMessage(qsTr("Key not changed"), qsTr("Dismiss")) 315 | return "pass" 316 | } 317 | 318 | changeKeySnackbar.openWithMessage(qsTr("Updating key"), qsTr("Cancel")) 319 | 320 | // Generate/sign the transaction 321 | var promise = rpc.call("get_dynamic_global_properties", []).then(function(properties) { 322 | var trx = foundry.keyUpdateTransaction(name, authorityLevel, newKey, properties.head_block_id) 323 | // Once the transaction is signed, update the key in the GUI. It's important that we sign first, or 324 | // else we are liable to update the key we need to sign with and sign with the new key instead of 325 | // the one on chain. Derp! 326 | key.replaceWith(newKey) 327 | return trx 328 | }) 329 | promise = Q.all([promise, keyUpdateTimer.triggered]) 330 | promise.reject(changeKeySnackbar.cancelClicked) 331 | 332 | return promise.then(function(list) { return list[0] }) 333 | }, function() { 334 | keysForm.StackView.view.pop(keysForm) 335 | }).then(function(trx) { 336 | if (!trx || trx === "pass") 337 | return 338 | 339 | changeKeySnackbar.close() 340 | console.log("Update key", JSON.stringify(trx)) 341 | return rpc.call("call", [rpc.networkApi, "broadcast_transaction_synchronous", [trx]]) 342 | }, function() { 343 | console.log("Key update canceled") 344 | }).then(function(confirmation) { 345 | if (confirmation) 346 | console.log(JSON.stringify(confirmation)) 347 | return rpc.refreshAccounts([name]) 348 | }, function(error) { 349 | changeKeySnackbar.openWithMessage(qsTr("It didn't work :("), qsTr("Dismiss")) 350 | console.error("Key update failed:", JSON.stringify(error)) 351 | key.replaceWith(backupKey) 352 | return rpc.call("get_accounts", [[name]]) 353 | }) 354 | } 355 | } 356 | 357 | newAccountButton.onClicked: addAccountPopup.open() 358 | emptyAccountListLabel.onLinkActivated: newAccountButton.clicked() 359 | deleteAccountButton.onClicked: accountList.model.remove(accountList.currentIndex) 360 | } 361 | -------------------------------------------------------------------------------- /vendor/QuickPromise/tests/unittests/tst_aplus_spec.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtTest 1.0 3 | import QuickPromise 1.0 4 | 5 | 6 | TestCase { 7 | name : "APlusSpec" 8 | 9 | function tick() { 10 | wait(0); 11 | wait(0); 12 | wait(0); 13 | } 14 | 15 | // The implementation is based on Promises/A+ 16 | // https://promisesaplus.com 17 | 18 | function test_2_2_7_1_resolve() { 19 | var promise1 = Q.promise(); 20 | var promise2 = promise1.then(function() { 21 | return "accepted" 22 | },function() { 23 | }); 24 | 25 | compare(promise2 !== undefined,true); 26 | var resolvedValue; 27 | promise2.then(function(value) { 28 | resolvedValue = value; 29 | }); 30 | 31 | promise1.resolve(); 32 | compare(resolvedValue,undefined); 33 | tick(); 34 | 35 | compare(resolvedValue,"accepted"); 36 | } 37 | 38 | function test_2_2_7_1_reject() { 39 | var promise1 = Q.promise(); 40 | var promise2 = promise1.then(function() { 41 | },function() { 42 | return "rejected" 43 | }); 44 | 45 | compare(promise1._onFulfilled.length,1); 46 | 47 | compare(promise2 !== undefined,true); 48 | var resolvedValue; 49 | promise2.then(function(value) { 50 | resolvedValue = value; 51 | }); 52 | 53 | promise1.reject(); 54 | compare(resolvedValue,undefined); 55 | tick(); 56 | compare(resolvedValue,"rejected"); 57 | } 58 | 59 | function test_2_2_7_2() { 60 | var promise1,promise2; 61 | promise1 = Q.promise(); 62 | promise2 = promise1.then(function() { 63 | throw new Error("Expected Error for Testing"); 64 | },function() { 65 | throw new Error("Exception"); 66 | }); 67 | 68 | promise1.resolve("x"); 69 | tick(); 70 | 71 | compare(promise2.state , "rejected"); 72 | compare(String(promise2._result) , "Error: Expected Error for Testing"); 73 | } 74 | 75 | function test_2_2_7_3() { 76 | var promise1 = Q.promise(); 77 | var promise2 = promise1.then(undefined,function() { 78 | }); 79 | 80 | compare(promise2 !== undefined,true); 81 | var resolvedValue; 82 | promise2.then(function(value) { 83 | resolvedValue = value; 84 | }); 85 | 86 | promise1.resolve("2_2_7_3"); 87 | 88 | tick(); 89 | compare(resolvedValue,"2_2_7_3"); 90 | } 91 | 92 | function test_2_2_7_4() { 93 | var promise1 = Q.promise(); 94 | var promise2 = promise1.then(function() { 95 | },undefined); 96 | 97 | compare(promise1._onFulfilled.length,1); 98 | 99 | compare(promise2 !== undefined,true); 100 | var resolvedValue; 101 | promise2.then(undefined,function(value) { 102 | resolvedValue = value; 103 | }); 104 | 105 | promise1.reject("2_2_7_4"); 106 | 107 | tick(); 108 | 109 | compare(resolvedValue,"2_2_7_4"); 110 | } 111 | 112 | function test_2_3_1() { 113 | var promise1 = Q.promise(); 114 | var promise2 = promise1.then(function() {} , function() {}); 115 | 116 | promise1.resolve(promise1); 117 | compare(promise1.state , "pending"); 118 | tick(); 119 | compare(promise1.state , "rejected"); 120 | } 121 | 122 | function test_2_3_2_1() { 123 | var promiseSrc = Q.promise(); 124 | var promise1 = Q.promise(); 125 | var promise2 = promise1.then(function() {} , function() {}); 126 | 127 | promise1.resolve(promiseSrc); 128 | compare(promise1.state, "pending"); 129 | 130 | promiseSrc.resolve("x"); 131 | tick(); 132 | compare(promise1.state, "fulfilled"); 133 | 134 | promiseSrc.reject("x"); 135 | compare(promise1.state, "fulfilled"); 136 | } 137 | 138 | function test_2_3_2_2() { 139 | var promiseSrc = Q.promise(); 140 | var promise1 = Q.promise(); 141 | var resolvedValue; 142 | var promise2 = promise1.then(function(value) { 143 | resolvedValue = value; 144 | } , function() {}); 145 | 146 | promiseSrc.resolve("x"); 147 | 148 | promise1.resolve(promiseSrc); 149 | tick(); 150 | compare(promise1.state, "fulfilled"); 151 | compare(resolvedValue, "x"); 152 | } 153 | 154 | function test_2_3_2_3() { 155 | var promiseSrc = Q.promise(); 156 | var promise1 = Q.promise(); 157 | var rejectedValue; 158 | var promise2 = promise1.then(function(value) { 159 | } , function(value) { 160 | rejectedValue = value; 161 | 162 | }); 163 | 164 | promiseSrc.reject("x"); 165 | 166 | promise1.resolve(promiseSrc); 167 | compare(promise1.state, "pending"); 168 | tick(); 169 | compare(promise1.state, "rejected"); 170 | compare(rejectedValue, "x"); 171 | } 172 | 173 | function test_q_all() { 174 | // Condition 1: Successful condition 175 | var promise1 = Q.promise(); 176 | var promise2 = Q.promise(); 177 | var promise3 = Q.promise(); 178 | 179 | var allPromise = Q.all([promise1,promise2,promise3]); 180 | compare(allPromise.state ,"pending"); 181 | promise2.resolve("b"); // resolve out of order 182 | tick(); 183 | compare(allPromise.state ,"pending"); 184 | promise1.resolve("a"); 185 | tick(); 186 | compare(allPromise.state ,"pending"); 187 | promise3.resolve("c"); 188 | tick(); 189 | compare(allPromise.state ,"fulfilled"); 190 | // results should keep original order regardless of resolution order 191 | compare(allPromise._result, ["a", "b", "c"]); 192 | 193 | // Condition 2. Reject one of the promise 194 | promise1 = Q.promise(); 195 | promise2 = Q.promise(); 196 | promise3 = Q.promise(); 197 | 198 | allPromise = Q.all([promise1,promise2,promise3]); 199 | compare(allPromise.state ,"pending"); 200 | promise2.reject(); 201 | compare(allPromise.state ,"pending"); 202 | tick(); 203 | compare(allPromise.state ,"rejected"); 204 | 205 | // Condition 3: Insert rejected promise 206 | promise1 = Q.promise(); 207 | promise2 = Q.promise(); 208 | promise3 = Q.promise(); 209 | promise2.reject(); 210 | 211 | allPromise = Q.all([promise1,promise2,promise3]); 212 | compare(allPromise.state ,"pending"); 213 | tick(); 214 | compare(allPromise.state ,"rejected"); 215 | 216 | // Condition 4: All the promise is fulfilled already 217 | 218 | promise1 = Q.promise(); 219 | promise2 = Q.promise(); 220 | promise3 = Q.promise(); 221 | 222 | promise2.resolve('y'); 223 | promise1.resolve('x'); 224 | promise3.resolve('z'); 225 | 226 | allPromise = Q.all([promise1,promise2,promise3]); 227 | tick(); 228 | compare(allPromise.state ,"fulfilled"); 229 | compare(allPromise._result, ["x", "y", "z"]); 230 | } 231 | 232 | function test_q_allSettled() { 233 | // Condition 1: Successful condition 234 | var promise1 = Q.promise(); 235 | var promise2 = Q.promise(); 236 | var promise3 = Q.promise(); 237 | 238 | var allPromise = Q.allSettled([promise1,promise2,promise3]); 239 | compare(allPromise.state ,"pending"); 240 | promise3.resolve("c"); // resolve out of order 241 | tick(); 242 | compare(allPromise.state ,"pending"); 243 | promise1.resolve("a"); 244 | tick(); 245 | compare(allPromise.state ,"pending"); 246 | promise2.resolve("b"); 247 | tick(); 248 | compare(allPromise.state ,"fulfilled"); 249 | // results should keep original order regardless of resolution order 250 | compare(allPromise._result, ["a", "b", "c"]); 251 | 252 | // Condition 2. Reject one of the promise 253 | promise1 = Q.promise(); 254 | promise2 = Q.promise(); 255 | promise3 = Q.promise(); 256 | 257 | allPromise = Q.allSettled([promise1,promise2,promise3]); 258 | compare(allPromise.state ,"pending"); 259 | 260 | promise2.reject(); 261 | tick(); 262 | compare(allPromise.state ,"pending"); 263 | promise3.resolve(); 264 | tick(); 265 | compare(allPromise.state ,"pending"); 266 | promise1.resolve(); 267 | tick(); 268 | compare(allPromise.state ,"rejected"); 269 | 270 | // Condition 3: Insert rejected promise 271 | promise1 = Q.promise(); 272 | promise2 = Q.promise(); 273 | promise3 = Q.promise(); 274 | promise2.reject(); 275 | 276 | allPromise = Q.allSettled([promise1,promise2,promise3]); 277 | tick(); 278 | compare(allPromise.state ,"pending"); 279 | promise1.resolve(); 280 | tick(); 281 | compare(allPromise.state ,"pending"); 282 | promise3.resolve(); 283 | tick(); 284 | compare(allPromise.state ,"rejected"); 285 | 286 | // Condition 4: All the promise is fulfilled already 287 | 288 | promise1 = Q.promise(); 289 | promise2 = Q.promise(); 290 | promise3 = Q.promise(); 291 | 292 | promise2.resolve('y'); 293 | promise1.resolve('x'); 294 | promise3.resolve('z'); 295 | 296 | allPromise = Q.allSettled([promise1,promise2,promise3]); 297 | tick(); 298 | compare(allPromise.state ,"fulfilled"); 299 | compare(allPromise._result, ["x", "y", "z"]); 300 | } 301 | 302 | Component { 303 | id : promiseCreator1 304 | Promise { 305 | property int x : 0 306 | resolveWhen: x > 0 307 | rejectWhen: x < 0 308 | } 309 | } 310 | 311 | Component { 312 | id : emitterCreator 313 | Item { 314 | signal emitted() 315 | } 316 | } 317 | 318 | function test_promise() { 319 | var onFulFilled = false; 320 | var onRejected = false; 321 | var value; 322 | 323 | // Condition 1: Resolve the promise 324 | 325 | var promise = promiseCreator1.createObject(); 326 | promise.onFulfilled.connect(function(v) { 327 | onFulFilled = true; 328 | value = v; 329 | }); 330 | 331 | compare(promise.isFulfilled,false); 332 | compare(promise.isRejected,false); 333 | compare(promise.isSettled,false); 334 | 335 | promise.resolve("x"); 336 | 337 | compare(promise.isFulfilled,false); 338 | compare(promise.isRejected,false); 339 | compare(promise.isSettled,false); 340 | 341 | tick(); 342 | 343 | compare(promise.isFulfilled,true); 344 | compare(promise.isRejected,false); 345 | compare(promise.isSettled,true); 346 | 347 | compare(onFulFilled, true); 348 | compare(value,"x"); 349 | 350 | // Condition 2: Reject the promise 351 | 352 | promise = promiseCreator1.createObject(); 353 | compare(promise.isFulfilled,false); 354 | compare(promise.isRejected,false); 355 | compare(promise.isSettled,false); 356 | 357 | promise.onRejected.connect(function() { 358 | onRejected = true; 359 | }); 360 | 361 | promise.reject(); 362 | compare(promise.isFulfilled,false); 363 | compare(promise.isRejected,false); 364 | compare(promise.isSettled,false); 365 | compare(onRejected, false); 366 | 367 | tick(); 368 | compare(promise.isFulfilled,false); 369 | compare(promise.isRejected,true); 370 | compare(promise.isSettled,true); 371 | 372 | compare(onRejected, true); 373 | 374 | 375 | // Condition 3: resvoleWhen : expression => true 376 | 377 | promise = promiseCreator1.createObject(); 378 | compare(promise.isFulfilled,false); 379 | compare(promise.isRejected,false); 380 | compare(promise.isSettled,false); 381 | 382 | promise.x = 1; 383 | 384 | compare(promise.isFulfilled,false); 385 | compare(promise.isRejected,false); 386 | compare(promise.isSettled,false); 387 | 388 | tick(); 389 | 390 | // resolveWhen should be done on next tick is 391 | // necessary for promise created via Component 392 | // and fulfill immediately 393 | 394 | compare(promise.isFulfilled,true); 395 | compare(promise.isRejected,false); 396 | compare(promise.isSettled,true); 397 | 398 | // Condition 4: rejectWhen: expression => true 399 | 400 | promise = promiseCreator1.createObject(); 401 | compare(promise.isFulfilled,false); 402 | compare(promise.isRejected,false); 403 | compare(promise.isSettled,false); 404 | 405 | promise.x = -1; 406 | compare(promise.isFulfilled,false); 407 | compare(promise.isRejected,false); 408 | compare(promise.isSettled,false); 409 | 410 | tick(); 411 | compare(promise.isFulfilled,false); 412 | compare(promise.isRejected,true); 413 | compare(promise.isSettled,true); 414 | 415 | // Condition 5: all() 416 | 417 | var promise1 = Q.promise(); 418 | var promise2 = Q.promise(); 419 | var promise3 = Q.promise(); 420 | 421 | promise = promiseCreator1.createObject(); 422 | promise.resolve(promise.all([promise1,promise2,promise3])); 423 | 424 | promise1.resolve(); 425 | tick(); 426 | compare(promise.isSettled,false); 427 | promise2.resolve(); 428 | tick(); 429 | compare(promise.isSettled,false); 430 | promise3.resolve(); 431 | tick(); 432 | 433 | compare(promise.isSettled,true); 434 | 435 | // Condition 6: resolveWhen = promise 436 | promise1 = promiseCreator1.createObject(); 437 | promise2 = promiseCreator1.createObject(); 438 | 439 | compare(promise1.isFulfilled,false); 440 | promise1.resolveWhen = promise2; 441 | compare(promise1.isFulfilled,false); 442 | promise2.resolve(); 443 | 444 | compare(promise1.isFulfilled,false); 445 | 446 | wait(0);wait(0); 447 | 448 | compare(promise1.isFulfilled,true); 449 | 450 | // Condition 7: resolveWhen = Q.promise 451 | promise1 = promiseCreator1.createObject(); 452 | promise2 = Q.promise(); 453 | 454 | compare(promise1.isFulfilled,false); 455 | promise1.resolveWhen = promise2; 456 | 457 | compare(promise1.isFulfilled,false); 458 | 459 | promise2.resolve(); 460 | 461 | compare(promise1.isFulfilled,false); 462 | 463 | wait(0);wait(0); 464 | 465 | compare(promise1.isFulfilled,true); 466 | 467 | /* Condition 8: rejectWhen = promise */ 468 | promise1 = promiseCreator1.createObject(); 469 | promise2 = promiseCreator1.createObject(); 470 | 471 | compare(promise1.isRejected,false); 472 | promise1.rejectWhen = promise2; 473 | compare(promise1.isRejected,false); 474 | promise2.resolve(); 475 | 476 | // rejectWhen ignore promise object 477 | compare(promise1.isRejected,false); 478 | } 479 | 480 | function test_promise_then() { 481 | var fulfilled = false; 482 | var rejected = false; 483 | var promise1 = promiseCreator1.createObject(); 484 | var promise2 = promise1.then(function() { 485 | fulfilled = true; 486 | },function() { 487 | rejected = true; 488 | }); 489 | 490 | compare(promise1.instanceOfPromise(promise2),false); 491 | 492 | compare(Q.instanceOfPromise(promise2),true); 493 | // It is not a Promise Component since it can not create QML recursively 494 | 495 | promise1.resolve("x"); 496 | tick(); 497 | compare(fulfilled,true); 498 | // @TODO. Support async callback 499 | } 500 | 501 | function test_promise_resolve_signal() { 502 | var emitter = emitterCreator.createObject(); 503 | 504 | var fulfilled = false; 505 | var rejected = false; 506 | var promise = promiseCreator1.createObject(); 507 | 508 | promise.onFulfilled.connect(function(v) { 509 | fulfilled = true; 510 | }); 511 | 512 | promise.onRejected.connect(function() { 513 | rejected = true; 514 | }); 515 | 516 | promise.resolve(emitter.onEmitted); 517 | compare(fulfilled,false); 518 | compare(rejected,false); 519 | 520 | wait(0); 521 | compare(fulfilled,false); 522 | compare(rejected,false); 523 | 524 | wait(0); 525 | 526 | emitter.emitted(); 527 | compare(fulfilled,false); 528 | compare(rejected,false); 529 | wait(0);wait(0); 530 | 531 | compare(fulfilled,true); 532 | compare(rejected,false); 533 | } 534 | } 535 | 536 | 537 | -------------------------------------------------------------------------------- /vendor/QtQmlModels/QQmlObjectListModel.h: -------------------------------------------------------------------------------- 1 | #ifndef QQMLOBJECTLISTMODEL_H 2 | #define QQMLOBJECTLISTMODEL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | template QList qListFromVariant (const QVariantList & list) { 20 | QList ret; 21 | ret.reserve (list.size ()); 22 | for (QVariantList::const_iterator it = list.constBegin (); it != list.constEnd (); it++) { 23 | const QVariant & var = static_cast(* it); 24 | ret.append (var.value ()); 25 | } 26 | return ret; 27 | } 28 | 29 | template QVariantList qListToVariant (const QList & list) { 30 | QVariantList ret; 31 | ret.reserve (list.size ()); 32 | for (typename QList::const_iterator it = list.constBegin (); it != list.constEnd (); it++) { 33 | const T & val = static_cast(* it); 34 | ret.append (QVariant::fromValue (val)); 35 | } 36 | return ret; 37 | } 38 | 39 | // custom foreach for QList, which uses no copy and check pointer non-null 40 | #define FOREACH_PTR_IN_QLIST(_type_, _var_, _list_) \ 41 | for (typename QList<_type_ *>::const_iterator it = _list_.begin (); it != _list_.end (); it++) \ 42 | for (_type_ * _var_ = static_cast<_type_ *> (* it); _var_ != Q_NULLPTR; _var_ = Q_NULLPTR) 43 | 44 | class QQmlObjectListModelBase : public QAbstractListModel { // abstract Qt base class 45 | Q_OBJECT 46 | Q_PROPERTY (int count READ count NOTIFY countChanged) 47 | 48 | public: 49 | explicit QQmlObjectListModelBase (QObject * parent = Q_NULLPTR) : QAbstractListModel (parent) { } 50 | 51 | public slots: // virtual methods API for QML 52 | virtual int size (void) const = 0; 53 | virtual int count (void) const = 0; 54 | virtual bool isEmpty (void) const = 0; 55 | virtual bool contains (QObject * item) const = 0; 56 | virtual int indexOf (QObject * item) const = 0; 57 | virtual int roleForName (const QByteArray & name) const = 0; 58 | virtual void clear (void) = 0; 59 | virtual void append (QObject * item) = 0; 60 | virtual void prepend (QObject * item) = 0; 61 | virtual void insert (int idx, QObject * item) = 0; 62 | virtual void move (int idx, int pos) = 0; 63 | virtual void remove (QObject * item) = 0; 64 | virtual void remove (int idx) = 0; 65 | virtual QObject * get (int idx) const = 0; 66 | virtual QObject * get (const QString & uid) const = 0; 67 | virtual QObject * getFirst (void) const = 0; 68 | virtual QObject * getLast (void) const = 0; 69 | virtual QVariantList toVarArray (void) const = 0; 70 | 71 | protected slots: // internal callback 72 | virtual void onItemPropertyChanged (void) = 0; 73 | 74 | signals: // notifier 75 | void countChanged (void); 76 | }; 77 | 78 | template class QQmlObjectListModel : public QQmlObjectListModelBase { 79 | public: 80 | explicit QQmlObjectListModel (QObject * parent = Q_NULLPTR, 81 | const QByteArray & displayRole = QByteArray (), 82 | const QByteArray & uidRole = QByteArray ()) 83 | : QQmlObjectListModelBase (parent) 84 | , m_count (0) 85 | , m_uidRoleName (uidRole) 86 | , m_dispRoleName (displayRole) 87 | , m_metaObj (ItemType::staticMetaObject) 88 | { 89 | static QSet roleNamesBlacklist; 90 | if (roleNamesBlacklist.isEmpty ()) { 91 | roleNamesBlacklist << QByteArrayLiteral ("id") 92 | << QByteArrayLiteral ("index") 93 | << QByteArrayLiteral ("class") 94 | << QByteArrayLiteral ("model") 95 | << QByteArrayLiteral ("modelData"); 96 | } 97 | static const char * HANDLER = "onItemPropertyChanged()"; 98 | m_handler = metaObject ()->method (metaObject ()->indexOfMethod (HANDLER)); 99 | if (!displayRole.isEmpty ()) { 100 | m_roles.insert (Qt::DisplayRole, QByteArrayLiteral ("display")); 101 | } 102 | m_roles.insert (baseRole (), QByteArrayLiteral ("qtObject")); 103 | const int len = m_metaObj.propertyCount (); 104 | for (int propertyIdx = 0, role = (baseRole () +1); propertyIdx < len; propertyIdx++, role++) { 105 | QMetaProperty metaProp = m_metaObj.property (propertyIdx); 106 | const QByteArray propName = QByteArray (metaProp.name ()); 107 | if (!roleNamesBlacklist.contains (propName)) { 108 | m_roles.insert (role, propName); 109 | if (metaProp.hasNotifySignal ()) { 110 | m_signalIdxToRole.insert (metaProp.notifySignalIndex (), role); 111 | } 112 | } 113 | else { 114 | static const QByteArray CLASS_NAME = (QByteArrayLiteral ("QQmlObjectListModel<") % m_metaObj.className () % '>'); 115 | qWarning () << "Can't have" << propName << "as a role name in" << qPrintable (CLASS_NAME); 116 | } 117 | } 118 | } 119 | bool setData (const QModelIndex & index, const QVariant & value, int role) { 120 | bool ret = false; 121 | ItemType * item = at (index.row ()); 122 | const QByteArray rolename = (role != Qt::DisplayRole ? m_roles.value (role, emptyBA ()) : m_dispRoleName); 123 | if (item != Q_NULLPTR && role != baseRole () && !rolename.isEmpty ()) { 124 | ret = item->setProperty (rolename, value); 125 | } 126 | return ret; 127 | } 128 | QVariant data (const QModelIndex & index, int role) const { 129 | QVariant ret; 130 | ItemType * item = at (index.row ()); 131 | const QByteArray rolename = (role != Qt::DisplayRole ? m_roles.value (role, emptyBA ()) : m_dispRoleName); 132 | if (item != Q_NULLPTR && !rolename.isEmpty ()) { 133 | ret.setValue (role != baseRole () ? item->property (rolename) : QVariant::fromValue (static_cast (item))); 134 | } 135 | return ret; 136 | } 137 | QHash roleNames (void) const { 138 | return m_roles; 139 | } 140 | typedef typename QList::const_iterator const_iterator; 141 | const_iterator begin (void) const { 142 | return m_items.begin (); 143 | } 144 | const_iterator end (void) const { 145 | return m_items.end (); 146 | } 147 | const_iterator constBegin (void) const { 148 | return m_items.constBegin (); 149 | } 150 | const_iterator constEnd (void) const { 151 | return m_items.constEnd (); 152 | } 153 | 154 | public: // C++ API 155 | ItemType * at (int idx) const { 156 | ItemType * ret = Q_NULLPTR; 157 | if (idx >= 0 && idx < m_items.size ()) { 158 | ret = m_items.value (idx); 159 | } 160 | return ret; 161 | } 162 | ItemType * getByUid (const QString & uid) const { 163 | return m_indexByUid.value (uid, Q_NULLPTR); 164 | } 165 | int roleForName (const QByteArray & name) const { 166 | return m_roles.key (name, -1); 167 | } 168 | int count (void) const { 169 | return m_count; 170 | } 171 | int size (void) const { 172 | return m_count; 173 | } 174 | bool isEmpty (void) const { 175 | return m_items.isEmpty (); 176 | } 177 | bool contains (ItemType * item) const { 178 | return m_items.contains (item); 179 | } 180 | int indexOf (ItemType * item) const { 181 | return m_items.indexOf (item); 182 | } 183 | void clear (void) { 184 | if (!m_items.isEmpty ()) { 185 | beginRemoveRows (noParent (), 0, m_items.count () -1); 186 | FOREACH_PTR_IN_QLIST (ItemType, item, m_items) { 187 | dereferenceItem (item); 188 | } 189 | m_items.clear (); 190 | endRemoveRows (); 191 | updateCounter (); 192 | } 193 | } 194 | void append (ItemType * item) { 195 | if (item != Q_NULLPTR) { 196 | const int pos = m_items.count (); 197 | beginInsertRows (noParent (), pos, pos); 198 | m_items.append (item); 199 | referenceItem (item); 200 | endInsertRows (); 201 | updateCounter (); 202 | } 203 | } 204 | void prepend (ItemType * item) { 205 | if (item != Q_NULLPTR) { 206 | beginInsertRows (noParent (), 0, 0); 207 | m_items.prepend (item); 208 | referenceItem (item); 209 | endInsertRows (); 210 | updateCounter (); 211 | } 212 | } 213 | void insert (int idx, ItemType * item) { 214 | if (item != Q_NULLPTR) { 215 | beginInsertRows (noParent (), idx, idx); 216 | m_items.insert (idx, item); 217 | referenceItem (item); 218 | endInsertRows (); 219 | updateCounter (); 220 | } 221 | } 222 | void append (const QList & itemList) { 223 | if (!itemList.isEmpty ()) { 224 | const int pos = m_items.count (); 225 | beginInsertRows (noParent (), pos, pos + itemList.count () -1); 226 | m_items.reserve (m_items.count () + itemList.count ()); 227 | m_items.append (itemList); 228 | FOREACH_PTR_IN_QLIST (ItemType, item, itemList) { 229 | referenceItem (item); 230 | } 231 | endInsertRows (); 232 | updateCounter (); 233 | } 234 | } 235 | void prepend (const QList & itemList) { 236 | if (!itemList.isEmpty ()) { 237 | beginInsertRows (noParent (), 0, itemList.count () -1); 238 | m_items.reserve (m_items.count () + itemList.count ()); 239 | int offset = 0; 240 | FOREACH_PTR_IN_QLIST (ItemType, item, itemList) { 241 | m_items.insert (offset, item); 242 | referenceItem (item); 243 | offset++; 244 | } 245 | endInsertRows (); 246 | updateCounter (); 247 | } 248 | } 249 | void insert (int idx, const QList & itemList) { 250 | if (!itemList.isEmpty ()) { 251 | beginInsertRows (noParent (), idx, idx + itemList.count () -1); 252 | m_items.reserve (m_items.count () + itemList.count ()); 253 | int offset = 0; 254 | FOREACH_PTR_IN_QLIST (ItemType, item, itemList) { 255 | m_items.insert (idx + offset, item); 256 | referenceItem (item); 257 | offset++; 258 | } 259 | endInsertRows (); 260 | updateCounter (); 261 | } 262 | } 263 | void move (int idx, int pos) { 264 | if (idx != pos) { 265 | const int lowest = qMin (idx, pos); 266 | const int highest = qMax (idx, pos); 267 | beginMoveRows (noParent (), highest, highest, noParent (), lowest); 268 | m_items.move (highest, lowest); 269 | endMoveRows (); 270 | } 271 | } 272 | void remove (ItemType * item) { 273 | if (item != Q_NULLPTR) { 274 | const int idx = m_items.indexOf (item); 275 | remove (idx); 276 | } 277 | } 278 | void remove (int idx) { 279 | if (idx >= 0 && idx < m_items.size ()) { 280 | beginRemoveRows (noParent (), idx, idx); 281 | ItemType * item = m_items.takeAt (idx); 282 | dereferenceItem (item); 283 | endRemoveRows (); 284 | updateCounter (); 285 | } 286 | } 287 | ItemType * first (void) const { 288 | return m_items.first (); 289 | } 290 | ItemType * last (void) const { 291 | return m_items.last (); 292 | } 293 | const QList & toList (void) const { 294 | return m_items; 295 | } 296 | 297 | public: // QML slots implementation 298 | void append (QObject * item) { 299 | append (qobject_cast (item)); 300 | } 301 | void prepend (QObject * item) { 302 | prepend (qobject_cast (item)); 303 | } 304 | void insert (int idx, QObject * item) { 305 | insert (idx, qobject_cast (item)); 306 | } 307 | void remove (QObject * item) { 308 | remove (qobject_cast (item)); 309 | } 310 | bool contains (QObject * item) const { 311 | return contains (qobject_cast (item)); 312 | } 313 | int indexOf (QObject * item) const { 314 | return indexOf (qobject_cast (item)); 315 | } 316 | int indexOf (const QString & uid) const { 317 | return indexOf (get (uid)); 318 | } 319 | QObject * get (int idx) const { 320 | return static_cast (at (idx)); 321 | } 322 | QObject * get (const QString & uid) const { 323 | return static_cast (getByUid (uid)); 324 | } 325 | QObject * getFirst (void) const { 326 | return static_cast (first ()); 327 | } 328 | QObject * getLast (void) const { 329 | return static_cast (last ()); 330 | } 331 | QVariantList toVarArray (void) const { 332 | return qListToVariant (m_items); 333 | } 334 | 335 | protected: // internal stuff 336 | static const QString & emptyStr (void) { 337 | static const QString ret = QStringLiteral (""); 338 | return ret; 339 | } 340 | static const QByteArray & emptyBA (void) { 341 | static const QByteArray ret = QByteArrayLiteral (""); 342 | return ret; 343 | } 344 | static const QModelIndex & noParent (void) { 345 | static const QModelIndex ret = QModelIndex (); 346 | return ret; 347 | } 348 | static const int & baseRole (void) { 349 | static const int ret = Qt::UserRole; 350 | return ret; 351 | } 352 | int rowCount (const QModelIndex & parent = QModelIndex ()) const { 353 | Q_UNUSED (parent); 354 | return m_items.count (); 355 | } 356 | void referenceItem (ItemType * item) { 357 | if (item != Q_NULLPTR) { 358 | if (item->parent () == Q_NULLPTR) { 359 | item->setParent (this); 360 | } 361 | const QList signalsIdxList = m_signalIdxToRole.keys (); 362 | for (QList::const_iterator it = signalsIdxList.constBegin (); it != signalsIdxList.constEnd (); it++) { 363 | const int signalIdx = static_cast (* it); 364 | QMetaMethod notifier = item->metaObject ()->method (signalIdx); 365 | connect (item, notifier, this, m_handler, Qt::UniqueConnection); 366 | } 367 | if (!m_uidRoleName.isEmpty ()) { 368 | const QString key = m_indexByUid.key (item, emptyStr ()); 369 | if (!key.isEmpty ()) { 370 | m_indexByUid.remove (key); 371 | } 372 | const QString value = item->property (m_uidRoleName).toString (); 373 | if (!value.isEmpty ()) { 374 | m_indexByUid.insert (value, item); 375 | } 376 | } 377 | } 378 | } 379 | void dereferenceItem (ItemType * item) { 380 | if (item != Q_NULLPTR) { 381 | disconnect (this, Q_NULLPTR, item, Q_NULLPTR); 382 | disconnect (item, Q_NULLPTR, this, Q_NULLPTR); 383 | if (!m_uidRoleName.isEmpty ()) { 384 | const QString key = m_indexByUid.key (item, emptyStr ()); 385 | if (!key.isEmpty ()) { 386 | m_indexByUid.remove (key); 387 | } 388 | } 389 | if (item->parent () == this) { // FIXME : maybe that's not the best way to test ownership ? 390 | item->deleteLater (); 391 | } 392 | } 393 | } 394 | void onItemPropertyChanged (void) { 395 | ItemType * item = qobject_cast (sender ()); 396 | const int row = m_items.indexOf (item); 397 | const int sig = senderSignalIndex (); 398 | const int role = m_signalIdxToRole.value (sig, -1); 399 | if (row >= 0 && role >= 0) { 400 | QModelIndex index = QAbstractListModel::index (row, 0, noParent ()); 401 | QVector rolesList; 402 | rolesList.append (role); 403 | if (m_roles.value (role) == m_dispRoleName) { 404 | rolesList.append (Qt::DisplayRole); 405 | } 406 | emit dataChanged (index, index, rolesList); 407 | } 408 | if (!m_uidRoleName.isEmpty ()) { 409 | const QByteArray roleName = m_roles.value (role, emptyBA ()); 410 | if (!roleName.isEmpty () && roleName == m_uidRoleName) { 411 | const QString key = m_indexByUid.key (item, emptyStr ()); 412 | if (!key.isEmpty ()) { 413 | m_indexByUid.remove (key); 414 | } 415 | const QString value = item->property (m_uidRoleName).toString (); 416 | if (!value.isEmpty ()) { 417 | m_indexByUid.insert (value, item); 418 | } 419 | } 420 | } 421 | } 422 | inline void updateCounter (void) { 423 | if (m_count != m_items.count ()) { 424 | m_count = m_items.count (); 425 | emit countChanged (); 426 | } 427 | } 428 | 429 | private: // data members 430 | int m_count; 431 | QByteArray m_uidRoleName; 432 | QByteArray m_dispRoleName; 433 | QMetaObject m_metaObj; 434 | QMetaMethod m_handler; 435 | QHash m_roles; 436 | QHash m_signalIdxToRole; 437 | QList m_items; 438 | QHash m_indexByUid; 439 | }; 440 | 441 | #define QML_OBJMODEL_PROPERTY(type, name) \ 442 | protected: Q_PROPERTY (QQmlObjectListModelBase * name READ get_##name CONSTANT) \ 443 | private: QQmlObjectListModel * m_##name; \ 444 | public: QQmlObjectListModel * get_##name (void) const { return m_##name; } \ 445 | private: 446 | 447 | #endif // QQMLOBJECTLISTMODEL_H 448 | --------------------------------------------------------------------------------