[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:
Nathan Hourt 2015-07-22 11:10:52 -04:00
parent 9a3a6a5234
commit 1667a72144
14 changed files with 426 additions and 384 deletions

View 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();
}
}

View 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();
};

View 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;
}

View 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();
};

View 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();
}
}

View 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();
};

View file

@ -18,7 +18,7 @@ if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
qt5_add_resources(QML_QRC qml/qml.qrc)
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)
add_dependencies(light_client gen_qrc)

View file

@ -1,4 +1,5 @@
#include "ClientDataModel.hpp"
#include "ChainDataModel.hpp"
#include "Balance.hpp"
#include "Operations.hpp"
#include <graphene/app/api.hpp>
@ -250,130 +251,3 @@ Account* ChainDataModel::getAccount(QString name)
}
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;
}

View 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;
};

View file

@ -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()>& );
};

View 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();
}

View 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()>&);
};

View file

@ -1,6 +1,6 @@
#pragma once
#include "ClientDataModel.hpp"
#include "ChainDataModel.hpp"
#include <graphene/chain/protocol/transfer.hpp>

View file

@ -2,8 +2,20 @@
#include <QQmlApplicationEngine>
#include <QtQml>
#include "ClientDataModel.hpp"
#include "GrapheneApplication.hpp"
#include "ChainDataModel.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[])
{
@ -38,3 +50,5 @@ int main(int argc, char *argv[])
return app.exec();
}
#include "main.moc"