From 6818edd94aba0027b3b45006e900f34283b6305e Mon Sep 17 00:00:00 2001 From: Scharel Clemens Date: Tue, 5 May 2020 19:44:31 +0200 Subject: [PATCH] Improved offline mode --- qml/harbour-nextcloudnotes.qml | 19 ++++---- qml/pages/EditPage.qml | 4 +- qml/pages/NotePage.qml | 12 ++--- qml/pages/NotesPage.qml | 10 ++-- rpm/harbour-nextcloudnotes.changes | 3 ++ rpm/harbour-nextcloudnotes.spec | 4 +- rpm/harbour-nextcloudnotes.yaml | 4 +- src/notesapi.cpp | 6 +++ src/notesapi.h | 1 + src/notesinterface.h | 1 + src/notesmodel.cpp | 65 ++++++++++++++------------ src/notesmodel.h | 2 +- src/notesstore.cpp | 15 +++--- src/notesstore.h | 2 +- translations/harbour-nextcloudnotes.ts | 62 ++++++++++++------------ 15 files changed, 112 insertions(+), 98 deletions(-) diff --git a/qml/harbour-nextcloudnotes.qml b/qml/harbour-nextcloudnotes.qml index 16903dd..dfb0d04 100644 --- a/qml/harbour-nextcloudnotes.qml +++ b/qml/harbour-nextcloudnotes.qml @@ -57,8 +57,7 @@ ApplicationWindow notesProxyModel.sourceModel.clear() account.path = "/apps/harbour-nextcloudnotes/accounts/" + currentAccount notesStore.account = currentAccount - notesStore.getAllNotes() - notesApi.getAllNotes() + notesModel.getAllNotes() } onSortByChanged: { @@ -148,13 +147,7 @@ ApplicationWindow running: interval > 0 && notesApi.networkAccessible && appWindow.visible triggeredOnStart: true onTriggered: { - notesStore.getAllNotes() - if (!notesApi.busy) { - notesApi.getAllNotes(); - } - else { - restart() - } + notesModel.getAllNotes() } onIntervalChanged: { if (interval > 0) { @@ -198,7 +191,13 @@ ApplicationWindow apiErrorNotification.publish() } } - onLastSyncChanged: account.update = lastSync + onLastSyncChanged: { + console.log("Last API sync: " + lastSync) + account.update = lastSync + } + } + + Component.onCompleted: { } Component.onDestruction: { diff --git a/qml/pages/EditPage.qml b/qml/pages/EditPage.qml index 89f89b5..5a6084e 100644 --- a/qml/pages/EditPage.qml +++ b/qml/pages/EditPage.qml @@ -28,7 +28,7 @@ Dialog { property string errorMessage onAccepted: { - notesApi.updateNote(id, { 'category': categoryField.text, 'content': contentArea.text, 'favorite': favoriteButton.selected, 'modified': new Date().valueOf() / 1000 } ) + notesModel.updateNote(id, { 'category': categoryField.text, 'content': contentArea.text, 'favorite': favoriteButton.selected, 'modified': new Date().valueOf() / 1000 } ) } SilicaFlickable { @@ -162,7 +162,7 @@ Dialog { } onFocusChanged: { if (focus === false && text !== category) { - notesApi.updateNote(id, {'content': content, 'category': text}) // This does not seem to work without adding the content + notesModel.updateNote(id, {'content': content, 'category': text}) // This does not seem to work without adding the content } } } diff --git a/qml/pages/NotePage.qml b/qml/pages/NotePage.qml index 617f737..8826c3e 100644 --- a/qml/pages/NotePage.qml +++ b/qml/pages/NotePage.qml @@ -99,12 +99,12 @@ Dialog { MenuItem { text: qsTr("Delete") - onClicked: remorse.execute("Deleting", function() { notesApi.deleteNote(note["id"]) } ) + onClicked: remorse.execute("Deleting", function() { notesModel.deleteNote(note["id"]) } ) } MenuItem { text: enabled ? qsTr("Reload") : qsTr("Updating...") enabled: !notesApi.busy - onClicked: notesApi.getNote(note["id"]) + onClicked: notesModel.getNote(note["id"]) } /*MenuItem { text: qsTr("Edit") @@ -158,7 +158,7 @@ Dialog { } ) note["content"] = newContent parseContent() - notesApi.updateNote(id, { 'content': note["content"] } ) + notesModel.updateNote(id, { 'content': note["content"] } ) } else if (/^tasklist:uncheckbox_(\d+)$/m.test(link)) { newContent = newContent.replace(/- \[[xX]\] (.*)$/gm, @@ -170,7 +170,7 @@ Dialog { } ) note["content"] = newContent parseContent() - notesApi.updateNote(id, { 'content': note["content"] } ) + notesModel.updateNote(id, { 'content': note["content"] } ) } else { Qt.openUrlExternally(link) @@ -230,7 +230,7 @@ Dialog { icon.source: (selected ? "image://theme/icon-m-favorite-selected?" : "image://theme/icon-m-favorite?") + (favoriteButton.highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor) onClicked: { - notesApi.updateNote(note["id"], {'favorite': selected, 'modified': new Date().valueOf() / 1000 }) + notesModel.updateNote(note["id"], {'favorite': selected, 'modified': new Date().valueOf() / 1000 }) } } TextField { @@ -244,7 +244,7 @@ Dialog { } onFocusChanged: { if (focus === false && text !== note["category"]) { - notesApi.updateNote(id, {'content': note["content"], 'category': text, 'modified': new Date().valueOf() / 1000}) // This does not seem to work without adding the content + notesModel.updateNote(id, {'content': note["content"], 'category': text, 'modified': new Date().valueOf() / 1000}) // This does not seem to work without adding the content } } } diff --git a/qml/pages/NotesPage.qml b/qml/pages/NotesPage.qml index b2835d2..69197dd 100644 --- a/qml/pages/NotesPage.qml +++ b/qml/pages/NotesPage.qml @@ -35,12 +35,12 @@ Page { MenuItem { text: qsTr("Add note") enabled: appSettings.currentAccount.length > 0 && notesApi.networkAccessible - onClicked: notesApi.createNote( { 'content': "", 'modified': new Date().valueOf() / 1000 } ) + onClicked: notesModel.createNote( { 'content': "", 'modified': new Date().valueOf() / 1000 } ) } MenuItem { text: notesApi.networkAccessible && !notesApi.busy ? qsTr("Reload") : qsTr("Updating...") enabled: appSettings.currentAccount.length > 0 && notesApi.networkAccessible && !notesApi.busy - onClicked: notesApi.getAllNotes() + onClicked: notesModel.getAllNotes() } MenuLabel { visible: appSettings.currentAccount.length > 0 @@ -124,8 +124,7 @@ Page { (note.highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor) onClicked: { var newFavorite = !favorite - notesStore.updateNote(id, {'favorite': newFavorite } ) - notesApi.updateNote(id, {'favorite': newFavorite } ) + notesModel.updateNote(id, {'favorite': newFavorite } ) } } @@ -195,8 +194,7 @@ Page { text: qsTr("Delete") onClicked: { remorse.execute(note, qsTr("Deleting note"), function() { - notesStore.deleteNote(id) - notesApi.deleteNote(id) + notesModel.deleteNote(id) }) } } diff --git a/rpm/harbour-nextcloudnotes.changes b/rpm/harbour-nextcloudnotes.changes index 579387c..5067a20 100644 --- a/rpm/harbour-nextcloudnotes.changes +++ b/rpm/harbour-nextcloudnotes.changes @@ -12,6 +12,9 @@ # * date Author's Name version-release # - Summary of changes +* Tue May 05 2020 Scharel Clemens 0.9-0 +- Working offline mode + * Thu Apr 13 2020 Scharel Clemens 0.8-1 - Updated Signals and Slots between the modules diff --git a/rpm/harbour-nextcloudnotes.spec b/rpm/harbour-nextcloudnotes.spec index d1aa964..facc601 100644 --- a/rpm/harbour-nextcloudnotes.spec +++ b/rpm/harbour-nextcloudnotes.spec @@ -13,8 +13,8 @@ Name: harbour-nextcloudnotes %{!?qtc_make:%define qtc_make make} %{?qtc_builddir:%define _builddir %qtc_builddir} Summary: Nextcloud Notes -Version: 0.8 -Release: 1 +Version: 0.9 +Release: 0 Group: Applications/Editors License: MIT URL: https://github.com/scharel/harbour-nextcloudnotes diff --git a/rpm/harbour-nextcloudnotes.yaml b/rpm/harbour-nextcloudnotes.yaml index d0db891..744aa5c 100644 --- a/rpm/harbour-nextcloudnotes.yaml +++ b/rpm/harbour-nextcloudnotes.yaml @@ -1,7 +1,7 @@ Name: harbour-nextcloudnotes Summary: Nextcloud Notes -Version: 0.8 -Release: 1 +Version: 0.9 +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 diff --git a/src/notesapi.cpp b/src/notesapi.cpp index 3e3236d..32ba832 100644 --- a/src/notesapi.cpp +++ b/src/notesapi.cpp @@ -57,6 +57,10 @@ const QList NotesApi::noteIds() { return m_syncedNotes.keys(); } +bool NotesApi::noteExists(const int id) { + return m_syncedNotes.contains(id); +} + int NotesApi::noteModified(const int id) { return m_syncedNotes.value(id, -1); } @@ -618,6 +622,8 @@ void NotesApi::updateApiNotes(const QJsonArray &json) { } } } + m_lastSync = QDateTime::currentDateTime(); + emit lastSyncChanged(m_lastSync); emit allNotesChanged(ids); } diff --git a/src/notesapi.h b/src/notesapi.h index 34d330e..228139c 100644 --- a/src/notesapi.h +++ b/src/notesapi.h @@ -145,6 +145,7 @@ public: void setAccount(const QString& account); const QList noteIds(); + bool noteExists(const int id); int noteModified(const int id); public slots: diff --git a/src/notesinterface.h b/src/notesinterface.h index 6f276bf..1d584e9 100644 --- a/src/notesinterface.h +++ b/src/notesinterface.h @@ -19,6 +19,7 @@ public: virtual void setAccount(const QString& account) = 0; virtual const QList noteIds() = 0; + virtual bool noteExists(const int id) = 0; virtual int noteModified(const int id) = 0; public slots: diff --git a/src/notesmodel.cpp b/src/notesmodel.cpp index 5b654a8..13b7d7c 100644 --- a/src/notesmodel.cpp +++ b/src/notesmodel.cpp @@ -27,18 +27,6 @@ int NotesProxyModel::roleFromName(const QString &name) const { return roleNames().key(name.toLocal8Bit()); } -const QVariantMap NotesProxyModel::getNote(const QModelIndex &index) const { - QMap item = sourceModel()->itemData(mapToSource(index)); - QHash names = roleNames(); - QVariantMap note; - QMapIterator i(item); - while (i.hasNext()) { - i.next(); - note[names.value(i.key())] = i.value(); - } - return note; -} - bool NotesProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const { QAbstractItemModel* source = sourceModel(); if (m_favoritesOnTop && source->data(source_left, NotesModel::FavoriteRole).toBool() != source->data(source_right, NotesModel::FavoriteRole).toBool()) @@ -131,15 +119,19 @@ void NotesModel::setAccount(const QString &account) { } const QList NotesModel::noteIds() { - return m_notes.keys(); + return mp_notesStore->noteIds(); +} + +bool NotesModel::noteExists(const int id) { + return mp_notesStore->noteExists(id); } int NotesModel::noteModified(const int id) { - return m_notes.value(id).value("modified").toInt(-1); + return mp_notesStore->noteModified(id); } const QVariantMap NotesModel::getNoteById(const int id) const { - return m_notes[id].toVariantMap(); + return mp_notesStore->readNoteFile(id).toVariantMap(); } bool NotesModel::getAllNotes(const QStringList &exclude) { @@ -205,11 +197,12 @@ void NotesModel::insert(const int id, const QJsonObject& note) { //emit noteInserted(id, note); qDebug() << "Note inserted"; } - if (sender() == mp_notesApi) { - // TODO - } - if (sender() == mp_notesStore) { - // TODO + if (mp_notesApi && mp_notesStore) { + if (sender() == mp_notesApi) { + if (!mp_notesStore->noteExists(id)) { + mp_notesStore->createNote(note); + } + } } } @@ -230,11 +223,17 @@ void NotesModel::update(const int id, const QJsonObject ¬e) { qDebug() << "Note changed"; } } - if (sender() == mp_notesApi) { - // TODO - } - if (sender() == mp_notesStore) { - // TODO + if (mp_notesApi && mp_notesStore) { + if (sender() == mp_notesApi) { + if (Note::modified(note) > mp_notesStore->noteModified(id)) { + mp_notesStore->updateNote(id, note); + } + } + if (sender() == mp_notesStore) { + if (Note::modified(note) > mp_notesApi->noteModified(id) && !mp_notesApi->lastSync().isNull()) { + mp_notesApi->updateNote(id, note); + } + } } } @@ -250,11 +249,13 @@ void NotesModel::remove(const int id) { } endRemoveRows(); } - if (sender() == mp_notesApi) { - // TODO - } - if (sender() == mp_notesStore) { - // TODO + if (mp_notesApi && mp_notesStore) { + if (sender() == mp_notesApi) { + mp_notesStore->deleteNote(id); + } + if (sender() == mp_notesStore) { + mp_notesApi->deleteNote(id); + } } } @@ -287,6 +288,7 @@ int NotesModel::rowCount(const QModelIndex &parent) const { } QVariant NotesModel::data(const QModelIndex &index, int role) const { + //qDebug(); QVariant data; if (index.isValid() && index.row() <= m_notes.size()) { QMap::const_iterator i = m_notes.cbegin(); @@ -297,6 +299,7 @@ QVariant NotesModel::data(const QModelIndex &index, int role) const { } bool NotesModel::setData(const QModelIndex &index, const QVariant &value, int role) { + //qDebug(); if (index.isValid() && index.row() <= m_notes.size()) { QMap::iterator i = m_notes.begin(); i += index.row(); @@ -308,6 +311,7 @@ bool NotesModel::setData(const QModelIndex &index, const QVariant &value, int ro } QMap NotesModel::itemData(const QModelIndex &index) const { + //qDebug(); QMap map; if (index.isValid() && index.row() <= m_notes.size()) { for (int role = IdRole; role < NoneRole; ++role) { @@ -318,6 +322,7 @@ QMap NotesModel::itemData(const QModelIndex &index) const { } bool NotesModel::setItemData(const QModelIndex &index, const QMap &roles) { + //qDebug(); bool retval = true; QMapIterator role(roles); while (role.hasNext()) { diff --git a/src/notesmodel.h b/src/notesmodel.h index 6f25d07..c96ff6e 100644 --- a/src/notesmodel.h +++ b/src/notesmodel.h @@ -23,7 +23,6 @@ public: Q_INVOKABLE void sort(); Q_INVOKABLE int roleFromName(const QString &name) const; - Q_INVOKABLE const QVariantMap getNote(const QModelIndex &index) const; protected: virtual bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const; @@ -73,6 +72,7 @@ public: void setAccount(const QString& account); const QList noteIds(); + bool noteExists(const int id); int noteModified(const int id); Q_INVOKABLE const QVariantMap getNoteById(const int id) const; diff --git a/src/notesstore.cpp b/src/notesstore.cpp index e126809..89793dd 100644 --- a/src/notesstore.cpp +++ b/src/notesstore.cpp @@ -1,4 +1,5 @@ #include "notesstore.h" +#include "note.h" #include #include @@ -64,8 +65,13 @@ const QList NotesStore::noteIds() { return ids; } +bool NotesStore::noteExists(const int id) { + QFileInfo fileinfo(m_dir, QString("%1.%2").arg(id).arg(m_suffix)); + return fileinfo.exists(); +} + int NotesStore::noteModified(const int id) { - return readNoteFile(id, { "content" }).value("modified").toInt(-1); + return Note::modified(readNoteFile(id, { "content" })); } const QString NotesStore::errorMessage(ErrorCodes error) const { @@ -99,11 +105,6 @@ const QString NotesStore::errorMessage(ErrorCodes error) const { return message; } -bool NotesStore::noteFileExists(const int id) const { - QFileInfo fileinfo(m_dir, QString("%1.%2").arg(id).arg(m_suffix)); - return fileinfo.exists(); -} - QJsonObject NotesStore::readNoteFile(const int id, const QStringList& exclude) { QJsonObject json; QFileInfo fileinfo(m_dir, QString("%1.%2").arg(id).arg(m_suffix)); @@ -201,7 +202,7 @@ bool NotesStore::createNote(const QJsonObject& note) { // TODO probably crate files with an '.json..new' extension qDebug() << "Creating notes without the server API is not supported yet!"; } - else if (!noteFileExists(id)) { + else if (!noteExists(id)) { if (writeNoteFile(id, note)) { emit noteUpdated(id, note); return true; diff --git a/src/notesstore.h b/src/notesstore.h index bd9e21c..c25f9c1 100644 --- a/src/notesstore.h +++ b/src/notesstore.h @@ -25,6 +25,7 @@ public: void setAccount(const QString& account); const QList noteIds(); + bool noteExists(const int id); int noteModified(const int id); enum ErrorCodes { @@ -39,7 +40,6 @@ public: Q_ENUM(ErrorCodes) Q_INVOKABLE const QString errorMessage(ErrorCodes error) const; - bool noteFileExists(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); diff --git a/translations/harbour-nextcloudnotes.ts b/translations/harbour-nextcloudnotes.ts index 1c1688b..bcf1d57 100644 --- a/translations/harbour-nextcloudnotes.ts +++ b/translations/harbour-nextcloudnotes.ts @@ -316,32 +316,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 @@ -384,62 +384,62 @@ - + 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 @@ -447,42 +447,42 @@ 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 @@ -883,27 +883,27 @@ You can also use other markdown syntax inside them. harbour-nextcloudnotes - + Notes - + Offline - + Synced - + API error - + File error