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));
}
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) {
qDebug() << "Deleting asset...";
if (magpie.getState() != Models::Magpie::Authenticated)

View File

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

View File

@ -15,11 +15,11 @@ class AddAsset : public Post {
public:
AddAsset (
const QString& title,
const QString& icon,
const QColor& color,
Models::Currency::ID currency,
const QUrl& baseUrl
const QString& title,
const QString& icon,
const QColor& color,
Models::Currency::ID currency,
const QUrl& baseUrl
);
};

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"
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):
@ -23,8 +23,8 @@ void Models::Assets::clear () {
}
void Models::Assets::add (const Asset& asset) {
QModelIndex index = getIndex(asset.id);
if (index.isValid())
int index = getIndexByID(asset.id);
if (index != -1)
throw std::runtime_error("An attempt to insert a duplicating Asset to an asset model");
beginInsertRows(QModelIndex(), records.size(), records.size());
@ -37,7 +37,7 @@ void Models::Assets::add (const std::deque<Asset>& assets) {
return;
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)");
beginInsertRows(QModelIndex(), records.size(), records.size() + assets.size() - 1);
@ -47,17 +47,24 @@ void Models::Assets::add (const std::deque<Asset>& assets) {
endInsertRows();
}
void Models::Assets::remove (unsigned int id) {
QModelIndex index = getIndex(id);
if (!index.isValid())
void Models::Assets::remove (Asset::ID id) {
int index = getIndexByID(id);
if (index == -1)
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);
beginRemoveRows(QModelIndex(), index, index);
records.erase(records.begin() + index);
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 {
//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
@ -104,7 +111,9 @@ QVariant Models::Assets::data (const QModelIndex& index, int role) const {
return records[row].color;
case Currency:
return currencies.getCode(records[row].currency);
case Id:
case CurrencyID:
return records[row].currency;
case ID:
return records[row].id;
}
}
@ -139,11 +148,27 @@ void Models::Assets::receivedAssets (const std::deque<Asset>& assets) {
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) {
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 {
struct Asset {
unsigned int id;
using ID = uint32_t;
ID id;
QString title;
QString icon;
QColor color;
@ -37,13 +39,16 @@ public:
Archived,
Color,
Currency,
Id
CurrencyID,
ID
};
void clear ();
void add (const Asset& asset);
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:
int rowCount (const QModelIndex& parent = QModelIndex()) const override;
@ -57,15 +62,16 @@ public:
static bool deserialize (const QVariantList& from, std::deque<Asset>& out);
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:
void requestAssets ();
public slots:
void receivedAssets (const std::deque<Asset>& assets);
private:
QModelIndex getIndex (unsigned int id) const;
private:
enum class State {
initial,

View File

@ -9,7 +9,11 @@ import magpie
import magpie.Components as Components
Item {
signal add
signal add()
signal edit(id: int)
signal remove(id: int)
id: item
ColumnLayout {
id: column
@ -33,6 +37,9 @@ Item {
delegate: Components.AssetLine {
height: 30
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
Item {
signal addAsset
signal addAsset()
signal removeAsset(id: int)
signal editAsset(id: int)
property string currentPage: "home"
RowLayout {
@ -71,6 +73,8 @@ Item {
Assets {
StackView.onActivating: currentPage = "assets"
onAdd: addAsset()
onRemove: id => removeAsset(id)
onEdit: id => editAsset(id)
}
}

View File

@ -19,6 +19,23 @@ Item {
id: main
Main {
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 {
target: Magpie
function onDisplayError (err) {

View File

@ -13,13 +13,14 @@ Item {
required property color color
required property string balance
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 {
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
spacing: 5
@ -37,29 +38,34 @@ Item {
}
}
Text {
Label {
width: parent.freespace / 3
height: parent.height
text: title
verticalAlignment: Text.AlignVCenter
color: palette.text
font.bold: true
}
Text {
Label {
width: parent.freespace / 3
height: parent.height
text: balance
verticalAlignment: Text.AlignVCenter
color: palette.text
}
Text {
Label {
width: parent.freespace / 3
height: parent.height
text: currency
verticalAlignment: Text.AlignVCenter
color: palette.text
}
Button {
id: editButton
text: qsTr("Edit")
flat: true
height: parent.height
onClicked: edit(assetID)
}
Button {
@ -67,10 +73,7 @@ Item {
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);
})
onClicked: remove(assetID)
}
}
}

View File

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

View File

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

View File

@ -1,11 +1,13 @@
// SPDX-FileCopyrightText: 2023 Yury Gubich <blue@macaw.me>
// SPDX-License-Identifier: GPL-3.0-or-later
//SPDX-FileCopyrightText: 2023 Yury Gubich <blue@macaw.me>
//SPDX-License-Identifier: GPL-3.0-or-later
#include "root.h"
#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),
root(root),
engine(),
@ -13,7 +15,11 @@ Root::Root(const QUrl& root, int& argc, char* argv[]) :
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");
setOrganizationDomain("macaw.me");
@ -24,8 +30,11 @@ Root::Root(const QUrl& root, int& argc, char* argv[]) :
magpie.installAPI(api);
connect(&engine, &QQmlApplicationEngine::objectCreated,
this, &Root::onObjectCreated,
connect(
&engine,
&QQmlApplicationEngine::objectCreated,
this,
&Root::onObjectCreated,
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");
}
Root::~Root() {
Root::~Root () {}
}
bool Root::notify(QObject* receiver, QEvent* e) {
bool Root::notify (QObject* receiver, QEvent* e) {
try {
return QGuiApplication::notify(receiver, e);
} catch (const std::exception& e) {
@ -69,17 +76,17 @@ bool Root::notify(QObject* receiver, QEvent* e) {
return false;
}
void Root::onObjectCreated(QObject* obj, const QUrl& objUrl) {
void Root::onObjectCreated (QObject* obj, const QUrl& objUrl) {
if (!obj && objUrl == root)
exit(-2);
}
void Root::onAPIAddressChanged(const QUrl& url) {
void Root::onAPIAddressChanged (const QUrl& url) {
QSettings settings;
settings.setValue("address", url);
}
void Root::onStoreTokens(const QString& access, const QString& renew) {
void Root::onStoreTokens (const QString& access, const QString& renew) {
QSettings settings;
settings.setValue("accessToken", access);
settings.setValue("renewToken", renew);