Implemented API in C++. Next: I discovered QSortFilterProxyModel :)

This commit is contained in:
Scharel Clemens 2019-11-10 23:04:42 +01:00
parent a026c2ee8d
commit 3f128bcd63
16 changed files with 334 additions and 660 deletions

View file

@ -18,13 +18,11 @@ DEFINES += APP_VERSION=\\\"$$VERSION\\\"
HEADERS += \ HEADERS += \
src/notesapi.h \ src/notesapi.h \
src/sslconfiguration.h \
src/notesmodel.h \ src/notesmodel.h \
src/note.h src/note.h
SOURCES += src/harbour-nextcloudnotes.cpp \ SOURCES += src/harbour-nextcloudnotes.cpp \
src/notesapi.cpp \ src/notesapi.cpp \
src/sslconfiguration.cpp \
src/notesmodel.cpp \ src/notesmodel.cpp \
src/note.cpp src/note.cpp
@ -46,8 +44,7 @@ DISTFILES += qml/harbour-nextcloudnotes.qml \
qml/pages/NotesApi.qml \ qml/pages/NotesApi.qml \
qml/pages/MITLicense.qml \ qml/pages/MITLicense.qml \
qml/pages/GPLLicense.qml \ qml/pages/GPLLicense.qml \
qml/pages/SyntaxPage.qml \ qml/pages/SyntaxPage.qml
qml/components/NotesApi.qml
SAILFISHAPP_ICONS = 86x86 108x108 128x128 172x172 SAILFISHAPP_ICONS = 86x86 108x108 128x128 172x172

View file

@ -1,175 +0,0 @@
import QtQuick 2.5
import Sailfish.Silica 1.0
import Nemo.Configuration 1.0
Item {
property string response
property var categories: [ ]
property string file: StandardPaths.data + "/" + appSettings.currentAccount + ".json"
property bool saveFile: false
property bool busy: jobsRunning > 0
property int jobsRunning: 0
property int status: 0 //204
property string statusText: "No Content"
onStatusChanged: {
//console.log("Network response: " + statusText + " (" + status + ")")
}
/*function getNote(id) {
var dict
if (id) {
for (var i = 0; i < model.count; i++) {
dict = model.get(i)
if (dict.id === id) {
return dict
}
}
}
}*/
function apiCall(method, data) {
jobsRunning++
var endpoint = account.server + "/index.php/apps/notes/api/" + account.version + "/notes"
if (data) {
if (method === "POST" || method === "PUT") {
console.log("Adding note...")
}
else if (data.id && method === "DELETE") {
console.log("Deleting note...")
}
if (method === "GET" || method === "PUT" || method === "DELETE") {
if (data.id) {
endpoint = endpoint + "/" + data.id
}
}
}
console.log("Calling " + endpoint)
var apiReq = new XMLHttpRequest
apiReq.open(method, endpoint, true)
apiReq.setRequestHeader('User-Agent', 'SailfishOS/harbour-nextcloudnotes')
apiReq.setRequestHeader('OCS-APIRequest', 'true')
apiReq.setRequestHeader("Content-Type", "application/json")
apiReq.setRequestHeader("Authorization", "Basic " + Qt.btoa(account.username + ":" + account.password))
apiReq.withCredentials = true
//apiReq.timeout = 5000
apiReq.onreadystatechange = function() {
if (apiReq.readyState === XMLHttpRequest.DONE) {
statusText = apiReq.statusText
status = apiReq.status
if (apiReq.status === 200) {
response = apiReq.responseText
//console.log(response)
console.log("Network response: " + statusText + " (" + status + ")")
}
else if(apiReq.status === 0) {
statusText = qsTr("Unable to connect")
}
/*
else if (apiReq.status === 304) {
console.log("ETag does not differ!")
}
else if (apiReq.status === 401) {
console.log("Unauthorized!")
}
else if (apiReq.status === 404) {
console.log("Note does not exist!")
}*/
else {
//console.log("Network error: " + apiReq.statusText + " (" + apiReq.status + ")")
}
jobsRunning--
}
}
if (method === "GET") {
apiReq.send()
}
else if (method === "POST" || method === "PUT" || method === "DELETE") {
apiReq.send(JSON.stringify(data))
}
else {
console.log("Unsupported method: " + method)
apiReq.abort()
}
}
function getNotesFromApi() {
apiCall("GET")
}
function getNoteFromApi(id) {
if (id) {
apiCall("GET", { 'id': id } )
}
}
function createNote(data) {
if (data)
apiCall("POST", data)
}
function updateNote(id, data) {
if (id && data) {
data.id = id
apiCall("PUT", data)
}
}
function deleteNote(id) {
if (id)
apiCall("DELETE", { 'id': id } )
}
// source: https://stackoverflow.com/a/14339782
/*function getPrettyDate(date) {
var today = new Date()
today.setHours(0)
today.setMinutes(0)
today.setSeconds(0)
today.setMilliseconds(0)
var compDate = new Date(date*1000)
compDate.setHours(0)
compDate.setMinutes(0)
compDate.setSeconds(0)
compDate.setMilliseconds(0)
if (compDate.getTime() === today.getTime()) {
return qsTr("Today")
} else if ((today.getTime() - compDate.getTime()) === (24 * 60 * 60 * 1000)) {
return qsTr("Yesterday")
} else if ((today.getTime() - compDate.getTime()) <= (7 * 24 * 60 * 60 * 1000)) {
return compDate.toLocaleDateString(Qt.locale(), "dddd")
} else if (today.getFullYear() === compDate.getFullYear()) {
return compDate.toLocaleDateString(Qt.locale(), "MMMM")
} else {
return compDate.toLocaleDateString(Qt.locale(), "MMMM yyyy")
}
}*/
/*Component.onCompleted: {
if (saveFile) {
if (account.name === "") {
saveFile = false
}
else {
busy = true
var fileReq = new XMLHttpRequest
fileReq.open("GET", file)
fileReq.onreadystatechange = function() {
if (fileReq.readyState === XMLHttpRequest.DONE) {
if (fileReq.responseText === "") {
update()
}
else {
console.log("Loaded " + account.name + " from local JSON file")
json = fileReq.responseText
busy = false
}
}
}
fileReq.send()
}
}
}*/
}

View file

