adding asset fetching
This commit is contained in:
commit
b9f6ee4f2c
12 changed files with 256 additions and 92 deletions
|
|
@ -174,10 +174,10 @@ void witness_plugin::block_production_loop()
|
|||
_production_enabled = true;
|
||||
|
||||
// is anyone scheduled to produce now or one second in the future?
|
||||
uint32_t slot = db.get_slot_at_time( graphene::time::now() + fc::seconds(1) );
|
||||
const fc::time_point_sec now = graphene::time::now();
|
||||
uint32_t slot = db.get_slot_at_time( now );
|
||||
graphene::chain::witness_id_type scheduled_witness = db.get_scheduled_witness( slot ).first;
|
||||
fc::time_point_sec scheduled_time = db.get_slot_time( slot );
|
||||
fc::time_point_sec now = graphene::time::now();
|
||||
graphene::chain::public_key_type scheduled_key = scheduled_witness( db ).signing_key;
|
||||
|
||||
auto is_scheduled = [&]()
|
||||
|
|
@ -194,7 +194,7 @@ void witness_plugin::block_production_loop()
|
|||
uint32_t prate = db.witness_participation_rate();
|
||||
if( prate < _required_witness_participation )
|
||||
{
|
||||
elog("Not producing block because node appers to be on a minority fork with only ${x}% witness participation",
|
||||
elog("Not producing block because node appears to be on a minority fork with only ${x}% witness participation",
|
||||
("x",uint32_t(100*uint64_t(prate) / GRAPHENE_1_PERCENT) ) );
|
||||
return false;
|
||||
}
|
||||
|
|
@ -212,21 +212,19 @@ void witness_plugin::block_production_loop()
|
|||
return false;
|
||||
}
|
||||
|
||||
// the local clock must be at least 1 second ahead of
|
||||
// head_block_time.
|
||||
if( (now - db.head_block_time()).to_seconds() <= 1 ) {
|
||||
// the local clock must be at least 1 second ahead of head_block_time.
|
||||
if( (now - db.head_block_time()).to_seconds() < GRAPHENE_MIN_BLOCK_INTERVAL ) {
|
||||
elog("Not producing block because head block is less than a second old.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// the local clock must be within 500 milliseconds of
|
||||
// the scheduled production time.
|
||||
if( llabs((scheduled_time - now).count()) > fc::milliseconds(250).count() ) {
|
||||
if( llabs((scheduled_time - now).count()) > fc::milliseconds( 500 ).count() ) {
|
||||
elog("Not producing block because network time is not within 250ms of scheduled block time.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// we must know the private key corresponding to the witness's
|
||||
// published block production key.
|
||||
if( _private_keys.find( scheduled_key ) == _private_keys.end() ) {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ file(GLOB QML qml/*)
|
|||
qt5_add_resources(QML_QRC qml/qml.qrc)
|
||||
|
||||
add_executable(light_client ClientDataModel.cpp ClientDataModel.hpp main.cpp ${QML_QRC} ${QML})
|
||||
add_dependencies(light_client gen_qrc)
|
||||
if (CMAKE_VERSION VERSION_LESS 3.0)
|
||||
add_dependencies(light_client gen_qrc)
|
||||
endif()
|
||||
|
||||
target_link_libraries(light_client PRIVATE Qt5::Core Qt5::Widgets Qt5::Quick graphene_chain graphene_utilities fc graphene_app )
|
||||
|
|
|
|||
|
|
@ -134,13 +134,14 @@ Asset* ChainDataModel::getAsset(QString symbol)
|
|||
|
||||
|
||||
|
||||
Account* ChainDataModel::getAccount(qint64 id)
|
||||
Account* ChainDataModel::getAccount(ObjectId id)
|
||||
{
|
||||
auto& by_id_idx = m_accounts.get<::by_id>();
|
||||
auto itr = by_id_idx.find(id);
|
||||
if( itr == by_id_idx.end() )
|
||||
{
|
||||
auto tmp = new Account;
|
||||
QQmlEngine::setObjectOwnership(tmp, QQmlEngine::CppOwnership);
|
||||
tmp->id = id; --m_account_query_num;
|
||||
tmp->name = QString::number( --m_account_query_num);
|
||||
auto result = m_accounts.insert( tmp );
|
||||
|
|
@ -173,7 +174,7 @@ Account* ChainDataModel::getAccount(qint64 id)
|
|||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
catch ( const fc::exception& e )
|
||||
{
|
||||
Q_EMIT exceptionThrown( QString::fromStdString(e.to_string()) );
|
||||
|
|
@ -191,6 +192,7 @@ Account* ChainDataModel::getAccount(QString name)
|
|||
if( itr == by_name_idx.end() )
|
||||
{
|
||||
auto tmp = new Account;
|
||||
QQmlEngine::setObjectOwnership(tmp, QQmlEngine::CppOwnership);
|
||||
tmp->id = --m_account_query_num;
|
||||
tmp->name = name;
|
||||
auto result = m_accounts.insert( tmp );
|
||||
|
|
@ -218,12 +220,12 @@ Account* ChainDataModel::getAccount(QString name)
|
|||
{
|
||||
by_symbol_idx.modify( itr,
|
||||
[=]( Account* a ){
|
||||
a->setProperty("id", result.front()->id.instance() );
|
||||
a->setProperty("id", ObjectId(result.front()->id.instance()));
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
catch ( const fc::exception& e )
|
||||
{
|
||||
Q_EMIT exceptionThrown( QString::fromStdString(e.to_string()) );
|
||||
|
|
@ -278,6 +280,7 @@ void GrapheneApplication::start( QString apiurl, QString user, QString pass )
|
|||
m_client = std::make_shared<fc::http::websocket_client>();
|
||||
ilog( "connecting...${s}", ("s",apiurl.toStdString()) );
|
||||
auto con = m_client->connect( apiurl.toStdString() );
|
||||
m_connectionClosed = con->closed.connect([this]{queueExecute([this]{setIsConnected(false);});});
|
||||
auto apic = std::make_shared<fc::rpc::websocket_api_connection>(*con);
|
||||
auto remote_api = apic->get_remote_api< login_api >(1);
|
||||
auto db_api = apic->get_remote_api< database_api >(0);
|
||||
|
|
|
|||
|
|
@ -11,25 +11,38 @@
|
|||
#include <fc/thread/thread.hpp>
|
||||
#include <graphene/app/api.hpp>
|
||||
|
||||
#include <QtQml>
|
||||
#include <QObject>
|
||||
#include <QQmlListProperty>
|
||||
|
||||
using boost::multi_index_container;
|
||||
using namespace boost::multi_index;
|
||||
|
||||
using ObjectId = qint64;
|
||||
Q_DECLARE_METATYPE(ObjectId)
|
||||
|
||||
Q_DECLARE_METATYPE(std::function<void()>)
|
||||
|
||||
class GrapheneObject : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(qint64 id MEMBER id NOTIFY idChanged)
|
||||
Q_PROPERTY(ObjectId id MEMBER id NOTIFY idChanged)
|
||||
|
||||
public:
|
||||
qint64 id;
|
||||
ObjectId id;
|
||||
|
||||
Q_SIGNALS:
|
||||
void idChanged();
|
||||
};
|
||||
class Crypto {
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
Q_INVOKABLE QString sha256(QByteArray data) {
|
||||
return QCryptographicHash::hash(data, QCryptographicHash::Sha256).toHex();
|
||||
}
|
||||
};
|
||||
QML_DECLARE_TYPE(Crypto)
|
||||
|
||||
|
||||
class Asset : public GrapheneObject {
|
||||
|
|
@ -77,7 +90,6 @@ class Account : public GrapheneObject {
|
|||
|
||||
public:
|
||||
const QString& getName()const { return name; }
|
||||
|
||||
QQmlListProperty<Balance> balances();
|
||||
|
||||
QString name;
|
||||
|
|
@ -90,19 +102,16 @@ struct by_account_name;
|
|||
typedef multi_index_container<
|
||||
Account*,
|
||||
indexed_by<
|
||||
hashed_unique< tag<by_id>, member<GrapheneObject, qint64, &GrapheneObject::id > >,
|
||||
hashed_unique< tag<by_id>, member<GrapheneObject, ObjectId, &GrapheneObject::id > >,
|
||||
ordered_unique< tag<by_account_name>, member<Account, QString, &Account::name> >
|
||||
>
|
||||
> account_multi_index_type;
|
||||
|
||||
|
||||
|
||||
|
||||
class ChainDataModel : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Q_INVOKABLE Account* getAccount(qint64 id);
|
||||
Q_INVOKABLE Account* getAccount(ObjectId id);
|
||||
Q_INVOKABLE Account* getAccount(QString name);
|
||||
Q_INVOKABLE Asset* getAsset(qint64 id);
|
||||
Q_INVOKABLE Asset* getAsset(QString symbol);
|
||||
|
|
@ -121,15 +130,11 @@ private:
|
|||
std::string m_api_url;
|
||||
fc::api<graphene::app::database_api> m_db_api;
|
||||
|
||||
qint64 m_account_query_num = -1;
|
||||
ObjectId m_account_query_num = -1;
|
||||
account_multi_index_type m_accounts;
|
||||
asset_multi_index_type m_assets;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class GrapheneApplication : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
|
|
@ -141,6 +146,8 @@ class GrapheneApplication : public QObject {
|
|||
ChainDataModel* m_model = nullptr;
|
||||
bool m_isConnected = false;
|
||||
|
||||
boost::signals2::scoped_connection m_connectionClosed;
|
||||
|
||||
std::shared_ptr<fc::http::websocket_client> m_client;
|
||||
fc::future<void> m_done;
|
||||
|
||||
|
|
|
|||
11
programs/light_client/README.md
Normal file
11
programs/light_client/README.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
== Graphene Client GUI ==
|
||||
|
||||
This is a Qt-based native GUI client for Graphene blockchains.
|
||||
|
||||
To build this GUI, run cmake with -DBUILD_QT_GUI=ON
|
||||
|
||||
This GUI depends on Qt 5.5 or later. If you do not have Qt 5.5 installed
|
||||
in the canonical location on your OS (or if your OS does not have a
|
||||
canonical location for libraries), you can specify the Qt path by running
|
||||
cmake with -DCMAKE_PREFIX_PATH=/path/to/Qt/5.5/gcc_64 as appropriate for
|
||||
your environment.
|
||||
|
|
@ -13,6 +13,7 @@ int main(int argc, char *argv[])
|
|||
app.setOrganizationName("Cryptonomex, Inc.");
|
||||
|
||||
qRegisterMetaType<std::function<void()>>();
|
||||
qRegisterMetaType<ObjectId>();
|
||||
|
||||
qmlRegisterType<Asset>("Graphene.Client", 0, 1, "Asset");
|
||||
qmlRegisterType<Balance>("Graphene.Client", 0, 1, "Balance");
|
||||
|
|
@ -21,11 +22,9 @@ int main(int argc, char *argv[])
|
|||
qmlRegisterType<GrapheneApplication>("Graphene.Client", 0, 1, "GrapheneApplication");
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
/*
|
||||
QVariant crypto;
|
||||
crypto.setValue(Crypto());
|
||||
engine.rootContext()->setContextProperty("Crypto", crypto);
|
||||
*/
|
||||
#ifdef NDEBUG
|
||||
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
|
||||
#else
|
||||
|
|
|
|||
72
programs/light_client/qml/AccountPicker.qml
Normal file
72
programs/light_client/qml/AccountPicker.qml
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Dialogs 1.2
|
||||
import QtQuick.Layouts 1.2
|
||||
|
||||
import Graphene.Client 0.1
|
||||
|
||||
import "."
|
||||
|
||||
RowLayout {
|
||||
property Account account
|
||||
|
||||
property alias placeholderText: accountNameField.placeholderText
|
||||
|
||||
function setFocus() {
|
||||
accountNameField.forceActiveFocus()
|
||||
}
|
||||
|
||||
Identicon {
|
||||
name: accountNameField.text
|
||||
width: Scaling.cm(2)
|
||||
height: Scaling.cm(2)
|
||||
}
|
||||
Column {
|
||||
Layout.fillWidth: true
|
||||
TextField {
|
||||
id: accountNameField
|
||||
|
||||
width: parent.width
|
||||
onEditingFinished: accountDetails.update(text)
|
||||
}
|
||||
Label {
|
||||
id: accountDetails
|
||||
function update(name) {
|
||||
if (!name)
|
||||
{
|
||||
text = ""
|
||||
return
|
||||
}
|
||||
|
||||
account = app.model.getAccount(name)
|
||||
if (account == null)
|
||||
text = qsTr("Error fetching account.")
|
||||
else
|
||||
text = Qt.binding(function() {
|
||||
if (account == null)
|
||||
return qsTr("Account does not exist.")
|
||||
return qsTr("Account ID: %1").arg(account.id < 0? qsTr("Loading...")
|
||||
: account.id)
|
||||
})
|
||||
}
|
||||
|
||||
Behavior on text {
|
||||
SequentialAnimation {
|
||||
PropertyAnimation {
|
||||
target: accountDetails
|
||||
property: "opacity"
|
||||
from: 1; to: 0
|
||||
duration: 100
|
||||
}
|
||||
PropertyAction { target: accountDetails; property: "text" }
|
||||
PropertyAnimation {
|
||||
target: accountDetails
|
||||
property: "opacity"
|
||||
from: 0; to: 1
|
||||
duration: 100
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -22,32 +22,49 @@ Rectangle {
|
|||
function showForm(formType, params, closedCallback) {
|
||||
if (formType.status === Component.Error)
|
||||
console.log(formType.errorString())
|
||||
if (!params instanceof Object)
|
||||
params = {app: app}
|
||||
else
|
||||
params.app = app
|
||||
|
||||
formContainer.data = [formType.createObject(formContainer, params)]
|
||||
var form = formType.createObject(formContainer, params)
|
||||
formContainer.data = [form]
|
||||
form.finished.connect(function(){state = "HIDDEN"})
|
||||
if (closedCallback instanceof Function)
|
||||
internal.callback = closedCallback
|
||||
state = "SHOWN"
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseTrap
|
||||
FocusScope {
|
||||
id: scope
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
mouse.accepted = true
|
||||
greySheet.state = "HIDDEN"
|
||||
|
||||
// Do not let focus leave this scope while form is open
|
||||
onFocusChanged: if (enabled && !focus) forceActiveFocus()
|
||||
|
||||
Keys.onEscapePressed: greySheet.state = "HIDDEN"
|
||||
|
||||
MouseArea {
|
||||
id: mouseTrap
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
mouse.accepted = true
|
||||
greySheet.state = "HIDDEN"
|
||||
}
|
||||
acceptedButtons: Qt.AllButtons
|
||||
}
|
||||
MouseArea {
|
||||
// This mouse area blocks clicks inside the form from reaching the mouseTrap
|
||||
anchors.fill: formContainer
|
||||
acceptedButtons: Qt.AllButtons
|
||||
onClicked: mouse.accepted = true
|
||||
}
|
||||
Item {
|
||||
id: formContainer
|
||||
anchors.centerIn: parent
|
||||
width: parent.width / 2
|
||||
height: parent.height / 2
|
||||
}
|
||||
acceptedButtons: Qt.AllButtons
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: formContainer
|
||||
acceptedButtons: Qt.AllButtons
|
||||
onClicked: mouse.accepted = true
|
||||
}
|
||||
Item {
|
||||
id: formContainer
|
||||
anchors.centerIn: parent
|
||||
width: parent.width / 2
|
||||
height: parent.height / 2
|
||||
}
|
||||
|
||||
states: [
|
||||
|
|
|
|||
39
programs/light_client/qml/Identicon.qml
Normal file
39
programs/light_client/qml/Identicon.qml
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import QtQuick 2.5
|
||||
|
||||
import "jdenticon/jdenticon-1.0.1.min.js" as Jdenticon
|
||||
|
||||
Canvas {
|
||||
id: identicon
|
||||
contextType: "2d"
|
||||
|
||||
property var name
|
||||
onNameChanged: requestPaint()
|
||||
|
||||
onPaint: {
|
||||
if (name)
|
||||
Jdenticon.draw(identicon, name)
|
||||
else {
|
||||
var context = identicon.context
|
||||
context.reset()
|
||||
var draw_circle = function(context, x, y, radius) {
|
||||
context.beginPath()
|
||||
context.arc(x, y, radius, 0, 2 * Math.PI, false)
|
||||
context.fillStyle = "rgba(0, 0, 0, 0.1)"
|
||||
context.fill()
|
||||
}
|
||||
var size = Math.min(identicon.height, identicon.width)
|
||||
var centerX = size / 2
|
||||
var centerY = size / 2
|
||||
var radius = size/15
|
||||
draw_circle(context, centerX, centerY, radius)
|
||||
draw_circle(context, 2*radius, 2*radius, radius)
|
||||
draw_circle(context, centerX, 2*radius, radius)
|
||||
draw_circle(context, size - 2*radius, 2*radius, radius)
|
||||
draw_circle(context, size - 2*radius, centerY, radius)
|
||||
draw_circle(context, size - 2*radius, size - 2*radius, radius)
|
||||
draw_circle(context, centerX, size - 2*radius, radius)
|
||||
draw_circle(context, 2*radius, size - 2*radius, radius)
|
||||
draw_circle(context, 2*radius, centerY, radius)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,57 +3,61 @@ import QtQuick.Controls 1.4
|
|||
import QtQuick.Dialogs 1.2
|
||||
import QtQuick.Layouts 1.2
|
||||
|
||||
import Graphene.Client 0.1
|
||||
|
||||
import "."
|
||||
import "jdenticon/jdenticon-1.0.1.min.js" as Jdenticon
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
|
||||
property alias senderAccount: senderPicker.account
|
||||
property alias receiverAccount: recipientPicker.account
|
||||
|
||||
property GrapheneApplication app
|
||||
signal finished
|
||||
|
||||
Component.onCompleted: console.log("Made a transfer form")
|
||||
Component.onDestruction: console.log("Destroyed a transfer form")
|
||||
|
||||
Column {
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - Scaling.cm(2)
|
||||
spacing: Scaling.cm(1)
|
||||
|
||||
AccountPicker {
|
||||
id: senderPicker
|
||||
width: parent.width
|
||||
Component.onCompleted: setFocus()
|
||||
placeholderText: qsTr("Sender")
|
||||
}
|
||||
AccountPicker {
|
||||
id: recipientPicker
|
||||
width: parent.width
|
||||
placeholderText: qsTr("Recipient")
|
||||
layoutDirection: Qt.RightToLeft
|
||||
}
|
||||
RowLayout {
|
||||
Canvas {
|
||||
id: identicon
|
||||
width: Scaling.cm(2)
|
||||
height: Scaling.cm(2)
|
||||
contextType: "2d"
|
||||
|
||||
onPaint: {
|
||||
if (nameField.text)
|
||||
Jdenticon.draw(identicon, nameField.text)
|
||||
else {
|
||||
var context = identicon.context
|
||||
context.reset()
|
||||
var draw_circle = function(context, x, y, radius) {
|
||||
context.beginPath()
|
||||
context.arc(x, y, radius, 0, 2 * Math.PI, false)
|
||||
context.fillStyle = "rgba(0, 0, 0, 0.1)"
|
||||
context.fill()
|
||||
}
|
||||
var size = Math.min(identicon.height, identicon.width)
|
||||
var centerX = size / 2
|
||||
var centerY = size / 2
|
||||
var radius = size/15
|
||||
draw_circle(context, centerX, centerY, radius)
|
||||
draw_circle(context, 2*radius, 2*radius, radius)
|
||||
draw_circle(context, centerX, 2*radius, radius)
|
||||
draw_circle(context, size - 2*radius, 2*radius, radius)
|
||||
draw_circle(context, size - 2*radius, centerY, radius)
|
||||
draw_circle(context, size - 2*radius, size - 2*radius, radius)
|
||||
draw_circle(context, centerX, size - 2*radius, radius)
|
||||
draw_circle(context, 2*radius, size - 2*radius, radius)
|
||||
draw_circle(context, 2*radius, centerY, radius)
|
||||
}
|
||||
}
|
||||
width: parent.width
|
||||
SpinBox {
|
||||
Layout.preferredWidth: Scaling.cm(4)
|
||||
Layout.minimumWidth: Scaling.cm(1.5)
|
||||
enabled: senderPicker.account
|
||||
minimumValue: 0
|
||||
maximumValue: Number.POSITIVE_INFINITY
|
||||
}
|
||||
TextField {
|
||||
id: nameField
|
||||
Layout.fillWidth: true
|
||||
onTextChanged: identicon.requestPaint()
|
||||
ComboBox {
|
||||
Layout.minimumWidth: Scaling.cm(3)
|
||||
enabled: senderPicker.account
|
||||
model: ["CORE", "USD", "GOLD"]
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
Button {
|
||||
text: qsTr("Cancel")
|
||||
onClicked: finished()
|
||||
}
|
||||
Button {
|
||||
text: qsTr("Transfer")
|
||||
enabled: senderPicker.account
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,10 +14,6 @@ ApplicationWindow {
|
|||
height: 480
|
||||
title: qsTr("Hello World")
|
||||
|
||||
Component.onCompleted: {
|
||||
app.start("ws://localhost:8090", "user", "pass")
|
||||
}
|
||||
|
||||
menuBar: MenuBar {
|
||||
Menu {
|
||||
title: qsTr("File")
|
||||
|
|
@ -28,12 +24,26 @@ ApplicationWindow {
|
|||
MenuItem {
|
||||
text: qsTr("Exit")
|
||||
onTriggered: Qt.quit();
|
||||
shortcut: "Ctrl+Q"
|
||||
}
|
||||
}
|
||||
}
|
||||
statusBar: StatusBar {
|
||||
Label {
|
||||
anchors.right: parent.right
|
||||
text: app.isConnected? qsTr("Connected") : qsTr("Disconnected")
|
||||
}
|
||||
}
|
||||
|
||||
GrapheneApplication {
|
||||
id: app
|
||||
}
|
||||
Timer {
|
||||
running: !app.isConnected
|
||||
interval: 5000
|
||||
repeat: true
|
||||
onTriggered: app.start("ws://localhost:8090", "user", "pass")
|
||||
triggeredOnStart: true
|
||||
}
|
||||
Settings {
|
||||
id: appSettings
|
||||
|
|
@ -46,6 +56,8 @@ ApplicationWindow {
|
|||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
enabled: app.isConnected
|
||||
|
||||
Button {
|
||||
text: "Transfer"
|
||||
onClicked: formBox.showForm(Qt.createComponent("TransferForm.qml"), {},
|
||||
|
|
@ -142,8 +154,6 @@ ApplicationWindow {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
FormBox {
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@
|
|||
<file>main.qml</file>
|
||||
<file>TransferForm.qml</file>
|
||||
<file>FormBox.qml</file>
|
||||
<file>jdenticon/jdenticon-1.0.1.min.js</file>
|
||||
<file>Scaling.qml</file>
|
||||
<file>Identicon.qml</file>
|
||||
<file>AccountPicker.qml</file>
|
||||
<file>jdenticon/jdenticon-1.0.1.min.js</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue