some experiments around therems, a request to update asset

This commit is contained in:
Blue 2024-04-07 17:07:52 -03:00
parent 374551d2bb
commit aa815a5bd7
Signed by: blue
GPG Key ID: 9B203B252A63EE38
14 changed files with 229 additions and 59 deletions

View File

@ -129,6 +129,15 @@ API::RequestId API::addAsset (
return registerAndSend(std::move(add)); return registerAndSend(std::move(add));
} }
API::RequestId API::updateAsset (
Models::Asset::ID id,
const QString& title,
const QString& icon,
const QColor& color,
Models::Currency::ID currency,
const QJSValue& finished
) {}
API::RequestId API::deleteAsset (unsigned int id, const QJSValue& finished) { API::RequestId API::deleteAsset (unsigned int id, const QJSValue& finished) {
qDebug() << "Deleting asset..."; qDebug() << "Deleting asset...";
if (magpie.getState() != Models::Magpie::Authenticated) if (magpie.getState() != Models::Magpie::Authenticated)

View File

@ -12,6 +12,7 @@ set(HEADERS
currencies.h currencies.h
addasset.h addasset.h
deleteasset.h deleteasset.h
updateasset.h
) )
set(SOURCES set(SOURCES
@ -25,6 +26,7 @@ set(SOURCES
currencies.cpp currencies.cpp
addasset.cpp addasset.cpp
deleteasset.cpp deleteasset.cpp
updateasset.cpp
) )
target_sources(magpie PRIVATE ${SOURCES}) target_sources(magpie PRIVATE ${SOURCES})

View File

@ -0,0 +1,19 @@
//SPDX-FileCopyrightText: 2023 Yury Gubich <blue@macaw.me>
//SPDX-License-Identifier: GPL-3.0-or-later
#include "updateasset.h"
Request::UpdateAsset::UpdateAsset (
Models::Asset::ID id,
const QString& title,
const QString& icon,
const QColor& color,
Models::Currency::ID currency,
const QUrl& baseUrl
):
Post(createUrl(baseUrl, "/updateAsset"), {
{"id", std::to_string(id).c_str()}, {"title", title}, {"icon", icon}, {"currency", std::to_string(currency).c_str()}, {"color", std::to_string(color.rgba()).c_str()}
})
{
emptyResult = true;
}

View File

@ -0,0 +1,27 @@
// SPDX-FileCopyrightText: 2023 Yury Gubich <blue@macaw.me>
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <QColor>
#include "post.h"
#include "models/assets.h"
#include "models/currencies.h"
namespace Request {
class UpdateAsset : public Post {
Q_OBJECT
public:
UpdateAsset(
Models::Asset::ID id,
const QString& title,
const QString& icon,
const QColor& color,
Models::Currency::ID currency,
const QUrl& baseUrl
);
};
}

View File

@ -6,7 +6,7 @@
#include "utils/helpers.h" #include "utils/helpers.h"
const QHash<int, QByteArray> Models::Assets::roles({ const QHash<int, QByteArray> Models::Assets::roles({
{Title, "title"}, {Icon, "icon"}, {Balance, "balance"}, {Archived, "archived"}, {Color, "color"}, {Currency, "currency"}, {Id, "assetId"} {Title, "title"}, {Icon, "icon"}, {Balance, "balance"}, {Archived, "archived"}, {Color, "color"}, {Currency, "currency"}, {CurrencyID, "currencyID"}, {ID, "assetID"}
}); });
Models::Assets::Assets (Currencies& currencies, QObject* parent): Models::Assets::Assets (Currencies& currencies, QObject* parent):
@ -23,8 +23,8 @@ void Models::Assets::clear () {
} }
void Models::Assets::add (const Asset& asset) { void Models::Assets::add (const Asset& asset) {
QModelIndex index = getIndex(asset.id); int index = getIndexByID(asset.id);
if (index.isValid()) if (index != -1)
throw std::runtime_error("An attempt to insert a duplicating Asset to an asset model"); throw std::runtime_error("An attempt to insert a duplicating Asset to an asset model");
beginInsertRows(QModelIndex(), records.size(), records.size()); beginInsertRows(QModelIndex(), records.size(), records.size());
@ -37,7 +37,7 @@ void Models::Assets::add (const std::deque<Asset>& assets) {
return; return;
for (const Asset& asset : assets) for (const Asset& asset : assets)
if (getIndex(asset.id).isValid()) if (getIndexByID(asset.id) != -1)
throw std::runtime_error("An attempt to insert a duplicating Asset to an asset model (bulk)"); throw std::runtime_error("An attempt to insert a duplicating Asset to an asset model (bulk)");
beginInsertRows(QModelIndex(), records.size(), records.size() + assets.size() - 1); beginInsertRows(QModelIndex(), records.size(), records.size() + assets.size() - 1);
@ -47,17 +47,24 @@ void Models::Assets::add (const std::deque<Asset>& assets) {
endInsertRows(); endInsertRows();
} }
void Models::Assets::remove (unsigned int id) { void Models::Assets::remove (Asset::ID id) {
QModelIndex index = getIndex(id); int index = getIndexByID(id);
if (!index.isValid()) if (index == -1)
throw std::runtime_error("An attempt 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(), index, index);
beginRemoveRows(QModelIndex(), row, row); records.erase(records.begin() + index);
records.erase(records.begin() + row);
endRemoveRows(); endRemoveRows();
} }
Models::Asset Models::Assets::get (Asset::ID id) const {
int index = getIndexByID(id);
if (index == -1)
throw std::runtime_error("An attempt to access non existing Asset from asset model");
return records[index];
}
int Models::Assets::rowCount (const QModelIndex& parent) const { int Models::Assets::rowCount (const QModelIndex& parent) const {
//For list models only the root node (an invalid parent) should return the //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 //list's size. For all other (valid) parents, rowCount() should return 0 so
@ -104,7 +111,9 @@ QVariant Models::Assets::data (const QModelIndex& index, int role) const {
return records[row].color; return records[row].color;
case Currency: case Currency:
return currencies.getCode(records[row].currency); return currencies.getCode(records[row].currency);
case Id: case CurrencyID:
return records[row].currency;
case ID:
return records[row].id; return records[row].id;
} }
} }
@ -139,11 +148,27 @@ void Models::Assets::receivedAssets (const std::deque<Asset>& assets) {
endResetModel(); endResetModel();
} }
QModelIndex Models::Assets::getIndex (unsigned int id) const { int Models::Assets::getIndexByID (Asset::ID id) const {
for (std::size_t i = 0; i < records.size(); ++i) { for (std::size_t i = 0; i < records.size(); ++i) {
if (records[i].id == id) if (records[i].id == id)
return createIndex(i, 0, &records[i]); return i;
} }
return QModelIndex(); return -1;
}
QVariantMap Models::Assets::getAssetByIndex (int index) const {
QVariantMap result;
if (index < 0 || index >= records.size())
return result;
QModelIndex idx = createIndex(index, 0, &records[index]);
for (int role = Roles::Title; role != Roles::ID; ++role)
result[roles[role]] = data(idx, role);
return result;
}
QVariantMap Models::Assets::getAssetByID (Asset::ID id) const {
return getAssetByIndex(getIndexByID(id));
} }

View File

@ -14,7 +14,9 @@
namespace Models { namespace Models {
struct Asset { struct Asset {
unsigned int id; using ID = uint32_t;
ID id;
QString title; QString title;
QString icon; QString icon;
QColor color; QColor color;
@ -37,13 +39,16 @@ public:
Archived, Archived,
Color, Color,
Currency, Currency,
Id CurrencyID,
ID
}; };
void clear (); void clear ();
void add (const Asset& asset); void add (const Asset& asset);
void add (const std::deque<Asset>& assets); void add (const std::deque<Asset>& assets);
void remove (unsigned int id); void remove (Asset::ID id);
Asset get(Asset::ID id) const;
//Basic functionality: //Basic functionality:
int rowCount (const QModelIndex& parent = QModelIndex()) const override; int rowCount (const QModelIndex& parent = QModelIndex()) const override;
@ -57,15 +62,16 @@ public:
static bool deserialize (const QVariantList& from, std::deque<Asset>& out); static bool deserialize (const QVariantList& from, std::deque<Asset>& out);
static const QHash<int, QByteArray> roles; static const QHash<int, QByteArray> roles;
Q_INVOKABLE int getIndexByID (Asset::ID id) const;
Q_INVOKABLE QVariantMap getAssetByIndex(int index) const;
Q_INVOKABLE QVariantMap getAssetByID(Asset::ID id) const;
signals: signals:
void requestAssets (); void requestAssets ();
public slots: public slots:
void receivedAssets (const std::deque<Asset>& assets); void receivedAssets (const std::deque<Asset>& assets);
private:
QModelIndex getIndex (unsigned int id) const;
private: private:
enum class State { enum class State {
initial, initial,

View File

@ -9,7 +9,11 @@ import magpie
import magpie.Components as Components import magpie.Components as Components
Item { Item {
signal add signal add()
signal edit(id: int)
signal remove(id: int)
id: item
ColumnLayout { ColumnLayout {
id: column id: column
@ -33,6 +37,9 @@ Item {
delegate: Components.AssetLine { delegate: Components.AssetLine {
height: 30 height: 30
width: listView.width width: listView.width
onRemove: id => item.remove(id)
onEdit: id => item.edit(id)
} }
} }
} }

View File

@ -6,7 +6,9 @@ import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
Item { Item {
signal addAsset signal addAsset()
signal removeAsset(id: int)
signal editAsset(id: int)
property string currentPage: "home" property string currentPage: "home"
RowLayout { RowLayout {
@ -71,6 +73,8 @@ Item {
Assets { Assets {
StackView.onActivating: currentPage = "assets" StackView.onActivating: currentPage = "assets"
onAdd: addAsset() onAdd: addAsset()
onRemove: id => removeAsset(id)
onEdit: id => editAsset(id)
} }
} }

View File

@ -19,6 +19,23 @@ Item {
id: main id: main
Main { Main {
onAddAsset: stack.push(addAssetForm); onAddAsset: stack.push(addAssetForm);
onEditAsset: function(id) {
const asset = Magpie.assets.getAssetByID(id);
stack.push(editAssetForm, {
name: asset.title,
icon: asset.icon,
color: asset.color,
currency: asset.currencyID,
assetID: asset.assetID,
title: qsTr("Editing asset") + " " + asset.title
});
}
onRemoveAsset: function(id) {
API.deleteAsset(id, function(err) {
if (err)
Magpie.displayError("Error deleting asset " + Magpie.assets.getAssetByID(id).title + ": " + err);
});
}
} }
} }
@ -53,6 +70,40 @@ Item {
} }
} }
Component {
id: editAssetForm
Forms.Asset {
required property int assetID
onCancel: stack.pop()
onConfirm: function (title, icon, color, currency) {
if (modal.inProgress)
return;
modal.inProgress = true;
modal.status = qsTr("Updating asset ") + " " + Magpie.assets.getAssetByID(assetID).title + "...";
modal.open();
API.updateAsset(assetID, title, icon, color, currency, function (err, result) {
if (!modal.inProgress)
return;
modal.inProgress = false;
if (err)
modal.status = err;
else
modal.status = qsTr("Success");
if (!!result) {
modal.close();
stack.pop()
}
});
}
}
}
Connections { Connections {
target: Magpie target: Magpie
function onDisplayError (err) { function onDisplayError (err) {

View File

@ -13,13 +13,14 @@ Item {
required property color color required property color color
required property string balance required property string balance
required property string currency required property string currency
required property int assetId required property int assetID
signal error (err:string) signal remove(id: int)
signal edit(id: int)
Row { Row {
readonly property int iconSize: height readonly property int iconSize: height
readonly property int freespace: width - deleteButton.width - iconSize - spacing * children.length - 1 readonly property int freespace: width - deleteButton.width - editButton.width - iconSize - spacing * children.length - 1
anchors.fill: parent anchors.fill: parent
spacing: 5 spacing: 5
@ -37,29 +38,34 @@ Item {
} }
} }
Text { Label {
width: parent.freespace / 3 width: parent.freespace / 3
height: parent.height height: parent.height
text: title text: title
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
color: palette.text
font.bold: true font.bold: true
} }
Text { Label {
width: parent.freespace / 3 width: parent.freespace / 3
height: parent.height height: parent.height
text: balance text: balance
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
color: palette.text
} }
Text { Label {
width: parent.freespace / 3 width: parent.freespace / 3
height: parent.height height: parent.height
text: currency text: currency
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
color: palette.text }
Button {
id: editButton
text: qsTr("Edit")
flat: true
height: parent.height
onClicked: edit(assetID)
} }
Button { Button {
@ -67,10 +73,7 @@ Item {
text: qsTr("Delete") text: qsTr("Delete")
flat: true flat: true
height: parent.height height: parent.height
onClicked: API.deleteAsset(line.assetId, function(err) { onClicked: remove(assetID)
if (err)
Magpie.displayError("Error deleting asset " + line.title + ": " + err);
})
} }
} }
} }

View File

@ -34,9 +34,17 @@ ComboBox {
onActivated: index => box.color = model[index] onActivated: index => box.color = model[index]
Component.onCompleted: { Component.onCompleted: {
popup.background.color = box.background.color if (box.background.color)
popup.background.border.color = box.background.border.color popup.background.color = box.background.color;
popup.background.border.width = box.background.border.width
if (!box.background.border)
return;
if (box.background.border.color)
popup.background.border.color = box.background.border.color;
if (box.background.border.width)
popup.background.border.width = box.background.border.width;
} }
popup: Popup { popup: Popup {
@ -69,8 +77,9 @@ ComboBox {
highlighted: view.currentIndex === index highlighted: view.currentIndex === index
contentItem: Rectangle { contentItem: Rectangle {
anchors.fill: parent
color: modelData color: modelData
radius: box.background.radius radius: box.background.radius || 0
} }
MouseArea { MouseArea {
@ -96,7 +105,7 @@ ComboBox {
} }
background: Rectangle { background: Rectangle {
radius: box.background.radius radius: box.background.radius || 0
} }
} }
} }

View File

@ -33,10 +33,11 @@ ComboBox {
Icon { Icon {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
iconName: icon iconName: icon
color: palette.text color: label.color
} }
Label { Label {
id: label
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
text: icon text: icon
} }

View File

@ -1,11 +1,13 @@
// SPDX-FileCopyrightText: 2023 Yury Gubich <blue@macaw.me> //SPDX-FileCopyrightText: 2023 Yury Gubich <blue@macaw.me>
// SPDX-License-Identifier: GPL-3.0-or-later //SPDX-License-Identifier: GPL-3.0-or-later
#include "root.h" #include "root.h"
#include <QSettings> #include <QSettings>
#include <QQuickStyle>
#include <QPalette>
Root::Root(const QUrl& root, int& argc, char* argv[]) : Root::Root (const QUrl& root, int& argc, char* argv[]):
QGuiApplication(argc, argv), QGuiApplication(argc, argv),
root(root), root(root),
engine(), engine(),
@ -13,7 +15,11 @@ Root::Root(const QUrl& root, int& argc, char* argv[]) :
magpie(), magpie(),
api(std::make_shared<API>(magpie)) api(std::make_shared<API>(magpie))
{ {
std::cout << "Starting Magpie..." << std::endl; //QQuickStyle::setStyle("Basic");
//QQuickStyle::setStyle("Material");
//QQuickStyle::setStyle("Universal");
//QQuickStyle::setStyle("Imagine");
std::cout << "Starting Magpie in style " << QQuickStyle::name().toStdString() << std::endl;
setOrganizationName("macaw.me"); setOrganizationName("macaw.me");
setOrganizationDomain("macaw.me"); setOrganizationDomain("macaw.me");
@ -24,8 +30,11 @@ Root::Root(const QUrl& root, int& argc, char* argv[]) :
magpie.installAPI(api); magpie.installAPI(api);
connect(&engine, &QQmlApplicationEngine::objectCreated, connect(
this, &Root::onObjectCreated, &engine,
&QQmlApplicationEngine::objectCreated,
this,
&Root::onObjectCreated,
Qt::QueuedConnection Qt::QueuedConnection
); );
@ -49,11 +58,9 @@ Root::Root(const QUrl& root, int& argc, char* argv[]) :
throw std::runtime_error("Couldn't looad root qml object"); throw std::runtime_error("Couldn't looad root qml object");
} }
Root::~Root() { Root::~Root () {}
} bool Root::notify (QObject* receiver, QEvent* e) {
bool Root::notify(QObject* receiver, QEvent* e) {
try { try {
return QGuiApplication::notify(receiver, e); return QGuiApplication::notify(receiver, e);
} catch (const std::exception& e) { } catch (const std::exception& e) {
@ -69,17 +76,17 @@ bool Root::notify(QObject* receiver, QEvent* e) {
return false; return false;
} }
void Root::onObjectCreated(QObject* obj, const QUrl& objUrl) { void Root::onObjectCreated (QObject* obj, const QUrl& objUrl) {
if (!obj && objUrl == root) if (!obj && objUrl == root)
exit(-2); exit(-2);
} }
void Root::onAPIAddressChanged(const QUrl& url) { void Root::onAPIAddressChanged (const QUrl& url) {
QSettings settings; QSettings settings;
settings.setValue("address", url); settings.setValue("address", url);
} }
void Root::onStoreTokens(const QString& access, const QString& renew) { void Root::onStoreTokens (const QString& access, const QString& renew) {
QSettings settings; QSettings settings;
settings.setValue("accessToken", access); settings.setValue("accessToken", access);
settings.setValue("renewToken", renew); settings.setValue("renewToken", renew);