@ -2,10 +2,7 @@ import QtQuick 2.0
import Sailfish.Silica 1.0 import Sailfish.Silica 1.0
import Nemo.Configuration 1.0 import Nemo.Configuration 1.0
import harbour.nextcloudnotes.notesapi 1.0 import harbour.nextcloudnotes.notesapi 1.0
import harbour.nextcloudnotes.notesmodel 1.0
import harbour.nextcloudnotes.sslconfiguration 1.0
import "pages" import "pages"
//import "components"
ApplicationWindow ApplicationWindow
{ {
@ -26,8 +23,8 @@ ApplicationWindow
property string version: value("version", "v0.2", String) property string version: value("version", "v0.2", String)
property string username: value("username", "", String) property string username: value("username", "", String)
property string password: account.value("password", "", String) property string password: account.value("password", "", String)
property bool unsecureConnection: account.value("unsecureConnection", false, Boolean) property bool doNotVerifySsl: account.value("doNotVerifySsl", false, Boolean)
property bool unencryptedConnection: account.value("unencryptedConnection", false, Boolean) property bool allowUnecrypted: account.value("allowUnecrypted", false, Boolean)
property date update: value("update", "", Date) property date update: value("update", "", Date)
onValuesChanged: console.log("A property of the current account has changed") onValuesChanged: console.log("A property of the current account has changed")
onNameChanged: console.log("Account: " + name) onNameChanged: console.log("Account: " + name)
@ -37,6 +34,7 @@ ApplicationWindow
id: appSettings id: appSettings
path: "/apps/harbour-nextcloudnotes/settings" path: "/apps/harbour-nextcloudnotes/settings"
property bool initialized: false
property string currentAccount: value("currentAccount", "", String) property string currentAccount: value("currentAccount", "", String)
property var accountIDs: value("accountIDs", [ ], Array) property var accountIDs: value("accountIDs", [ ], Array)
property int autoSyncInterval: value("autoSyncInterval", 0, Number) property int autoSyncInterval: value("autoSyncInterval", 0, Number)
@ -48,10 +46,12 @@ ApplicationWindow
property bool useCapitalX: value("useCapitalX", false, Boolean) property bool useCapitalX: value("useCapitalX", false, Boolean)
onCurrentAccountChanged: { onCurrentAccountChanged: {
account.path = "/apps/harbour-nextcloudnotes/accounts/" + currentAccount account.path = "/apps/harbour-nextcloudnotes/accounts/" + currentAccount
//noteListModel.clear() account.sync()
//api.getNotesFromApi() if (initialized)
api.getAllNotes(); notesApi.getAllNotes();
autoSyncTimer.restart()
} }
Component.onCompleted: initialized = true
function addAccount() { function addAccount() {
var uuid = uuidv4() var uuid = uuidv4()
@ -88,26 +88,18 @@ ApplicationWindow
} }
} }
/*SslConfiguration {
id: ssl
checkCert: !account.unsecureConnection
}*/
Timer { Timer {
id: autoSyncTimer id: autoSyncTimer
interval: appSettings.autoSyncInterval * 1000 interval: appSettings.autoSyncInterval * 1000
repeat: true repeat: true
running: interval > 0 && appWindow.visible running: interval > 0 && appWindow.visible
triggeredOnStart: true triggeredOnStart: false
onTriggered: { onTriggered: {
if (!api.busy) { if (!notesApi.busy) {
//api.getNotesFromApi() notesApi.getAllNotes();
api.getAllNotes();
} }
else { else {
triggeredOnStart = false
restart() restart()
triggeredOnStart = true
} }
} }
onIntervalChanged: { onIntervalChanged: {
@ -118,33 +110,16 @@ ApplicationWindow
} }
NotesApi { NotesApi {
id: api id: notesApi
/*scheme: "https" scheme: account.allowUnecrypted ? "http" : "https"
host: account.server host: account.server
path: "/index.php/apps/notes/api/" + account.version path: "/index.php/apps/notes/api/" + account.version
username: account.username username: account.username
password: account.password*/ password: account.password
sslVerify: !account.doNotVerifySsl
Component.onCompleted: getAllNotes()
} }
Component.onCompleted: {
api.scheme = "https"
api.host = account.server
api.path = "/index.php/apps/notes/api/" + account.version
api.username = account.username
api.password = account.password
}
/*NotesApi {
id: api
onResponseChanged: noteListModel.applyJSON(response)
}
/*NotesModel {
id: noteListModel
sortBy: appSettisignangs.sortBy
favoritesOnTop: appSettings.favoritesOnTop
}*/
initialPage: Component { NotesPage { } } initialPage: Component { NotesPage { } }
cover: Qt.resolvedUrl("cover/CoverPage.qml") cover: Qt.resolvedUrl("cover/CoverPage.qml")
allowedOrientations: defaultAllowedOrientations allowedOrientations: defaultAllowedOrientations

View file

@ -1,12 +1,12 @@
import QtQuick 2.0 import QtQuick 2.0
import Sailfish.Silica 1.0 import Sailfish.Silica 1.0
import harbour.nextcloudnotes.note 1.0 import harbour.nextcloudnotes.note 1.0
import "../components" import harbour.nextcloudnotes.notesmodel 1.0
Page { Page {
id: page id: page
property string searchText: "" property NotesModel notesModel: notesApi.model()
onStatusChanged: { onStatusChanged: {
if (status === PageStatus.Active) { if (status === PageStatus.Active) {
@ -25,7 +25,7 @@ Page {
spacing: Theme.paddingLarge spacing: Theme.paddingLarge
PullDownMenu { PullDownMenu {
busy: api.busy busy: notesApi.busy
MenuItem { MenuItem {
text: qsTr("Settings") text: qsTr("Settings")
@ -34,12 +34,12 @@ Page {
MenuItem { MenuItem {
text: qsTr("Add note") text: qsTr("Add note")
enabled: appSettings.currentAccount.length > 0 enabled: appSettings.currentAccount.length > 0
onClicked: api.createNote( { 'content': "" } ) onClicked: notesApi.createNote( { 'content': "" } )
} }
MenuItem { MenuItem {
text: enabled ? qsTr("Reload") : qsTr("Updating...") text: enabled ? qsTr("Reload") : qsTr("Updating...")
enabled: appSettings.currentAccount.length > 0 && !api.busy enabled: appSettings.currentAccount.length > 0 && !notesApi.busy
onClicked: api.getNotesFromApi() onClicked: notesApi.getAllNotes()
} }
MenuLabel { MenuLabel {
visible: appSettings.currentAccount.length > 0 visible: appSettings.currentAccount.length > 0
@ -59,7 +59,7 @@ Page {
placeholderText: account.name.length > 0 ? account.name : qsTr("Nextcloud Notes") placeholderText: account.name.length > 0 ? account.name : qsTr("Nextcloud Notes")
EnterKey.iconSource: "image://theme/icon-m-enter-close" EnterKey.iconSource: "image://theme/icon-m-enter-close"
EnterKey.onClicked: focus = false EnterKey.onClicked: focus = false
onTextChanged: noteListModel.searchText = text onTextChanged: notesModel.searchText = text
} }
Label { Label {
id: description id: description
@ -70,24 +70,25 @@ Page {
anchors.bottomMargin: Theme.paddingMedium anchors.bottomMargin: Theme.paddingMedium
color: Theme.secondaryHighlightColor color: Theme.secondaryHighlightColor
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
text: account.username + "@" + account.server.toString().split("://")[1] text: account.username + "@" + account.server
} }
BusyIndicator { BusyIndicator {
anchors.verticalCenter: searchField.verticalCenter anchors.verticalCenter: searchField.verticalCenter
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: Theme.horizontalPageMargin anchors.rightMargin: Theme.horizontalPageMargin
size: BusyIndicatorSize.Medium size: BusyIndicatorSize.Medium
running: api.busy && !busyIndicator.running running: notesApi.busy && !busyIndicator.running
} }
} }
currentIndex: -1 currentIndex: -1
model: api.model() model: notesModel
delegate: BackgroundItem { delegate: BackgroundItem {
id: note id: note
visible: inSearch
contentHeight: titleLabel.height + previewLabel.height + 2*Theme.paddingSmall contentHeight: titleLabel.height + previewLabel.height + 2*Theme.paddingSmall
height: contentHeight + menu.height height: contentHeight + menu.height
width: parent.width width: parent.width
@ -103,7 +104,7 @@ Page {
} }
onClicked: pageStack.push(Qt.resolvedUrl("../pages/NotePage.qml"), onClicked: pageStack.push(Qt.resolvedUrl("../pages/NotePage.qml"),
{ note: noteListModel.get(index), { //note: noteListModel.get(index),
id: id, id: id,
modified: modified, modified: modified,
title: title, title: title,
@ -112,8 +113,8 @@ Page {
favorite: favorite, favorite: favorite,
etag: etag, etag: etag,
error: error, error: error,
errorMessage: errorMessage, errorMessage: errorMessage
date: date //date: date
}) })
onPressAndHold: menu.open(note) onPressAndHold: menu.open(note)
@ -131,7 +132,7 @@ Page {
icon.source: (favorite ? "image://theme/icon-m-favorite-selected?" : "image://theme/icon-m-favorite?") + icon.source: (favorite ? "image://theme/icon-m-favorite-selected?" : "image://theme/icon-m-favorite?") +
(note.highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor) (note.highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor)
onClicked: { onClicked: {
api.updateNote(id, {'favorite': !favorite} ) notesApi.updateNote(id, {'favorite': !favorite} )
} }
} }
@ -201,7 +202,7 @@ Page {
text: qsTr("Delete") text: qsTr("Delete")
onClicked: { onClicked: {
remorse.execute(note, qsTr("Deleting note"), function() { remorse.execute(note, qsTr("Deleting note"), function() {
api.deleteNote(id) notesApi.deleteNote(id)
}) })
} }
} }
@ -219,7 +220,7 @@ Page {
id: busyIndicator id: busyIndicator
anchors.centerIn: parent anchors.centerIn: parent
size: BusyIndicatorSize.Large size: BusyIndicatorSize.Large
running: notesList.count === 0 && api.busy running: notesList.count === 0 && notesApi.busy
} }
Label { Label {
id: busyLabel id: busyLabel
@ -232,7 +233,7 @@ Page {
horizontalAlignment: Qt.AlignHCenter horizontalAlignment: Qt.AlignHCenter
text: qsTr("Loading notes...") text: qsTr("Loading notes...")
} }
/*
ViewPlaceholder { ViewPlaceholder {
id: noLoginPlaceholder id: noLoginPlaceholder
enabled: appSettings.accountIDs.length <= 0 enabled: appSettings.accountIDs.length <= 0
@ -242,14 +243,14 @@ Page {
ViewPlaceholder { ViewPlaceholder {
id: noNotesPlaceholder id: noNotesPlaceholder
enabled: api.status === 204 && !busyIndicator.running && !noLoginPlaceholder.enabled enabled: notesApi.status === 204 && !busyIndicator.running && !noLoginPlaceholder.enabled
text: qsTr("No notes yet") text: qsTr("No notes yet")
hintText: qsTr("Pull down to add a note") hintText: qsTr("Pull down to add a note")
} }
ViewPlaceholder { ViewPlaceholder {
id: noSearchPlaceholder id: noSearchPlaceholder
enabled: notesList.count === 0 && noteListModel.searchText !== "" enabled: notesList.count === 0 && notesModel.searchText !== ""
text: qsTr("No result") text: qsTr("No result")
hintText: qsTr("Try another query") hintText: qsTr("Try another query")
} }
@ -258,9 +259,9 @@ Page {
id: errorPlaceholder id: errorPlaceholder
enabled: notesList.count === 0 && !busyIndicator.running && !noSearchPlaceholder.enabled && !noNotesPlaceholder.enabled && !noLoginPlaceholder.enabled enabled: notesList.count === 0 && !busyIndicator.running && !noSearchPlaceholder.enabled && !noNotesPlaceholder.enabled && !noLoginPlaceholder.enabled
text: qsTr("An error occurred") text: qsTr("An error occurred")
hintText: api.statusText hintText: notesApi.statusText
} }
*/
TouchInteractionHint { TouchInteractionHint {
id: addAccountHint id: addAccountHint
interactionMode: TouchInteraction.Pull interactionMode: TouchInteraction.Pull

View file

@ -5,7 +5,6 @@
#include "notesapi.h" #include "notesapi.h"
#include "note.h" #include "note.h"
#include "notesmodel.h" #include "notesmodel.h"
#include "sslconfiguration.h"
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
@ -20,7 +19,6 @@ int main(int argc, char *argv[])
qmlRegisterType<NotesApi>("harbour.nextcloudnotes.notesapi", 1, 0, "NotesApi"); qmlRegisterType<NotesApi>("harbour.nextcloudnotes.notesapi", 1, 0, "NotesApi");
qmlRegisterType<Note>("harbour.nextcloudnotes.note", 1, 0, "Note"); qmlRegisterType<Note>("harbour.nextcloudnotes.note", 1, 0, "Note");
qmlRegisterType<NotesModel>("harbour.nextcloudnotes.notesmodel", 1, 0, "NotesModel"); qmlRegisterType<NotesModel>("harbour.nextcloudnotes.notesmodel", 1, 0, "NotesModel");
qmlRegisterType<SslConfiguration>("harbour.nextcloudnotes.sslconfiguration", 1, 0, "SslConfiguration");
QQuickView* view = SailfishApp::createView(); QQuickView* view = SailfishApp::createView();

View file

@ -42,7 +42,7 @@ Note& Note::operator=(const Note& note) {
} }
bool Note::operator==(const Note& note) const { bool Note::operator==(const Note& note) const {
return same(note); return equal(note);
} }
bool Note::same(const Note& note) const { bool Note::same(const Note& note) const {
@ -118,3 +118,37 @@ bool Note::searchInNote(const QString &query, const Note &note, SearchAttributes
} }
return queryFound; return queryFound;
} }
bool Note::lessThanByDate(const Note &n1, const Note &n2) {
return n1.modified() > n2.modified();
}
bool Note::lessThanByCategory(const Note &n1, const Note &n2) {
return n1.category() > n2.category();
}
bool Note::lessThanByTitle(const Note &n1, const Note &n2) {
return n1.title() > n2.title();
}
bool Note::lessThanByDateFavOnTop(const Note &n1, const Note &n2) {
if (n1.favorite() != n2.favorite())
return n1.favorite();
else
return n1.modified() > n2.modified();
}
bool Note::lessThanByCategoryFavOnTop(const Note &n1, const Note &n2) {
if (n1.favorite() != n2.favorite())
return n1.favorite();
else
return n1.category() > n2.category();
}
bool Note::lessThanByTitleFavOnTop(const Note &n1, const Note &n2) {
if (n1.favorite() != n2.favorite())
return n1.favorite();
else
return n1.title() > n2.title();
}

View file

@ -64,6 +64,12 @@ public:
static Note fromjson(const QJsonObject& jobj); static Note fromjson(const QJsonObject& jobj);
static bool searchInNote(const QString &query, const Note &note, SearchAttributes criteria = QFlag(SearchAll), Qt::CaseSensitivity cs = Qt::CaseInsensitive); static bool searchInNote(const QString &query, const Note &note, SearchAttributes criteria = QFlag(SearchAll), Qt::CaseSensitivity cs = Qt::CaseInsensitive);
static bool lessThanByDate(const Note &n1, const Note &n2);
static bool lessThanByCategory(const Note &n1, const Note &n2);
static bool lessThanByTitle(const Note &n1, const Note &n2);
static bool lessThanByDateFavOnTop(const Note &n1, const Note &n2);
static bool lessThanByCategoryFavOnTop(const Note &n1, const Note &n2);
static bool lessThanByTitleFavOnTop(const Note &n1, const Note &n2);
signals: signals:
void idChanged(int id); void idChanged(int id);

View file

@ -147,24 +147,24 @@ void NotesApi::getNote(int noteId, QStringList excludeFields) {
} }
} }
void NotesApi::createNote(QVariantHash fields) { void NotesApi::createNote(QVariantMap fields) {
QUrl url = m_url; QUrl url = m_url;
url.setPath(url.path() + "/notes"); url.setPath(url.path() + "/notes");
if (url.isValid()) { if (url.isValid()) {
qDebug() << "POST" << url.toDisplayString(); qDebug() << "POST" << url.toDisplayString();
m_request.setUrl(url); m_request.setUrl(url);
m_replies << m_manager.post(m_request, QJsonDocument(QJsonObject::fromVariantHash(fields)).toJson()); m_replies << m_manager.post(m_request, QJsonDocument(QJsonObject::fromVariantMap(fields)).toJson());
emit busyChanged(busy()); emit busyChanged(busy());
} }
} }
void NotesApi::updateNote(int noteId, QVariantHash fields) { void NotesApi::updateNote(int noteId, QVariantMap fields) {
QUrl url = m_url; QUrl url = m_url;
url.setPath(url.path() + QString("/notes/%1").arg(noteId)); url.setPath(url.path() + QString("/notes/%1").arg(noteId));
if (url.isValid()) { if (url.isValid()) {
qDebug() << "PUT" << url.toDisplayString(); qDebug() << "PUT" << url.toDisplayString();
m_request.setUrl(url); m_request.setUrl(url);
m_replies << m_manager.put(m_request, QJsonDocument(QJsonObject::fromVariantHash(fields)).toJson()); m_replies << m_manager.put(m_request, QJsonDocument(QJsonObject::fromVariantMap(fields)).toJson());
emit busyChanged(busy()); emit busyChanged(busy());
} }
} }
@ -197,7 +197,7 @@ void NotesApi::replyFinished(QNetworkReply *reply) {
//qDebug() << json; //qDebug() << json;
} }
else { else {
qDebug() << reply->errorString(); qDebug() << reply->error() << reply->errorString();
} }
m_replies.removeAll(reply); m_replies.removeAll(reply);
reply->deleteLater(); reply->deleteLater();

View file

@ -5,6 +5,7 @@
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QNetworkReply> #include <QNetworkReply>
#include <QSortFilterProxyModel>
#include <QDebug> #include <QDebug>
#include "notesmodel.h" #include "notesmodel.h"
@ -58,10 +59,10 @@ public:
Q_INVOKABLE void getAllNotes(QStringList excludeFields = QStringList()); Q_INVOKABLE void getAllNotes(QStringList excludeFields = QStringList());
Q_INVOKABLE void getNote(int noteId, QStringList excludeFields = QStringList()); Q_INVOKABLE void getNote(int noteId, QStringList excludeFields = QStringList());
Q_INVOKABLE void createNote(QVariantHash fields = QVariantHash()); Q_INVOKABLE void createNote(QVariantMap fields = QVariantMap());
Q_INVOKABLE void updateNote(int noteId, QVariantHash fields = QVariantHash()); Q_INVOKABLE void updateNote(int noteId, QVariantMap fields = QVariantMap());
Q_INVOKABLE void deleteNote(int noteId); Q_INVOKABLE void deleteNote(int noteId);
Q_INVOKABLE NotesModel& model() const { return *mp_model; } Q_INVOKABLE NotesModel* model() const { return mp_model; }
signals: signals:
void sslVerifyChanged(bool verify); void sslVerifyChanged(bool verify);
@ -91,6 +92,7 @@ private:
QNetworkRequest m_request; QNetworkRequest m_request;
QVector<QNetworkReply*> m_replies; QVector<QNetworkReply*> m_replies;
NotesModel* mp_model; NotesModel* mp_model;
QSortFilterProxyModel* mp_modelProxy; // TODO: use!
}; };
#endif // NOTESAPI_H #endif // NOTESAPI_H

View file

@ -1,4 +1,5 @@
#include "notesmodel.h" #include "notesmodel.h"
#include <algorithm> // std::sort
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include <QJsonArray> #include <QJsonArray>
@ -8,11 +9,11 @@
NotesModel::NotesModel(QObject *parent) : QAbstractListModel(parent) NotesModel::NotesModel(QObject *parent) : QAbstractListModel(parent)
{ {
m_sortBy = noSorting; m_sortBy = noSorting;
m_favoritesOnTop = false; m_favoritesOnTop = true;
} }
NotesModel::~NotesModel() { NotesModel::~NotesModel() {
clear(); //clear();
} }
void NotesModel::setSortBy(QString sortBy) { void NotesModel::setSortBy(QString sortBy) {
@ -37,15 +38,24 @@ void NotesModel::setSearchText(QString searchText) {
qDebug() << "Searching by:" << searchText; qDebug() << "Searching by:" << searchText;
if (searchText != m_searchText) { if (searchText != m_searchText) {
m_searchText = searchText; m_searchText = searchText;
for (int i = 0; i < m_notes.size(); i++) { emit searchTextChanged(m_searchText);
if (m_searchText.isEmpty()) { if (m_searchText.isEmpty()) {
m_notes[i].param = true; m_invisibleIds.clear();
emit dataChanged(this->index(0), this->index(m_notes.size()));
} }
else { else {
m_notes[i].param = Note::searchInNote(m_searchText, m_notes[i].note); for (int i = 0; i < m_notes.size(); i++) {
if (Note::searchInNote(m_searchText, m_notes[i])) {
//qDebug() << "Note" << m_notes[i].title() << "in search";
m_invisibleIds.removeAll(m_notes[i].id());
}
else {
//qDebug() << "Note" << m_notes[i].title() << "not in search";
m_invisibleIds.append(m_notes[i].id());
}
emit dataChanged(this->index(i), this->index(i));
} }
} }
emit searchTextChanged(m_searchText);
} }
} }
@ -57,54 +67,46 @@ void NotesModel::clearSearch() {
search(); search();
} }
bool NotesModel::applyJSONobject(const QJsonObject &jobj) {
if (!jobj.isEmpty()) {
Note note = Note::fromjson(jobj); // TODO connect signals
if (!note.error()) {
int position = indexOf(note.id());
Note oldNote = get(position);
if (position >= 0 && note.etag() != oldNote.etag()) {
qDebug() << "-- Existing note " << note.title() << "changed, updating the model.";
replaceNote(note);
}
else if (position < 0) {
qDebug() << "-- New note" << note.title() << ", adding it to the model.";
insertNote(note);
}
else {
qDebug() << "-- Existing note " << note.title() << "unchanged, nothing to do.";
}
}
else {
qDebug() << "Note contains an error:" << note.errorMessage();
}
}
else {
qDebug() << "Unknown JSON object. This message should never occure!";
return false;
}
return true;
}
bool NotesModel::applyJSON(const QJsonDocument &jdoc) { bool NotesModel::applyJSON(const QJsonDocument &jdoc) {
qDebug() << "Applying new JSON input";// << json; qDebug() << "Applying new JSON input";// << json;
if (!jdoc.isNull()) { if (!jdoc.isNull()) {
if (jdoc.isArray()) { if (jdoc.isArray()) {
qDebug() << "- It's an array..."; qDebug() << "- It's an array...";
QVector<Note> newNotes;
QJsonArray jarr = jdoc.array(); QJsonArray jarr = jdoc.array();
while (!jarr.empty()) { while (!jarr.empty()) {
//qDebug() << jarr.count() << "JSON Objects to handle..."; //qDebug() << jarr.count() << "JSON Objects to handle...";
QJsonValue jval = jarr.first(); QJsonValue jval = jarr.first();
if (jval.isObject()) { if (jval.isObject()) {
//qDebug() << "It's an object, all fine..."; //qDebug() << "It's an object, all fine...";
applyJSONobject(jval.toObject()); QJsonObject jobj = jval.toObject();
if (!jobj.isEmpty()) {
newNotes.append(Note::fromjson(jobj));
}
} }
jarr.pop_front(); jarr.pop_front();
} }
for (int i = 0; i < m_notes.size(); ++i) {
bool noteToBeRemoved = true;
for (int j = 0; j < newNotes.size(); ++j) {
if (m_notes[i].id() == newNotes[j].id())
noteToBeRemoved = false;
}
if (noteToBeRemoved) {
qDebug() << "-- Removing note " << m_notes[i].title();
removeNote(m_notes[i]);
}
}
while (!newNotes.empty()) {
insertNote(newNotes.first());
newNotes.pop_front();
}
return true;
} }
else if (jdoc.isObject()) { else if (jdoc.isObject()) {
qDebug() << "- It's a single object..."; qDebug() << "- It's a single object...";
return applyJSONobject(jdoc.object()); insertNote(Note::fromjson(jdoc.object()));
return true;
} }
else { else {
qDebug() << "Unknown JSON document. This message should never occure!"; qDebug() << "Unknown JSON document. This message should never occure!";
@ -127,22 +129,29 @@ bool NotesModel::applyJSON(const QString &json) {
} }
int NotesModel::insertNote(const Note &note) { int NotesModel::insertNote(const Note &note) {
int position = insertPosition(note); int position = indexOf(note.id());
ModelNote<Note, bool> modelNote; if (position >= 0) {
modelNote.note = note; if (note.etag() != m_notes[position].etag()) {
modelNote.param = true; qDebug() << "-- Existing note " << note.title() << "changed, updating the model.";
m_notes.replace(position, note);
emit dataChanged(this->index(position), this->index(position));
}
else {
qDebug() << "-- Existing note " << note.title() << "unchanged, nothing to do.";
}
}
else {
qDebug() << "-- New note" << note.title() << ", adding it to the model.";
position = insertPosition(note);
beginInsertRows(QModelIndex(), position, position); beginInsertRows(QModelIndex(), position, position);
m_notes.insert(position, modelNote); m_notes.insert(position, note);
endInsertRows(); endInsertRows();
}
return position; return position;
} }
bool NotesModel::removeNote(const Note &note) { bool NotesModel::removeNote(const Note &note) {
return removeNote(note.id()); int position = m_notes.indexOf(note);
}
bool NotesModel::removeNote(int id) {
int position = indexOf(id);
if (position >= 0 && position < m_notes.size()) { if (position >= 0 && position < m_notes.size()) {
beginRemoveRows(QModelIndex(), position, position); beginRemoveRows(QModelIndex(), position, position);
m_notes.removeAt(position); m_notes.removeAt(position);
@ -152,61 +161,19 @@ bool NotesModel::removeNote(int id) {
return false; return false;
} }
bool NotesModel::replaceNote(const Note &note) { bool NotesModel::removeNote(int id) {
int position = indexOf(note.id()); bool retval = false;
if (position >= 0 && position < m_notes.size()) { for (int i = 0; i < m_notes.size(); ++i) {
ModelNote<Note, bool> modelNote; if (m_notes[i].id() == id) {
modelNote.note = note; retval |= removeNote(m_notes[i]);
modelNote.param = m_notes[position].param; if (i > 0) i--;
m_notes.replace(position, modelNote);
QVector<int> roles;
roles << ModifiedRole << TitleRole << CategoryRole << ContentRole << FavoriteRole << EtagRole;
emit dataChanged(this->index(position), this->index(position), roles);
return true;
} }
return false;
}
void NotesModel::clear() {
m_searchText.clear();
beginRemoveRows(QModelIndex(), 0, rowCount());
m_notes.clear();
endRemoveRows();
}
int NotesModel::indexOf(int id) const {
for (int i = 0; i < m_notes.size(); i++) {
if (m_notes[i].note.id() == id)
return i;
} }
return -1; return retval;
} }
Note NotesModel::get(int index) const {
Note note;
if (index >= 0 && index < m_notes.size()) {
note = m_notes[index].note;
}
return note;
}
/*
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> NotesModel::roleNames() const {
return QHash<int, QByteArray> { return QHash<int, QByteArray> {
{NotesModel::VisibleRole, "visible"},
{NotesModel::IdRole, "id"}, {NotesModel::IdRole, "id"},
{NotesModel::ModifiedRole, "modified"}, {NotesModel::ModifiedRole, "modified"},
{NotesModel::TitleRole, "title"}, {NotesModel::TitleRole, "title"},
@ -216,17 +183,17 @@ QHash<int, QByteArray> NotesModel::roleNames() const {
{NotesModel::EtagRole, "etag"}, {NotesModel::EtagRole, "etag"},
{NotesModel::ErrorRole, "error"}, {NotesModel::ErrorRole, "error"},
{NotesModel::ErrorMessageRole, "errorMessage"}, {NotesModel::ErrorMessageRole, "errorMessage"},
{NotesModel::DateStringRole, "date"} {NotesModel::InSearchRole, "inSearch"}
}; };
} }
QHash<int, QByteArray> NotesModel::sortingNames() const { QHash<int, QByteArray> NotesModel::sortingNames() const {
QHash<int, QByteArray> criteria; return QHash<int, QByteArray> {
criteria[sortByDate] = "date"; {NotesModel::sortByDate, "date"},
criteria[sortByCategory] = "category"; {NotesModel::sortByCategory, "category"},
criteria[sortByTitle] = "title"; {NotesModel::sortByTitle, "title"},
criteria[noSorting] = "none"; {NotesModel::noSorting, "none"}
return criteria; };
} }
QStringList NotesModel::sortingCriteria() const { QStringList NotesModel::sortingCriteria() const {
@ -257,17 +224,19 @@ int NotesModel::rowCount(const QModelIndex &parent) const {
QVariant NotesModel::data(const QModelIndex &index, int role) const { QVariant NotesModel::data(const QModelIndex &index, int role) const {
if (!index.isValid()) return QVariant(); if (!index.isValid()) return QVariant();
else if (role == VisibleRole) return m_notes[index.row()].param; else if (role == IdRole) return m_notes[index.row()].id();
else if (role == IdRole) return m_notes[index.row()].note.id(); else if (role == ModifiedRole) return m_notes[index.row()].modified();
else if (role == ModifiedRole) return m_notes[index.row()].note.modified(); else if (role == TitleRole) return m_notes[index.row()].title();
else if (role == TitleRole) return m_notes[index.row()].note.title(); else if (role == CategoryRole) return m_notes[index.row()].category();
else if (role == CategoryRole) return m_notes[index.row()].note.category(); else if (role == ContentRole) return m_notes[index.row()].content();
else if (role == ContentRole) return m_notes[index.row()].note.content(); else if (role == FavoriteRole) return m_notes[index.row()].favorite();
else if (role == FavoriteRole) return m_notes[index.row()].note.favorite(); else if (role == EtagRole) return m_notes[index.row()].etag();
else if (role == EtagRole) return m_notes[index.row()].note.etag(); else if (role == ErrorRole) return m_notes[index.row()].error();
else if (role == ErrorRole) return m_notes[index.row()].note.error(); else if (role == ErrorMessageRole) return m_notes[index.row()].errorMessage();
else if (role == ErrorMessageRole) return m_notes[index.row()].note.errorMessage(); else if (role == InSearchRole) {
else if (role == DateStringRole) return m_notes[index.row()].note.dateString(); qDebug() << "Invisible:" << m_invisibleIds.contains(m_notes[index.row()].id());
return !m_invisibleIds.contains(m_notes[index.row()].id());
}
return QVariant(); return QVariant();
} }
@ -275,99 +244,47 @@ QMap<int, QVariant> NotesModel::itemData(const QModelIndex &index) const {
QMap<int, QVariant> map; QMap<int, QVariant> map;
if (!index.isValid()) return map; if (!index.isValid()) return map;
else { else {
for (int role = VisibleRole; role <= ErrorMessageRole; role++) { for (int role = Qt::UserRole; role < Qt::UserRole + 10; ++role) {
map.insert(role, data(index, role)); map.insert(role, data(index, role));
} }
} }
return map; return map;
} }
bool NotesModel::setData(const QModelIndex &index, const QVariant &value, int role) {
if (!index.isValid()) return false;
else if (role == ModifiedRole && m_notes[index.row()].note.modified() != value.toUInt()) {
m_notes[index.row()].note.setModified(value.toInt());
emit dataChanged(this->index(index.row()), this->index(index.row()), QVector<int> { 1, role } ); // TODO remove when signals from Note are connected
emit dataChanged(this->index(index.row()), this->index(index.row()), QVector<int> { 1, DateStringRole} ); // TODO remove when signals from Note are connected
sort();
return true;
}
else if (role == CategoryRole && m_notes[index.row()].note.category() != value.toString()) {
m_notes[index.row()].note.setCategory(value.toString());
emit dataChanged(this->index(index.row()), this->index(index.row()), QVector<int> { 1, role } ); // TODO remove when signals from Note are connected
sort();
return true;
}
else if (role == ContentRole && m_notes[index.row()].note.content() != value.toString()) {
m_notes[index.row()].note.setContent(value.toString());
emit dataChanged(this->index(index.row()), this->index(index.row()), QVector<int> { 1, role } ); // TODO remove when signals from Note are connected
sort();
return true;
}
else if (role == FavoriteRole && m_notes[index.row()].note.favorite() != value.toBool()) {
m_notes[index.row()].note.setFavorite(value.toBool());
emit dataChanged(this->index(index.row()), this->index(index.row()), QVector<int> { 1, role } ); // TODO remove when signals from Note are connected
sort();
return true;
}
return false;
}
bool NotesModel::setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles) {
if (!index.isValid()) return false;
else if (roles.contains(ModifiedRole) || roles.contains(CategoryRole) || roles.contains(ContentRole) || roles.contains(FavoriteRole)) {
QMap<int, QVariant>::const_iterator i = roles.constBegin();
while (i != roles.constEnd()) {
setData(index, i.value(), i.key());
i++;
}
}
return false;
}
void NotesModel::sort() { void NotesModel::sort() {
qDebug() << "Sorting notes in the model"; qDebug() << "Sorting notes in the model";
QList<ModelNote<Note, bool> > notes;
QMap<QString, ModelNote<Note, bool> > map;
QMap<QString, ModelNote<Note, bool> > favorites;
if (m_sortBy == sortingNames()[sortByDate]) {
emit layoutAboutToBeChanged(QList<QPersistentModelIndex> (), VerticalSortHint); emit layoutAboutToBeChanged(QList<QPersistentModelIndex> (), VerticalSortHint);
for (int i = 0; i < m_notes.size(); i++) { if (m_favoritesOnTop) {
if (m_favoritesOnTop && m_notes[i].note.favorite()) if (m_sortBy == sortingNames()[sortByDate]) {
favorites.insert(QString::number(std::numeric_limits<uint>::max() - m_notes[i].note.modified()), m_notes[i]); std::sort(m_notes.begin(), m_notes.end(), Note::lessThanByDateFavOnTop);
else
map.insert(QString::number(std::numeric_limits<uint>::max() - m_notes[i].note.modified()), m_notes[i]);
}
notes = favorites.values();
notes.append(map.values());
m_notes = notes;
emit layoutChanged(QList<QPersistentModelIndex> (), VerticalSortHint);
} }
else if (m_sortBy == sortingNames()[sortByCategory]) { else if (m_sortBy == sortingNames()[sortByCategory]) {
emit layoutAboutToBeChanged(QList<QPersistentModelIndex> (), VerticalSortHint); std::sort(m_notes.begin(), m_notes.end(), Note::lessThanByCategoryFavOnTop);
for (int i = 0; i < m_notes.size(); i++) {
if (m_favoritesOnTop && m_notes[i].note.favorite())
favorites.insert(m_notes[i].note.category(), m_notes[i]);
else
map.insert(m_notes[i].note.category(), m_notes[i]);
}
notes = favorites.values();
notes.append(map.values());
m_notes = notes;
emit layoutChanged(QList<QPersistentModelIndex> (), VerticalSortHint);
} }
else if (m_sortBy == sortingNames()[sortByTitle]) { else if (m_sortBy == sortingNames()[sortByTitle]) {
emit layoutAboutToBeChanged(QList<QPersistentModelIndex> (), VerticalSortHint); std::sort(m_notes.begin(), m_notes.end(), Note::lessThanByTitleFavOnTop);
for (int i = 0; i < m_notes.size(); i++) { }
if (m_favoritesOnTop && m_notes[i].note.favorite()) }
favorites.insert(m_notes[i].note.title(), m_notes[i]); else {
else if (m_sortBy == sortingNames()[sortByDate]) {
map.insert(m_notes[i].note.title(), m_notes[i]); std::sort(m_notes.begin(), m_notes.end(), Note::lessThanByDate);
}
else if (m_sortBy == sortingNames()[sortByCategory]) {
std::sort(m_notes.begin(), m_notes.end(), Note::lessThanByCategory);
}
else if (m_sortBy == sortingNames()[sortByTitle]) {
std::sort(m_notes.begin(), m_notes.end(), Note::lessThanByTitle);
}
} }
notes = favorites.values();
notes.append(map.values());
m_notes = notes;
emit layoutChanged(QList<QPersistentModelIndex> (), VerticalSortHint); emit layoutChanged(QList<QPersistentModelIndex> (), VerticalSortHint);
}
int NotesModel::indexOf(int id) const {
for (int i = 0; i < m_notes.size(); i++) {
if (m_notes[i].id() == id)
return i;
} }
return -1;
} }
int NotesModel::insertPosition(const Note &n) const { int NotesModel::insertPosition(const Note &n) const {
@ -375,7 +292,7 @@ int NotesModel::insertPosition(const Note &n) const {
int upper = m_notes.size(); int upper = m_notes.size();
while (lower < upper) { while (lower < upper) {
int middle = qFloor(lower + (upper-lower) / 2); int middle = qFloor(lower + (upper-lower) / 2);
bool result = noteLessThan(n, m_notes[middle].note); bool result = noteLessThan(n, m_notes[middle]);
if (result) if (result)
upper = middle; upper = middle;
else else
@ -386,22 +303,13 @@ int NotesModel::insertPosition(const Note &n) const {
bool NotesModel::noteLessThan(const Note &n1, const Note &n2) const { bool NotesModel::noteLessThan(const Note &n1, const Note &n2) const {
if (m_sortBy == sortingNames()[sortByDate]) { if (m_sortBy == sortingNames()[sortByDate]) {
if (m_favoritesOnTop && n1.favorite() != n2.favorite()) return m_favoritesOnTop ? Note::lessThanByDateFavOnTop(n1, n2) : Note::lessThanByDate(n1, n2);
return n1.favorite();
else
return n1.modified() > n2.modified();
} }
else if (m_sortBy == sortingNames()[sortByCategory]) { else if (m_sortBy == sortingNames()[sortByCategory]) {
if (m_favoritesOnTop && n1.favorite() != n2.favorite()) return m_favoritesOnTop ? Note::lessThanByCategoryFavOnTop(n1, n2) : Note::lessThanByCategory(n1, n2);
return n1.favorite();
else
return n1.category() < n2.category();
} }
else if (m_sortBy == sortingNames()[sortByTitle]) { else if (m_sortBy == sortingNames()[sortByTitle]) {
if (m_favoritesOnTop && n1.favorite() != n2.favorite()) return m_favoritesOnTop ? Note::lessThanByTitleFavOnTop(n1, n2) : Note::lessThanByTitle(n1, n2);
return n1.favorite();
else
return n1.title() < n2.title();
} }
else { else {
if (m_favoritesOnTop && n1.favorite() != n2.favorite()) if (m_favoritesOnTop && n1.favorite() != n2.favorite())
@ -409,73 +317,3 @@ bool NotesModel::noteLessThan(const Note &n1, const Note &n2) const {
} }
return true; return true;
} }
/*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::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;
}
}
*/

View file

@ -5,12 +5,6 @@
#include <QDateTime> #include <QDateTime>
#include "note.h" #include "note.h"
template <typename N, typename P>
struct ModelNote {
N note;
P param;
};
class NotesModel : public QAbstractListModel { class NotesModel : public QAbstractListModel {
Q_OBJECT Q_OBJECT
public: public:
@ -32,29 +26,20 @@ public:
Q_INVOKABLE void search(QString searchText = QString()); Q_INVOKABLE void search(QString searchText = QString());
Q_INVOKABLE void clearSearch(); Q_INVOKABLE void clearSearch();
Q_INVOKABLE bool applyJSON(const QJsonDocument &jdoc); bool applyJSON(const QJsonDocument &jdoc);
Q_INVOKABLE bool applyJSON(const QString &json); bool applyJSON(const QString &json);
Q_INVOKABLE int insertNote(const Note &note);
Q_INVOKABLE bool removeNote(const Note &note);
Q_INVOKABLE bool removeNote(int position);
Q_INVOKABLE bool replaceNote(const Note &note);
Q_INVOKABLE void clear();
Q_INVOKABLE int indexOf(int id) const;
Q_INVOKABLE Note get(int index) const;
enum NoteRoles { enum NoteRoles {
VisibleRole = Qt::UserRole, IdRole = Qt::UserRole,
IdRole = Qt::UserRole + 1, ModifiedRole = Qt::UserRole + 1,
ModifiedRole = Qt::UserRole + 2, TitleRole = Qt::UserRole + 2,
TitleRole = Qt::UserRole + 3, CategoryRole = Qt::UserRole + 3,
CategoryRole = Qt::UserRole + 4, ContentRole = Qt::UserRole + 4,
ContentRole = Qt::UserRole + 5, FavoriteRole = Qt::UserRole + 5,
FavoriteRole = Qt::UserRole + 6, EtagRole = Qt::UserRole + 6,
EtagRole = Qt::UserRole + 7, ErrorRole = Qt::UserRole + 7,
ErrorRole = Qt::UserRole + 8, ErrorMessageRole = Qt::UserRole + 8,
ErrorMessageRole = Qt::UserRole + 9, InSearchRole = Qt::UserRole + 9
DateStringRole = Qt::UserRole + 10
}; };
QHash<int, QByteArray> roleNames() const; QHash<int, QByteArray> roleNames() const;
@ -71,14 +56,6 @@ public:
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
virtual QVariant data(const QModelIndex &index, int role) const; virtual QVariant data(const QModelIndex &index, int role) const;
QMap<int, QVariant> itemData(const QModelIndex &index) const; QMap<int, QVariant> itemData(const QModelIndex &index) const;
//virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
virtual bool setData(const QModelIndex &index, const QVariant &value, int role);
virtual bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles);
//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: protected:
@ -89,19 +66,21 @@ signals:
void searchTextChanged(QString searchText); void searchTextChanged(QString searchText);
private: private:
QList<ModelNote<Note, bool> > m_notes; QVector<Note> m_notes;
QVector<int> m_invisibleIds;
QString m_sortBy; QString m_sortBy;
bool m_favoritesOnTop; bool m_favoritesOnTop;
QString m_searchText; QString m_searchText;
void sort(); void sort();
//void update(); //void update();
bool applyJSONobject(const QJsonObject &jobj); int insertNote(const Note &note);
bool replaceNote(const Note &note);
bool removeNote(const Note &note);
bool removeNote(int id);
int indexOf(int id) const;
int insertPosition(const Note &n) const; int insertPosition(const Note &n) const;
bool noteLessThan(const Note &n1, const Note &n2) const; bool noteLessThan(const Note &n1, const Note &n2) const;
/*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);*/
}; };
#endif // NOTESMODEL_H #endif // NOTESMODEL_H

View file

@ -1,22 +0,0 @@
#include "sslconfiguration.h"
#include <QDebug>
SslConfiguration::SslConfiguration(QObject *parent) : QObject(parent), _checkCert(true) {
checkCertConfig = noCheckConfig = QSslConfiguration::defaultConfiguration();
noCheckConfig.setPeerVerifyMode(QSslSocket::VerifyNone);
QSslConfiguration::setDefaultConfiguration(checkCertConfig);
}
bool SslConfiguration::checkCert() {
return _checkCert;
}
void SslConfiguration::setCheckCert(bool check) {
if (_checkCert != check) {
qDebug() << "Changing SSL Cert check to" << check;
_checkCert = check;
QSslConfiguration::setDefaultConfiguration(_checkCert ? checkCertConfig : noCheckConfig);
emit checkCertChanged(_checkCert);
}
}

View file

@ -1,28 +0,0 @@
#ifndef SSLCONFIGURATION_H
#define SSLCONFIGURATION_H
#include <QObject>
#include <QSslConfiguration>
#include <QSslSocket>
class SslConfiguration : public QObject
{
Q_OBJECT
Q_PROPERTY(bool checkCert READ checkCert WRITE setCheckCert NOTIFY checkCertChanged)
public:
explicit SslConfiguration(QObject *parent = nullptr);
public slots:
bool checkCert();
void setCheckCert(bool check);
signals:
void checkCertChanged(bool check);
private:
bool _checkCert;
QSslConfiguration checkCertConfig;
QSslConfiguration noCheckConfig;
};
#endif // SSLCONFIGURATION_H

View file

@ -213,13 +213,6 @@
<translation>Geändert</translation> <translation>Geändert</translation>
</message> </message>
</context> </context>
<context>
<name>NotesApi</name>
<message>
<source>Unable to connect</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>NotesPage</name> <name>NotesPage</name>
<message> <message>
@ -270,6 +263,34 @@
<source>Open the settings to configure your Nextcloud accounts</source> <source>Open the settings to configure your Nextcloud accounts</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>No account yet</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Got to the settings to add an account</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No notes yet</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pull down to add a note</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No result</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Try another query</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>An error occurred</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SettingsPage</name> <name>SettingsPage</name>

View file

@ -213,13 +213,6 @@
<translation>Ingen kategori</translation> <translation>Ingen kategori</translation>
</message> </message>
</context> </context>
<context>
<name>NotesApi</name>
<message>
<source>Unable to connect</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>NotesPage</name> <name>NotesPage</name>
<message> <message>
@ -270,6 +263,34 @@
<source>Open the settings to configure your Nextcloud accounts</source> <source>Open the settings to configure your Nextcloud accounts</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>No account yet</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Got to the settings to add an account</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No notes yet</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Pull down to add a note</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No result</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Try another query</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>An error occurred</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>SettingsPage</name> <name>SettingsPage</name>

View file

@ -259,73 +259,100 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context>
<name>NotesApi</name>
<message>
<location filename="../qml/components/NotesApi.qml" line="68"/>
<source>Unable to connect</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>NotesPage</name> <name>NotesPage</name>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="31"/> <location filename="../qml/pages/NotesPage.qml" line="38"/>
<source>Settings</source> <source>Settings</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="35"/> <location filename="../qml/pages/NotesPage.qml" line="42"/>
<source>Add note</source> <source>Add note</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="40"/> <location filename="../qml/pages/NotesPage.qml" line="47"/>
<source>Reload</source> <source>Reload</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="40"/> <location filename="../qml/pages/NotesPage.qml" line="47"/>
<source>Updating...</source> <source>Updating...</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="46"/> <location filename="../qml/pages/NotesPage.qml" line="53"/>
<source>Last update</source> <source>Last update</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="49"/> <location filename="../qml/pages/NotesPage.qml" line="56"/>
<source>never</source> <source>never</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="59"/> <location filename="../qml/pages/NotesPage.qml" line="66"/>
<source>Nextcloud Notes</source> <source>Nextcloud Notes</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="198"/> <location filename="../qml/pages/NotesPage.qml" line="206"/>
<source>Modified</source> <source>Modified</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="201"/> <location filename="../qml/pages/NotesPage.qml" line="209"/>
<source>Delete</source> <source>Delete</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="203"/> <location filename="../qml/pages/NotesPage.qml" line="211"/>
<source>Deleting note</source> <source>Deleting note</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="233"/> <location filename="../qml/pages/NotesPage.qml" line="241"/>
<source>Loading notes...</source> <source>Loading notes...</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="271"/> <location filename="../qml/pages/NotesPage.qml" line="247"/>
<source>No account yet</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="248"/>
<source>Got to the settings to add an account</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="254"/>
<source>No notes yet</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="255"/>
<source>Pull down to add a note</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="261"/>
<source>No result</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="262"/>
<source>Try another query</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="268"/>
<source>An error occurred</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="279"/>
<source>Open the settings to configure your Nextcloud accounts</source> <source>Open the settings to configure your Nextcloud accounts</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>