[GUI] Break up ClientDataModel.{c,h}pp into many files
This has been needing to happen. Now it's done. There's no going back.
This commit is contained in:
parent
9a3a6a5234
commit
1667a72144
14 changed files with 426 additions and 384 deletions
37
programs/light_client/Account.cpp
Normal file
37
programs/light_client/Account.cpp
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
#include "Balance.hpp"
|
||||||
|
#include "ChainDataModel.hpp"
|
||||||
|
|
||||||
|
#include <graphene/chain/account_object.hpp>
|
||||||
|
|
||||||
|
QQmlListProperty<Balance> Account::balances()
|
||||||
|
{
|
||||||
|
auto count = [](QQmlListProperty<Balance>* list) {
|
||||||
|
return reinterpret_cast<Account*>(list->data)->m_balances.size();
|
||||||
|
};
|
||||||
|
auto at = [](QQmlListProperty<Balance>* list, int index) {
|
||||||
|
return reinterpret_cast<Account*>(list->data)->m_balances[index];
|
||||||
|
};
|
||||||
|
|
||||||
|
return QQmlListProperty<Balance>(this, this, count, at);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Account::update(const graphene::chain::account_balance_object& balance)
|
||||||
|
{
|
||||||
|
auto balanceItr = std::find_if(m_balances.begin(), m_balances.end(),
|
||||||
|
[&balance](Balance* b) { return b->type()->id() == balance.asset_type.instance.value; });
|
||||||
|
|
||||||
|
if (balanceItr != m_balances.end()) {
|
||||||
|
ilog("Updating ${a}'s balance: ${b}", ("a", m_name.toStdString())("b", balance));
|
||||||
|
(*balanceItr)->update(balance);
|
||||||
|
Q_EMIT balancesChanged();
|
||||||
|
} else {
|
||||||
|
ilog("Adding to ${a}'s new balance: ${b}", ("a", m_name.toStdString())("b", balance));
|
||||||
|
Balance* newBalance = new Balance;
|
||||||
|
newBalance->setParent(this);
|
||||||
|
auto model = qobject_cast<ChainDataModel*>(parent());
|
||||||
|
newBalance->setProperty("type", QVariant::fromValue(model->getAsset(balance.asset_type.instance.value)));
|
||||||
|
newBalance->setProperty("amount", QVariant::fromValue(balance.balance.value));
|
||||||
|
m_balances.append(newBalance);
|
||||||
|
Q_EMIT balancesChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
42
programs/light_client/Account.hpp
Normal file
42
programs/light_client/Account.hpp
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
#pragma once
|
||||||
|
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
|
||||||
|
|
||||||
|
#include "GrapheneObject.hpp"
|
||||||
|
|
||||||
|
#include <QQmlListProperty>
|
||||||
|
|
||||||
|
namespace graphene { namespace chain {
|
||||||
|
class account_balance_object;
|
||||||
|
}}
|
||||||
|
|
||||||
|
class Balance;
|
||||||
|
class Account : public GrapheneObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(QString name MEMBER m_name READ name NOTIFY nameChanged)
|
||||||
|
Q_PROPERTY(QQmlListProperty<Balance> balances READ balances NOTIFY balancesChanged)
|
||||||
|
|
||||||
|
QString m_name;
|
||||||
|
QList<Balance*> m_balances;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Account(ObjectId id = -1, QString name = QString(), QObject* parent = nullptr)
|
||||||
|
: GrapheneObject(id, parent), m_name(name)
|
||||||
|
{}
|
||||||
|
|
||||||
|
QString name()const { return m_name; }
|
||||||
|
QQmlListProperty<Balance> balances();
|
||||||
|
|
||||||
|
void setBalances(QList<Balance*> balances) {
|
||||||
|
if (balances != m_balances) {
|
||||||
|
m_balances = balances;
|
||||||
|
Q_EMIT balancesChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(const graphene::chain::account_balance_object& balance);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void nameChanged();
|
||||||
|
void balancesChanged();
|
||||||
|
};
|
||||||
22
programs/light_client/Asset.cpp
Normal file
22
programs/light_client/Asset.cpp
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
#include "Asset.hpp"
|
||||||
|
|
||||||
|
#include <graphene/chain/asset_object.hpp>
|
||||||
|
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
void Asset::update(const graphene::chain::asset_object& asset)
|
||||||
|
{
|
||||||
|
if (asset.id.instance() != id())
|
||||||
|
setProperty("id", QVariant::fromValue(asset.id.instance()));
|
||||||
|
if (asset.symbol != m_symbol.toStdString()) {
|
||||||
|
m_symbol = QString::fromStdString(asset.symbol);
|
||||||
|
Q_EMIT symbolChanged();
|
||||||
|
}
|
||||||
|
if (asset.precision != m_precision) {
|
||||||
|
m_precision = asset.precision;
|
||||||
|
Q_EMIT precisionChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (asset.options.core_exchange_rate != coreExchangeRate)
|
||||||
|
coreExchangeRate = asset.options.core_exchange_rate;
|
||||||
|
}
|
||||||
39
programs/light_client/Asset.hpp
Normal file
39
programs/light_client/Asset.hpp
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "GrapheneObject.hpp"
|
||||||
|
|
||||||
|
#include "graphene/chain/protocol/asset.hpp"
|
||||||
|
|
||||||
|
class Asset : public GrapheneObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(QString symbol MEMBER m_symbol READ symbol NOTIFY symbolChanged)
|
||||||
|
Q_PROPERTY(quint32 precision MEMBER m_precision NOTIFY precisionChanged)
|
||||||
|
|
||||||
|
QString m_symbol;
|
||||||
|
quint32 m_precision;
|
||||||
|
|
||||||
|
graphene::chain::price coreExchangeRate;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Asset(ObjectId id = -1, QString symbol = QString(), quint32 precision = 0, QObject* parent = nullptr)
|
||||||
|
: GrapheneObject(id, parent), m_symbol(symbol), m_precision(precision)
|
||||||
|
{}
|
||||||
|
|
||||||
|
QString symbol() const {
|
||||||
|
return m_symbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint64 precisionPower() const {
|
||||||
|
quint64 power = 1;
|
||||||
|
for (int i = 0; i < m_precision; ++i)
|
||||||
|
power *= 10;
|
||||||
|
return power;
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(const graphene::chain::asset_object& asset);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void symbolChanged();
|
||||||
|
void precisionChanged();
|
||||||
|
};
|
||||||
16
programs/light_client/Balance.cpp
Normal file
16
programs/light_client/Balance.cpp
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
#include "Balance.hpp"
|
||||||
|
#include "Asset.hpp"
|
||||||
|
|
||||||
|
#include <graphene/chain/account_object.hpp>
|
||||||
|
|
||||||
|
qreal Balance::amountReal() const {
|
||||||
|
return amount / qreal(m_type->precisionPower());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Balance::update(const graphene::chain::account_balance_object& update)
|
||||||
|
{
|
||||||
|
if (update.balance != amount) {
|
||||||
|
amount = update.balance.value;
|
||||||
|
emit amountChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
33
programs/light_client/Balance.hpp
Normal file
33
programs/light_client/Balance.hpp
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
#pragma once
|
||||||
|
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
|
||||||
|
|
||||||
|
#include "GrapheneObject.hpp"
|
||||||
|
|
||||||
|
namespace graphene { namespace chain {
|
||||||
|
class account_balance_object;
|
||||||
|
}}
|
||||||
|
|
||||||
|
class Asset;
|
||||||
|
class Balance : public GrapheneObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(Asset* type MEMBER m_type READ type NOTIFY typeChanged)
|
||||||
|
Q_PROPERTY(qint64 amount MEMBER amount NOTIFY amountChanged)
|
||||||
|
|
||||||
|
Asset* m_type;
|
||||||
|
qint64 amount;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// This ultimately needs to be replaced with a string equivalent
|
||||||
|
Q_INVOKABLE qreal amountReal() const;
|
||||||
|
|
||||||
|
Asset* type()const {
|
||||||
|
return m_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(const graphene::chain::account_balance_object& update);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void typeChanged();
|
||||||
|
void amountChanged();
|
||||||
|
};
|
||||||
|
|
@ -18,7 +18,7 @@ if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
qt5_add_resources(QML_QRC qml/qml.qrc)
|
qt5_add_resources(QML_QRC qml/qml.qrc)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_executable(light_client ClientDataModel.cpp ClientDataModel.hpp Operations.cpp main.cpp ${QML_QRC} ${QML})
|
add_executable(light_client ChainDataModel.cpp Operations.cpp GrapheneApplication.cpp GrapheneObject.cpp Asset.cpp Account.cpp Balance.cpp main.cpp ${QML_QRC} ${QML})
|
||||||
|
|
||||||
if (CMAKE_VERSION VERSION_LESS 3.0)
|
if (CMAKE_VERSION VERSION_LESS 3.0)
|
||||||
add_dependencies(light_client gen_qrc)
|
add_dependencies(light_client gen_qrc)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "ClientDataModel.hpp"
|
#include "ChainDataModel.hpp"
|
||||||
|
#include "Balance.hpp"
|
||||||
#include "Operations.hpp"
|
#include "Operations.hpp"
|
||||||
|
|
||||||
#include <graphene/app/api.hpp>
|
#include <graphene/app/api.hpp>
|
||||||
|
|
@ -250,130 +251,3 @@ Account* ChainDataModel::getAccount(QString name)
|
||||||
}
|
}
|
||||||
return *itr;
|
return *itr;
|
||||||
}
|
}
|
||||||
|
|
||||||
QQmlListProperty<Balance> Account::balances()
|
|
||||||
{
|
|
||||||
auto count = [](QQmlListProperty<Balance>* list) {
|
|
||||||
return reinterpret_cast<Account*>(list->data)->m_balances.size();
|
|
||||||
};
|
|
||||||
auto at = [](QQmlListProperty<Balance>* list, int index) {
|
|
||||||
return reinterpret_cast<Account*>(list->data)->m_balances[index];
|
|
||||||
};
|
|
||||||
|
|
||||||
return QQmlListProperty<Balance>(this, this, count, at);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Account::update(const account_balance_object& balance)
|
|
||||||
{
|
|
||||||
auto balanceItr = std::find_if(m_balances.begin(), m_balances.end(),
|
|
||||||
[&balance](Balance* b) { return b->type()->id() == balance.asset_type.instance.value; });
|
|
||||||
|
|
||||||
if (balanceItr != m_balances.end()) {
|
|
||||||
ilog("Updating ${a}'s balance: ${b}", ("a", m_name.toStdString())("b", balance));
|
|
||||||
(*balanceItr)->update(balance);
|
|
||||||
Q_EMIT balancesChanged();
|
|
||||||
} else {
|
|
||||||
ilog("Adding to ${a}'s new balance: ${b}", ("a", m_name.toStdString())("b", balance));
|
|
||||||
Balance* newBalance = new Balance;
|
|
||||||
newBalance->setParent(this);
|
|
||||||
auto model = qobject_cast<ChainDataModel*>(parent());
|
|
||||||
newBalance->setProperty("type", QVariant::fromValue(model->getAsset(balance.asset_type.instance.value)));
|
|
||||||
newBalance->setProperty("amount", QVariant::fromValue(balance.balance.value));
|
|
||||||
m_balances.append(newBalance);
|
|
||||||
Q_EMIT balancesChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GrapheneApplication::GrapheneApplication(QObject* parent)
|
|
||||||
:QObject(parent),m_thread("app")
|
|
||||||
{
|
|
||||||
connect(this, &GrapheneApplication::queueExecute,
|
|
||||||
this, &GrapheneApplication::execute);
|
|
||||||
|
|
||||||
m_model = new ChainDataModel(m_thread, this);
|
|
||||||
m_operationBuilder = new OperationBuilder(*m_model, this);
|
|
||||||
|
|
||||||
connect(m_model, &ChainDataModel::queueExecute,
|
|
||||||
this, &GrapheneApplication::execute);
|
|
||||||
|
|
||||||
connect(m_model, &ChainDataModel::exceptionThrown,
|
|
||||||
this, &GrapheneApplication::exceptionThrown);
|
|
||||||
}
|
|
||||||
|
|
||||||
GrapheneApplication::~GrapheneApplication()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void GrapheneApplication::setIsConnected(bool v)
|
|
||||||
{
|
|
||||||
if (v != m_isConnected)
|
|
||||||
{
|
|
||||||
m_isConnected = v;
|
|
||||||
Q_EMIT isConnectedChanged(m_isConnected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GrapheneApplication::start(QString apiurl, QString user, QString pass)
|
|
||||||
{
|
|
||||||
if (!m_thread.is_current())
|
|
||||||
{
|
|
||||||
m_done = m_thread.async([=](){ return start(apiurl, user, pass); });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
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);
|
|
||||||
if (!remote_api->login(user.toStdString(), pass.toStdString()))
|
|
||||||
{
|
|
||||||
elog("login failed");
|
|
||||||
Q_EMIT loginFailed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ilog("connecting...");
|
|
||||||
queueExecute([=](){
|
|
||||||
m_model->setDatabaseAPI(db_api);
|
|
||||||
});
|
|
||||||
|
|
||||||
queueExecute([=](){ setIsConnected(true); });
|
|
||||||
} catch (const fc::exception& e)
|
|
||||||
{
|
|
||||||
Q_EMIT exceptionThrown(QString::fromStdString(e.to_string()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_SLOT void GrapheneApplication::execute(const std::function<void()>& func)const
|
|
||||||
{
|
|
||||||
func();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Balance::update(const account_balance_object& update)
|
|
||||||
{
|
|
||||||
if (update.balance != amount) {
|
|
||||||
amount = update.balance.value;
|
|
||||||
emit amountChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Asset::update(const asset_object& asset)
|
|
||||||
{
|
|
||||||
if (asset.id.instance() != id())
|
|
||||||
setProperty("id", QVariant::fromValue(asset.id.instance()));
|
|
||||||
if (asset.symbol != m_symbol.toStdString()) {
|
|
||||||
m_symbol = QString::fromStdString(asset.symbol);
|
|
||||||
Q_EMIT symbolChanged();
|
|
||||||
}
|
|
||||||
if (asset.precision != m_precision) {
|
|
||||||
m_precision = asset.precision;
|
|
||||||
Q_EMIT precisionChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (asset.options.core_exchange_rate != coreExchangeRate)
|
|
||||||
coreExchangeRate = asset.options.core_exchange_rate;
|
|
||||||
}
|
|
||||||
73
programs/light_client/ChainDataModel.hpp
Normal file
73
programs/light_client/ChainDataModel.hpp
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
#pragma once
|
||||||
|
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
|
||||||
|
|
||||||
|
#include <fc/network/http/websocket.hpp>
|
||||||
|
#include <graphene/app/api.hpp>
|
||||||
|
|
||||||
|
#include "BoostMultiIndex.hpp"
|
||||||
|
#include "Asset.hpp"
|
||||||
|
#include "Account.hpp"
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
using graphene::chain::by_id;
|
||||||
|
|
||||||
|
namespace fc {
|
||||||
|
class thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct by_symbol_name;
|
||||||
|
typedef multi_index_container<
|
||||||
|
Asset*,
|
||||||
|
indexed_by<
|
||||||
|
hashed_unique< tag<by_id>, const_mem_fun<GrapheneObject, ObjectId, &GrapheneObject::id > >,
|
||||||
|
ordered_unique< tag<by_symbol_name>, const_mem_fun<Asset, QString, &Asset::symbol> >
|
||||||
|
>
|
||||||
|
> asset_multi_index_type;
|
||||||
|
|
||||||
|
struct by_account_name;
|
||||||
|
typedef multi_index_container<
|
||||||
|
Account*,
|
||||||
|
indexed_by<
|
||||||
|
hashed_unique< tag<by_id>, const_mem_fun<GrapheneObject, ObjectId, &GrapheneObject::id > >,
|
||||||
|
ordered_unique< tag<by_account_name>, const_mem_fun<Account, QString, &Account::name> >
|
||||||
|
>
|
||||||
|
> account_multi_index_type;
|
||||||
|
|
||||||
|
class ChainDataModel : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
void processUpdatedObject(const fc::variant& update);
|
||||||
|
|
||||||
|
void getAssetImpl(QString assetIdentifier, Asset* const * assetInContainer);
|
||||||
|
void getAccountImpl(QString accountIdentifier, Account* const * accountInContainer);
|
||||||
|
|
||||||
|
public:
|
||||||
|
Q_INVOKABLE Account* getAccount(ObjectId id);
|
||||||
|
Q_INVOKABLE Account* getAccount(QString name);
|
||||||
|
Q_INVOKABLE Asset* getAsset(ObjectId id);
|
||||||
|
Q_INVOKABLE Asset* getAsset(QString symbol);
|
||||||
|
|
||||||
|
ChainDataModel(){}
|
||||||
|
ChainDataModel(fc::thread& t, QObject* parent = nullptr);
|
||||||
|
|
||||||
|
void setDatabaseAPI(fc::api<graphene::app::database_api> dbapi);
|
||||||
|
|
||||||
|
const graphene::chain::global_property_object& global_properties() const { return m_global_properties; }
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void queueExecute(const std::function<void()>&);
|
||||||
|
void exceptionThrown(QString message);
|
||||||
|
|
||||||
|
private:
|
||||||
|
fc::thread* m_rpc_thread = nullptr;
|
||||||
|
std::string m_api_url;
|
||||||
|
fc::api<graphene::app::database_api> m_db_api;
|
||||||
|
|
||||||
|
graphene::chain::global_property_object m_global_properties;
|
||||||
|
|
||||||
|
ObjectId m_account_query_num = -1;
|
||||||
|
account_multi_index_type m_accounts;
|
||||||
|
asset_multi_index_type m_assets;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
@ -1,253 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
|
|
||||||
|
|
||||||
#include <boost/multi_index_container.hpp>
|
|
||||||
#include <boost/multi_index/member.hpp>
|
|
||||||
#include <boost/multi_index/ordered_index.hpp>
|
|
||||||
#include <boost/multi_index/hashed_index.hpp>
|
|
||||||
#include <boost/multi_index/mem_fun.hpp>
|
|
||||||
|
|
||||||
#include <fc/network/http/websocket.hpp>
|
|
||||||
#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 graphene::chain::by_id;
|
|
||||||
|
|
||||||
using ObjectId = qint64;
|
|
||||||
Q_DECLARE_METATYPE(ObjectId)
|
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(std::function<void()>)
|
|
||||||
|
|
||||||
class GrapheneObject : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
Q_PROPERTY(ObjectId id MEMBER m_id READ id NOTIFY idChanged)
|
|
||||||
|
|
||||||
ObjectId m_id;
|
|
||||||
|
|
||||||
public:
|
|
||||||
GrapheneObject(ObjectId id = -1, QObject* parent = nullptr)
|
|
||||||
: QObject(parent), m_id(id)
|
|
||||||
{}
|
|
||||||
|
|
||||||
ObjectId id() const {
|
|
||||||
return m_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 {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
Q_PROPERTY(QString symbol MEMBER m_symbol READ symbol NOTIFY symbolChanged)
|
|
||||||
Q_PROPERTY(quint32 precision MEMBER m_precision NOTIFY precisionChanged)
|
|
||||||
|
|
||||||
QString m_symbol;
|
|
||||||
quint32 m_precision;
|
|
||||||
|
|
||||||
graphene::chain::price coreExchangeRate;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Asset(ObjectId id = -1, QString symbol = QString(), quint32 precision = 0, QObject* parent = nullptr)
|
|
||||||
: GrapheneObject(id, parent), m_symbol(symbol), m_precision(precision)
|
|
||||||
{}
|
|
||||||
|
|
||||||
QString symbol() const {
|
|
||||||
return m_symbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
quint64 precisionPower() const {
|
|
||||||
quint64 power = 1;
|
|
||||||
for (int i = 0; i < m_precision; ++i)
|
|
||||||
power *= 10;
|
|
||||||
return power;
|
|
||||||
}
|
|
||||||
|
|
||||||
void update(const graphene::chain::asset_object& asset);
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void symbolChanged();
|
|
||||||
void precisionChanged();
|
|
||||||
};
|
|
||||||
|
|
||||||
struct by_symbol_name;
|
|
||||||
typedef multi_index_container<
|
|
||||||
Asset*,
|
|
||||||
indexed_by<
|
|
||||||
hashed_unique< tag<by_id>, const_mem_fun<GrapheneObject, ObjectId, &GrapheneObject::id > >,
|
|
||||||
ordered_unique< tag<by_symbol_name>, const_mem_fun<Asset, QString, &Asset::symbol> >
|
|
||||||
>
|
|
||||||
> asset_multi_index_type;
|
|
||||||
|
|
||||||
class Balance : public GrapheneObject {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
Q_PROPERTY(Asset* type MEMBER m_type READ type NOTIFY typeChanged)
|
|
||||||
Q_PROPERTY(qint64 amount MEMBER amount NOTIFY amountChanged)
|
|
||||||
|
|
||||||
Asset* m_type;
|
|
||||||
qint64 amount;
|
|
||||||
|
|
||||||
public:
|
|
||||||
// This ultimately needs to be replaced with a string equivalent
|
|
||||||
Q_INVOKABLE qreal amountReal() const {
|
|
||||||
return amount / qreal(m_type->precisionPower());
|
|
||||||
}
|
|
||||||
|
|
||||||
Asset* type()const {
|
|
||||||
return m_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
void update(const graphene::app::account_balance_object& update);
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void typeChanged();
|
|
||||||
void amountChanged();
|
|
||||||
};
|
|
||||||
|
|
||||||
class Account : public GrapheneObject {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
Q_PROPERTY(QString name MEMBER m_name READ name NOTIFY nameChanged)
|
|
||||||
Q_PROPERTY(QQmlListProperty<Balance> balances READ balances NOTIFY balancesChanged)
|
|
||||||
|
|
||||||
QString m_name;
|
|
||||||
QList<Balance*> m_balances;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Account(ObjectId id = -1, QString name = QString(), QObject* parent = nullptr)
|
|
||||||
: GrapheneObject(id, parent), m_name(name)
|
|
||||||
{}
|
|
||||||
|
|
||||||
QString name()const { return m_name; }
|
|
||||||
QQmlListProperty<Balance> balances();
|
|
||||||
|
|
||||||
void setBalances(QList<Balance*> balances) {
|
|
||||||
if (balances != m_balances) {
|
|
||||||
m_balances = balances;
|
|
||||||
Q_EMIT balancesChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void update(const graphene::app::account_balance_object& balance);
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void nameChanged();
|
|
||||||
void balancesChanged();
|
|
||||||
};
|
|
||||||
|
|
||||||
struct by_account_name;
|
|
||||||
typedef multi_index_container<
|
|
||||||
Account*,
|
|
||||||
indexed_by<
|
|
||||||
hashed_unique< tag<by_id>, const_mem_fun<GrapheneObject, ObjectId, &GrapheneObject::id > >,
|
|
||||||
ordered_unique< tag<by_account_name>, const_mem_fun<Account, QString, &Account::name> >
|
|
||||||
>
|
|
||||||
> account_multi_index_type;
|
|
||||||
|
|
||||||
class ChainDataModel : public QObject {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
void processUpdatedObject(const fc::variant& update);
|
|
||||||
|
|
||||||
void getAssetImpl(QString assetIdentifier, Asset* const * assetInContainer);
|
|
||||||
void getAccountImpl(QString accountIdentifier, Account* const * accountInContainer);
|
|
||||||
|
|
||||||
public:
|
|
||||||
Q_INVOKABLE Account* getAccount(ObjectId id);
|
|
||||||
Q_INVOKABLE Account* getAccount(QString name);
|
|
||||||
Q_INVOKABLE Asset* getAsset(ObjectId id);
|
|
||||||
Q_INVOKABLE Asset* getAsset(QString symbol);
|
|
||||||
|
|
||||||
ChainDataModel(){}
|
|
||||||
ChainDataModel( fc::thread& t, QObject* parent = nullptr );
|
|
||||||
|
|
||||||
void setDatabaseAPI(fc::api<graphene::app::database_api> dbapi);
|
|
||||||
|
|
||||||
const graphene::chain::global_property_object& global_properties() const { return m_global_properties; }
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void queueExecute( const std::function<void()>& );
|
|
||||||
void exceptionThrown( QString message );
|
|
||||||
|
|
||||||
private:
|
|
||||||
fc::thread* m_rpc_thread = nullptr;
|
|
||||||
std::string m_api_url;
|
|
||||||
fc::api<graphene::app::database_api> m_db_api;
|
|
||||||
|
|
||||||
graphene::chain::global_property_object m_global_properties;
|
|
||||||
|
|
||||||
ObjectId m_account_query_num = -1;
|
|
||||||
account_multi_index_type m_accounts;
|
|
||||||
asset_multi_index_type m_assets;
|
|
||||||
};
|
|
||||||
|
|
||||||
class OperationBuilder;
|
|
||||||
class GrapheneApplication : public QObject {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
Q_PROPERTY(ChainDataModel* model READ model CONSTANT)
|
|
||||||
Q_PROPERTY(OperationBuilder* operationBuilder READ operationBuilder CONSTANT)
|
|
||||||
Q_PROPERTY(bool isConnected READ isConnected NOTIFY isConnectedChanged)
|
|
||||||
|
|
||||||
|
|
||||||
fc::thread m_thread;
|
|
||||||
ChainDataModel* m_model = nullptr;
|
|
||||||
OperationBuilder* m_operationBuilder = 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;
|
|
||||||
|
|
||||||
void setIsConnected(bool v);
|
|
||||||
|
|
||||||
protected Q_SLOTS:
|
|
||||||
void execute(const std::function<void()>&)const;
|
|
||||||
|
|
||||||
public:
|
|
||||||
GrapheneApplication(QObject* parent = nullptr);
|
|
||||||
~GrapheneApplication();
|
|
||||||
|
|
||||||
ChainDataModel* model() const {
|
|
||||||
return m_model;
|
|
||||||
}
|
|
||||||
OperationBuilder* operationBuilder() const {
|
|
||||||
return m_operationBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_INVOKABLE void start(QString apiUrl,
|
|
||||||
QString user,
|
|
||||||
QString pass);
|
|
||||||
|
|
||||||
bool isConnected() const
|
|
||||||
{
|
|
||||||
return m_isConnected;
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void exceptionThrown( QString message );
|
|
||||||
void loginFailed();
|
|
||||||
void isConnectedChanged(bool isConnected);
|
|
||||||
void queueExecute( const std::function<void()>& );
|
|
||||||
};
|
|
||||||
80
programs/light_client/GrapheneApplication.cpp
Normal file
80
programs/light_client/GrapheneApplication.cpp
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
#include "GrapheneApplication.hpp"
|
||||||
|
#include "ChainDataModel.hpp"
|
||||||
|
#include "Operations.hpp"
|
||||||
|
|
||||||
|
#include <graphene/app/api.hpp>
|
||||||
|
|
||||||
|
#include <fc/rpc/websocket_api.hpp>
|
||||||
|
|
||||||
|
using graphene::app::login_api;
|
||||||
|
using graphene::app::database_api;
|
||||||
|
|
||||||
|
GrapheneApplication::GrapheneApplication(QObject* parent)
|
||||||
|
:QObject(parent),m_thread("app")
|
||||||
|
{
|
||||||
|
connect(this, &GrapheneApplication::queueExecute,
|
||||||
|
this, &GrapheneApplication::execute);
|
||||||
|
|
||||||
|
m_model = new ChainDataModel(m_thread, this);
|
||||||
|
m_operationBuilder = new OperationBuilder(*m_model, this);
|
||||||
|
|
||||||
|
connect(m_model, &ChainDataModel::queueExecute,
|
||||||
|
this, &GrapheneApplication::execute);
|
||||||
|
|
||||||
|
connect(m_model, &ChainDataModel::exceptionThrown,
|
||||||
|
this, &GrapheneApplication::exceptionThrown);
|
||||||
|
}
|
||||||
|
|
||||||
|
GrapheneApplication::~GrapheneApplication()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void GrapheneApplication::setIsConnected(bool v)
|
||||||
|
{
|
||||||
|
if (v != m_isConnected)
|
||||||
|
{
|
||||||
|
m_isConnected = v;
|
||||||
|
Q_EMIT isConnectedChanged(m_isConnected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GrapheneApplication::start(QString apiurl, QString user, QString pass)
|
||||||
|
{
|
||||||
|
if (!m_thread.is_current())
|
||||||
|
{
|
||||||
|
m_done = m_thread.async([=](){ return start(apiurl, user, pass); });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
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);
|
||||||
|
if (!remote_api->login(user.toStdString(), pass.toStdString()))
|
||||||
|
{
|
||||||
|
elog("login failed");
|
||||||
|
Q_EMIT loginFailed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ilog("connecting...");
|
||||||
|
queueExecute([=](){
|
||||||
|
m_model->setDatabaseAPI(db_api);
|
||||||
|
});
|
||||||
|
|
||||||
|
queueExecute([=](){ setIsConnected(true); });
|
||||||
|
} catch (const fc::exception& e)
|
||||||
|
{
|
||||||
|
Q_EMIT exceptionThrown(QString::fromStdString(e.to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_SLOT void GrapheneApplication::execute(const std::function<void()>& func)const
|
||||||
|
{
|
||||||
|
func();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
65
programs/light_client/GrapheneApplication.hpp
Normal file
65
programs/light_client/GrapheneApplication.hpp
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
#pragma once
|
||||||
|
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
|
||||||
|
|
||||||
|
#include <fc/thread/thread.hpp>
|
||||||
|
|
||||||
|
#include <boost/signals2.hpp>
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
namespace fc { namespace http {
|
||||||
|
class websocket_client;
|
||||||
|
}}
|
||||||
|
|
||||||
|
class ChainDataModel;
|
||||||
|
class OperationBuilder;
|
||||||
|
class GrapheneApplication : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(ChainDataModel* model READ model CONSTANT)
|
||||||
|
Q_PROPERTY(OperationBuilder* operationBuilder READ operationBuilder CONSTANT)
|
||||||
|
Q_PROPERTY(bool isConnected READ isConnected NOTIFY isConnectedChanged)
|
||||||
|
|
||||||
|
|
||||||
|
fc::thread m_thread;
|
||||||
|
ChainDataModel* m_model = nullptr;
|
||||||
|
OperationBuilder* m_operationBuilder = 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;
|
||||||
|
|
||||||
|
void setIsConnected(bool v);
|
||||||
|
|
||||||
|
protected Q_SLOTS:
|
||||||
|
void execute(const std::function<void()>&)const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
GrapheneApplication(QObject* parent = nullptr);
|
||||||
|
~GrapheneApplication();
|
||||||
|
|
||||||
|
ChainDataModel* model() const {
|
||||||
|
return m_model;
|
||||||
|
}
|
||||||
|
OperationBuilder* operationBuilder() const {
|
||||||
|
return m_operationBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_INVOKABLE void start(QString apiUrl,
|
||||||
|
QString user,
|
||||||
|
QString pass);
|
||||||
|
|
||||||
|
bool isConnected() const
|
||||||
|
{
|
||||||
|
return m_isConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void exceptionThrown(QString message);
|
||||||
|
void loginFailed();
|
||||||
|
void isConnectedChanged(bool isConnected);
|
||||||
|
void queueExecute(const std::function<void()>&);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "ClientDataModel.hpp"
|
#include "ChainDataModel.hpp"
|
||||||
|
|
||||||
#include <graphene/chain/protocol/transfer.hpp>
|
#include <graphene/chain/protocol/transfer.hpp>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,20 @@
|
||||||
#include <QQmlApplicationEngine>
|
#include <QQmlApplicationEngine>
|
||||||
#include <QtQml>
|
#include <QtQml>
|
||||||
|
|
||||||
#include "ClientDataModel.hpp"
|
#include "GrapheneApplication.hpp"
|
||||||
|
#include "ChainDataModel.hpp"
|
||||||
#include "Operations.hpp"
|
#include "Operations.hpp"
|
||||||
|
#include "Balance.hpp"
|
||||||
|
|
||||||
|
class Crypto {
|
||||||
|
Q_GADGET
|
||||||
|
|
||||||
|
public:
|
||||||
|
Q_INVOKABLE QString sha256(QByteArray data) {
|
||||||
|
return QCryptographicHash::hash(data, QCryptographicHash::Sha256).toHex();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
QML_DECLARE_TYPE(Crypto)
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
|
@ -38,3 +50,5 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
return app.exec();
|
return app.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "main.moc"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue