From 4ff667e3d16d852acb19a87aff863625b6595e54 Mon Sep 17 00:00:00 2001 From: Scharel Clemens Date: Mon, 13 Apr 2020 19:13:14 +0200 Subject: [PATCH] Improved communication between the modules. Offline mode seems to work fine now. Some kind of synchronisation of offline changes is now needed --- qml/harbour-nextcloudnotes.qml | 40 ++++- qml/pages/NotesPage.qml | 1 + src/harbour-nextcloudnotes.cpp | 16 +- src/note.cpp | 22 +++ src/note.h | 1 + src/notesapi.cpp | 115 +++++---------- src/notesapi.h | 20 ++- src/notesinterface.h | 25 ++-- src/notesmodel.cpp | 23 +-- src/notesmodel.h | 4 +- src/notesstore.cpp | 170 ++++++++++++++-------- src/notesstore.h | 34 +++-- translations/harbour-nextcloudnotes-de.ts | 47 +++++- translations/harbour-nextcloudnotes-sv.ts | 45 +++++- translations/harbour-nextcloudnotes.ts | 93 +++++++++--- 15 files changed, 432 insertions(+), 224 deletions(-) diff --git a/qml/harbour-nextcloudnotes.qml b/qml/harbour-nextcloudnotes.qml index e631f10..8e52599 100644 --- a/qml/harbour-nextcloudnotes.qml +++ b/qml/harbour-nextcloudnotes.qml @@ -129,9 +129,16 @@ ApplicationWindow } Notification { - id: errorNotification + id: storeErrorNotification appName: offlineNotification.appName - summary: qsTr("Error") + summary: qsTr("File error") + Component.onDestruction: close() + } + + Notification { + id: apiErrorNotification + appName: offlineNotification.appName + summary: qsTr("API error") Component.onDestruction: close() } @@ -157,6 +164,19 @@ ApplicationWindow } } + Connections { + target: notesStore + + onNoteError: { + storeErrorNotification.close() + if (error) { + console.log("Notes Store error (" + error + "): " + notesStore.errorMessage(error)) + storeErrorNotification.body = notesStore.errorMessage(error) + storeErrorNotification.publish() + } + } + } + Connections { target: notesApi @@ -164,18 +184,24 @@ ApplicationWindow console.log("Device is " + (accessible ? "online" : "offline")) accessible ? offlineNotification.close(Notification.Closed) : offlineNotification.publish() } - onError: { + onNoteError: { + apiErrorNotification.close() if (error) - console.log("Error (" + error + "): " + notesApi.errorMessage(error)) - errorNotification.close() + console.log("Notes API error (" + error + "): " + notesApi.errorMessage(error)) if (error && notesApi.networkAccessible) { - errorNotification.body = notesApi.errorMessage(error) - errorNotification.publish() + apiErrorNotification.body = notesApi.errorMessage(error) + apiErrorNotification.publish() } } onLastSyncChanged: account.update = lastSync } + Component.onDestruction: { + offlineNotification.close() + storeErrorNotification.close() + apiErrorNotification.close() + } + initialPage: Component { NotesPage { } } cover: Qt.resolvedUrl("cover/CoverPage.qml") allowedOrientations: defaultAllowedOrientations diff --git a/qml/pages/NotesPage.qml b/qml/pages/NotesPage.qml index dbec369..e3649bd 100644 --- a/qml/pages/NotesPage.qml +++ b/qml/pages/NotesPage.qml @@ -120,6 +120,7 @@ Page { icon.source: (favorite ? "image://theme/icon-m-favorite-selected?" : "image://theme/icon-m-favorite?") + (note.highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor) onClicked: { + console.log("Setting favoirte: " + !favorite) notesStore.updateNote(id, {'favorite': !favorite, 'modified': new Date().valueOf() / 1000 } ) notesApi.updateNote(id, {'favorite': !favorite, 'modified': new Date().valueOf() / 1000 } ) } diff --git a/src/harbour-nextcloudnotes.cpp b/src/harbour-nextcloudnotes.cpp index eef617c..8e1e0cd 100644 --- a/src/harbour-nextcloudnotes.cpp +++ b/src/harbour-nextcloudnotes.cpp @@ -30,15 +30,15 @@ int main(int argc, char *argv[]) NotesStore* notesStore = new NotesStore; NotesApi* notesApi = new NotesApi; - QObject::connect(notesApi, SIGNAL(noteUpdated(Note)), notesStore, SLOT(updateNote(Note))); + QObject::connect(notesApi, SIGNAL(noteUpdated(int, QJsonObject)), notesStore, SLOT(updateNote(int, QJsonObject))); //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(noteUpdated(int, QJsonObject)), notesModel, SLOT(insertNote(int, QJsonObject))); 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))); + //QObject::connect(notesApi, SIGNAL(noteUpdated(Note)), notesModel, SLOT(insertNote(Note))); + //QObject::connect(notesApi, SIGNAL(noteDeleted(int)), notesModel, SLOT(removeNote(int))); QQuickView* view = SailfishApp::createView(); #ifdef QT_DEBUG @@ -54,15 +54,15 @@ int main(int argc, char *argv[]) view->show(); 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(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(noteUpdated(int, QJsonObject)), notesModel, SLOT(insertNote(int, QJsonObject))); //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))); + QObject::disconnect(notesApi, SIGNAL(noteUpdated(int, QJsonObject)), notesStore, SLOT(updateNote(int, QJsonObject))); notesApi->deleteLater(); notesStore->deleteLater(); diff --git a/src/note.cpp b/src/note.cpp index ba24455..6741bf1 100644 --- a/src/note.cpp +++ b/src/note.cpp @@ -70,6 +70,28 @@ Note& Note::operator =(const QJsonObject& note) { return *this; } +Note& Note::operator <<(const QJsonObject& note) { + if (note.contains(Note::noteFieldName(Note::Id))) + setId(id(note)); + if (note.contains(Note::noteFieldName(Note::Modified))) + setModified(modified(note)); + if (note.contains(Note::noteFieldName(Note::Title))) + setTitle(title(note)); + if (note.contains(Note::noteFieldName(Note::Category))) + setCategory(category(note)); + if (note.contains(Note::noteFieldName(Note::Content))) + setContent(content(note)); + if (note.contains(Note::noteFieldName(Note::Favorite))) + setFavorite(favorite(note)); + if (note.contains(Note::noteFieldName(Note::Etag))) + setEtag(etag(note)); + if (note.contains(Note::noteFieldName(Note::Error))) + setError(error(note)); + if (note.contains(Note::noteFieldName(Note::ErrorMessage))) + setErrorMessage(errorMessage(note)); + return *this; +} + bool Note::operator ==(const Note& note) const { return id() == note.id(); } diff --git a/src/note.h b/src/note.h index 07607b0..a070b8a 100644 --- a/src/note.h +++ b/src/note.h @@ -19,6 +19,7 @@ public: Note& operator =(const Note& note); Note& operator =(const QJsonObject& note); + Note& operator <<(const QJsonObject& note); bool operator ==(const Note& note) const; bool operator ==(const QJsonObject& note) const; bool equal(const Note& note) const; diff --git a/src/notesapi.cpp b/src/notesapi.cpp index 47b9de3..9907d20 100644 --- a/src/notesapi.cpp +++ b/src/notesapi.cpp @@ -50,24 +50,12 @@ void NotesApi::setAccount(const QString &account) { } } -void NotesApi::getAllNotes(const QStringList exclude) { - getAllNotes(Note::noteFieldsFromStringList(exclude)); -} - -void NotesApi::getAllNotes(Note::NoteField exclude) { +void NotesApi::getAllNotes(const QStringList& exclude) { qDebug() << "Getting all notes"; QUrl url = apiEndpointUrl(m_notesEndpoint); - QStringList excludeFields; - QList noteFields = Note::noteFields(); - QFlags 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 (!exclude.isEmpty()) + url.setQuery(QString(EXCLUDE_QUERY).append(exclude.join(","))); if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { qDebug() << "GET" << url.toDisplayString(); @@ -77,24 +65,11 @@ void NotesApi::getAllNotes(Note::NoteField exclude) { } } -void NotesApi::getNote(const int id, const QStringList exclude) { - getNote(id, Note::noteFieldsFromStringList(exclude)); -} - -void NotesApi::getNote(const int id, Note::NoteField exclude) { +void NotesApi::getNote(const int id, const QStringList& exclude) { qDebug() << "Getting note: " << id; QUrl url = apiEndpointUrl(m_notesEndpoint + QString("/%1").arg(id)); - QStringList excludeFields; - QList noteFields = Note::noteFields(); - QFlags 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 (!exclude.isEmpty()) + url.setQuery(QString(EXCLUDE_QUERY).append(exclude.join(","))); if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { qDebug() << "GET" << url.toDisplayString(); @@ -104,34 +79,24 @@ void NotesApi::getNote(const int id, Note::NoteField exclude) { } } -void NotesApi::createNote(const QVariantMap ¬e) { - createNote(Note(QJsonObject::fromVariantMap(note))); -} - -void NotesApi::createNote(const Note ¬e) { +void NotesApi::createNote(const QJsonObject& note) { qDebug() << "Creating note"; QUrl url = apiEndpointUrl(m_notesEndpoint); if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { qDebug() << "POST" << url.toDisplayString(); m_authenticatedRequest.setUrl(url); - m_createNoteReplies << m_manager.post(m_authenticatedRequest, noteApiData(note)); + m_createNoteReplies << m_manager.post(m_authenticatedRequest, QJsonDocument(note).toJson()); emit busyChanged(true); } } -void NotesApi::updateNote(const int id, const QVariantMap ¬e) { - Note newNote(QJsonObject::fromVariantMap(note)); - newNote.setId(id); - updateNote(newNote); -} - -void NotesApi::updateNote(const Note ¬e) { - qDebug() << "Updating note: " << note.id(); - QUrl url = apiEndpointUrl(m_notesEndpoint + QString("/%1").arg(note.id())); - if (note.isValid() && url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { +void NotesApi::updateNote(const int id, const QJsonObject& note) { + qDebug() << "Updating note: " << id; + QUrl url = apiEndpointUrl(m_notesEndpoint + QString("/%1").arg(id)); + if (id >= 0 && url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { qDebug() << "PUT" << url.toDisplayString(); m_authenticatedRequest.setUrl(url); - m_updateNoteReplies << m_manager.put(m_authenticatedRequest, noteApiData(note)); + m_updateNoteReplies << m_manager.put(m_authenticatedRequest, QJsonDocument(note).toJson()); emit busyChanged(true); } } @@ -159,26 +124,6 @@ bool NotesApi::busy() const { m_ocsReplies.empty()); } -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); @@ -359,10 +304,11 @@ void NotesApi::verifyLogin(QString username, QString password) { } } -const QString NotesApi::errorMessage(ErrorCodes error) const { +const QString NotesApi::errorMessage(int error) const { QString message; switch (error) { case NoError: + message = tr("No error"); break; case NoConnectionError: message = tr("No network connection available"); @@ -393,7 +339,7 @@ void NotesApi::requireAuthentication(QNetworkReply *reply, QAuthenticator *authe authenticator->setPassword(password()); } else - emit error(AuthenticationError); + emit noteError(AuthenticationError); } void NotesApi::onNetworkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible) { @@ -405,13 +351,13 @@ void NotesApi::replyFinished(QNetworkReply *reply) { qDebug() << reply->error() << reply->errorString(); if (reply->error() == QNetworkReply::NoError) - emit error(NoError); + emit noteError(NoError); else if (reply->error() == QNetworkReply::AuthenticationRequiredError) - emit error(AuthenticationError); + emit noteError(AuthenticationError); else if (reply->error() == QNetworkReply::ContentNotFoundError && m_pollReplies.contains(reply)) - emit error(NoError); + emit noteError(NoError); else - emit error(CommunicationError); + emit noteError(CommunicationError); QByteArray data = reply->readAll(); QJsonDocument json = QJsonDocument::fromJson(data); @@ -435,9 +381,14 @@ void NotesApi::replyFinished(QNetworkReply *reply) { m_createNoteReplies.removeOne(reply); } else if (m_updateNoteReplies.contains(reply)) { - qDebug() << "Update note reply"; - if (reply->error() == QNetworkReply::NoError && json.isObject()) - updateApiNote(json.object()); + qDebug() << "Update note reply";bool ok; + QString idString = reply->url().path().split('/', QString::SkipEmptyParts).last(); + int id = idString.toInt(&ok); + if (reply->error() == QNetworkReply::NoError && json.isObject() && id >= 0 && ok) { + QJsonObject obj = json.object(); + obj["id"] = id; + updateApiNote(obj); + } m_updateNoteReplies.removeOne(reply); } else if (m_deleteNoteReplies.contains(reply)) { @@ -445,7 +396,7 @@ void NotesApi::replyFinished(QNetworkReply *reply) { bool ok; QString idString = reply->url().path().split('/', QString::SkipEmptyParts).last(); int id = idString.toInt(&ok); - if (reply->error() == QNetworkReply::NoError && ok) + if (reply->error() == QNetworkReply::NoError && id >= 0 && ok) emit noteDeleted(id); m_deleteNoteReplies.removeOne(reply); } @@ -510,7 +461,7 @@ void NotesApi::sslError(QNetworkReply *reply, const QList &errors) { for (int i = 0; i < errors.size(); ++i) { qDebug() << errors[i].errorString(); } - emit error(SslHandshakeError); + emit noteError(SslHandshakeError); } QUrl NotesApi::apiEndpointUrl(const QString endpoint) const { @@ -640,7 +591,7 @@ void NotesApi::updateApiNotes(const QJsonArray &json) { } void NotesApi::updateApiNote(const QJsonObject &json) { - Note note(json); - if (!note.error()) - emit noteUpdated(note); + int id = json["id"].toInt(-1); + if (id >= 0) + emit noteUpdated(id, json); } diff --git a/src/notesapi.h b/src/notesapi.h index a144904..66fb37c 100644 --- a/src/notesapi.h +++ b/src/notesapi.h @@ -138,20 +138,20 @@ public: AuthenticationError }; Q_ENUM(ErrorCodes) - Q_INVOKABLE const QString errorMessage(ErrorCodes error) const; + Q_INVOKABLE const QString errorMessage(int error) const; QString account() const { return m_account; } void setAccount(const QString& account); public slots: - Q_INVOKABLE void getAllNotes(const QStringList exclude = QStringList()); - void getAllNotes(Note::NoteField exclude); - Q_INVOKABLE void getNote(const int id, const QStringList exclude = QStringList()); - void getNote(const int id, Note::NoteField exclude); - Q_INVOKABLE void createNote(const QVariantMap& note); - void createNote(const Note& note); - Q_INVOKABLE void updateNote(const int id, const QVariantMap& note); - void updateNote(const Note& note); + Q_INVOKABLE void getAllNotes(const QStringList& exclude = QStringList()); + //void getAllNotes(Note::NoteField exclude); + Q_INVOKABLE void getNote(const int id, const QStringList& exclude = QStringList()); + //void getNote(const int id, Note::NoteField exclude); + Q_INVOKABLE void createNote(const QJsonObject& note); + //void createNote(const Note& note); + Q_INVOKABLE void updateNote(const int id, const QJsonObject& note); + //void updateNote(const Note& note); Q_INVOKABLE void deleteNote(const int id); signals: @@ -182,7 +182,6 @@ signals: void loginStatusChanged(LoginStatus status); void loginUrlChanged(QUrl url); - void error(ErrorCodes error); public slots: @@ -196,7 +195,6 @@ private slots: private: QString m_account; - static const QByteArray noteApiData(const Note& note); QUrl m_url; QNetworkAccessManager m_manager; diff --git a/src/notesinterface.h b/src/notesinterface.h index c05aec8..2cc3283 100644 --- a/src/notesinterface.h +++ b/src/notesinterface.h @@ -2,8 +2,7 @@ #define NOTESINTERFACE_H #include - -#include "note.h" +#include class NotesInterface : public QObject { @@ -18,22 +17,24 @@ public: virtual QString account() const = 0; virtual void setAccount(const QString& account) = 0; + Q_INVOKABLE virtual const QString errorMessage(int error) const = 0; public slots: - Q_INVOKABLE virtual void getAllNotes(const QStringList exclude = QStringList()) = 0; - virtual void getAllNotes(Note::NoteField exclude) = 0; - Q_INVOKABLE virtual void getNote(const int id, const QStringList exclude = QStringList()) = 0; - virtual void getNote(const int id, Note::NoteField) = 0; - Q_INVOKABLE virtual void createNote(const QVariantMap& note) = 0; - virtual void createNote(const Note& note) = 0; - Q_INVOKABLE virtual void updateNote(const int id, const QVariantMap& note) = 0; - virtual void updateNote(const Note& note) = 0; + Q_INVOKABLE virtual void getAllNotes(const QStringList& exclude = QStringList()) = 0; + //virtual void getAllNotes(Note::NoteField exclude) = 0; + Q_INVOKABLE virtual void getNote(const int id, const QStringList& exclude = QStringList()) = 0; + //virtual void getNote(const int id, Note::NoteField) = 0; + Q_INVOKABLE virtual void createNote(const QJsonObject& note) = 0; + //virtual void createNote(const Note& note) = 0; + Q_INVOKABLE virtual void updateNote(const int id, const QJsonObject& note) = 0; + //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 QVariantMap& note); - void noteUpdated(const Note& note); + void noteError(int error); + void noteUpdated(const int id, const QJsonObject& note); + //void noteUpdated(const Note& note); void noteDeleted(const int id); }; diff --git a/src/notesmodel.cpp b/src/notesmodel.cpp index 39be5cd..e328abe 100644 --- a/src/notesmodel.cpp +++ b/src/notesmodel.cpp @@ -70,16 +70,17 @@ QList NotesModel::ids() const { return ids; } -int NotesModel::insertNote(const Note ¬e) { - qDebug() << "Inserting note: " << note.id(); - int position = m_notes.indexOf(note); +int NotesModel::insertNote(const int id, const QJsonObject& note) { + qDebug() << "Inserting note: " << id; + Note tmpNote(note); + int position = m_notes.indexOf(tmpNote); if (position >= 0) { - if (m_notes.at(position).equal(note)) { + if (m_notes.at(position).equal(tmpNote)) { qDebug() << "Note already present and unchanged."; } else { qDebug() << "Note already present, updating it."; - m_notes.replace(position, note); + m_notes.replace(position, tmpNote); emit dataChanged(index(position), index(position)); } } @@ -87,16 +88,17 @@ int NotesModel::insertNote(const Note ¬e) { qDebug() << "New note, adding it"; position = m_notes.size(); beginInsertRows(QModelIndex(), position, position); - m_notes.append(note); + m_notes.append(tmpNote); endInsertRows(); emit dataChanged(index(position), index(position)); } return position; } -bool NotesModel::removeNote(const Note ¬e) { - qDebug() << "Removing note: " << note.id(); - int position = m_notes.indexOf(note); +bool NotesModel::removeNote(const QJsonObject ¬e) { + Note tmpNote(note); + qDebug() << "Removing note: " << tmpNote.id(); + int position = m_notes.indexOf(tmpNote); if (position >= 0 && position < m_notes.size()) { beginRemoveRows(QModelIndex(), position, position); m_notes.removeAt(position); @@ -108,8 +110,7 @@ bool NotesModel::removeNote(const Note ¬e) { } bool NotesModel::removeNote(int id) { - qDebug() << "Removing note: " << id; - return removeNote(Note(QJsonObject{ {"id", id} } )); + return removeNote(QJsonObject{ {"id", id} } ); } void NotesModel::clear() { diff --git a/src/notesmodel.h b/src/notesmodel.h index ad587b7..fc83c6e 100644 --- a/src/notesmodel.h +++ b/src/notesmodel.h @@ -66,8 +66,8 @@ public: virtual bool setItemData(const QModelIndex &index, const QMap &roles); public slots: - int insertNote(const Note ¬e); - bool removeNote(const Note ¬e); + int insertNote(const int id, const QJsonObject& note); + bool removeNote(const QJsonObject& note); bool removeNote(int id); Q_INVOKABLE void clear(); Q_INVOKABLE QList ids() const; diff --git a/src/notesstore.cpp b/src/notesstore.cpp index 8cdb308..5f8a730 100644 --- a/src/notesstore.cpp +++ b/src/notesstore.cpp @@ -1,5 +1,7 @@ #include "notesstore.h" +#include +#include #include const QString NotesStore::m_suffix = "json"; @@ -26,61 +28,93 @@ void NotesStore::setAccount(const QString& account) { qDebug() << "Setting account: " << account; if (account != m_dir.path()) { if (m_dir != QDir(QStandardPaths::writableLocation(QStandardPaths::DataLocation))) { - m_dir.cdUp(); + m_dir = QDir(QStandardPaths::writableLocation(QStandardPaths::DataLocation)); } if (!account.isEmpty()) { m_dir.setPath(account); - if (!m_dir.mkpath(".")) { + if (m_dir.mkpath(".")) { + emit accountChanged(m_dir.path()); + } + else { qDebug() << "Failed to create or already present: " << m_dir.path(); + m_dir = QDir(QStandardPaths::writableLocation(QStandardPaths::DataLocation)); + emit noteError(DirCannotWriteError); } } //qDebug() << account << m_dir.path(); - emit accountChanged(m_dir.path()); } } -void NotesStore::getAllNotes(const QStringList exclude) { - getAllNotes(Note::noteFieldsFromStringList(exclude)); +const QString NotesStore::errorMessage(int error) const { + QString message; + switch (error) { + case NoError: + message = tr("No error"); + break; + case FileNotFoundError: + message = tr("File not found"); + break; + case FileCannotReadError: + message = tr("Cannot read from the file"); + break; + case FileCannotWriteError: + message = tr("Cannot write to the file"); + break; + case DirNotFoundError: + message = tr("Directory not found"); + break; + case DirCannotReadError: + message = tr("Cannot read from directory"); + break; + case DirCannotWriteError: + message = tr("Cannot create or write to directory"); + break; + default: + message = tr("Unknown error"); + break; + } + return message; } -void NotesStore::getAllNotes(Note::NoteField exclude) { +void NotesStore::getAllNotes(const QStringList& exclude) { qDebug() << "Getting all notes"; - QFileInfoList files = m_dir.entryInfoList(); - for (int i = 0; i < files.size(); ++i) { - bool ok; - int id = files[i].baseName().toInt(&ok); - if (ok) { - getNote(id, exclude); + if (m_dir.exists()) { + QFileInfoList files = m_dir.entryInfoList(); + for (int i = 0; i < files.size(); ++i) { + bool ok; + int id = files[i].baseName().toInt(&ok); + if (ok) { + getNote(id, exclude); + } } } -} - -void NotesStore::getNote(const int id, const QStringList exclude) { - getNote(id, Note::noteFieldsFromStringList(exclude)); -} - -void NotesStore::getNote(const int id, Note::NoteField exclude) { - qDebug() << "Getting note: " << id; - if (id >= 0) { - Note note = readNoteFile(id, exclude); - if (note.isValid()) - emit noteUpdated(note); + else { + emit noteError(DirCannotReadError); } } -void NotesStore::createNote(const QVariantMap ¬e) { - createNote(Note(QJsonObject::fromVariantMap(note))); +void NotesStore::getNote(const int id, const QStringList& exclude) { + qDebug() << "Getting note: " << id; + if (id >= 0) { + QJsonObject note = readNoteFile(id, exclude); + if (note.value("id").toInt(-1) >= 0) + emit noteUpdated(id, note); + } + else { + qDebug() << "Skipping, invalid ID"; + } } -void NotesStore::createNote(const Note& note) { - qDebug() << "Creating note: " << note.id(); - if (!note.isValid()) { +void NotesStore::createNote(const QJsonObject& note) { + int id = note.value("id").toInt(-1); + qDebug() << "Creating note: " << id; + if (id < 0) { // TODO probably crate files with an '.json..new' extension qDebug() << "Creating notes without the server API is not supported yet!"; } - else if (!noteFileExists(note.id())) { - if (writeNoteFile(note)) { - emit noteUpdated(note); + else if (!noteFileExists(id)) { + if (writeNoteFile(id, note)) { + emit noteUpdated(id, note); } } else { @@ -88,21 +122,33 @@ void NotesStore::createNote(const Note& note) { } } -void NotesStore::updateNote(const int id, const QVariantMap ¬e) { - Note newNote(QJsonObject::fromVariantMap(note)); - newNote.setId(id); - updateNote(newNote); -} - -void NotesStore::updateNote(const Note& note) { - qDebug() << "Updating note: " << note.id(); - if (note.isValid()) { - Note file = readNoteFile(note.id()); - if (!file.equal(note) && note > file) { - if (writeNoteFile(note)) { - emit noteUpdated(note); +void NotesStore::updateNote(const int id, const QJsonObject& note) { + qDebug() << "Updating note: " << id; + if (id >= 0) { + QJsonObject tmpNote = readNoteFile(id); + if (note != tmpNote) { + if (note.value("modified").toInt() >= tmpNote.value("modified").toInt() || note.value("modified").toInt() == 0) { + QStringList fields = note.keys(); + for (int i = 0; i < fields.size(); ++i) { + tmpNote[fields[i]] = note[fields[i]]; + } + if (tmpNote.value("modified").toInt() == 0) { + tmpNote["modified"] = QDateTime::currentDateTime().toMSecsSinceEpoch() / 1000; + } + if (writeNoteFile(id, tmpNote)) { + emit noteUpdated(id, tmpNote); + } + } + else { + qDebug() << "Skipping, note is older" << note.value("modified") << tmpNote.value("modified"); } } + else { + qDebug() << "Skipping, note is equal"; + } + } + else { + qDebug() << "Skipping, invalid ID"; } } @@ -118,7 +164,7 @@ bool NotesStore::noteFileExists(const int id) const { return fileinfo.exists(); } -Note NotesStore::readNoteFile(const int id, Note::NoteField exclude) const { +QJsonObject NotesStore::readNoteFile(const int id, const QStringList& exclude) { QJsonObject json; QFileInfo fileinfo(m_dir, QString("%1.%2").arg(id).arg(m_suffix)); QFile file(fileinfo.filePath()); @@ -127,36 +173,40 @@ Note NotesStore::readNoteFile(const int id, Note::NoteField exclude) const { QByteArray data = file.readAll(); json = QJsonDocument::fromJson(data).object(); file.close(); - QList noteFields = Note::noteFields(); - QFlags flags(exclude); - for (int i = 0; i < noteFields.size(); ++i) { - if (flags.testFlag(noteFields[i])) { - json.remove(Note::noteFieldName(noteFields[i])); - } + for (int i = 0; i < exclude.size(); ++i) { + json.remove(exclude[i]); } } + else { + emit noteError(FileCannotReadError); + } + } + else { + //emit noteError(FileNotFoundError); } return json; } -bool NotesStore::writeNoteFile(const Note ¬e) const { +bool NotesStore::writeNoteFile(const int id, const QJsonObject& note) { bool success = false; if (!account().isEmpty()) { - QJsonDocument json = note.toJsonDocument(); - QFileInfo fileinfo(m_dir, QString("%1.%2").arg(note.id()).arg(m_suffix)); + QFileInfo fileinfo(m_dir, QString("%1.%2").arg(id).arg(m_suffix)); QFile file(fileinfo.filePath()); if (file.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) { - QByteArray data = json.toJson(); + QByteArray data = QJsonDocument(note).toJson(); if (file.write(data) == data.size()) { success = true; } file.close(); } + else { + emit noteError(FileCannotWriteError); + } } return success; } -bool NotesStore::removeNoteFile(const int id) const { +bool NotesStore::removeNoteFile(const int id) { bool success = false; if (!account().isEmpty()) { QFileInfo fileinfo(m_dir, QString("%1.%2").arg(id).arg(m_suffix)); @@ -165,6 +215,12 @@ bool NotesStore::removeNoteFile(const int id) const { if (file.remove()) { success = true; } + else { + emit noteError(FileCannotWriteError); + } + } + else { + emit noteError(FileNotFoundError); } } return success; diff --git a/src/notesstore.h b/src/notesstore.h index b9a0c64..05703aa 100644 --- a/src/notesstore.h +++ b/src/notesstore.h @@ -20,15 +20,27 @@ public: QString account() const; void setAccount(const QString& account); + enum ErrorCodes { + NoError, + FileNotFoundError, + FileCannotReadError, + FileCannotWriteError, + DirNotFoundError, + DirCannotReadError, + DirCannotWriteError + }; + Q_ENUM(ErrorCodes) + Q_INVOKABLE const QString errorMessage(int error) const; + public slots: - Q_INVOKABLE void getAllNotes(const QStringList exclude = QStringList()); - void getAllNotes(Note::NoteField exclude); - Q_INVOKABLE void getNote(const int id, const QStringList exclude = QStringList()); - void getNote(const int id, Note::NoteField exclude); - Q_INVOKABLE void createNote(const QVariantMap& note); - void createNote(const Note& note); - Q_INVOKABLE void updateNote(const int id, const QVariantMap& note); - void updateNote(const Note& note); + Q_INVOKABLE void getAllNotes(const QStringList& exclude = QStringList()); + //void getAllNotes(Note::NoteField exclude); + Q_INVOKABLE void getNote(const int id, const QStringList& exclude = QStringList()); + //void getNote(const int id, Note::NoteField exclude); + Q_INVOKABLE void createNote(const QJsonObject& note); + //void createNote(const Note& note); + Q_INVOKABLE void updateNote(const int id, const QJsonObject& note); + //void updateNote(const Note& note); Q_INVOKABLE void deleteNote(const int id); private: @@ -36,9 +48,9 @@ private: const static QString m_suffix; bool noteFileExists(const int id) const; - Note readNoteFile(const int id, Note::NoteField exclude = Note::None) const; - bool writeNoteFile(const Note& note) const; - bool removeNoteFile(const int id) const; + QJsonObject readNoteFile(const int id, const QStringList& exclude = QStringList()); + bool writeNoteFile(const int id, const QJsonObject& note); + bool removeNoteFile(const int id); }; #endif // NOTESSTORE_H diff --git a/translations/harbour-nextcloudnotes-de.ts b/translations/harbour-nextcloudnotes-de.ts index bc012af..7a624c5 100644 --- a/translations/harbour-nextcloudnotes-de.ts +++ b/translations/harbour-nextcloudnotes-de.ts @@ -274,6 +274,10 @@ Unknown error Unbekannter Fehler + + No error + + NotesPage @@ -354,6 +358,41 @@ Ein Fehler ist aufgetreten + + NotesStore + + File not found + + + + Cannot read from the file + + + + Cannot write to the file + + + + Directory not found + + + + Cannot read from directory + + + + Cannot create or write to directory + + + + Unknown error + Unbekannter Fehler + + + No error + + + SettingsPage @@ -686,8 +725,12 @@ You can also use other markdown syntax inside them. Synchronisiert - Error - Fehler + API error + + + + File error + diff --git a/translations/harbour-nextcloudnotes-sv.ts b/translations/harbour-nextcloudnotes-sv.ts index 7e0daa2..3dd7e85 100644 --- a/translations/harbour-nextcloudnotes-sv.ts +++ b/translations/harbour-nextcloudnotes-sv.ts @@ -274,6 +274,10 @@ Unknown error + + No error + + NotesPage @@ -354,6 +358,41 @@ + + NotesStore + + File not found + + + + Cannot read from the file + + + + Cannot write to the file + + + + Directory not found + + + + Cannot read from directory + + + + Cannot create or write to directory + + + + Unknown error + + + + No error + + + SettingsPage @@ -686,7 +725,11 @@ You can also use other markdown syntax inside them. - Error + API error + + + + File error diff --git a/translations/harbour-nextcloudnotes.ts b/translations/harbour-nextcloudnotes.ts index 02d02ba..d1fffd4 100644 --- a/translations/harbour-nextcloudnotes.ts +++ b/translations/harbour-nextcloudnotes.ts @@ -245,12 +245,12 @@ Note - + Today - + Yesterday @@ -311,27 +311,32 @@ NotesApi - + + No error + + + + No network connection available - + Failed to communicate with the Nextcloud server - + An error occured while establishing an encrypted connection - + Could not authenticate to the Nextcloud instance - + Unknown error @@ -374,66 +379,109 @@ - + Modified - + Delete - + Deleting note - + Loading notes... - + No account yet - + Got to the settings to add an account - + No notes yet - + Pull down to add a note - + No result - + Try another query - + An error occurred - + Open the settings to configure your Nextcloud accounts + + NotesStore + + + No error + + + + + File not found + + + + + Cannot read from the file + + + + + Cannot write to the file + + + + + Directory not found + + + + + Cannot read from directory + + + + + Cannot create or write to directory + + + + + Unknown error + + + SettingsPage @@ -844,9 +892,14 @@ You can also use other markdown syntax inside them. Synced + + + API error + + - Error + File error