NotesStore and NotesApi Classes based on a common interface class. Seems to work fine :)
This commit is contained in:
parent
1d4022bdbd
commit
4ea61c565f
16 changed files with 453 additions and 384 deletions
|
@ -2,10 +2,6 @@ import QtQuick 2.2
|
|||
import Sailfish.Silica 1.0
|
||||
import Nemo.Configuration 1.0
|
||||
import Nemo.Notifications 1.0
|
||||
//import harbour.nextcloudnotes.note 1.0
|
||||
//import harbour.nextcloudnotes.notesstore 1.0
|
||||
import harbour.nextcloudnotes.notesapi 1.0
|
||||
import harbour.nextcloudnotes.notesmodel 1.0
|
||||
import "pages"
|
||||
|
||||
ApplicationWindow
|
||||
|
@ -35,10 +31,10 @@ ApplicationWindow
|
|||
onServerChanged: notesApi.server = server
|
||||
onUsernameChanged: notesApi.username = username
|
||||
onPasswordChanged: notesApi.password = password
|
||||
onDoNotVerifySslChanged: notesApi.sslVerify = !doNotVerifySsl
|
||||
onDoNotVerifySslChanged: notesApi.verifySsl = !doNotVerifySsl
|
||||
onPathChanged: {
|
||||
notesStore.account = appSettings.currentAccount
|
||||
notesApi.dataFile = appSettings.currentAccount !== "" ? StandardPaths.data + "/" + appSettings.currentAccount + ".json" : ""
|
||||
notesApi.account = appSettings.currentAccount
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,55 +138,40 @@ ApplicationWindow
|
|||
}
|
||||
}
|
||||
|
||||
/*NotesStore {
|
||||
id: notesStore
|
||||
|
||||
Component.onCompleted: getAllNotes()
|
||||
onAccountChanged: {
|
||||
console.log(account)
|
||||
if (account !== "")
|
||||
getAllNotes()
|
||||
}
|
||||
onNoteCreated: {
|
||||
//console.log("Note created", createdNote.id)
|
||||
}
|
||||
onNoteUpdated: {
|
||||
//console.log("Note updated", updatedNote.id)
|
||||
}
|
||||
onNoteDeleted: {
|
||||
//console.log("Note deleted", deletedNoteId)
|
||||
}
|
||||
}*/
|
||||
|
||||
Connections {
|
||||
target: notesStore
|
||||
|
||||
onAccountChanged: {
|
||||
console.log(notesStore.account)
|
||||
//console.log(notesStore.account)
|
||||
if (notesStore.account !== "")
|
||||
notesStore.getAllNotes()
|
||||
}
|
||||
onNoteUpdated: {
|
||||
console.log("Note updated", note["id"])
|
||||
//console.log("Note updated", note.id)
|
||||
}
|
||||
onNoteDeleted: {
|
||||
console.log("Note deleted", note["id"])
|
||||
//console.log("Note deleted", note.id)
|
||||
}
|
||||
}
|
||||
|
||||
NotesApi {
|
||||
id: notesApi
|
||||
Connections {
|
||||
target: notesApi
|
||||
|
||||
onAccountChanged: {
|
||||
//console.log(notesStore.account)
|
||||
if (notesApi.account !== "")
|
||||
notesApi.getAllNotes()
|
||||
}
|
||||
onNetworkAccessibleChanged: {
|
||||
console.log("Device is " + (networkAccessible ? "online" : "offline"))
|
||||
networkAccessible ? offlineNotification.close(Notification.Closed) : offlineNotification.publish()
|
||||
console.log("Device is " + (accessible ? "online" : "offline"))
|
||||
accessible ? offlineNotification.close(Notification.Closed) : offlineNotification.publish()
|
||||
}
|
||||
onError: {
|
||||
if (error)
|
||||
console.log("Error (" + error + "): " + errorMessage(error))
|
||||
console.log("Error (" + error + "): " + notesApi.errorMessage(error))
|
||||
errorNotification.close()
|
||||
if (error && networkAccessible) {
|
||||
errorNotification.body = errorMessage(error)
|
||||
if (error && notesApi.networkAccessible) {
|
||||
errorNotification.body = notesApi.errorMessage(error)
|
||||
errorNotification.publish()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ Page {
|
|||
notesApi.server = server
|
||||
notesApi.username = username
|
||||
notesApi.password = password
|
||||
notesApi.sslVerify = !doNotVerifySsl
|
||||
notesApi.verifySsl = !doNotVerifySsl
|
||||
notesApi.verifyLogin()
|
||||
}
|
||||
}
|
||||
|
@ -277,7 +277,7 @@ Page {
|
|||
description: qsTr("Enable this option to allow selfsigned certificates")
|
||||
onCheckedChanged: {
|
||||
account.doNotVerifySsl = checked
|
||||
notesApi.sslVerify = !account.doNotVerifySsl
|
||||
notesApi.verifySsl = !account.doNotVerifySsl
|
||||
}
|
||||
}
|
||||
TextSwitch {
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import QtQuick 2.2
|
||||
import Sailfish.Silica 1.0
|
||||
import harbour.nextcloudnotes.notesmodel 1.0
|
||||
//import harbour.nextcloudnotes.notesmodel 1.0
|
||||
|
||||
Page {
|
||||
id: page
|
||||
|
||||
property NotesModel notesModel: notesApi.model()
|
||||
property string searchString
|
||||
|
||||
Connections {
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
# * date Author's Name <author's email> version-release
|
||||
# - Summary of changes
|
||||
|
||||
* Fri Apr 10 2020 Scharel Clemens <harbour-nextcloudnotes@scharel.rocks> 0.7-0
|
||||
- New methods for data handling are now usable
|
||||
|
||||
* Sun Apr 05 2020 Scharel Clemens <harbour-nextcloudnotes@scharel.rocks> 0.6-1
|
||||
- Implementing new methods for data handling (files and API targets)
|
||||
|
||||
|
|
|
@ -13,8 +13,8 @@ Name: harbour-nextcloudnotes
|
|||
%{!?qtc_make:%define qtc_make make}
|
||||
%{?qtc_builddir:%define _builddir %qtc_builddir}
|
||||
Summary: Nextcloud Notes
|
||||
Version: 0.6
|
||||
Release: 1
|
||||
Version: 0.7
|
||||
Release: 0
|
||||
Group: Applications/Editors
|
||||
License: MIT
|
||||
URL: https://github.com/scharel/harbour-nextcloudnotes
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
Name: harbour-nextcloudnotes
|
||||
Summary: Nextcloud Notes
|
||||
Version: 0.6
|
||||
Release: 1
|
||||
Version: 0.7
|
||||
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
|
||||
|
|
|
@ -19,23 +19,54 @@ int main(int argc, char *argv[])
|
|||
qDebug() << app->applicationDisplayName() << app->applicationVersion();
|
||||
|
||||
qRegisterMetaType<Note>();
|
||||
qmlRegisterType<Note>("harbour.nextcloudnotes.note", 1, 0, "Note");
|
||||
qmlRegisterType<NotesApi>("harbour.nextcloudnotes.notesapi", 1, 0, "NotesApi");
|
||||
//qmlRegisterType<NotesStore>("harbour.nextcloudnotes.notesstore", 1, 0, "NotesStore");
|
||||
qmlRegisterType<NotesProxyModel>("harbour.nextcloudnotes.notesmodel", 1, 0, "NotesModel");
|
||||
NotesModel* notesModel = new NotesModel;
|
||||
NotesProxyModel* notesProxyModel = new NotesProxyModel;
|
||||
notesProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
|
||||
notesProxyModel->setSortLocaleAware(true);
|
||||
notesProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
notesProxyModel->setFilterRole(NotesModel::ContentRole);
|
||||
notesProxyModel->setSourceModel(notesModel);
|
||||
|
||||
NotesStore* notesStore = new NotesStore;
|
||||
NotesApi* notesApi = new NotesApi;
|
||||
|
||||
QObject::connect(notesApi, SIGNAL(noteUpdated(Note)), notesStore, SLOT(updateNote(Note)));
|
||||
//QObject::connect(notesStore, SIGNAL(noteUpdated(Note)), notesApi, SLOT(updateNote(Note)));
|
||||
QObject::connect(notesApi, SIGNAL(noteDeleted(int)), notesStore, SLOT(deleteNote(int)));
|
||||
//QObject::connect(notesStore, SIGNAL(noteDeleted(int)), notesApi, SLOT(deleteNote(int)));
|
||||
|
||||
QObject::connect(notesStore, SIGNAL(noteUpdated(Note)), notesModel, SLOT(insertNote(Note)));
|
||||
QObject::connect(notesStore, SIGNAL(noteDeleted(int)), notesModel, SLOT(removeNote(int)));
|
||||
QObject::connect(notesApi, SIGNAL(noteUpdated(Note)), notesModel, SLOT(insertNote(Note)));
|
||||
QObject::connect(notesApi, SIGNAL(noteDeleted(int)), notesModel, SLOT(removeNote(int)));
|
||||
|
||||
QQuickView* view = SailfishApp::createView();
|
||||
view->rootContext()->setContextProperty("notesStore", notesStore);
|
||||
view->setSource(SailfishApp::pathTo("qml/harbour-nextcloudnotes.qml"));
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
view->rootContext()->setContextProperty("debug", QVariant(true));
|
||||
#else
|
||||
view->rootContext()->setContextProperty("debug", QVariant(false));
|
||||
#endif
|
||||
view->rootContext()->setContextProperty("notesModel", notesProxyModel);
|
||||
view->rootContext()->setContextProperty("notesStore", notesStore);
|
||||
view->rootContext()->setContextProperty("notesApi", notesApi);
|
||||
|
||||
view->setSource(SailfishApp::pathTo("qml/harbour-nextcloudnotes.qml"));
|
||||
view->show();
|
||||
|
||||
return app->exec();
|
||||
int retval = app->exec();
|
||||
QObject::disconnect(notesApi, SIGNAL(noteDeleted(int)), notesModel, SLOT(removeNote(int)));
|
||||
QObject::disconnect(notesApi, SIGNAL(noteUpdated(Note)), notesModel, SLOT(insertNote(Note)));
|
||||
QObject::disconnect(notesStore, SIGNAL(noteDeleted(int)), notesModel, SLOT(removeNote(int)));
|
||||
QObject::disconnect(notesStore, SIGNAL(noteUpdated(Note)), notesModel, SLOT(insertNote(Note)));
|
||||
|
||||
//QObject::disconnect(notesStore, SIGNAL(noteDeleted(int)), notesApi, SLOT(deleteNote(int)));
|
||||
QObject::disconnect(notesApi, SIGNAL(noteDeleted(int)), notesStore, SLOT(deleteNote(int)));
|
||||
//QObject::disconnect(notesStore, SIGNAL(noteUpdated(Note)), notesApi, SLOT(updateNote(Note)));
|
||||
QObject::disconnect(notesApi, SIGNAL(noteUpdated(Note)), notesStore, SLOT(updateNote(Note)));
|
||||
|
||||
notesApi->deleteLater();
|
||||
notesStore->deleteLater();
|
||||
notesProxyModel->deleteLater();
|
||||
notesModel->deleteLater();
|
||||
return retval;
|
||||
}
|
||||
|
|
125
src/note.cpp
125
src/note.cpp
|
@ -1,5 +1,16 @@
|
|||
#include "note.h"
|
||||
|
||||
const QMap<Note::NoteField, QString> Note::m_noteFieldNames = QMap<Note::NoteField, QString>( {
|
||||
{Note::Id, "id"},
|
||||
{Note::Modified, "modified"},
|
||||
{Note::Title, "title"},
|
||||
{Note::Category, "category"},
|
||||
{Note::Content, "content"},
|
||||
{Note::Favorite, "favorite"},
|
||||
{Note::Etag, "etag"},
|
||||
{Note::Error, "error"},
|
||||
{Note::ErrorMessage, "errorMessage"} } );
|
||||
|
||||
Note::Note(QObject *parent) : QObject(parent) {
|
||||
connectSignals();
|
||||
}
|
||||
|
@ -103,40 +114,54 @@ bool Note::operator >(const QJsonObject& note) const {
|
|||
return modified() > modified(note);
|
||||
}
|
||||
|
||||
QJsonObject Note::toJsonObject() const {
|
||||
const QJsonObject Note::toJsonObject() const {
|
||||
return m_json;
|
||||
}
|
||||
|
||||
QJsonValue Note::toJsonValue() const {
|
||||
const QJsonValue Note::toJsonValue() const {
|
||||
return QJsonValue(m_json);
|
||||
}
|
||||
|
||||
QJsonDocument Note::toJsonDocument() const {
|
||||
const QJsonDocument Note::toJsonDocument() const {
|
||||
return QJsonDocument(m_json);
|
||||
}
|
||||
|
||||
int Note::id() const {
|
||||
return m_json.value(ID).toInt(-1);
|
||||
return m_json.value(noteFieldName(Id)).toInt(-1);
|
||||
}
|
||||
int Note::id(const QJsonObject &jobj) {
|
||||
return jobj.value(ID).toInt(-1);
|
||||
return jobj.value(noteFieldName(Id)).toInt(-1);
|
||||
}
|
||||
void Note::setId(int id) {
|
||||
if (id >= 0) {
|
||||
if (id != this->id()) {
|
||||
m_json.insert(ID, QJsonValue(id));
|
||||
m_json.insert(noteFieldName(Id), QJsonValue(id));
|
||||
emit idChanged(this->id());
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_json.remove(noteFieldName(Id));
|
||||
emit idChanged(this->id());
|
||||
}
|
||||
}
|
||||
|
||||
int Note::modified() const {
|
||||
return m_json.value(MODIFIED).toInt();
|
||||
return m_json.value(noteFieldName(Modified)).toInt();
|
||||
}
|
||||
int Note::modified(const QJsonObject &jobj) {
|
||||
return jobj.value(MODIFIED).toInt();
|
||||
return jobj.value(noteFieldName(Modified)).toInt();
|
||||
}
|
||||
void Note::setModified(int modified) {
|
||||
if (modified != this->modified()){
|
||||
m_json.insert(MODIFIED, QJsonValue(modified));
|
||||
if (modified > 0) {
|
||||
if (modified != this->modified()) {
|
||||
m_json.insert(noteFieldName(Modified), QJsonValue(modified));
|
||||
emit modifiedChanged(this->modified());
|
||||
emit modifiedStringChanged(modifiedString());
|
||||
emit modifiedDateTimeChanged(modifiedDateTime());
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_json.remove(noteFieldName(Modified));
|
||||
emit modifiedChanged(this->modified());
|
||||
emit modifiedStringChanged(modifiedString());
|
||||
emit modifiedDateTimeChanged(modifiedDateTime());
|
||||
|
@ -144,92 +169,128 @@ void Note::setModified(int modified) {
|
|||
}
|
||||
|
||||
QString Note::title() const {
|
||||
return m_json.value(TITLE).toString();
|
||||
return m_json.value(noteFieldName(Title)).toString();
|
||||
}
|
||||
QString Note::title(const QJsonObject &jobj) {
|
||||
return jobj.value(TITLE).toString();
|
||||
return jobj.value(noteFieldName(Title)).toString();
|
||||
}
|
||||
void Note::setTitle(QString title) {
|
||||
if (!title.isNull()) {
|
||||
if (title != this->title()) {
|
||||
m_json.insert(TITLE, QJsonValue(title));
|
||||
m_json.insert(noteFieldName(Title), QJsonValue(title));
|
||||
emit titleChanged(this->title());
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_json.remove(noteFieldName(Title));
|
||||
emit titleChanged(this->title());
|
||||
}
|
||||
}
|
||||
|
||||
QString Note::category() const {
|
||||
return m_json.value(CATEGORY).toString();
|
||||
return m_json.value(noteFieldName(Category)).toString();
|
||||
}
|
||||
QString Note::category(const QJsonObject &jobj) {
|
||||
return jobj.value(CATEGORY).toString();
|
||||
return jobj.value(noteFieldName(Category)).toString();
|
||||
}
|
||||
void Note::setCategory(QString category) {
|
||||
if (!category.isNull()) {
|
||||
if (category != this->category()) {
|
||||
m_json.insert(CATEGORY, QJsonValue(category));
|
||||
m_json.insert(noteFieldName(Category), QJsonValue(category));
|
||||
emit categoryChanged(this->category());
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_json.remove(noteFieldName(Category));
|
||||
emit categoryChanged(this->category());
|
||||
}
|
||||
}
|
||||
|
||||
QString Note::content() const {
|
||||
return m_json.value(CONTENT).toString();
|
||||
return m_json.value(noteFieldName(Content)).toString();
|
||||
}
|
||||
QString Note::content(const QJsonObject &jobj) {
|
||||
return jobj.value(CONTENT).toString();
|
||||
return jobj.value(noteFieldName(Content)).toString();
|
||||
}
|
||||
void Note::setContent(QString content) {
|
||||
if (!content.isNull()) {
|
||||
if (content != this->content()) {
|
||||
m_json.insert(CONTENT, QJsonValue(content));
|
||||
m_json.insert(noteFieldName(Content), QJsonValue(content));
|
||||
emit contentChanged(this->content());
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_json.remove(noteFieldName(Content));
|
||||
emit contentChanged(this->content());
|
||||
}
|
||||
}
|
||||
|
||||
bool Note::favorite() const {
|
||||
return m_json.value(FAVORITE).toBool();
|
||||
return m_json.value(noteFieldName(Favorite)).toBool();
|
||||
}
|
||||
bool Note::favorite(const QJsonObject &jobj) {
|
||||
return jobj.value(FAVORITE).toBool();
|
||||
return jobj.value(noteFieldName(Favorite)).toBool();
|
||||
}
|
||||
void Note::setFavorite(bool favorite) {
|
||||
if (favorite != this->favorite()) {
|
||||
m_json.insert(FAVORITE, QJsonValue(favorite));
|
||||
m_json.insert(noteFieldName(Favorite), QJsonValue(favorite));
|
||||
emit favoriteChanged(this->favorite());
|
||||
}
|
||||
}
|
||||
|
||||
QString Note::etag() const {
|
||||
return m_json.value(ETAG).toString();
|
||||
return m_json.value(noteFieldName(Etag)).toString();
|
||||
}
|
||||
QString Note::etag(const QJsonObject &jobj) {
|
||||
return jobj.value(ETAG).toString();
|
||||
return jobj.value(noteFieldName(Etag)).toString();
|
||||
}
|
||||
void Note::setEtag(QString etag) {
|
||||
if (!etag.isNull()) {
|
||||
if (etag != this->etag()) {
|
||||
m_json.insert(ETAG, QJsonValue(etag));
|
||||
m_json.insert(noteFieldName(Etag), QJsonValue(etag));
|
||||
emit etagChanged(this->etag());
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_json.remove(noteFieldName(Etag));
|
||||
emit etagChanged(this->etag());
|
||||
}
|
||||
}
|
||||
|
||||
bool Note::error() const {
|
||||
return m_json.value(ERROR).toBool(true);
|
||||
return m_json.value(noteFieldName(Error)).toBool();
|
||||
}
|
||||
bool Note::error(const QJsonObject &jobj) {
|
||||
return jobj.value(ERROR).toBool(true);
|
||||
return jobj.value(noteFieldName(Error)).toBool();
|
||||
}
|
||||
void Note::setError(bool error) {
|
||||
if (error) {
|
||||
if (error != this->error()) {
|
||||
m_json.insert(ERROR, QJsonValue(error));
|
||||
m_json.insert(noteFieldName(Error), QJsonValue(error));
|
||||
emit errorChanged(this->error());
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_json.remove(noteFieldName(Error));
|
||||
emit errorChanged(this->error());
|
||||
}
|
||||
}
|
||||
|
||||
QString Note::errorMessage() const {
|
||||
return m_json.value(ERRORMESSAGE).toString();
|
||||
return m_json.value(noteFieldName(ErrorMessage)).toString();
|
||||
}
|
||||
QString Note::errorMessage(const QJsonObject &jobj) {
|
||||
return jobj.value(ERRORMESSAGE).toString();
|
||||
return jobj.value(noteFieldName(ErrorMessage)).toString();
|
||||
}
|
||||
void Note::setErrorMessage(QString errorMessage) {
|
||||
if (!errorMessage.isNull()) {
|
||||
if (errorMessage != this->errorMessage()) {
|
||||
m_json.insert(ERRORMESSAGE, QJsonValue(errorMessage));
|
||||
m_json.insert(noteFieldName(ErrorMessage), QJsonValue(errorMessage));
|
||||
emit errorMessageChanged(this->errorMessage());
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_json.remove(noteFieldName(ErrorMessage));
|
||||
emit errorMessageChanged(this->errorMessage());
|
||||
}
|
||||
}
|
||||
|
@ -249,7 +310,7 @@ QString Note::modifiedString(const QJsonObject &jobj) {
|
|||
dateString = date.toLocalTime().toString("dddd");
|
||||
else if (date.toLocalTime().toString("yyyy") == QDateTime::currentDateTime().toString("yyyy"))
|
||||
dateString = date.toLocalTime().toString("MMMM");
|
||||
else
|
||||
else if (!date.isValid())
|
||||
dateString = date.toLocalTime().toString("MMMM yyyy");
|
||||
return dateString;
|
||||
}
|
||||
|
|
69
src/note.h
69
src/note.h
|
@ -1,22 +1,13 @@
|
|||
#ifndef NOTE_H
|
||||
#ifndef NOTE_H
|
||||
#define NOTE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonDocument>
|
||||
#include <QDateTime>
|
||||
#include <QMap>
|
||||
#include <QDebug>
|
||||
|
||||
#define ID "id"
|
||||
#define MODIFIED "modified"
|
||||
#define TITLE "title"
|
||||
#define CATEGORY "category"
|
||||
#define CONTENT "content"
|
||||
#define FAVORITE "favorite"
|
||||
#define ETAG "etag"
|
||||
#define ERROR "error"
|
||||
#define ERRORMESSAGE "errorMessage"
|
||||
|
||||
class Note : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -37,50 +28,77 @@ public:
|
|||
bool operator >(const Note& note) const;
|
||||
bool operator >(const QJsonObject& note) const;
|
||||
|
||||
QJsonObject toJsonObject() const;
|
||||
QJsonValue toJsonValue() const;
|
||||
QJsonDocument toJsonDocument() const;
|
||||
bool isValid() const { return id() >= 0; }
|
||||
|
||||
const QJsonObject toJsonObject() const;
|
||||
const QJsonValue toJsonValue() const;
|
||||
const QJsonDocument toJsonDocument() const;
|
||||
|
||||
enum NoteField {
|
||||
None = 0x0000,
|
||||
Id = 0x0001,
|
||||
Modified = 0x0002,
|
||||
Title = 0x0004,
|
||||
Category = 0x0008,
|
||||
Content = 0x0010,
|
||||
Favorite = 0x0020,
|
||||
Etag = 0x0040,
|
||||
Error = 0x0080,
|
||||
ErrorMessage = 0x0100
|
||||
};
|
||||
Q_DECLARE_FLAGS(NoteFields, NoteField)
|
||||
Q_FLAG(NoteFields)
|
||||
Q_INVOKABLE static QList<NoteField> noteFields() {
|
||||
return m_noteFieldNames.keys();
|
||||
}
|
||||
Q_INVOKABLE static QString noteFieldName(NoteField field) {
|
||||
return m_noteFieldNames[field];
|
||||
}
|
||||
Q_INVOKABLE static QStringList noteFieldNames() {
|
||||
return m_noteFieldNames.values();
|
||||
}
|
||||
|
||||
Q_PROPERTY(int id READ id WRITE setId NOTIFY idChanged)
|
||||
Q_INVOKABLE int id() const;
|
||||
int id() const;
|
||||
void setId(int id);
|
||||
|
||||
Q_PROPERTY(int modified READ modified WRITE setModified NOTIFY modifiedChanged)
|
||||
Q_INVOKABLE int modified() const;
|
||||
int modified() const;
|
||||
void setModified(int modified);
|
||||
|
||||
Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged)
|
||||
Q_INVOKABLE QString title() const;
|
||||
QString title() const;
|
||||
void setTitle(QString title);
|
||||
|
||||
Q_PROPERTY(QString category READ category WRITE setCategory NOTIFY categoryChanged)
|
||||
Q_INVOKABLE QString category() const;
|
||||
QString category() const;
|
||||
void setCategory(QString category);
|
||||
|
||||
Q_PROPERTY(QString content READ content WRITE setContent NOTIFY contentChanged)
|
||||
Q_INVOKABLE QString content() const;
|
||||
QString content() const;
|
||||
void setContent(QString content);
|
||||
|
||||
Q_PROPERTY(bool favorite READ favorite WRITE setFavorite NOTIFY favoriteChanged)
|
||||
Q_INVOKABLE bool favorite() const;
|
||||
bool favorite() const;
|
||||
void setFavorite(bool favorite);
|
||||
|
||||
Q_PROPERTY(QString etag READ etag WRITE setEtag NOTIFY etagChanged)
|
||||
Q_INVOKABLE QString etag() const;
|
||||
QString etag() const;
|
||||
void setEtag(QString etag);
|
||||
|
||||
Q_PROPERTY(bool error READ error WRITE setError NOTIFY errorChanged)
|
||||
Q_INVOKABLE bool error() const;
|
||||
bool error() const;
|
||||
void setError(bool error);
|
||||
|
||||
Q_PROPERTY(QString errorMessage READ errorMessage WRITE setErrorMessage NOTIFY errorMessageChanged)
|
||||
Q_INVOKABLE QString errorMessage() const;
|
||||
QString errorMessage() const;
|
||||
void setErrorMessage(QString errorMessage);
|
||||
|
||||
Q_PROPERTY(QString modifiedString READ modifiedString NOTIFY modifiedStringChanged)
|
||||
Q_INVOKABLE QString modifiedString() const;
|
||||
QString modifiedString() const;
|
||||
|
||||
Q_INVOKABLE QDateTime modifiedDateTime() const;
|
||||
Q_PROPERTY(QDateTime modifiedDateTime READ modifiedDateTime NOTIFY modifiedDateTimeChanged)
|
||||
QDateTime modifiedDateTime() const;
|
||||
|
||||
static int id(const QJsonObject& jobj);
|
||||
static int modified(const QJsonObject& jobj);
|
||||
|
@ -110,6 +128,7 @@ signals:
|
|||
|
||||
private:
|
||||
QJsonObject m_json;
|
||||
const static QMap<NoteField, QString> m_noteFieldNames;
|
||||
|
||||
void connectSignals();
|
||||
};
|
||||
|
|
239
src/notesapi.cpp
239
src/notesapi.cpp
|
@ -6,7 +6,7 @@
|
|||
#include <QDir>
|
||||
|
||||
NotesApi::NotesApi(const QString statusEndpoint, const QString loginEndpoint, const QString ocsEndpoint, const QString notesEndpoint, QObject *parent)
|
||||
: QObject(parent), m_statusEndpoint(statusEndpoint), m_loginEndpoint(loginEndpoint), m_ocsEndpoint(ocsEndpoint), m_notesEndpoint(notesEndpoint)
|
||||
: NotesInterface(parent), m_statusEndpoint(statusEndpoint), m_loginEndpoint(loginEndpoint), m_ocsEndpoint(ocsEndpoint), m_notesEndpoint(notesEndpoint)
|
||||
{
|
||||
// TODO verify connections (also in destructor)
|
||||
m_loginPollTimer.setInterval(POLL_INTERVALL);
|
||||
|
@ -22,13 +22,6 @@ NotesApi::NotesApi(const QString statusEndpoint, const QString loginEndpoint, co
|
|||
m_status_needsDbUpgrade = false;
|
||||
m_status_extendedSupport = false;
|
||||
m_loginStatus = LoginStatus::LoginUnknown;
|
||||
mp_model = new NotesModel(this);
|
||||
mp_modelProxy = new NotesProxyModel(this);
|
||||
mp_modelProxy->setSourceModel(mp_model);
|
||||
mp_modelProxy->setSortCaseSensitivity(Qt::CaseInsensitive);
|
||||
mp_modelProxy->setSortLocaleAware(true);
|
||||
mp_modelProxy->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
mp_modelProxy->setFilterRole(NotesModel::ContentRole);
|
||||
connect(this, SIGNAL(urlChanged(QUrl)), this, SLOT(verifyUrl(QUrl)));
|
||||
connect(&m_manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), this, SLOT(requireAuthentication(QNetworkReply*,QAuthenticator*)));
|
||||
connect(&m_manager, SIGNAL(networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility)), this, SLOT(onNetworkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility)));
|
||||
|
@ -40,7 +33,6 @@ NotesApi::NotesApi(const QString statusEndpoint, const QString loginEndpoint, co
|
|||
m_request.setRawHeader("OCS-APIREQUEST", "true");
|
||||
m_authenticatedRequest = m_request;
|
||||
m_authenticatedRequest.setHeader(QNetworkRequest::ContentTypeHeader, QString("application/json").toUtf8());
|
||||
connect(mp_model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), this, SLOT(saveToFile(QModelIndex,QModelIndex,QVector<int>)));
|
||||
}
|
||||
|
||||
NotesApi::~NotesApi() {
|
||||
|
@ -50,22 +42,118 @@ NotesApi::~NotesApi() {
|
|||
disconnect(&m_manager, SIGNAL(networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility)), this, SLOT(onNetworkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility)));
|
||||
disconnect(&m_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
|
||||
disconnect(&m_manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)), this, SLOT(sslError(QNetworkReply*,QList<QSslError>)));
|
||||
disconnect(mp_model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), this, SLOT(saveToFile(QModelIndex,QModelIndex,QVector<int>)));
|
||||
m_jsonFile.close();
|
||||
if (mp_modelProxy)
|
||||
delete mp_modelProxy;
|
||||
if (mp_model)
|
||||
delete mp_model;
|
||||
}
|
||||
|
||||
void NotesApi::setSslVerify(bool verify) {
|
||||
void NotesApi::setAccount(const QString &account) {
|
||||
if (account != m_account) {
|
||||
m_account = account;
|
||||
// TODO reset the class
|
||||
emit accountChanged(m_account);
|
||||
}
|
||||
}
|
||||
|
||||
void NotesApi::getAllNotes(Note::NoteField exclude) {
|
||||
QUrl url = apiEndpointUrl(m_notesEndpoint);
|
||||
QStringList excludeFields;
|
||||
QList<Note::NoteField> noteFields = Note::noteFields();
|
||||
QFlags<Note::NoteField> flags(exclude);
|
||||
|
||||
for (int i = 0; i < noteFields.size(); ++i) {
|
||||
if (flags.testFlag(noteFields[i])) {
|
||||
excludeFields << Note::noteFieldName(noteFields[i]);
|
||||
}
|
||||
}
|
||||
if (!excludeFields.isEmpty())
|
||||
url.setQuery(QString(EXCLUDE_QUERY).append(excludeFields.join(",")));
|
||||
|
||||
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
|
||||
qDebug() << "GET" << url.toDisplayString();
|
||||
m_authenticatedRequest.setUrl(url);
|
||||
m_notesReplies << m_manager.get(m_authenticatedRequest);
|
||||
emit busyChanged(true);
|
||||
}
|
||||
}
|
||||
|
||||
void NotesApi::getNote(const int id, Note::NoteField exclude) {
|
||||
QUrl url = apiEndpointUrl(m_notesEndpoint + QString("/%1").arg(id));
|
||||
QStringList excludeFields;
|
||||
QList<Note::NoteField> noteFields = Note::noteFields();
|
||||
QFlags<Note::NoteField> flags(exclude);
|
||||
|
||||
for (int i = 0; i < noteFields.size(); ++i) {
|
||||
if (flags.testFlag(noteFields[i])) {
|
||||
excludeFields << Note::noteFieldName(noteFields[i]);
|
||||
}
|
||||
}
|
||||
if (!excludeFields.isEmpty())
|
||||
url.setQuery(QString(EXCLUDE_QUERY).append(excludeFields.join(",")));
|
||||
|
||||
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
|
||||
qDebug() << "GET" << url.toDisplayString();
|
||||
m_authenticatedRequest.setUrl(url);
|
||||
m_notesReplies << m_manager.get(m_authenticatedRequest);
|
||||
emit busyChanged(true);
|
||||
}
|
||||
}
|
||||
|
||||
void NotesApi::createNote(const Note ¬e) {
|
||||
QUrl url = apiEndpointUrl(m_notesEndpoint);
|
||||
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
|
||||
qDebug() << "POST" << url.toDisplayString();
|
||||
m_authenticatedRequest.setUrl(url);
|
||||
m_notesReplies << m_manager.post(m_authenticatedRequest, noteApiData(note));
|
||||
emit busyChanged(true);
|
||||
}
|
||||
}
|
||||
|
||||
void NotesApi::updateNote(const Note ¬e) {
|
||||
QUrl url = apiEndpointUrl(m_notesEndpoint + QString("/%1").arg(note.id()));
|
||||
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
|
||||
qDebug() << "PUT" << url.toDisplayString();
|
||||
m_authenticatedRequest.setUrl(url);
|
||||
m_notesReplies << m_manager.put(m_authenticatedRequest, noteApiData(note));
|
||||
emit busyChanged(true);
|
||||
}
|
||||
}
|
||||
|
||||
void NotesApi::deleteNote(const int id) {
|
||||
QUrl url = apiEndpointUrl(m_notesEndpoint + QString("/%1").arg(id));
|
||||
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
|
||||
qDebug() << "DELETE" << url.toDisplayString();
|
||||
m_authenticatedRequest.setUrl(url);
|
||||
m_notesReplies << m_manager.deleteResource(m_authenticatedRequest);
|
||||
emit busyChanged(true);
|
||||
}
|
||||
}
|
||||
|
||||
const QByteArray NotesApi::noteApiData(const Note ¬e) {
|
||||
QJsonObject json = note.toJsonObject();
|
||||
json.remove(Note::noteFieldName(Note::Id));
|
||||
if (note.modified() == 0)
|
||||
json.remove(Note::noteFieldName(Note::Modified));
|
||||
if (note.title().isNull())
|
||||
json.remove(Note::noteFieldName(Note::Title));
|
||||
if (note.category().isNull())
|
||||
json.remove(Note::noteFieldName(Note::Category));
|
||||
if (note.content().isNull())
|
||||
json.remove(Note::noteFieldName(Note::Content));
|
||||
json.remove(Note::noteFieldName(Note::Etag));
|
||||
json.remove(Note::noteFieldName(Note::Error));
|
||||
json.remove(Note::noteFieldName(Note::ErrorMessage));
|
||||
|
||||
return QJsonDocument(json).toJson(QJsonDocument::Compact);
|
||||
}
|
||||
|
||||
// TODO ab hier überarbeiten
|
||||
|
||||
void NotesApi::setVerifySsl(bool verify) {
|
||||
if (verify != (m_request.sslConfiguration().peerVerifyMode() == QSslSocket::VerifyPeer)) {
|
||||
m_request.sslConfiguration().setPeerVerifyMode(verify ? QSslSocket::VerifyPeer : QSslSocket::VerifyNone);
|
||||
emit sslVerifyChanged(verify);
|
||||
emit verifySslChanged(verify);
|
||||
}
|
||||
if (verify != (m_authenticatedRequest.sslConfiguration().peerVerifyMode() == QSslSocket::VerifyPeer)) {
|
||||
m_authenticatedRequest.sslConfiguration().setPeerVerifyMode(verify ? QSslSocket::VerifyPeer : QSslSocket::VerifyNone);
|
||||
emit sslVerifyChanged(verify);
|
||||
emit verifySslChanged(verify);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -164,17 +252,6 @@ void NotesApi::setPath(QString path) {
|
|||
}
|
||||
}
|
||||
|
||||
void NotesApi::setDataFile(const QString &dataFile) {
|
||||
if (dataFile != m_jsonFile.fileName()) {
|
||||
m_jsonFile.close();
|
||||
m_jsonFile.setFileName(dataFile);
|
||||
QFileInfo fileinfo(m_jsonFile);
|
||||
QDir filepath;
|
||||
filepath.mkpath(fileinfo.absolutePath());
|
||||
emit dataFileChanged(m_jsonFile.fileName());
|
||||
}
|
||||
}
|
||||
|
||||
bool NotesApi::getNcStatus() {
|
||||
QUrl url = apiEndpointUrl(m_statusEndpoint);
|
||||
qDebug() << "POST" << url.toDisplayString();
|
||||
|
@ -249,75 +326,6 @@ void NotesApi::verifyLogin(QString username, QString password) {
|
|||
}
|
||||
}
|
||||
|
||||
void NotesApi::getAllNotes(QStringList excludeFields) {
|
||||
QUrl url = apiEndpointUrl(m_notesEndpoint + "/notes");
|
||||
if (!excludeFields.isEmpty())
|
||||
url.setQuery(QString("exclude=").append(excludeFields.join(",")));
|
||||
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
|
||||
qDebug() << "GET" << url.toDisplayString();
|
||||
m_authenticatedRequest.setUrl(url);
|
||||
m_notesReplies << m_manager.get(m_authenticatedRequest);
|
||||
emit busyChanged(true);
|
||||
}
|
||||
}
|
||||
|
||||
void NotesApi::getNote(int noteId, QStringList excludeFields) {
|
||||
QUrl url = apiEndpointUrl(m_notesEndpoint + QString("/notes/%1").arg(noteId));
|
||||
if (!excludeFields.isEmpty())
|
||||
url.setQuery(QString("exclude=").append(excludeFields.join(",")));
|
||||
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
|
||||
qDebug() << "GET" << url.toDisplayString();
|
||||
m_authenticatedRequest.setUrl(url);
|
||||
m_notesReplies << m_manager.get(m_authenticatedRequest);
|
||||
emit busyChanged(true);
|
||||
}
|
||||
}
|
||||
|
||||
void NotesApi::createNote(QVariantMap fields) {
|
||||
// Update note in the model
|
||||
Note note(QJsonObject::fromVariantMap(fields));
|
||||
//mp_model->insertNote(note);
|
||||
|
||||
// Create note via the API
|
||||
QUrl url = apiEndpointUrl(m_notesEndpoint + "/notes");
|
||||
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
|
||||
qDebug() << "POST" << url.toDisplayString();
|
||||
m_authenticatedRequest.setUrl(url);
|
||||
m_notesReplies << m_manager.post(m_authenticatedRequest, note.toJsonDocument().toJson());
|
||||
emit busyChanged(true);
|
||||
}
|
||||
}
|
||||
|
||||
void NotesApi::updateNote(int noteId, QVariantMap fields) {
|
||||
// Update note in the model
|
||||
Note note(QJsonObject::fromVariantMap(fields));
|
||||
mp_model->insertNote(note);
|
||||
|
||||
// Update note on the server
|
||||
QUrl url = apiEndpointUrl(m_notesEndpoint + QString("/notes/%1").arg(noteId));
|
||||
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
|
||||
qDebug() << "PUT" << url.toDisplayString();
|
||||
m_authenticatedRequest.setUrl(url);
|
||||
m_notesReplies << m_manager.put(m_authenticatedRequest, note.toJsonDocument().toJson());
|
||||
emit busyChanged(true);
|
||||
}
|
||||
}
|
||||
|
||||
void NotesApi::deleteNote(int noteId) {
|
||||
// Remove note from the model
|
||||
mp_model->removeNote(noteId);
|
||||
|
||||
// Remove note from the server
|
||||
QUrl url = apiEndpointUrl(m_notesEndpoint + QString("/notes/%1").arg(noteId));
|
||||
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
|
||||
qDebug() << "DELETE" << url.toDisplayString();
|
||||
m_authenticatedRequest.setUrl(url);
|
||||
m_notesReplies << m_manager.deleteResource(m_authenticatedRequest);
|
||||
emit busyChanged(true);
|
||||
}
|
||||
mp_model->removeNote(noteId);
|
||||
}
|
||||
|
||||
const QString NotesApi::errorMessage(ErrorCodes error) const {
|
||||
QString message;
|
||||
switch (error) {
|
||||
|
@ -407,12 +415,10 @@ void NotesApi::replyFinished(QNetworkReply *reply) {
|
|||
}
|
||||
else if (m_notesReplies.contains(reply)) {
|
||||
qDebug() << "Notes reply";
|
||||
if (mp_model) {
|
||||
if (mp_model->fromJsonDocument(json)) {
|
||||
m_lastSync = QDateTime::currentDateTimeUtc();
|
||||
emit lastSyncChanged(m_lastSync);
|
||||
}
|
||||
}
|
||||
if (json.isArray())
|
||||
updateApiNotes(json.array());
|
||||
else if (json.isObject())
|
||||
updateApiNote(json.object());
|
||||
m_notesReplies.removeOne(reply);
|
||||
emit busyChanged(busy());
|
||||
}
|
||||
|
@ -463,18 +469,6 @@ void NotesApi::sslError(QNetworkReply *reply, const QList<QSslError> &errors) {
|
|||
emit error(SslHandshakeError);
|
||||
}
|
||||
|
||||
void NotesApi::saveToFile(QModelIndex, QModelIndex, QVector<int>) {
|
||||
if (m_jsonFile.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) {
|
||||
//qDebug() << "Writing data to file" << m_jsonFile.fileName();
|
||||
QByteArray data = mp_model->toJsonDocument().toJson();
|
||||
if (m_jsonFile.write(data) < data.size())
|
||||
emit error(LocalFileWriteError);
|
||||
m_jsonFile.close();
|
||||
}
|
||||
else
|
||||
emit error(LocalFileWriteError);
|
||||
}
|
||||
|
||||
QUrl NotesApi::apiEndpointUrl(const QString endpoint) const {
|
||||
QUrl url = server();
|
||||
url.setPath(url.path() + endpoint);
|
||||
|
@ -593,3 +587,16 @@ void NotesApi::setLoginStatus(LoginStatus status, bool *changed) {
|
|||
emit loginStatusChanged(m_loginStatus);
|
||||
}
|
||||
}
|
||||
|
||||
void NotesApi::updateApiNotes(const QJsonArray &json) {
|
||||
for (int i = 0; i < json.size(); ++i) {
|
||||
if (json[i].isObject())
|
||||
updateApiNote(json[i].toObject());
|
||||
}
|
||||
}
|
||||
|
||||
void NotesApi::updateApiNote(const QJsonObject &json) {
|
||||
Note note(json);
|
||||
if (!note.error())
|
||||
emit noteUpdated(note);
|
||||
}
|
||||
|
|
|
@ -8,31 +8,22 @@
|
|||
#include <QFile>
|
||||
#include <QTimer>
|
||||
#include <QDebug>
|
||||
|
||||
#include "notesinterface.h"
|
||||
#include "notesmodel.h"
|
||||
|
||||
#define STATUS_ENDPOINT "/status.php"
|
||||
#define LOGIN_ENDPOINT "/index.php/login/v2"
|
||||
#define NOTES_ENDPOINT "/index.php/apps/notes/api/v0.2"
|
||||
#define NOTES_ENDPOINT "/index.php/apps/notes/api/v0.2/notes"
|
||||
#define OCS_ENDPOINT "/ocs/v1.php/cloud"
|
||||
#define EXCLUDE_QUERY "exclude="
|
||||
#define POLL_INTERVALL 5000
|
||||
|
||||
/*
|
||||
m_noteFieldNames[Id] = "id";
|
||||
m_noteFieldNames[Modified] = "modified";
|
||||
m_noteFieldNames[Title] = "title";
|
||||
m_noteFieldNames[Category] = "category";
|
||||
m_noteFieldNames[Content] = "content";
|
||||
m_noteFieldNames[Favorite] = "favorite";
|
||||
m_noteFieldNames[Etag] = "etag";
|
||||
m_noteFieldNames[Error] = "error";
|
||||
m_noteFieldNames[ErrorMessage] = "errorMessage";
|
||||
*/
|
||||
|
||||
class NotesApi : public QObject
|
||||
class NotesApi : public NotesInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(bool sslVerify READ sslVerify WRITE setSslVerify NOTIFY sslVerifyChanged)
|
||||
Q_PROPERTY(bool verifySsl READ verifySsl() WRITE setVerifySsl NOTIFY verifySslChanged)
|
||||
Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged)
|
||||
Q_PROPERTY(bool urlValid READ urlValid NOTIFY urlValidChanged)
|
||||
Q_PROPERTY(QString server READ server WRITE setServer NOTIFY serverChanged)
|
||||
|
@ -42,7 +33,6 @@ class NotesApi : public QObject
|
|||
Q_PROPERTY(QString username READ username WRITE setUsername NOTIFY usernameChanged)
|
||||
Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged)
|
||||
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
|
||||
Q_PROPERTY(QString dataFile READ dataFile WRITE setDataFile NOTIFY dataFileChanged)
|
||||
Q_PROPERTY(bool networkAccessible READ networkAccessible NOTIFY networkAccessibleChanged)
|
||||
Q_PROPERTY(QDateTime lastSync READ lastSync NOTIFY lastSyncChanged)
|
||||
Q_PROPERTY(bool busy READ busy NOTIFY busyChanged)
|
||||
|
@ -68,8 +58,8 @@ public:
|
|||
QObject *parent = nullptr);
|
||||
virtual ~NotesApi();
|
||||
|
||||
bool sslVerify() const { return m_authenticatedRequest.sslConfiguration().peerVerifyMode() == QSslSocket::VerifyPeer; }
|
||||
void setSslVerify(bool verify);
|
||||
bool verifySsl() const { return m_authenticatedRequest.sslConfiguration().peerVerifyMode() == QSslSocket::VerifyPeer; }
|
||||
void setVerifySsl(bool verify);
|
||||
|
||||
QUrl url() const { return m_url; }
|
||||
void setUrl(QUrl url);
|
||||
|
@ -97,9 +87,6 @@ public:
|
|||
QString path() const { return m_url.path(); }
|
||||
void setPath(QString path);
|
||||
|
||||
QString dataFile() const { return m_jsonFile.fileName(); }
|
||||
void setDataFile(const QString &dataFile);
|
||||
|
||||
bool networkAccessible() const { return m_manager.networkAccessible() == QNetworkAccessManager::Accessible; }
|
||||
|
||||
QDateTime lastSync() const { return m_lastSync; }
|
||||
|
@ -142,12 +129,6 @@ public:
|
|||
Q_INVOKABLE bool initiateFlowV2Login();
|
||||
Q_INVOKABLE void abortFlowV2Login();
|
||||
Q_INVOKABLE void verifyLogin(QString username = QString(), QString password = QString());
|
||||
Q_INVOKABLE void getAllNotes(QStringList excludeFields = QStringList());
|
||||
Q_INVOKABLE void getNote(int noteId, QStringList excludeFields = QStringList());
|
||||
Q_INVOKABLE void createNote(QVariantMap fields = QVariantMap());
|
||||
Q_INVOKABLE void updateNote(int noteId, QVariantMap fields = QVariantMap());
|
||||
Q_INVOKABLE void deleteNote(int noteId);
|
||||
Q_INVOKABLE NotesProxyModel* model() const { return mp_modelProxy; }
|
||||
|
||||
enum ErrorCodes {
|
||||
NoError,
|
||||
|
@ -161,8 +142,18 @@ public:
|
|||
Q_ENUM(ErrorCodes)
|
||||
Q_INVOKABLE const QString errorMessage(ErrorCodes error) const;
|
||||
|
||||
QString account() const { return m_account; }
|
||||
void setAccount(const QString& account);
|
||||
|
||||
public slots:
|
||||
Q_INVOKABLE void getAllNotes(Note::NoteField exclude = Note::None);
|
||||
Q_INVOKABLE void getNote(const int id, Note::NoteField exclude = Note::None);
|
||||
Q_INVOKABLE void createNote(const Note& note);
|
||||
Q_INVOKABLE void updateNote(const Note& note);
|
||||
Q_INVOKABLE void deleteNote(const int id);
|
||||
|
||||
signals:
|
||||
void sslVerifyChanged(bool verify);
|
||||
void verifySslChanged(bool verify);
|
||||
void urlChanged(QUrl url);
|
||||
void urlValidChanged(bool valid);
|
||||
void serverChanged(QString server);
|
||||
|
@ -200,17 +191,16 @@ private slots:
|
|||
void replyFinished(QNetworkReply* reply);
|
||||
void pollLoginUrl();
|
||||
void sslError(QNetworkReply* reply, const QList<QSslError> &errors);
|
||||
void saveToFile(QModelIndex,QModelIndex,QVector<int>);
|
||||
|
||||
private:
|
||||
QString m_account;
|
||||
static const QByteArray noteApiData(const Note& note);
|
||||
|
||||
QUrl m_url;
|
||||
QNetworkAccessManager m_manager;
|
||||
QNetworkRequest m_request;
|
||||
QNetworkRequest m_authenticatedRequest;
|
||||
QNetworkRequest m_ocsRequest;
|
||||
QFile m_jsonFile;
|
||||
NotesModel* mp_model;
|
||||
NotesProxyModel* mp_modelProxy;
|
||||
QUrl apiEndpointUrl(const QString endpoint) const;
|
||||
|
||||
// Nextcloud status.php
|
||||
|
@ -248,6 +238,8 @@ private:
|
|||
// Nextcloud Notes API - https://github.com/nextcloud/notes/wiki/Notes-0.2
|
||||
const QString m_notesEndpoint;
|
||||
QVector<QNetworkReply*> m_notesReplies;
|
||||
void updateApiNotes(const QJsonArray& json);
|
||||
void updateApiNote(const QJsonObject& json);
|
||||
QDateTime m_lastSync;
|
||||
};
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
#define NOTESINTERFACE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QMap>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "note.h"
|
||||
|
||||
class NotesInterface : public QObject
|
||||
{
|
||||
|
@ -13,50 +13,22 @@ class NotesInterface : public QObject
|
|||
Q_CLASSINFO("url", "https://github.com/scharel/harbour-nextcloudnotes")
|
||||
|
||||
public:
|
||||
explicit NotesInterface(QObject *parent = nullptr) : QObject(parent) {
|
||||
m_noteFieldNames = QMap<NoteField, QString>( { {Id, "id"}, {Modified, "modified"}, {Title, "title"}, {Category, "category"}, {Content, "content"}, {Favorite, "favorite"}, {Etag, "etag"}, {Error, "error"}, {ErrorMessage, "errorMessage"} } );
|
||||
}
|
||||
explicit NotesInterface(QObject *parent = nullptr) : QObject(parent) { }
|
||||
|
||||
virtual QString account() const = 0;
|
||||
virtual void setAccount(const QString& account) = 0;
|
||||
|
||||
enum NoteField {
|
||||
None = 0x0000,
|
||||
Id = 0x0001,
|
||||
Modified = 0x0002,
|
||||
Title = 0x0004,
|
||||
Category = 0x0008,
|
||||
Content = 0x0010,
|
||||
Favorite = 0x0020,
|
||||
Etag = 0x0040,
|
||||
Error = 0x0080,
|
||||
ErrorMessage = 0x0100
|
||||
};
|
||||
Q_DECLARE_FLAGS(NoteFields, NoteField)
|
||||
Q_FLAG(NoteFields)
|
||||
Q_INVOKABLE QList<NoteField> noteFields() const {
|
||||
return m_noteFieldNames.keys();
|
||||
}
|
||||
Q_INVOKABLE QString noteFieldName(NoteField field) {
|
||||
return m_noteFieldNames[field];
|
||||
}
|
||||
|
||||
Q_INVOKABLE virtual void getAllNotes(NoteField exclude = None) = 0;
|
||||
Q_INVOKABLE virtual void getNote(const int id, NoteField exclude = None) = 0;
|
||||
Q_INVOKABLE virtual void createNote(const QJsonObject& note) = 0;
|
||||
Q_INVOKABLE virtual void updateNote(const QJsonObject& note) = 0;
|
||||
public slots:
|
||||
Q_INVOKABLE virtual void getAllNotes(Note::NoteField exclude = Note::None) = 0;
|
||||
Q_INVOKABLE virtual void getNote(const int id, Note::NoteField exclude = Note::None) = 0;
|
||||
Q_INVOKABLE virtual void createNote(const Note& note) = 0;
|
||||
Q_INVOKABLE virtual void updateNote(const Note& note) = 0;
|
||||
Q_INVOKABLE virtual void deleteNote(const int id) = 0;
|
||||
|
||||
signals:
|
||||
void accountChanged(const QString account);
|
||||
void noteUpdated(const QJsonObject note);
|
||||
void noteDeleted(const int deletedNoteId);
|
||||
|
||||
public slots:
|
||||
|
||||
protected:
|
||||
QMap<NoteField, QString> m_noteFieldNames;
|
||||
|
||||
void accountChanged(const QString& account);
|
||||
void noteUpdated(const Note& note);
|
||||
void noteDeleted(const int id);
|
||||
};
|
||||
|
||||
#endif // NOTESINTERFACE_H
|
||||
|
|
|
@ -41,10 +41,6 @@ public:
|
|||
bool fromJsonDocument(const QJsonDocument &jdoc);
|
||||
QJsonDocument toJsonDocument() const;
|
||||
|
||||
int insertNote(const Note ¬e);
|
||||
bool removeNote(const Note ¬e);
|
||||
bool removeNote(int id);
|
||||
|
||||
enum NoteRoles {
|
||||
IdRole = Qt::UserRole,
|
||||
ModifiedRole = Qt::UserRole + 1,
|
||||
|
@ -67,6 +63,12 @@ public:
|
|||
QMap<int, QVariant> itemData(const QModelIndex &index) const;
|
||||
virtual bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles);
|
||||
|
||||
public slots:
|
||||
int insertNote(const Note ¬e);
|
||||
bool removeNote(const Note ¬e);
|
||||
bool removeNote(int id);
|
||||
|
||||
|
||||
protected:
|
||||
//void addNote(const QJsonValue ¬e);
|
||||
QVector<int> ids() const;
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
#include "notesstore.h"
|
||||
|
||||
#include "note.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
const QString NotesStore::m_suffix = "json";
|
||||
|
||||
NotesStore::NotesStore(QString directory, QObject *parent) : NotesInterface(parent)
|
||||
{
|
||||
m_dir.setCurrent(directory);
|
||||
m_dir.setPath("");
|
||||
m_dir.setFilter(QDir::Files);
|
||||
m_dir.setNameFilters( { "*.json" } );
|
||||
m_dir.setNameFilters( { "*." + m_suffix } );
|
||||
}
|
||||
|
||||
NotesStore::~NotesStore() {
|
||||
|
@ -39,7 +39,7 @@ void NotesStore::setAccount(const QString& account) {
|
|||
}
|
||||
}
|
||||
|
||||
void NotesStore::getAllNotes(NoteField exclude) {
|
||||
void NotesStore::getAllNotes(Note::NoteField exclude) {
|
||||
QFileInfoList files = m_dir.entryInfoList();
|
||||
for (int i = 0; i < files.size(); ++i) {
|
||||
bool ok;
|
||||
|
@ -50,30 +50,30 @@ void NotesStore::getAllNotes(NoteField exclude) {
|
|||
}
|
||||
}
|
||||
|
||||
void NotesStore::getNote(const int id, NoteField exclude) {
|
||||
void NotesStore::getNote(const int id, Note::NoteField exclude) {
|
||||
if (id >= 0) {
|
||||
QJsonObject file = readNoteFile(id, exclude);
|
||||
if (!file.empty())
|
||||
emit noteUpdated(file);
|
||||
Note note = readNoteFile(id, exclude);
|
||||
if (note.isValid())
|
||||
emit noteUpdated(note);
|
||||
}
|
||||
}
|
||||
|
||||
void NotesStore::createNote(const QJsonObject& note) {
|
||||
if (Note::id(note) < 0) {
|
||||
void NotesStore::createNote(const Note& note) {
|
||||
if (!note.isValid()) {
|
||||
// TODO probably crate files with an '.json.<NUMBER>.new' extension
|
||||
qDebug() << "Creating notes without the server API is not supported yet!";
|
||||
}
|
||||
else if (!noteFileExists(Note::id(note))) {
|
||||
else if (!noteFileExists(note.id())) {
|
||||
if (writeNoteFile(note)) {
|
||||
emit noteUpdated(note);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NotesStore::updateNote(const QJsonObject& note) {
|
||||
if (Note::id(note) >= 0) {
|
||||
QJsonObject file = readNoteFile(Note::id(note));
|
||||
if (!Note(file).equal(note)) {
|
||||
void NotesStore::updateNote(const Note& note) {
|
||||
if (note.isValid()) {
|
||||
Note file = readNoteFile(note.id());
|
||||
if (!file.equal(note)) {
|
||||
if (writeNoteFile(note)) {
|
||||
emit noteUpdated(note);
|
||||
}
|
||||
|
@ -88,25 +88,24 @@ void NotesStore::deleteNote(const int id) {
|
|||
}
|
||||
|
||||
bool NotesStore::noteFileExists(const int id) const {
|
||||
QFileInfo fileinfo(m_dir, QString("%1.json").arg(id));
|
||||
QFileInfo fileinfo(m_dir, QString("%1.%2").arg(id).arg(m_suffix));
|
||||
return fileinfo.exists();
|
||||
}
|
||||
|
||||
QJsonObject NotesStore::readNoteFile(const int id, NoteField exclude) const {
|
||||
Note NotesStore::readNoteFile(const int id, Note::NoteField exclude) const {
|
||||
QJsonObject json;
|
||||
QFileInfo fileinfo(m_dir, QString("%1.json").arg(id));
|
||||
QFileInfo fileinfo(m_dir, QString("%1.%2").arg(id).arg(m_suffix));
|
||||
QFile file(fileinfo.filePath());
|
||||
if (file.exists()) {
|
||||
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
QByteArray data = file.readAll();
|
||||
json = QJsonDocument::fromJson(data).object();
|
||||
file.close();
|
||||
QFlags<NoteField> flags(exclude);
|
||||
QMapIterator<NoteField, QString> fields(m_noteFieldNames);
|
||||
while (fields.hasNext()) {
|
||||
fields.next();
|
||||
if (flags.testFlag(fields.key())) {
|
||||
json.remove(fields.value());
|
||||
QList<Note::NoteField> noteFields = Note::noteFields();
|
||||
QFlags<Note::NoteField> flags(exclude);
|
||||
for (int i = 0; i < noteFields.size(); ++i) {
|
||||
if (flags.testFlag(noteFields[i])) {
|
||||
json.remove(Note::noteFieldName(noteFields[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -114,10 +113,11 @@ QJsonObject NotesStore::readNoteFile(const int id, NoteField exclude) const {
|
|||
return json;
|
||||
}
|
||||
|
||||
bool NotesStore::writeNoteFile(const QJsonObject ¬e) const {
|
||||
bool NotesStore::writeNoteFile(const Note ¬e) const {
|
||||
bool success = false;
|
||||
QJsonDocument json(note);
|
||||
QFileInfo fileinfo(m_dir, QString("%1.json").arg(Note::id(note)));
|
||||
if (!account().isEmpty()) {
|
||||
QJsonDocument json = note.toJsonDocument();
|
||||
QFileInfo fileinfo(m_dir, QString("%1.%2").arg(note.id()).arg(m_suffix));
|
||||
QFile file(fileinfo.filePath());
|
||||
if (file.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) {
|
||||
QByteArray data = json.toJson();
|
||||
|
@ -126,17 +126,20 @@ bool NotesStore::writeNoteFile(const QJsonObject ¬e) const {
|
|||
}
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool NotesStore::removeNoteFile(const int id) const {
|
||||
bool success = false;
|
||||
QFileInfo fileinfo(m_dir, QString("%1.json").arg(id));
|
||||
if (!account().isEmpty()) {
|
||||
QFileInfo fileinfo(m_dir, QString("%1.%2").arg(id).arg(m_suffix));
|
||||
QFile file(fileinfo.filePath());
|
||||
if (file.exists()) {
|
||||
if (file.remove()) {
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
#ifndef NOTESSTORE_H
|
||||
#define NOTESSTORE_H
|
||||
|
||||
#include "notesinterface.h"
|
||||
#include <QObject>
|
||||
#include <QStandardPaths>
|
||||
#include <QDir>
|
||||
|
||||
#include "notesinterface.h"
|
||||
|
||||
class NotesStore : public NotesInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -19,22 +20,20 @@ public:
|
|||
QString account() const;
|
||||
void setAccount(const QString& account);
|
||||
|
||||
Q_INVOKABLE void getAllNotes(NoteField exclude = None);
|
||||
Q_INVOKABLE void getNote(const int id, NoteField exclude = None);
|
||||
Q_INVOKABLE void createNote(const QJsonObject& note);
|
||||
Q_INVOKABLE void updateNote(const QJsonObject& note);
|
||||
Q_INVOKABLE void deleteNote(const int id);
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
Q_INVOKABLE void getAllNotes(Note::NoteField exclude = Note::None);
|
||||
Q_INVOKABLE void getNote(const int id, Note::NoteField exclude = Note::None);
|
||||
Q_INVOKABLE void createNote(const Note& note);
|
||||
Q_INVOKABLE void updateNote(const Note& note);
|
||||
Q_INVOKABLE void deleteNote(const int id);
|
||||
|
||||
private:
|
||||
QDir m_dir;
|
||||
const static QString m_suffix;
|
||||
|
||||
bool noteFileExists(const int id) const;
|
||||
QJsonObject readNoteFile(const int id, NoteField exclude = None) const;
|
||||
bool writeNoteFile(const QJsonObject& note) const;
|
||||
Note readNoteFile(const int id, Note::NoteField exclude = Note::None) const;
|
||||
bool writeNoteFile(const Note& note) const;
|
||||
bool removeNoteFile(const int id) const;
|
||||
};
|
||||
|
||||
|
|
|
@ -245,12 +245,12 @@
|
|||
<context>
|
||||
<name>Note</name>
|
||||
<message>
|
||||
<location filename="../src/note.cpp" line="245"/>
|
||||
<location filename="../src/note.cpp" line="306"/>
|
||||
<source>Today</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/note.cpp" line="247"/>
|
||||
<location filename="../src/note.cpp" line="308"/>
|
||||
<source>Yesterday</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -311,37 +311,37 @@
|
|||
<context>
|
||||
<name>NotesApi</name>
|
||||
<message>
|
||||
<location filename="../src/notesapi.cpp" line="327"/>
|
||||
<location filename="../src/notesapi.cpp" line="335"/>
|
||||
<source>No network connection available</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/notesapi.cpp" line="330"/>
|
||||
<location filename="../src/notesapi.cpp" line="338"/>
|
||||
<source>Failed to communicate with the Nextcloud server</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/notesapi.cpp" line="333"/>
|
||||
<location filename="../src/notesapi.cpp" line="341"/>
|
||||
<source>An error happened while reading from the local storage</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/notesapi.cpp" line="336"/>
|
||||
<location filename="../src/notesapi.cpp" line="344"/>
|
||||
<source>An error happened while writing to the local storage</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/notesapi.cpp" line="339"/>
|
||||
<location filename="../src/notesapi.cpp" line="347"/>
|
||||
<source>An error occured while establishing an encrypted connection</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/notesapi.cpp" line="342"/>
|
||||
<location filename="../src/notesapi.cpp" line="350"/>
|
||||
<source>Could not authenticate to the Nextcloud instance</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/notesapi.cpp" line="345"/>
|
||||
<location filename="../src/notesapi.cpp" line="353"/>
|
||||
<source>Unknown error</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -349,97 +349,97 @@
|
|||
<context>
|
||||
<name>NotesPage</name>
|
||||
<message>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="55"/>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="54"/>
|
||||
<source>Settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="59"/>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="58"/>
|
||||
<source>Add note</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="64"/>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="63"/>
|
||||
<source>Reload</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="64"/>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="63"/>
|
||||
<source>Updating...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="70"/>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="69"/>
|
||||
<source>Last update</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="73"/>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="72"/>
|
||||
<source>never</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="83"/>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="82"/>
|
||||
<source>Nextcloud Notes</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="224"/>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="223"/>
|
||||
<source>Modified</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="227"/>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="226"/>
|
||||
<source>Delete</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="229"/>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="228"/>
|
||||
<source>Deleting note</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="259"/>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="258"/>
|
||||
<source>Loading notes...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="265"/>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="264"/>
|
||||
<source>No account yet</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="266"/>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="265"/>
|
||||
<source>Got to the settings to add an account</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="272"/>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="271"/>
|
||||
<source>No notes yet</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="273"/>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="272"/>
|
||||
<source>Pull down to add a note</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="279"/>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="278"/>
|
||||
<source>No result</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="280"/>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="279"/>
|
||||
<source>Try another query</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="286"/>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="285"/>
|
||||
<source>An error occurred</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="297"/>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="296"/>
|
||||
<source>Open the settings to configure your Nextcloud accounts</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
|
Loading…
Reference in a new issue