diff --git a/API/CMakeLists.txt b/API/CMakeLists.txt index 1ece631..a359167 100644 --- a/API/CMakeLists.txt +++ b/API/CMakeLists.txt @@ -4,15 +4,11 @@ set(HEADERS api.h codes.h - finalaction.h - helpers.h ) set(SOURCES api.cpp codes.cpp - finalaction.cpp - helpers.cpp ) target_sources(magpie PRIVATE ${SOURCES}) diff --git a/API/api.cpp b/API/api.cpp index 1295971..ee9da39 100644 --- a/API/api.cpp +++ b/API/api.cpp @@ -13,13 +13,13 @@ #include "requests/poll.h" #include "requests/listassets.h" #include "requests/addasset.h" +#include "requests/deleteasset.h" API::API (Models::Magpie& magpie, QObject* parent): QObject(parent), idCounter(0), magpie(magpie), network(), - pollReply(), requests() {} @@ -90,18 +90,6 @@ API::RequestId API::sendLogin (const QString& login, const QString& password, co return registerAndSend(std::move(log)); } -API::RequestId API::addAsset (const QString& title, const QString& icon, const QJSValue& finished) { - qDebug() << "Adding asset..."; - if (magpie.getState() != Models::Magpie::Authenticated) - return callCallback(finished, "Can not add assets in current state"), 0; - - auto add = std::make_unique(title, icon, QColor::fromString("black"), 1, magpie.getAddress()); - add->setAuthorizationToken(magpie.getAccessToken()); - connect(add.get(), &Request::AddAsset::success, std::bind(&API::callCallback, this, finished, QString(), QJSValueList{QJSValue(true)})); - connect(add.get(), &Request::AddAsset::error, std::bind(&API::callCallback, this, finished, std::placeholders::_1, QJSValueList{QJSValue(false)})); - return registerAndSend(std::move(add)); -} - API::RequestId API::requestAssets (const SuccessListHandler& success, const ErrorHandler& error) { qDebug() << "Requesting assets..."; @@ -112,6 +100,30 @@ API::RequestId API::requestAssets (const SuccessListHandler& success, const Erro return registerAndSend(std::move(list)); } +API::RequestId API::addAsset (const QString& title, const QString& icon, const QColor& color, const QJSValue& finished) { + qDebug() << "Adding asset..."; + if (magpie.getState() != Models::Magpie::Authenticated) + return callCallback(finished, "Can not add assets in current state"), 0; + + auto add = std::make_unique(title, icon, color, 1, magpie.getAddress()); + add->setAuthorizationToken(magpie.getAccessToken()); + connect(add.get(), &Request::AddAsset::success, std::bind(&API::callCallback, this, finished, QString(), QJSValueList{QJSValue(true)})); + connect(add.get(), &Request::AddAsset::error, std::bind(&API::callCallback, this, finished, std::placeholders::_1, QJSValueList{QJSValue(false)})); + return registerAndSend(std::move(add)); +} + +API::RequestId API::deleteAsset (unsigned int id, const QJSValue& finished) { + qDebug() << "Deleting asset..."; + if (magpie.getState() != Models::Magpie::Authenticated) + return callCallback(finished, "Can not delete assets in current state"), 0; + + auto del = std::make_unique(id, magpie.getAddress()); + del->setAuthorizationToken(magpie.getAccessToken()); + connect(del.get(), &Request::DeleteAsset::success, std::bind(&API::callCallback, this, finished, QString(), QJSValueList{QJSValue(true)})); + connect(del.get(), &Request::DeleteAsset::error, std::bind(&API::callCallback, this, finished, std::placeholders::_1, QJSValueList{QJSValue(false)})); + return registerAndSend(std::move(del)); +} + API::RequestId API::poll (const SuccessMapHandler& success, const ErrorHandler& error, bool clear) { auto poll = std::make_unique(magpie.getAddress(), clear); poll->setAuthorizationToken(magpie.getAccessToken()); diff --git a/API/api.h b/API/api.h index c0ab576..9d6590a 100644 --- a/API/api.h +++ b/API/api.h @@ -29,7 +29,6 @@ public: explicit API(Models::Magpie& magpie, QObject* parent = nullptr); - RequestId requestAssets(const SuccessListHandler& success, const ErrorHandler& error); RequestId poll(const SuccessMapHandler& success, const ErrorHandler& error, bool clear = false); @@ -40,7 +39,8 @@ public slots: RequestId test(const QString& path, const QJSValue& finished = QJSValue()); RequestId sendRegister(const QString& login, const QString& password, const QJSValue& finished = QJSValue()); RequestId sendLogin(const QString& login, const QString& password, const QJSValue& finished = QJSValue()); - RequestId addAsset(const QString& title, const QString& icon, const QJSValue& finished = QJSValue()); + RequestId addAsset(const QString& title, const QString& icon, const QColor& color, const QJSValue& finished = QJSValue()); + RequestId deleteAsset(unsigned int id, const QJSValue& finished = QJSValue()); private slots: void onRequestDone(RequestId id); @@ -54,5 +54,4 @@ private: Models::Magpie& magpie; QNetworkAccessManager network; std::map> requests; - std::unique_ptr pollReply; }; diff --git a/API/requests/CMakeLists.txt b/API/requests/CMakeLists.txt index a72d940..3bdb9e6 100644 --- a/API/requests/CMakeLists.txt +++ b/API/requests/CMakeLists.txt @@ -10,6 +10,7 @@ set(HEADERS poll.h listassets.h addasset.h + deleteasset.h ) set(SOURCES @@ -21,6 +22,7 @@ set(SOURCES poll.cpp listassets.cpp addasset.cpp + deleteasset.cpp ) target_sources(magpie PRIVATE ${SOURCES}) diff --git a/API/requests/addasset.cpp b/API/requests/addasset.cpp index 24170f3..d03de19 100644 --- a/API/requests/addasset.cpp +++ b/API/requests/addasset.cpp @@ -4,7 +4,12 @@ #include "addasset.h" Request::AddAsset::AddAsset (const QString& title, const QString& icon, const QColor& color, unsigned int currency, const QUrl& baseUrl): - Post(createUrl(baseUrl, "/addAsset"), {{"title", title}, {"icon", icon}, {"currency", std::to_string(currency).c_str()}, {"color", "0"}}) + Post(createUrl(baseUrl, "/addAsset"), { + {"title", title}, + {"icon", icon}, + {"currency", std::to_string(currency).c_str()}, + {"color", std::to_string(color.rgba()).c_str()} + }) { emptyResult = true; } diff --git a/API/requests/deleteasset.cpp b/API/requests/deleteasset.cpp new file mode 100644 index 0000000..5b86cf1 --- /dev/null +++ b/API/requests/deleteasset.cpp @@ -0,0 +1,12 @@ +//SPDX-FileCopyrightText: 2023 Yury Gubich +//SPDX-License-Identifier: GPL-3.0-or-later + +#include "deleteasset.h" + +Request::DeleteAsset::DeleteAsset(unsigned int id, const QUrl &baseUrl): + Post(createUrl(baseUrl, "/deleteAsset"), { + {"id", std::to_string(id).c_str()} + }) +{ + emptyResult = true; +} diff --git a/API/requests/deleteasset.h b/API/requests/deleteasset.h new file mode 100644 index 0000000..93d7428 --- /dev/null +++ b/API/requests/deleteasset.h @@ -0,0 +1,20 @@ +//SPDX-FileCopyrightText: 2023 Yury Gubich +//SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#include "post.h" + +namespace Request { + +class DeleteAsset : public Post { + Q_OBJECT + +public: + DeleteAsset(unsigned int id, const QUrl& baseUrl); +}; + +} + diff --git a/API/requests/listassets.cpp b/API/requests/listassets.cpp index 45dfa33..1e04e79 100644 --- a/API/requests/listassets.cpp +++ b/API/requests/listassets.cpp @@ -3,7 +3,7 @@ #include "listassets.h" -#include "API/helpers.h" +#include "utils/helpers.h" Request::ListAssets::ListAssets (const QUrl& baseUrl): Request(createUrl(baseUrl, "/listAssets")) {} diff --git a/CMakeLists.txt b/CMakeLists.txt index 1370203..40fe706 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,6 +60,7 @@ endif() add_subdirectory(qml) add_subdirectory(API) add_subdirectory(models) +add_subdirectory(utils) target_include_directories(magpie PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(magpie PRIVATE diff --git a/models/assets.cpp b/models/assets.cpp index fafcc37..941a3f5 100644 --- a/models/assets.cpp +++ b/models/assets.cpp @@ -3,7 +3,7 @@ #include "assets.h" -#include "API/helpers.h" +#include "utils/helpers.h" Models::Assets::Assets (QObject* parent): QAbstractListModel(parent), @@ -45,14 +45,11 @@ void Models::Assets::addAssets (const std::deque& assets) { void Models::Assets::deleteAsset (unsigned int id) { QModelIndex index = getIndex(id); if (!index.isValid()) - throw std::runtime_error("An attempt to insert to delete non existing Asset from asset model"); + throw std::runtime_error("An attempt to delete non existing Asset from asset model"); int row = index.row(); beginRemoveRows(QModelIndex(), row, row); records.erase(records.begin() + row); - if (state == State::syncronized) //give a second thought - state = State::initial; - endRemoveRows(); } @@ -67,9 +64,9 @@ 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"} - }; + static const QHash roleNames({ + {Title, "title"}, {Icon, "icon"}, {Balance, "balance"}, {Archived, "archived"}, {Color, "color"}, {Id, "assetId"} + }); return roleNames; } @@ -103,6 +100,8 @@ QVariant Models::Assets::data (const QModelIndex& index, int role) const { return records[row].archived; case Color: return records[row].color; + case Id: + return records[row].id; } } @@ -122,8 +121,7 @@ bool Models::Assets::deserialize (const QVariantList& from, std::deque& o asset.id = ser.value("id").toUInt(); uint32_t color = ser.value("color").toUInt(); - uint8_t* rgba = reinterpret_cast(&color); - asset.color = QColor::fromRgb(rgba[0], rgba[1], rgba[2], rgba[3]); + asset.color = QColor::fromRgba(color); } return true; diff --git a/models/assets.h b/models/assets.h index 0109c11..1e49c7a 100644 --- a/models/assets.h +++ b/models/assets.h @@ -33,7 +33,8 @@ public: Icon, Balance, Archived, - Color + Color, + Id }; void clear(); diff --git a/models/magpie.cpp b/models/magpie.cpp index 5baeec8..1468d1a 100644 --- a/models/magpie.cpp +++ b/models/magpie.cpp @@ -6,8 +6,8 @@ #include #include "API/api.h" -#include "API/helpers.h" #include "API/codes.h" +#include "utils/helpers.h" Models::Magpie::Magpie (QObject* parent): QObject(parent), @@ -190,6 +190,17 @@ bool Models::Magpie::handleChanges (const QVariantMap& changes) { else Magpie::assets.addAssets(added); } + + 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; diff --git a/models/magpie.h b/models/magpie.h index 5a299af..7402463 100644 --- a/models/magpie.h +++ b/models/magpie.h @@ -52,6 +52,7 @@ signals: void stateChanged(State state); void storeTokens(const QString& access, const QString& renew); + void displayError(const QString& err); public: Assets assets; diff --git a/qml/Application/Root.qml b/qml/Application/Root.qml index c62e292..66ed231 100644 --- a/qml/Application/Root.qml +++ b/qml/Application/Root.qml @@ -4,7 +4,9 @@ import QtQuick import QtQuick.Controls +import magpie import magpie.Forms as Forms +import magpie.Components as Components Item { StackView { @@ -27,4 +29,17 @@ Item { onSuccess: stack.pop() } } + + Connections { + target: Magpie + function onDisplayError (err) { + modal.status = err; + modal.open(); + } + } + + Components.Modal { + id: modal + closable: true + } } diff --git a/qml/Components/AssetLine.qml b/qml/Components/AssetLine.qml index eb39161..12578f7 100644 --- a/qml/Components/AssetLine.qml +++ b/qml/Components/AssetLine.qml @@ -4,16 +4,21 @@ import QtQuick import QtQuick.Controls +import magpie + Item { id: line required property string title required property string icon required property color color required property string balance + required property int assetId + + signal error (err:string) Row { readonly property int iconSize: height - readonly property int freespace: width - iconSize - spacing * children.length - 1 + readonly property int freespace: width - deleteButton.width - iconSize - spacing * children.length - 1 anchors.fill: parent spacing: 5 @@ -49,5 +54,16 @@ Item { verticalAlignment: Text.AlignVCenter color: palette.text } + + Button { + id: deleteButton + text: qsTr("Delete") + flat: true + height: parent.height + onClicked: API.deleteAsset(line.assetId, function(err) { + if (err) + Magpie.displayError("Error deleting asset " + line.title + ": " + err); + }) + } } } diff --git a/qml/Forms/AddAsset.qml b/qml/Forms/AddAsset.qml index f08321a..e35283d 100644 --- a/qml/Forms/AddAsset.qml +++ b/qml/Forms/AddAsset.qml @@ -86,7 +86,7 @@ Item { modal.status = qsTr("Creating new asset ") + " " + title + "..."; modal.open(); - API.addAsset(title, icon, function (err, result) { + API.addAsset(title, icon, "blue", function (err, result) { if (!modal.inProgress) return; diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt new file mode 100644 index 0000000..a512274 --- /dev/null +++ b/utils/CMakeLists.txt @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2023 Yury Gubich +# SPDX-License-Identifier: GPL-3.0-or-later + +set(HEADERS + finalaction.h + helpers.h +) + +set(SOURCES + finalaction.cpp + helpers.cpp +) + +target_sources(magpie PRIVATE ${SOURCES}) diff --git a/API/finalaction.cpp b/utils/finalaction.cpp similarity index 100% rename from API/finalaction.cpp rename to utils/finalaction.cpp diff --git a/API/finalaction.h b/utils/finalaction.h similarity index 100% rename from API/finalaction.h rename to utils/finalaction.h diff --git a/API/helpers.cpp b/utils/helpers.cpp similarity index 100% rename from API/helpers.cpp rename to utils/helpers.cpp diff --git a/API/helpers.h b/utils/helpers.h similarity index 100% rename from API/helpers.h rename to utils/helpers.h