├── 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 : [](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 |
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 |
4 |
5 | Quick Promise - QML Promise Library
6 | ===================================
7 | [](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 |
--------------------------------------------------------------------------------