Began implementing the data model in C++

This commit is contained in:
scharel 2018-12-29 01:40:11 +01:00
parent 03098b7354
commit 86a3755ad2
11 changed files with 581 additions and 75 deletions

View file

@ -17,10 +17,12 @@ CONFIG += sailfishapp
DEFINES += APP_VERSION=\\\"$$VERSION\\\"
HEADERS += \
src/sslconfiguration.h
src/sslconfiguration.h \
src/notesmodel.h
SOURCES += src/harbour-nextcloudnotes.cpp \
src/sslconfiguration.cpp
src/sslconfiguration.cpp \
src/notesmodel.cpp
DISTFILES += qml/harbour-nextcloudnotes.qml \
qml/cover/CoverPage.qml \

View file

@ -11,7 +11,7 @@ Item {
property bool saveFile: false
property bool busy: jobsRunning > 0
property int jobsRunning: 0
property int status: 204
property int status: 0 //204
property string statusText: "No Content"
signal noteCreated(int id)
@ -22,10 +22,10 @@ Item {
console.log("Network status: " + statusText + " (" + status + ")")
}
onUuidChanged: {
onUuidChanged: console.log("Account : " + uuid)
appSettings.currentAccount = uuid
account.path = "/apps/harbour-nextcloudnotes/accounts/" + uuid
model.clear()
appSettings.currentAccount = uuid
onUuidChanged: console.log("Account : " + account.name)
}
function clear() {
model.clear()
@ -38,10 +38,12 @@ Item {
var endpoint = account.server + "/index.php/apps/notes/api/" + account.version + "/notes"
if (data) {
if (method === "POST" || method === "PUT") {
addToModel(data)
console.log("Adding note...")
//addToModel(data)
}
else if (data.id && method === "DELETE") {
removeFromModel(data.id)
console.log("Deleting note...")
//removeFromModel(data.id)
}
if (method === "GET" || method === "PUT" || method === "DELETE") {
if (data.id) {

View file

@ -1,7 +1,8 @@
import QtQuick 2.0
import Sailfish.Silica 1.0
import Nemo.Configuration 1.0
import SslConfiguration 1.0
import harbour.nextcloudnotes.notesmodel 1.0
import harbour.nextcloudnotes.sslconfiguration 1.0
import "pages"
import "components"
@ -17,7 +18,7 @@ ApplicationWindow
ConfigurationGroup {
id: account
//path: "/apps/harbour-nextcloudnotes/accounts/" + appSettings.currentAccount
path: "/apps/harbour-nextcloudnotes/accounts/" + appSettings.currentAccount
property string name: value("name", "", String)
property url server: value("server", "", String)
property string version: value("version", "v0.2", String)
@ -27,7 +28,6 @@ ApplicationWindow
property bool unencryptedConnection: account.value("unencryptedConnection", false, Boolean)
property date update: value("update", "", Date)
onValuesChanged: console.log("A property of the current account has changed")
//onUnsecureConnectionChanged: ssl.checkCert = !unsecureConnection
}
ConfigurationGroup {
@ -43,7 +43,6 @@ ApplicationWindow
property bool showSeparator: value("showSeparator", false, Boolean)
property bool useMonoFont: value("useMonoFont", false, Boolean)
property bool useCapitalX: value("useCapitalX", false, Boolean)
onCurrentAccountChanged: api.uuid = currentAccount
function addAccount() {

View file

@ -12,6 +12,9 @@
# * date Author's Name <author's email> version-release
# - Summary of changes
* Fri Dec 28 2018 Scharel Clemens <harbour-nextcloudnotes@scharel.name> 0.3-0
- Began implementing the data model in C++
* Thu Dec 27 2018 Scharel Clemens <harbour-nextcloudnotes@scharel.name> 0.2-10
- Implemented #24: "Option to show favorite notes on the top of the list"
- Implemented #32: "Allow selfsigned SSL certificates" (not tested!)

View file

@ -13,8 +13,8 @@ Name: harbour-nextcloudnotes
%{!?qtc_make:%define qtc_make make}
%{?qtc_builddir:%define _builddir %qtc_builddir}
Summary: Nextcloud Notes
Version: 0.2
Release: 10
Version: 0.3
Release: 0
Group: Applications/Editors
License: MIT
URL: https://github.com/scharel/harbour-nextcloudnotes

View file

@ -0,0 +1,72 @@
#
# Do NOT Edit the Auto-generated Part!
# Generated by: spectacle version 0.27
#
Name: harbour-nextcloudnotes
# >> macros
# << macros
%{!?qtc_qmake:%define qtc_qmake %qmake}
%{!?qtc_qmake5:%define qtc_qmake5 %qmake5}
%{!?qtc_make:%define qtc_make make}
%{?qtc_builddir:%define _builddir %qtc_builddir}
Summary: Nextcloud Notes
Version: 0.3
Release: 0
Group: Applications/Editors
License: MIT
URL: https://github.com/scharel/harbour-nextcloudnotes
Source0: %{name}-%{version}.tar.bz2
Source100: harbour-nextcloudnotes.yaml
Requires: sailfishsilica-qt5 >= 0.10.9
BuildRequires: pkgconfig(sailfishapp) >= 1.0.2
BuildRequires: pkgconfig(Qt5Core)
BuildRequires: pkgconfig(Qt5Qml)
BuildRequires: pkgconfig(Qt5Quick)
BuildRequires: desktop-file-utils
%description
A client app for the Nextcloud Notes server app
%prep
%setup -q -n %{name}-%{version}
# >> setup
# << setup
%build
# >> build pre
# << build pre
%qtc_qmake5 \
VERSION='%{version}-%{release}'
%qtc_make %{?_smp_mflags}
# >> build post
# << build post
%install
rm -rf %{buildroot}
# >> install pre
# << install pre
%qmake5_install
# >> install post
# << install post
desktop-file-install --delete-original \
--dir %{buildroot}%{_datadir}/applications \
%{buildroot}%{_datadir}/applications/*.desktop
%files
%defattr(-,root,root,-)
%{_bindir}
%{_datadir}/%{name}
%{_datadir}/applications/%{name}.desktop
%{_datadir}/icons/hicolor/*/apps/%{name}.png
# >> files
# << files

View file

@ -1,7 +1,7 @@
Name: harbour-nextcloudnotes
Summary: Nextcloud Notes
Version: 0.2
Release: 10
Version: 0.3
Release: 0
# The contents of the Group field should be one of the groups listed here:
# https://github.com/mer-tools/spectacle/blob/master/data/GROUPS
Group: Applications/Editors

View file

@ -1,6 +1,8 @@
#include <QtQuick>
#include <sailfishapp.h>
#include <QtQml>
#include <QObject>
#include "notesmodel.h"
#include "sslconfiguration.h"
int main(int argc, char *argv[])
@ -13,7 +15,8 @@ int main(int argc, char *argv[])
app->setOrganizationName("harbour-nextcloudnotes");
qDebug() << app->applicationDisplayName() << app->applicationVersion();
qmlRegisterType<SslConfiguration>("SslConfiguration", 1, 0, "SslConfiguration");
qmlRegisterType<NotesModel>("harbour.nextcloudnotes.notesmodel", 1, 0, "NotesModel");
qmlRegisterType<SslConfiguration>("harbour.nextcloudnotes.sslconfiguration", 1, 0, "SslConfiguration");
QQuickView* view = SailfishApp::createView();

347
src/notesmodel.cpp Normal file
View file

@ -0,0 +1,347 @@
#include "notesmodel.h"
#include <QJsonDocument>
#include <QJsonArray>
const QHash<int, QByteArray> noteRoles = QHash<int, QByteArray>{
{NotesModel::idRole, "id"},
{NotesModel::modifiedRole, "modified"},
{NotesModel::titleRole, "title"},
{NotesModel::categoryRole, "category"},
{NotesModel::contentRole, "content"},
{NotesModel::favoriteRole, "favorite"},
{NotesModel::etagRole, "etag"},
{NotesModel::errorRole, "error"},
{NotesModel::errorMessageRole, "errorMessage"}
};
struct Note {
int id;
uint modified;
QString title;
QString category;
QString content;
bool favorite;
QString etag;
bool error;
QString errorMessage;
bool operator==(const Note& n) {
return id == n.id;
}
void fromjson(const QJsonObject& jobj) {
id = jobj.value(noteRoles[NotesModel::idRole]).toInt();
modified = jobj.value(noteRoles[NotesModel::modifiedRole]).toInt();
title = jobj.value(noteRoles[NotesModel::titleRole]).toString();
category = jobj.value(noteRoles[NotesModel::categoryRole]).toString();
content = jobj.value(noteRoles[NotesModel::contentRole]).toString();
favorite = jobj.value(noteRoles[NotesModel::favoriteRole]).toBool();
etag = jobj.value(noteRoles[NotesModel::etagRole]).toString();
error = jobj.value(noteRoles[NotesModel::errorRole]).toBool(true);
errorMessage = jobj.value(noteRoles[NotesModel::errorMessageRole]).toString();
}
};
NotesModel::NotesModel(QObject *parent) : QAbstractListModel(parent)
{
m_sortBy = sortByDate;
m_favoritesOnTop = true;
}
NotesModel::~NotesModel() {
m_notes.clear();
}
void NotesModel::setSortBy(int sortBy) {
if (sortBy != m_sortBy) {
m_sortBy = sortBy;
sort();
emit sortByChanged(m_sortBy);
}
}
void NotesModel::setFavoritesOnTop(bool favoritesOnTop) {
if (favoritesOnTop != m_favoritesOnTop) {
m_favoritesOnTop = favoritesOnTop;
sort();
emit favoritesOnTopChanged(m_favoritesOnTop);
}
}
bool NotesModel::applyJSON(QString json, bool replaceIfArray) {
QJsonDocument jdoc = QJsonDocument::fromJson(json.toUtf8());
if (!jdoc.isNull()) {
if (jdoc.isArray()) {
QJsonArray jarr = jdoc.array();
while (!jarr.empty()) {
QJsonValue jval = jarr.first();
if (jval.isObject()) {
QJsonObject jobj = jval.toObject();
if (!jobj.isEmpty() && !jobj.value(noteRoles[errorRole]).toBool(true)) {
Note note;
note.fromjson(jobj);
int index = m_notes.indexOf(note);
if (index >= 0) {
m_notes.replace(index, note);
}
else {
// TODO
}
}
}
jarr.pop_front();
}
}
else if (jdoc.isObject()) {
QJsonObject jobj = jdoc.object();
if (!jobj.isEmpty() && !jobj.value(noteRoles[errorRole]).toBool(true)) {
Note note;
note.fromjson(jobj);
int index = m_notes.indexOf(note);
if (index >= 0) {
m_notes.replace(index, note);
}
else {
// TODO
}
}
}
sort();
return true;
}
return false;
}
bool NotesModel::removeNote(int id) {
// TODO
return false;
}
void NotesModel::search(QString query) const {
// TODO
}
void NotesModel::clearSearch() const {
// TODO
}
/*
bool NotesModel::addNote(Note &note) {
m_notes.append(note);
return false;
}
bool NotesModel::addNotes(QList<Note> &notes) {
for (int i = 0; i < notes.length(); i++) {
addNote(notes[i]);
}
return false;
}
*/
QHash<int, QByteArray> NotesModel::roleNames() const {
QHash<int, QByteArray> roles;
roles[idRole] = "id";
roles[modifiedRole] = "modified";
roles[titleRole] = "title";
roles[categoryRole] = "category";
roles[contentRole] = "content";
roles[favoriteRole] = "favorite";
roles[etagRole] = "etag";
roles[errorRole] = "error";
roles[errorMessageRole] = "errorMessage";
return noteRoles;
}
QHash<int, QByteArray> NotesModel::sortingNames() const {
QHash<int, QByteArray> criteria;
criteria[sortByDate] = "date";
criteria[sortByCategory] = "category";
criteria[sortByTitle] = "title";
return criteria;
}
Qt::ItemFlags NotesModel::flags(const QModelIndex &index) const {
return Qt::ItemIsEnabled; //| Qt::ItemIsEditable | Qt::ItemIsSelectable
}
int NotesModel::rowCount(const QModelIndex &parent) const {
return m_notes.size();
}
QVariant NotesModel::data(const QModelIndex &index, int role) const {
if (!index.isValid()) return QVariant();
else if (role == idRole) return m_notes[index.row()].id;
else if (role == modifiedRole) return m_notes[index.row()].modified;
else if (role == titleRole) return m_notes[index.row()].title;
else if (role == categoryRole) return m_notes[index.row()].category;
else if (role == contentRole) return m_notes[index.row()].content;
else if (role == favoriteRole) return m_notes[index.row()].favorite;
else if (role == etagRole) return m_notes[index.row()].etag;
else if (role == errorRole) return m_notes[index.row()].error;
else if (role == errorMessageRole) return m_notes[index.row()].errorMessage;
return QVariant();
}
void NotesModel::sort() {
QList<Note> notes;
QMap<QString, Note> map;
QMap<QString, Note> favorites;
switch (m_sortBy) {
case sortByDate:
emit layoutAboutToBeChanged();
foreach (const Note &note, m_notes) {
if (m_favoritesOnTop && note.favorite)
favorites.insert(QString::number(note.modified), note);
else
map.insert(QString::number(note.modified), note);
}
notes = favorites.values();
notes.append(map.values());
m_notes = notes;
emit layoutChanged();
break;
case sortByCategory:
emit layoutAboutToBeChanged();
foreach (const Note &note, m_notes) {
if (m_favoritesOnTop && note.favorite)
favorites.insert(note.category, note);
else
map.insert(note.category, note);
}
notes = favorites.values();
notes.append(map.values());
m_notes = notes;
emit layoutChanged();
break;
case sortByTitle:
emit layoutAboutToBeChanged();
foreach (const Note &note, m_notes) {
if (m_favoritesOnTop && note.favorite)
favorites.insert(note.title, note);
else
map.insert(note.title, note);
}
notes = favorites.values();
notes.append(map.values());
m_notes = notes;
emit layoutChanged();
break;
default:
break;
}
}
/*bool NotesModel::noteLessThanByDate(const Note &n1, const Note &n2) {
if (m_favoritesOnTop && n1.favorite != n2.favorite)
return n1.favorite;
else
return n1.modified > n2.modified;
}
bool NotesModel::noteLessThanByCategory(const Note &n1, const Note &n2) {
if (m_favoritesOnTop && n1.favorite != n2.favorite)
return n1.favorite;
else
return n1.category < n2.category;
}
bool NotesModel::noteLessThanByTitle(const Note &n1, const Note &n2) {
if (m_favoritesOnTop && n1.favorite != n2.favorite)
return n1.favorite;
else
return n1.title < n2.title;
}*/
/*bool NotesModel::noteLessThan(const Note &n1, const Note &n2) const {
switch (m_sortBy) {
case sortByDate:
if (m_favoritesOnTop && n1.favorite != n2.favorite)
return n1.favorite;
else
return n1.modified > n2.modified;
break;
case sortByCategory:
if (m_favoritesOnTop && n1.favorite != n2.favorite)
return n1.favorite;
else
return n1.category < n2.category;
break;
case sortByTitle:
if (m_favoritesOnTop && n1.favorite != n2.favorite)
return n1.favorite;
else
return n1.title < n2.title;
break;
default:
break;
}
}*/
/*
bool NotesModel::setData(const QModelIndex &index, const QVariant &value, int role) {
if (!index.isValid()) return false;
else if (role == modifiedRole) {
m_notes[index.row()].modified = value.toDateTime();
return true;
}
else if (role == categoryRole) {
m_notes[index.row()].category = value.toString();
return true;
}
else if (role == contentRole) {
m_notes[index.row()].content = value.toString();
return true;
}
else if (role == favoriteRole) {
m_notes[index.row()].favorite = value.toBool();
return true;
}
return false;
}
bool NotesModel::insertRow(int row, const QModelIndex &parent) {
beginInsertRows(parent, row, row);
m_notes.insert(row, Note());
endInsertRows();
return true;
}
bool NotesModel::insertRows(int row, int count, const QModelIndex &parent) {
if (count > 0) {
beginInsertRows(parent, row, row+count);
for (int i = 0; i < count; i++) {
m_notes.insert(row + i, Note());
}
endInsertRows();
return true;
}
else {
return false;
}
}
bool NotesModel::removeRow(int row, const QModelIndex &parent) {
if (row >= 0 && row < m_notes.size()) {
beginRemoveRows(parent, row, row);
m_notes.removeAt(row);
endRemoveRows();
return true;
}
else {
return false;
}
}
bool NotesModel::removeRows(int row, int count, const QModelIndex &parent) {
if (row >= 0 && row < m_notes.size()) {
beginRemoveRows(parent, row, count);
for (int i = 0; i < count && row + i < m_notes.size(); i++) {
m_notes.removeAt(row);
}
endRemoveRows();
return true;
}
else {
return false;
}
}
*/

78
src/notesmodel.h Normal file
View file

@ -0,0 +1,78 @@
#ifndef NOTESMODEL_H
#define NOTESMODEL_H
#include <QAbstractListModel>
#include <QDateTime>
#include <QJsonObject>
struct Note;
class NotesModel : public QAbstractListModel {
Q_OBJECT
public:
explicit NotesModel(QObject *parent = 0);
virtual ~NotesModel();
Q_PROPERTY(int sortBy READ sortBy WRITE setSortBy NOTIFY sortByChanged)
int sortBy() { return m_sortBy; }
void setSortBy(int sortBy);
Q_PROPERTY(bool favoritesOnTop READ favoritesOnTop WRITE setFavoritesOnTop NOTIFY favoritesOnTopChanged)
bool favoritesOnTop() { return m_favoritesOnTop; }
void setFavoritesOnTop(bool favoritesOnTop);
Q_INVOKABLE bool applyJSON(QString json, bool replaceIfArray = true);
Q_INVOKABLE bool removeNote(int id);
Q_INVOKABLE void search(QString query) const;
Q_INVOKABLE void clearSearch() const;
enum NoteRoles {
idRole = Qt::UserRole + 1,
modifiedRole = Qt::UserRole + 2,
titleRole = Qt::UserRole + 3,
categoryRole = Qt::UserRole + 4,
contentRole = Qt::UserRole + 5,
favoriteRole = Qt::UserRole + 6,
etagRole = Qt::UserRole + 7,
errorRole = Qt::UserRole + 8,
errorMessageRole = Qt::UserRole + 9
};
QHash<int, QByteArray> roleNames() const;
enum SortingCriteria {
sortByDate,
sortByCategory,
sortByTitle
};
QHash<int, QByteArray> sortingNames() const;
Qt::ItemFlags flags(const QModelIndex &index) const;
virtual int rowCount(const QModelIndex &parent) const;
virtual QVariant data(const QModelIndex &index, int role) const;
//virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
//virtual bool setData(const QModelIndex &index, const QVariant &value, int role);
//bool insertRow(int row, const QModelIndex &parent);
//bool insertRows(int row, int count, const QModelIndex &parent);
//bool removeRow(int row, const QModelIndex &parent);
//bool removeRows(int row, int count, const QModelIndex &parent);
protected:
static bool noteLessThanByDate(const Note &n1, const Note &n2);
static bool noteLessThanByCategory(const Note &n1, const Note &n2);
static bool noteLessThanByTitle(const Note &n1, const Note &n2);
signals:
void sortByChanged(int sortBy);
void favoritesOnTopChanged(bool favoritesOnTop);
private:
QList<Note> m_notes;
int m_sortBy;
bool m_favoritesOnTop;
void sort();
};
#endif // NOTESMODEL_H

View file

@ -272,17 +272,17 @@
<context>
<name>NotesApi</name>
<message>
<location filename="../qml/components/NotesApi.qml" line="118"/>
<location filename="../qml/components/NotesApi.qml" line="107"/>
<source>Unable to connect</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/components/NotesApi.qml" line="256"/>
<location filename="../qml/components/NotesApi.qml" line="245"/>
<source>Today</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/components/NotesApi.qml" line="258"/>
<location filename="../qml/components/NotesApi.qml" line="247"/>
<source>Yesterday</source>
<translation type="unfinished"></translation>
</message>