[GUI] Use assets and balances in transfer form
This commit is contained in:
parent
2ec17e2254
commit
b433f90a55
7 changed files with 132 additions and 60 deletions
|
|
@ -71,7 +71,7 @@ namespace graphene { namespace app {
|
|||
*/
|
||||
optional<signed_block> get_block(uint32_t block_num)const;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief used to fetch an individual transaction.
|
||||
*/
|
||||
processed_transaction get_transaction( uint32_t block_num, uint32_t trx_in_block )const;
|
||||
|
|
@ -120,7 +120,7 @@ namespace graphene { namespace app {
|
|||
/**
|
||||
* @brief Get an account's balances in various assets
|
||||
* @param id ID of the account to get balances for
|
||||
* @param assets IDs of the assets to get balances of
|
||||
* @param assets IDs of the assets to get balances of; if empty, get all assets account has a balance in
|
||||
* @return Balances of the account
|
||||
*/
|
||||
vector<asset> get_account_balances(account_id_type id, const flat_set<asset_id_type>& assets)const;
|
||||
|
|
@ -194,7 +194,7 @@ namespace graphene { namespace app {
|
|||
* @return Map of witness names to corresponding IDs
|
||||
*/
|
||||
map<string, witness_id_type> lookup_witness_accounts(const string& lower_bound_name, uint32_t limit)const;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get names and IDs for registered committee_members
|
||||
* @param lower_bound_name Lower bound of the first name to return
|
||||
|
|
@ -202,7 +202,7 @@ namespace graphene { namespace app {
|
|||
* @return Map of committee_member names to corresponding IDs
|
||||
*/
|
||||
map<string, committee_member_id_type> lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get a list of witnesses by ID
|
||||
* @param witness_ids IDs of the witnesses to retrieve
|
||||
|
|
@ -322,7 +322,7 @@ namespace graphene { namespace app {
|
|||
unsigned limit = 100,
|
||||
operation_history_id_type start = operation_history_id_type())const;
|
||||
|
||||
vector<bucket_object> get_market_history( asset_id_type a, asset_id_type b, uint32_t bucket_seconds,
|
||||
vector<bucket_object> get_market_history( asset_id_type a, asset_id_type b, uint32_t bucket_seconds,
|
||||
fc::time_point_sec start, fc::time_point_sec end )const;
|
||||
flat_set<uint32_t> get_market_history_buckets()const;
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -13,7 +13,10 @@ find_package(Qt5Quick)
|
|||
|
||||
file(GLOB QML qml/*)
|
||||
|
||||
qt5_add_resources(QML_QRC qml/qml.qrc)
|
||||
# Skip building QRC in debug mode, since we access the QML files directly on disk in debug mode
|
||||
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
qt5_add_resources(QML_QRC qml/qml.qrc)
|
||||
endif()
|
||||
|
||||
add_executable(light_client ClientDataModel.cpp ClientDataModel.hpp main.cpp ${QML_QRC} ${QML})
|
||||
if (CMAKE_VERSION VERSION_LESS 3.0)
|
||||
|
|
|
|||
|
|
@ -11,16 +11,13 @@ ChainDataModel::ChainDataModel( fc::thread& t, QObject* parent )
|
|||
:QObject(parent),m_thread(&t){}
|
||||
|
||||
|
||||
Asset* ChainDataModel::getAsset(qint64 id)
|
||||
Asset* ChainDataModel::getAsset(ObjectId id)
|
||||
{
|
||||
auto& by_id_idx = m_assets.get<::by_id>();
|
||||
auto itr = by_id_idx.find(id);
|
||||
if( itr == by_id_idx.end() )
|
||||
{
|
||||
auto tmp = new Asset;
|
||||
QQmlEngine::setObjectOwnership(tmp, QQmlEngine::CppOwnership);
|
||||
tmp->id = id; --m_account_query_num;
|
||||
tmp->symbol = QString::number( --m_account_query_num);
|
||||
auto tmp = new Asset(id, QString::number(--m_account_query_num), 0, this);
|
||||
auto result = m_assets.insert( tmp );
|
||||
assert( result.second );
|
||||
|
||||
|
|
@ -71,10 +68,7 @@ Asset* ChainDataModel::getAsset(QString symbol)
|
|||
auto itr = by_symbol_idx.find(symbol);
|
||||
if( itr == by_symbol_idx.end() )
|
||||
{
|
||||
auto tmp = new Asset;
|
||||
QQmlEngine::setObjectOwnership(tmp, QQmlEngine::CppOwnership);
|
||||
tmp->id = --m_account_query_num;
|
||||
tmp->symbol = symbol;
|
||||
auto tmp = new Asset(--m_account_query_num, symbol, 0, this);
|
||||
auto result = m_assets.insert( tmp );
|
||||
assert( result.second );
|
||||
|
||||
|
|
@ -123,10 +117,7 @@ Account* ChainDataModel::getAccount(ObjectId 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 tmp = new Account(id, QString::number(--m_account_query_num), this);
|
||||
auto result = m_accounts.insert( tmp );
|
||||
assert( result.second );
|
||||
|
||||
|
|
@ -174,10 +165,7 @@ Account* ChainDataModel::getAccount(QString name)
|
|||
auto itr = by_name_idx.find(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 tmp = new Account(--m_account_query_num, name, this);
|
||||
auto result = m_accounts.insert( tmp );
|
||||
assert( result.second );
|
||||
|
||||
|
|
@ -221,6 +209,19 @@ Account* ChainDataModel::getAccount(QString name)
|
|||
|
||||
QQmlListProperty<Balance> Account::balances()
|
||||
{
|
||||
// This entire block is dummy data. Throw it away when this function gets a real implementation.
|
||||
if (m_balances.empty())
|
||||
{
|
||||
auto asset = new Asset(0, QStringLiteral("CORE"), 5, this);
|
||||
m_balances.append(new Balance);
|
||||
m_balances.back()->setProperty("amount", 5000000);
|
||||
m_balances.back()->setProperty("type", QVariant::fromValue(asset));
|
||||
asset = new Asset(0, QStringLiteral("USD"), 2, this);
|
||||
m_balances.append(new Balance);
|
||||
m_balances.back()->setProperty("amount", 3099);
|
||||
m_balances.back()->setProperty("type", QVariant::fromValue(asset));
|
||||
}
|
||||
|
||||
return QQmlListProperty<Balance>(this, m_balances);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,13 +26,21 @@ Q_DECLARE_METATYPE(std::function<void()>)
|
|||
class GrapheneObject : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(ObjectId id MEMBER id NOTIFY idChanged)
|
||||
Q_PROPERTY(ObjectId id MEMBER m_id READ id NOTIFY idChanged)
|
||||
|
||||
public:
|
||||
ObjectId id;
|
||||
ObjectId m_id;
|
||||
|
||||
Q_SIGNALS:
|
||||
void idChanged();
|
||||
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
|
||||
|
|
@ -45,19 +53,34 @@ public:
|
|||
QML_DECLARE_TYPE(Crypto)
|
||||
|
||||
|
||||
class Asset : public GrapheneObject {
|
||||
class Asset : public GrapheneObject {
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QString symbol MEMBER symbol)
|
||||
Q_PROPERTY(quint32 precision MEMBER precision)
|
||||
Q_PROPERTY(QString symbol MEMBER m_symbol READ symbol NOTIFY symbolChanged)
|
||||
Q_PROPERTY(quint32 precision MEMBER m_precision NOTIFY precisionChanged)
|
||||
|
||||
public:
|
||||
QString symbol;
|
||||
quint32 precision;
|
||||
QString m_symbol;
|
||||
quint32 m_precision;
|
||||
|
||||
public:
|
||||
Asset(ObjectId id = -1, QString symbol = QString(), quint32 precision = 0, QObject* parent = nullptr)
|
||||
: GrapheneObject(id, parent), m_symbol(symbol), m_precision(precision)
|
||||
{}
|
||||
|
||||
Q_SIGNALS:
|
||||
void symbolChanged();
|
||||
QString symbol() const {
|
||||
return m_symbol;
|
||||
}
|
||||
|
||||
quint64 precisionPower() const {
|
||||
quint64 power = 1;
|
||||
for (int i = 0; i < m_precision; ++i)
|
||||
power *= 10;
|
||||
return power;
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
void symbolChanged();
|
||||
void precisionChanged();
|
||||
};
|
||||
|
||||
struct by_id;
|
||||
|
|
@ -65,45 +88,59 @@ struct by_symbol_name;
|
|||
typedef multi_index_container<
|
||||
Asset*,
|
||||
indexed_by<
|
||||
hashed_unique< tag<by_id>, member<GrapheneObject, qint64, &GrapheneObject::id > >,
|
||||
ordered_unique< tag<by_symbol_name>, member<Asset, QString, &Asset::symbol> >
|
||||
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 type)
|
||||
Q_PROPERTY(qint64 amount MEMBER amount)
|
||||
Q_PROPERTY(Asset* type MEMBER type NOTIFY typeChanged)
|
||||
Q_PROPERTY(qint64 amount MEMBER amount NOTIFY amountChanged)
|
||||
|
||||
Asset* type;
|
||||
qint64 amount;
|
||||
|
||||
public:
|
||||
// This ultimately needs to be replaced with a string equivalent
|
||||
Q_INVOKABLE qreal amountReal() const {
|
||||
return amount / qreal(type->precisionPower());
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
void typeChanged();
|
||||
void amountChanged();
|
||||
};
|
||||
|
||||
class Account : public GrapheneObject {
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QString name MEMBER name NOTIFY nameChanged)
|
||||
Q_PROPERTY(QQmlListProperty<Balance> balances READ balances)
|
||||
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:
|
||||
const QString& getName()const { return name; }
|
||||
QQmlListProperty<Balance> balances();
|
||||
public:
|
||||
Account(ObjectId id = -1, QString name = QString(), QObject* parent = nullptr)
|
||||
: GrapheneObject(id, parent), m_name(name)
|
||||
{}
|
||||
|
||||
QString name;
|
||||
QString name()const { return m_name; }
|
||||
QQmlListProperty<Balance> balances();
|
||||
|
||||
Q_SIGNALS:
|
||||
void nameChanged();
|
||||
Q_SIGNALS:
|
||||
void nameChanged();
|
||||
void balancesChanged();
|
||||
};
|
||||
|
||||
struct by_account_name;
|
||||
typedef multi_index_container<
|
||||
Account*,
|
||||
indexed_by<
|
||||
hashed_unique< tag<by_id>, member<GrapheneObject, ObjectId, &GrapheneObject::id > >,
|
||||
ordered_unique< tag<by_account_name>, member<Account, QString, &Account::name> >
|
||||
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;
|
||||
|
||||
|
|
@ -113,7 +150,7 @@ class ChainDataModel : public QObject {
|
|||
public:
|
||||
Q_INVOKABLE Account* getAccount(ObjectId id);
|
||||
Q_INVOKABLE Account* getAccount(QString name);
|
||||
Q_INVOKABLE Asset* getAsset(qint64 id);
|
||||
Q_INVOKABLE Asset* getAsset(ObjectId id);
|
||||
Q_INVOKABLE Asset* getAsset(QString symbol);
|
||||
|
||||
ChainDataModel(){}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ int main(int argc, char *argv[])
|
|||
#ifdef NDEBUG
|
||||
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
|
||||
#else
|
||||
QQmlDebuggingEnabler enabler;
|
||||
engine.load(QUrl(QStringLiteral("qml/main.qml")));
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -9,8 +9,11 @@ import "."
|
|||
|
||||
RowLayout {
|
||||
property Account account
|
||||
property var balances: account? Object.keys(account.balances).map(function(key){return account.balances[key]})
|
||||
: null
|
||||
|
||||
property alias placeholderText: accountNameField.placeholderText
|
||||
property int showBalance: -1
|
||||
|
||||
function setFocus() {
|
||||
accountNameField.forceActiveFocus()
|
||||
|
|
@ -29,25 +32,37 @@ RowLayout {
|
|||
width: parent.width
|
||||
onEditingFinished: accountDetails.update(text)
|
||||
}
|
||||
Label {
|
||||
Text {
|
||||
id: accountDetails
|
||||
width: parent.width
|
||||
height: text? implicitHeight : 0
|
||||
|
||||
Behavior on height { NumberAnimation{ easing.type: Easing.InOutQuad } }
|
||||
|
||||
function update(name) {
|
||||
if (!name)
|
||||
{
|
||||
text = ""
|
||||
account = null
|
||||
return
|
||||
}
|
||||
|
||||
account = app.model.getAccount(name)
|
||||
if (account == null)
|
||||
if (account == null) {
|
||||
text = qsTr("Error fetching account.")
|
||||
else
|
||||
} 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)
|
||||
var text = qsTr("Account ID: %1").arg(account.id < 0? qsTr("Loading...")
|
||||
: account.id)
|
||||
if (showBalance >= 0) {
|
||||
text += "\n" + qsTr("Balance: %1 %2").arg(balances[showBalance].amountReal())
|
||||
.arg(balances[showBalance].type.symbol)
|
||||
}
|
||||
return text
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on text {
|
||||
|
|
|
|||
|
|
@ -27,28 +27,42 @@ Rectangle {
|
|||
AccountPicker {
|
||||
id: senderPicker
|
||||
width: parent.width
|
||||
Layout.minimumWidth: Scaling.cm(5)
|
||||
Component.onCompleted: setFocus()
|
||||
placeholderText: qsTr("Sender")
|
||||
showBalance: balances? balances.reduce(function(foundIndex, balance, index) {
|
||||
if (foundIndex >= 0) return foundIndex
|
||||
return balance.type.symbol === assetBox.currentText? index : -1
|
||||
}, -1) : -1
|
||||
}
|
||||
AccountPicker {
|
||||
id: recipientPicker
|
||||
width: parent.width
|
||||
Layout.minimumWidth: Scaling.cm(5)
|
||||
placeholderText: qsTr("Recipient")
|
||||
layoutDirection: Qt.RightToLeft
|
||||
}
|
||||
RowLayout {
|
||||
width: parent.width
|
||||
SpinBox {
|
||||
id: amountField
|
||||
Layout.preferredWidth: Scaling.cm(4)
|
||||
Layout.minimumWidth: Scaling.cm(1.5)
|
||||
enabled: senderPicker.account
|
||||
enabled: maxBalance
|
||||
minimumValue: 0
|
||||
maximumValue: Number.POSITIVE_INFINITY
|
||||
maximumValue: maxBalance? maxBalance.amountReal() : 0
|
||||
decimals: maxBalance? maxBalance.type.precision : 0
|
||||
|
||||
property Balance maxBalance: senderPicker.balances && senderPicker.showBalance >= 0?
|
||||
senderPicker.balances[senderPicker.showBalance] : null
|
||||
}
|
||||
ComboBox {
|
||||
id: assetBox
|
||||
Layout.minimumWidth: Scaling.cm(3)
|
||||
enabled: senderPicker.account
|
||||
model: ["CORE", "USD", "GOLD"]
|
||||
enabled: Boolean(senderPicker.balances)
|
||||
model: enabled? senderPicker.balances.filter(function(balance) { return balance.amount > 0 })
|
||||
.map(function(balance) { return balance.type.symbol })
|
||||
: ["Asset Type"]
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
Button {
|
||||
|
|
@ -58,6 +72,7 @@ Rectangle {
|
|||
Button {
|
||||
text: qsTr("Transfer")
|
||||
enabled: senderPicker.account
|
||||
onClicked: console.log(amountField.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue