From 45f924a4cff2f30d0ddcc2e8fa163d66ad67b9a2 Mon Sep 17 00:00:00 2001 From: blue Date: Wed, 24 Jan 2024 18:00:15 -0300 Subject: [PATCH] started to work over currencies --- models/CMakeLists.txt | 2 + models/assets.cpp | 15 ++--- models/assets.h | 17 +++--- models/currencies.cpp | 132 ++++++++++++++++++++++++++++++++++++++++++ models/currencies.h | 68 ++++++++++++++++++++++ models/magpie.cpp | 87 +++++++++++++++++++--------- models/magpie.h | 8 +++ 7 files changed, 287 insertions(+), 42 deletions(-) create mode 100644 models/currencies.cpp create mode 100644 models/currencies.h diff --git a/models/CMakeLists.txt b/models/CMakeLists.txt index 1d43315..1d5f418 100644 --- a/models/CMakeLists.txt +++ b/models/CMakeLists.txt @@ -4,11 +4,13 @@ set(HEADERS magpie.h assets.h + currencies.h ) set(SOURCES magpie.cpp assets.cpp + currencies.cpp ) target_sources(magpie PRIVATE ${SOURCES}) diff --git a/models/assets.cpp b/models/assets.cpp index 941a3f5..5c2623c 100644 --- a/models/assets.cpp +++ b/models/assets.cpp @@ -5,6 +5,10 @@ #include "utils/helpers.h" +const QHash Models::Assets::roles({ + {Title, "title"}, {Icon, "icon"}, {Balance, "balance"}, {Archived, "archived"}, {Color, "color"}, {Id, "assetId"} +}); + Models::Assets::Assets (QObject* parent): QAbstractListModel(parent), records(), @@ -17,7 +21,7 @@ void Models::Assets::clear () { endResetModel(); } -void Models::Assets::addAsset (const Asset& asset) { +void Models::Assets::add (const Asset& asset) { QModelIndex index = getIndex(asset.id); if (index.isValid()) throw std::runtime_error("An attempt to insert a duplicating Asset to an asset model"); @@ -27,7 +31,7 @@ void Models::Assets::addAsset (const Asset& asset) { endInsertRows(); } -void Models::Assets::addAssets (const std::deque& assets) { +void Models::Assets::add (const std::deque& assets) { if (assets.empty()) return; @@ -42,7 +46,7 @@ void Models::Assets::addAssets (const std::deque& assets) { endInsertRows(); } -void Models::Assets::deleteAsset (unsigned int id) { +void Models::Assets::remove (unsigned int id) { QModelIndex index = getIndex(id); if (!index.isValid()) throw std::runtime_error("An attempt to delete non existing Asset from asset model"); @@ -64,10 +68,7 @@ int Models::Assets::rowCount (const QModelIndex& parent) const { } QHash Models::Assets::roleNames () const { - static const QHash roleNames({ - {Title, "title"}, {Icon, "icon"}, {Balance, "balance"}, {Archived, "archived"}, {Color, "color"}, {Id, "assetId"} - }); - return roleNames; + return roles; } bool Models::Assets::canFetchMore (const QModelIndex& parent) const { diff --git a/models/assets.h b/models/assets.h index 1e49c7a..55b3a88 100644 --- a/models/assets.h +++ b/models/assets.h @@ -37,10 +37,10 @@ public: Id }; - void clear(); - void addAsset(const Asset& asset); - void addAssets(const std::deque& assets); - void deleteAsset(unsigned int id); + void clear (); + void add (const Asset& asset); + void add (const std::deque& assets); + void remove (unsigned int id); //Basic functionality: int rowCount (const QModelIndex& parent = QModelIndex()) const override; @@ -51,16 +51,17 @@ public: void fetchMore (const QModelIndex& parent) override; QVariant data (const QModelIndex& index, int role = Qt::DisplayRole) const override; - static bool deserialize(const QVariantList& from, std::deque& out); + static bool deserialize (const QVariantList& from, std::deque& out); + static const QHash roles; signals: - void requestAssets(); + void requestAssets (); public slots: - void receivedAssets(const std::deque& assets); + void receivedAssets (const std::deque& assets); private: - QModelIndex getIndex(unsigned int id) const; + QModelIndex getIndex (unsigned int id) const; private: enum class State { diff --git a/models/currencies.cpp b/models/currencies.cpp new file mode 100644 index 0000000..5e6dacc --- /dev/null +++ b/models/currencies.cpp @@ -0,0 +1,132 @@ +//SPDX-FileCopyrightText: 2023 Yury Gubich +//SPDX-License-Identifier: GPL-3.0-or-later + +#include "currencies.h" + +#include "utils/helpers.h" + +const QHash Models::Currencies::roles({ + {Id, "id"}, {Code, "code"}, {Title, "title"}, {Type, "type"}, {Value, "value"}, {Description, "description"}, {Icon, "icon"} +}); + +Models::Currencies::Currencies (QObject* parent): + QAbstractListModel(parent), + order(), + index(), + map() +{} + +void Models::Currencies::clear () { + beginResetModel(); + order.clear(); + index.clear(); + map.clear(); + endResetModel(); +} + +void Models::Currencies::add (const Currency& currency) { + if (map.count(currency.id)) + throw std::runtime_error("An attempt to insert a duplicating currency to a currency model"); + + beginInsertRows(QModelIndex(), index.size(), index.size()); + Order::iterator itr = order.insert(order.end(), currency); + index.push_back(itr); + map.emplace(currency.id, itr); + endInsertRows(); +} + +void Models::Currencies::add (const std::deque& currencies) { + if (currencies.empty()) + return; + + for (const Currency& currency : currencies) + if (map.count(currency.id)) + throw std::runtime_error("An attempt to insert a duplicating currency to a currency model (bulk)"); + + beginInsertRows(QModelIndex(), index.size(), index.size() + currencies.size() - 1); + for (const Currency& currency : currencies) { + Order::iterator itr = order.insert(order.end(), currency); + index.push_back(itr); + map.emplace(currency.id, itr); + } + + endInsertRows(); +} + +void Models::Currencies::remove (Currency::ID id) { + Map::iterator mItr = map.find(id); + if (mItr == map.end()) + throw std::runtime_error("An attempt to delete non existing currency from currency model"); + + Order::iterator itr = mItr->second; + Index::iterator iItr = std::find(index.begin(), index.end(), itr); + int row = std::distance(index.begin(), iItr); + + beginRemoveRows(QModelIndex(), row, row); + map.erase(mItr); + index.erase(iItr); + order.erase(itr); + endRemoveRows(); +} + +int Models::Currencies::rowCount (const QModelIndex& parent) const { + //For list models only the root node (an invalid parent) should return the + //list's size. For all other (valid) parents, rowCount() should return 0 so + //that it does not become a tree model. + if (parent.isValid()) + return 0; + + return index.size(); +} + +QHash Models::Currencies::roleNames () const { + return roles; +} + +QVariant Models::Currencies::data (const QModelIndex& index, int role) const { + if (!index.isValid()) + return QVariant(); + + int row = index.row(); + if (row >= 0 && row < Currencies::index.size()) { + const Order::iterator& itr = Currencies::index[row]; + switch (role) { + case Qt::DisplayRole: + case Title: + return itr->title; + case Icon: + return itr->icon; + case Description: + return itr->description; + case Code: + return itr->code; + case Type: + return itr->type; + case Id: + return itr->id; + case Value: + return itr->value; + } + } + + return QVariant(); +} + +bool Models::Currencies::deserialize (const QVariantList& from, std::deque& out) { + for (const QVariant& item : from) { + if (!item.canConvert()) + return false; + + const QVariantMap& ser = qast(item); + Currency& currency = out.emplace_back(); + currency.title = ser.value("title").toString(); + currency.icon = ser.value("icon", "").toString(); + currency.description = ser.value("archived", "").toString(); + currency.id = ser.value("id").toUInt(); + currency.code = ser.value("code").toString(); + currency.value = ser.value("value").toDouble(); + currency.type = ser.value("type", 1).toUInt(); + } + + return true; +} diff --git a/models/currencies.h b/models/currencies.h new file mode 100644 index 0000000..8d7fccf --- /dev/null +++ b/models/currencies.h @@ -0,0 +1,68 @@ +//SPDX-FileCopyrightText: 2023 Yury Gubich +//SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +namespace Models { +struct Currency { + using ID = uint32_t; + + ID id; + QString code; + QString title; + unsigned int type; + double value; + QString description; + QString icon; +}; + +class Currencies : public QAbstractListModel { + Q_OBJECT + QML_ELEMENT + +public: + Currencies (QObject* parent = nullptr); + + enum Roles { + Id = Qt::UserRole + 1, + Code, + Title, + Type, + Value, + Description, + Icon + }; + + void clear (); + void add (const Currency& currency); + void add (const std::deque& currencies); + void remove (Currency::ID id); + + //Basic functionality: + int rowCount (const QModelIndex& parent = QModelIndex()) const override; + QHash roleNames () const override; + QVariant data (const QModelIndex& index, int role = Qt::DisplayRole) const override; + + static bool deserialize(const QVariantList& from, std::deque& out); + static const QHash roles; + +private: + using Order = std::list; + using Index = std::deque; + using Map = std::map; + + Order order; + Index index; + Map map; +}; + +} diff --git a/models/magpie.cpp b/models/magpie.cpp index 1468d1a..c24298d 100644 --- a/models/magpie.cpp +++ b/models/magpie.cpp @@ -18,7 +18,8 @@ Models::Magpie::Magpie (QObject* parent): renewToken(), api(), firstPoll(), - pollRequestId(API::none) + pollRequestId(API::none), + requestingCurrencies(false) { firstPoll.setSingleShot(true); firstPoll.setInterval(2000); @@ -89,6 +90,10 @@ Models::Assets* Models::Magpie::getAssets () { return &assets; } +Models::Currencies* Models::Magpie::getCurrencies () { + return ¤cies; +} + void Models::Magpie::requestAssets () { api->requestAssets( [this] (const QVariantList& list) { @@ -101,10 +106,10 @@ void Models::Magpie::requestAssets () { qDebug() << "Assets successfully received"; } - assets.addAssets(result); + assets.add(result); }, [this] (const QString& error, const std::optional& data) { - assets.addAssets({}); + assets.add(std::deque()); } ); } @@ -172,35 +177,55 @@ bool Models::Magpie::handleChanges (const QVariantMap& changes) { } } + bool result = true; itr = changes.constFind("assets"); - if (itr != changes.constEnd() && itr.value().canConvert()) { - const QVariantMap& assets = qast(itr.value()); - QVariantMap::ConstIterator aItr = assets.constFind("invalidate"); - if (aItr != assets.constEnd()) { - const QVariant& vinv = aItr.value(); - if (vinv.canConvert() && vinv.toBool()) - Magpie::assets.clear(); - } + if (itr != changes.constEnd() && itr.value().canConvert()) + result = handleAssetChanges(qast(itr.value())); - aItr = assets.constFind("added"); - if (aItr != assets.constEnd() && aItr.value().canConvert()) { - std::deque added; - if (!Models::Assets::deserialize(qast(aItr.value()), added)) - qDebug() << "Error deserializng added assets"; + itr = changes.constFind("currencies"); + if (itr != changes.constEnd() && itr.value().canConvert()) + result = result && handleCurrenciesChanges(qast(itr.value())); + + return result; +} + +bool Models::Magpie::handleAssetChanges (const QVariantMap& changes) { + QVariantMap::ConstIterator aItr = changes.constFind("invalidate"); + if (aItr != changes.constEnd()) { + const QVariant& vinv = aItr.value(); + if (vinv.canConvert() && vinv.toBool()) + assets.clear(); + } + + aItr = changes.constFind("added"); + if (aItr != changes.constEnd() && aItr.value().canConvert()) { + std::deque added; + if (!Models::Assets::deserialize(qast(aItr.value()), added)) + qDebug() << "Error deserializng added assets"; + else + assets.add(added); + } + + aItr = changes.constFind("removed"); + if (aItr != changes.constEnd() && aItr.value().canConvert()) { + const QVariantList rem = qast(aItr.value()); + for (const QVariant& vId : rem) { + if (vId.isValid() && vId.canConvert()) + assets.remove(vId.toUInt()); else - Magpie::assets.addAssets(added); + qDebug() << "Error deserializing removed assets"; } + } - aItr = assets.constFind("removed"); - if (aItr != assets.constEnd() && aItr.value().canConvert()) { - const QVariantList rem = qast(aItr.value()); - for (const QVariant& vId : rem) { - if (vId.isValid() && vId.canConvert()) - Magpie::assets.deleteAsset(vId.toUInt()); - else - qDebug() << "Error deserializing removed assets"; - } - } + return true; +} + +bool Models::Magpie::handleCurrenciesChanges (const QVariantMap& changes) { + QVariantMap::ConstIterator aItr = changes.constFind("invalidate"); + if (aItr != changes.constEnd()) { + const QVariant& vinv = aItr.value(); + if (vinv.canConvert() && vinv.toBool()) + requestCurrencies(); } return true; @@ -208,4 +233,12 @@ bool Models::Magpie::handleChanges (const QVariantMap& changes) { void Models::Magpie::resetAllModels () { assets.clear(); + requestCurrencies(); +} + +void Models::Magpie::requestCurrencies () { + if (requestingCurrencies) + return; + + currencies.clear(); } diff --git a/models/magpie.h b/models/magpie.h index 7402463..2589189 100644 --- a/models/magpie.h +++ b/models/magpie.h @@ -9,6 +9,7 @@ #include #include +#include "currencies.h" #include "assets.h" class API; @@ -32,6 +33,7 @@ public: Q_PROPERTY(QUrl address READ getAddress NOTIFY addressChanged) Q_PROPERTY(State state READ getState NOTIFY stateChanged) Q_PROPERTY(Assets* assets READ getAssets CONSTANT) + Q_PROPERTY(Currencies* currencies READ getCurrencies CONSTANT) explicit Magpie(QObject *parent = nullptr); @@ -46,6 +48,7 @@ public: void setTokens(const QString access, const QString& renew, bool notify = false); void setState(State newState); Assets* getAssets(); + Currencies* getCurrencies(); signals: void addressChanged(const QUrl& path); @@ -56,6 +59,7 @@ signals: public: Assets assets; + Currencies currencies; private slots: void requestAssets(); @@ -67,7 +71,10 @@ private: void onPollSuccess(const QVariantMap& data); void onPollError(const QString& err, const std::optional& data); bool handleChanges(const QVariantMap& changes); + bool handleAssetChanges(const QVariantMap& changes); + bool handleCurrenciesChanges(const QVariantMap& changes); void resetAllModels(); + void requestCurrencies(); private: QUrl address; @@ -77,6 +84,7 @@ private: std::shared_ptr api; QTimer firstPoll; unsigned int pollRequestId; + bool requestingCurrencies; }; }