From 09c5a68b2f9007dba884a91e136a295ae6742cda Mon Sep 17 00:00:00 2001 From: Scharel Clemens Date: Tue, 12 May 2020 01:30:32 +0200 Subject: [PATCH] NotesModel now directly uses files as backend --- harbour-nextcloudnotes.pro | 6 +- qml/harbour-nextcloudnotes.qml | 19 +- src/harbour-nextcloudnotes.cpp | 9 +- src/notesapi.cpp | 14 - src/notesapi.h | 9 +- src/notesmodel.cpp | 308 +++++++++------------- src/notesmodel.h | 49 ++-- src/notesstore.cpp | 256 ------------------ src/notesstore.h | 65 ----- translations/harbour-nextcloudnotes-de.ts | 35 --- translations/harbour-nextcloudnotes-sv.ts | 35 --- translations/harbour-nextcloudnotes.ts | 65 +---- 12 files changed, 160 insertions(+), 710 deletions(-) delete mode 100644 src/notesstore.cpp delete mode 100644 src/notesstore.h diff --git a/harbour-nextcloudnotes.pro b/harbour-nextcloudnotes.pro index 9b6c178..fea69f6 100644 --- a/harbour-nextcloudnotes.pro +++ b/harbour-nextcloudnotes.pro @@ -18,14 +18,12 @@ DEFINES += APP_VERSION=\\\"$$VERSION\\\" HEADERS += src/note.h \ src/notesapi.h \ - src/notesmodel.h \ - src/notesstore.h + src/notesmodel.h SOURCES += src/harbour-nextcloudnotes.cpp \ src/note.cpp \ src/notesapi.cpp \ - src/notesmodel.cpp \ - src/notesstore.cpp + src/notesmodel.cpp DISTFILES += qml/harbour-nextcloudnotes.qml \ qml/cover/CoverPage.qml \ diff --git a/qml/harbour-nextcloudnotes.qml b/qml/harbour-nextcloudnotes.qml index f2d6162..a670724 100644 --- a/qml/harbour-nextcloudnotes.qml +++ b/qml/harbour-nextcloudnotes.qml @@ -54,10 +54,8 @@ ApplicationWindow property bool useCapitalX: value("useCapitalX", false, Boolean) onCurrentAccountChanged: { - notesProxyModel.sourceModel.clear() account.path = "/apps/harbour-nextcloudnotes/accounts/" + currentAccount - notesStore.account = currentAccount - notesModel.getAllNotes() + notesModel.account = currentAccount } onSortByChanged: { @@ -147,7 +145,7 @@ ApplicationWindow running: interval > 0 && notesApi.networkAccessible && appWindow.visible triggeredOnStart: true onTriggered: { - notesModel.getAllNotes() + notesApi.getAllNotes() } onIntervalChanged: { if (interval > 0) { @@ -156,19 +154,6 @@ 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 diff --git a/src/harbour-nextcloudnotes.cpp b/src/harbour-nextcloudnotes.cpp index febc5fb..f2c1e52 100644 --- a/src/harbour-nextcloudnotes.cpp +++ b/src/harbour-nextcloudnotes.cpp @@ -4,7 +4,6 @@ #include #include "note.h" #include "notesapi.h" -#include "notesstore.h" #include "notesmodel.h" int main(int argc, char *argv[]) @@ -27,10 +26,10 @@ int main(int argc, char *argv[]) notesProxyModel->setFilterRole(NotesModel::ContentRole); notesProxyModel->setSourceModel(notesModel); - NotesStore* notesStore = new NotesStore; + //NotesStore* notesStore = new NotesStore; NotesApi* notesApi = new NotesApi; notesModel->setNotesApi(notesApi); - notesModel->setNotesStore(notesStore); + //notesModel->setNotesStore(notesStore); QQuickView* view = SailfishApp::createView(); #ifdef QT_DEBUG @@ -40,7 +39,7 @@ int main(int argc, char *argv[]) #endif view->rootContext()->setContextProperty("notesModel", notesModel); view->rootContext()->setContextProperty("notesProxyModel", notesProxyModel); - view->rootContext()->setContextProperty("notesStore", notesStore); + //view->rootContext()->setContextProperty("notesStore", notesStore); view->rootContext()->setContextProperty("notesApi", notesApi); view->setSource(SailfishApp::pathTo("qml/harbour-nextcloudnotes.qml")); @@ -49,7 +48,7 @@ int main(int argc, char *argv[]) int retval = app->exec(); notesApi->deleteLater(); - notesStore->deleteLater(); + //notesStore->deleteLater(); notesProxyModel->deleteLater(); notesModel->deleteLater(); return retval; diff --git a/src/notesapi.cpp b/src/notesapi.cpp index 32ba832..4842786 100644 --- a/src/notesapi.cpp +++ b/src/notesapi.cpp @@ -42,17 +42,6 @@ NotesApi::~NotesApi() { disconnect(&m_manager, SIGNAL(sslErrors(QNetworkReply*,QList)), this, SLOT(sslError(QNetworkReply*,QList))); } -QString NotesApi::account() const { - return m_account; -} - -void NotesApi::setAccount(const QString &account) { - if (account != m_account) { - m_account = account; - emit accountChanged(account); - } -} - const QList NotesApi::noteIds() { return m_syncedNotes.keys(); } @@ -612,19 +601,16 @@ void NotesApi::setLoginStatus(LoginStatus status, bool *changed) { } void NotesApi::updateApiNotes(const QJsonArray &json) { - QList ids; for (int i = 0; i < json.size(); ++i) { if (json[i].isObject()) { QJsonObject object = json[i].toObject(); if (!object.isEmpty()) { updateApiNote(json[i].toObject()); - ids << object.value("id").toInt(-1); } } } m_lastSync = QDateTime::currentDateTime(); emit lastSyncChanged(m_lastSync); - emit allNotesChanged(ids); } void NotesApi::updateApiNote(const QJsonObject &json) { diff --git a/src/notesapi.h b/src/notesapi.h index 88925f9..f2c6446 100644 --- a/src/notesapi.h +++ b/src/notesapi.h @@ -182,11 +182,9 @@ signals: void loginStatusChanged(LoginStatus status); void loginUrlChanged(QUrl url); - void accountChanged(const QString& account); - void allNotesChanged(const QList& ids); - void noteCreated(const int id, const QJsonObject& note); - void noteUpdated(const int id, const QJsonObject& note); - void noteDeleted(const int id); + void noteCreated(int id, const QJsonObject& note); + void noteUpdated(int id, const QJsonObject& note); + void noteDeleted(int id); void noteError(ErrorCodes error); private slots: @@ -199,7 +197,6 @@ private slots: private: QUrl m_url; - QString m_account; QMap m_syncedNotes; QNetworkAccessManager m_manager; QNetworkRequest m_request; diff --git a/src/notesmodel.cpp b/src/notesmodel.cpp index df0d530..2e1674b 100644 --- a/src/notesmodel.cpp +++ b/src/notesmodel.cpp @@ -72,19 +72,25 @@ const QHash NotesModel::m_roleNames = QHash ( {NotesModel::ModifiedStringRole, "modifiedString"}, {NotesModel::NoneRole, "none"} } ); +const QString NotesModel::m_fileSuffix = "json"; + NotesModel::NotesModel(QObject *parent) : QAbstractListModel(parent) { mp_notesApi = nullptr; - mp_notesStore = nullptr; //m_fileDir.setCurrent(directory); m_fileDir.setPath(""); - m_fileDir.setFilter(QDir::Files); + m_fileDir.setFilter(QDir::Files | QDir::NoDotAndDotDot); + m_fileDir.setSorting(QDir::Name | QDir::DirsLast); m_fileDir.setNameFilters( { "*." + m_fileSuffix } ); } NotesModel::~NotesModel() { + /*QMapIterator i(m_files); + while (i.hasNext()) { + i.next(); + m_files.take(i.key()).close(); + i.toFront(); + }*/ setNotesApi(nullptr); - setNotesStore(nullptr); - clear(); } void NotesModel::setNotesApi(NotesApi *notesApi) { @@ -98,31 +104,13 @@ void NotesModel::setNotesApi(NotesApi *notesApi) { mp_notesApi = notesApi; if (mp_notesApi) { // connect stuff - connect(mp_notesApi, SIGNAL(accountChanged(QString)), this, SIGNAL(accountChanged(QString))); + //connect(mp_notesApi, SIGNAL(accountChanged(QString)), this, SIGNAL(accountChanged(QString))); connect(mp_notesApi, SIGNAL(noteCreated(int,QJsonObject)), this, SLOT(insert(int,QJsonObject))); connect(mp_notesApi, SIGNAL(noteUpdated(int,QJsonObject)), this, SLOT(update(int,QJsonObject))); connect(mp_notesApi, SIGNAL(noteDeleted(int)), this, SLOT(remove(int))); } } -void NotesModel::setNotesStore(NotesStore *notesStore) { - if (mp_notesStore) { - // disconnect stuff - disconnect(mp_notesStore, SIGNAL(accountChanged(QString)), this, SIGNAL(accountChanged(QString))); - disconnect(mp_notesStore, SIGNAL(noteCreated(int,QJsonObject)), this, SLOT(insert(int,QJsonObject))); - disconnect(mp_notesStore, SIGNAL(noteUpdated(int,QJsonObject)), this, SLOT(update(int,QJsonObject))); - disconnect(mp_notesStore, SIGNAL(noteDeleted(int)), this, SLOT(remove(int))); - } - mp_notesStore = notesStore; - if (mp_notesStore) { - // connect stuff - connect(mp_notesStore, SIGNAL(accountChanged(QString)), this, SIGNAL(accountChanged(QString))); - connect(mp_notesStore, SIGNAL(noteCreated(int,QJsonObject)), this, SLOT(insert(int,QJsonObject))); - connect(mp_notesStore, SIGNAL(noteUpdated(int,QJsonObject)), this, SLOT(update(int,QJsonObject))); - connect(mp_notesStore, SIGNAL(noteDeleted(int)), this, SLOT(remove(int))); - } -} - QString NotesModel::account() const { if (m_fileDir != QDir(QStandardPaths::writableLocation(QStandardPaths::DataLocation))) { return m_fileDir.path(); @@ -132,6 +120,7 @@ QString NotesModel::account() const { void NotesModel::setAccount(const QString& account) { qDebug() << "Setting account: " << account; + beginRemoveRows(QModelIndex(), 0, rowCount() - 1); if (account != m_fileDir.path()) { if (m_fileDir != QDir(QStandardPaths::writableLocation(QStandardPaths::DataLocation))) { m_fileDir = QDir(QStandardPaths::writableLocation(QStandardPaths::DataLocation)); @@ -139,6 +128,9 @@ void NotesModel::setAccount(const QString& account) { if (!account.isEmpty()) { m_fileDir.setPath(account); if (m_fileDir.mkpath(".")) { + m_fileDir.setFilter(QDir::Files | QDir::NoDotAndDotDot); + m_fileDir.setSorting(QDir::Name | QDir::DirsLast); + m_fileDir.setNameFilters( { "*." + m_fileSuffix } ); emit accountChanged(m_fileDir.path()); } else { @@ -149,6 +141,10 @@ void NotesModel::setAccount(const QString& account) { } //qDebug() << account << m_dir.path(); } + endRemoveRows(); + beginInsertRows(QModelIndex(), 0, rowCount() - 1); + qDebug() << rowCount() << " notes in account"; + endInsertRows(); } /* const QList NotesModel::noteIds() { @@ -179,181 +175,134 @@ int NotesModel::noteModified(const int id) { return Note::modified(QJsonObject::fromVariantMap(getNoteById(id))); } */ + +int NotesModel::newNotePosition(const int id) const { + if (m_fileDir.exists() && !account().isEmpty() && id >= 0) { + QStringList fileList = m_fileDir.entryList(); + qDebug() << fileList; + fileList << QString("%1.%2").arg(id).arg(m_fileSuffix); + fileList.sort(Qt::CaseInsensitive); + qDebug() << fileList; + return fileList.indexOf(QString("%1.%2").arg(id).arg(m_fileSuffix)); + } + return -1; +} + const QVariantMap NotesModel::note(const int id) const { QVariantMap json; - QFileInfo fileinfo(m_fileDir, QString("%1.%2").arg(id).arg(m_fileSuffix)); - QFile file(fileinfo.filePath()); - if (file.exists()) { - if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { - json = QJsonDocument::fromJson(file.readAll()).object().toVariantMap(); - file.close(); + if (m_fileDir.exists() && !account().isEmpty() && id >= 0) { + QFileInfo fileinfo(m_fileDir, QString("%1.%2").arg(id).arg(m_fileSuffix)); + QFile file(fileinfo.filePath()); + if (file.exists()) { + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { + json = QJsonDocument::fromJson(file.readAll()).object().toVariantMap(); + file.close(); + } } - else { - //emit noteError(FileCannotReadError); - } - } - else { - //emit noteError(FileNotFoundError); } return json; } -bool NotesModel::setNote(const QVariantMap ¬e, int id) const { +bool NotesModel::setNote(const QVariantMap ¬e, int id) { bool ok; if (id < 0) { id = note.value(m_roleNames[IdRole]).toInt(&ok); + if (!ok) id = -1; } - else { - ok = true; - } - if (id >= 0 && ok) { - ok = false; + ok = false; + if (m_fileDir.exists() && !account().isEmpty() && id >= 0) { QFileInfo fileinfo(m_fileDir, QString("%1.%2").arg(id).arg(m_fileSuffix)); QFile file(fileinfo.filePath()); - if (file.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) { + if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { QByteArray data = QJsonDocument(QJsonObject::fromVariantMap(note)).toJson(); if (file.write(data) == data.size()) { ok = true; } + file.close(); } } return ok; } -bool NotesModel::getAllNotes(const QStringList &exclude) { - bool success = true; - if (mp_notesApi) - success &= mp_notesApi->getAllNotes(exclude); - if (mp_notesStore) - success &= mp_notesStore->getAllNotes(exclude); - return success; -} - -bool NotesModel::getNote(const int id, const QStringList &exclude) { - bool success = true; - if (mp_notesApi) - success &= mp_notesApi->getNote(id, exclude); - if (mp_notesStore) - success &= mp_notesStore->getNote(id, exclude); - return success; -} - -bool NotesModel::createNote(const QJsonObject ¬e) { - bool success = true; - if (mp_notesApi) - success &= mp_notesApi->createNote(note); - return success; -} - -bool NotesModel::updateNote(const int id, const QJsonObject ¬e) { - bool success = true; - if (mp_notesApi) - success &= mp_notesApi->updateNote(id, note); - if (mp_notesStore) - success &= mp_notesStore->updateNote(id, note); - return success; -} - bool NotesModel::deleteNote(const int id) { - bool success = true; - if (mp_notesApi) - success &= mp_notesApi->deleteNote(id); - if (mp_notesStore) - success &= mp_notesStore->deleteNote(id); - return success; -} - -bool NotesModel::syncNotes() { - if (mp_notesApi && mp_notesStore) { - // TODO + if (m_fileDir.exists() && !account().isEmpty() && id >= 0) { + QFileInfo fileinfo(m_fileDir, QString("%1.%2").arg(id).arg(m_fileSuffix)); + QFile file(fileinfo.filePath()); + if (file.exists()) { + if (file.remove()) { + return true; + } + } } return false; } -void NotesModel::insert(const int id, const QJsonObject& note) { - qDebug() << "Inserting note: " << id; - if (m_notes.contains(id)) { - qDebug() << "Note already present"; - update(id, note); +void NotesModel::insert(int id, const QJsonObject& json) { + if (id < 0) { + id = json.value(m_roleNames[IdRole]).toInt(-1); } - else { - beginInsertRows(QModelIndex(), indexOfNoteById(id), indexOfNoteById(id)); - m_notes.insert(id, note); - endInsertRows(); - //emit noteInserted(id, note); - qDebug() << "Note inserted"; - } - if (mp_notesApi && mp_notesStore) { - if (sender() == mp_notesApi) { - if (!mp_notesStore->noteExists(id)) { - mp_notesStore->createNote(note); - } + if (id >= 0) { + qDebug() << "Inserting note: " << id; + if (indexOfNoteById(id) >= 0) { + qDebug() << "Note already present"; + update(id, json); + } + else { + qDebug() << "New position: " << newNotePosition(id); + beginInsertRows(QModelIndex(), newNotePosition(id), newNotePosition(id)); + setNote(json.toVariantMap(), id); + endInsertRows(); + //emit noteInserted(id, note); + qDebug() << "Note inserted"; } } } -void NotesModel::update(const int id, const QJsonObject ¬e) { - qDebug() << "Updating note: " << id; - if (!m_notes.contains(id)) { - qDebug() << "Note is new"; - insert(id, note); +void NotesModel::update(int id, const QJsonObject &json) { + if (id < 0) { + id = json.value(m_roleNames[IdRole]).toInt(-1); } - else { - if (m_notes.value(id) == note) { - qDebug() << "Note unchanged"; + if (id >= 0) { + qDebug() << "Updating note: " << id; + if (indexOfNoteById(id) < 0) { + qDebug() << "Note is new"; + insert(id, json); } else { - m_notes.insert(id, note); + setNote(json.toVariantMap(), id); emit dataChanged(index(indexOfNoteById(id)), index(indexOfNoteById(id))); - emit noteUpdated(id, note); qDebug() << "Note changed"; } } - 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); - } - } - } } -void NotesModel::remove(const int id) { +void NotesModel::remove(int id) { qDebug() << "Removing note: " << id; - if (m_notes.contains(id)) { + if (indexOfNoteById(id) >= 0) { beginRemoveRows(QModelIndex(), indexOfNoteById(id), indexOfNoteById(id)); - if (m_notes.remove(id) > 0) { - emit noteDeleted(id); - } - else { - qDebug() << "Note not found"; - } + deleteNote(id); endRemoveRows(); - } - if (mp_notesApi && mp_notesStore) { - if (sender() == mp_notesApi) { - mp_notesStore->deleteNote(id); - } - if (sender() == mp_notesStore) { - mp_notesApi->deleteNote(id); - } + qDebug() << "Note removed"; } } -void NotesModel::clear() { - qDebug() << "Clearing model"; - beginResetModel(); - m_notes.clear(); - endResetModel(); +int NotesModel::indexOfNoteById(int id) const { + int index = -1; + if (m_fileDir.exists() && !account().isEmpty()) { + index = m_fileDir.entryList().indexOf(QRegExp(QString("^%1.%2$").arg(id).arg(m_fileSuffix))); + } + return index; } -int NotesModel::indexOfNoteById(int id) const { - return std::distance(m_notes.begin(), m_notes.lowerBound(id)); +int NotesModel::idOfNoteByINdex(int index) const { + int id = -1; + if (m_fileDir.exists() && !account().isEmpty()) { + QFileInfo fileName = m_fileDir.entryInfoList().value(index); + bool ok; + id = fileName.baseName().toInt(&ok); + if (!ok) id = -1; + } + return id; } QHash NotesModel::roleNames() const { @@ -374,7 +323,7 @@ Qt::ItemFlags NotesModel::flags(const QModelIndex &index) const { } int NotesModel::rowCount(const QModelIndex &parent) const { - if (parent.column() == 0 && m_fileDir.exists() && !account().isEmpty()) { + if (m_fileDir.exists() && !account().isEmpty()) { return static_cast(m_fileDir.count()); } else { @@ -382,58 +331,39 @@ int NotesModel::rowCount(const QModelIndex &parent) const { } } -QVariant NotesModel::data(const QModelIndex &index, int role) { - if (role == ModifiedStringRole) +QVariant NotesModel::data(const QModelIndex &index, int role) const { return itemData(index).value(role); } bool NotesModel::setData(const QModelIndex &index, const QVariant &value, int role) { - return setItemData(index, QMap{ { role, value } } ); + if (setItemData(index, QMap{ { role, value } } )) { + emit dataChanged(index, index); + return true; + } + return false; } -QMap NotesModel::itemData(const QModelIndex &index) { +QMap NotesModel::itemData(const QModelIndex &index) const { QMap map; - if (index.isValid() && index.row() < m_files.size()) { - QMap::iterator i = m_files.begin(); - i += index.row(); - if (i.value().isReadable()) { - QJsonObject json = QJsonDocument::fromJson(i.value().readAll()).object(); - for (int role = IdRole; role <= ErrorMessageRole; ++role) { - map.insert(role, json.value(m_roleNames[role])); - } - } - else { - qDebug() << "File not readable: " << i.value().fileName(); + if (index.isValid() && index.row() < rowCount()) { + QVariantMap note = this->note(idOfNoteByINdex(index.row())); + for (int role = IdRole; role <= ErrorMessageRole; ++role) { + map.insert(role, note.value(m_roleNames[role])); } } return map; } bool NotesModel::setItemData(const QModelIndex &index, const QMap &roles) { - if (index.isValid() && index.row() < m_files.size()) { - QMap::iterator i = m_files.begin(); - i += index.row(); - if (i.value().isReadable() && i.value().isWritable()) { - QJsonObject json = QJsonDocument::fromJson(i.value().readAll()).object(); - QMapIterator i(roles); - while (i.hasNext()) { - i.next(); - json.insert(m_roleNames[i.key()], QJsonValue::fromVariant(i.value())); - } - - } + QVariantMap note; + if (index.isValid() && index.row() < rowCount()) { + note = this->note(idOfNoteByINdex(index.row())); + QMapIterator i(roles); + while (i.hasNext()) { + i.next(); + note.insert(m_roleNames[i.key()], i.value()); } - else { - qDebug() << "File not writable: " << i.value().fileName(); - } - - - //qDebug(); - bool retval = true; - QMapIterator role(roles); - while (role.hasNext()) { - role.next(); - retval &= setData(index, role.value(), role.key()); + return setNote(note, idOfNoteByINdex(index.row())); } - return retval; + return false; } diff --git a/src/notesmodel.h b/src/notesmodel.h index 7bdb6fa..2bbfaed 100644 --- a/src/notesmodel.h +++ b/src/notesmodel.h @@ -3,12 +3,13 @@ #include #include +#include +#include #include #include #include #include "note.h" #include "notesapi.h" -#include "notesstore.h" class NotesProxyModel : public QSortFilterProxyModel { Q_OBJECT @@ -50,6 +51,10 @@ public: explicit NotesModel(QObject *parent = nullptr); virtual ~NotesModel(); + Q_PROPERTY(QString account READ account WRITE setAccount NOTIFY accountChanged) + QString account() const; + void setAccount(const QString& account); + enum NoteRoles { IdRole = Qt::UserRole, ModifiedRole = Qt::UserRole + 1, @@ -68,57 +73,41 @@ public: Qt::ItemFlags flags(const QModelIndex &index) const; virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; - virtual QVariant data(const QModelIndex &index, int role); - virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + virtual QVariant data(const QModelIndex &index, int role) const; + //virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; virtual bool setData(const QModelIndex &index, const QVariant &value, int role); - virtual bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()); - virtual bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); - QMap itemData(const QModelIndex &index); + //virtual bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()); + //virtual bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + virtual QMap itemData(const QModelIndex &index) const; virtual bool setItemData(const QModelIndex &index, const QMap &roles); void setNotesApi(NotesApi* notesApi); - void setNotesStore(NotesStore *notesStore); - - QString account() const; - void setAccount(const QString& account); + int newNotePosition(const int id) const; Q_INVOKABLE const QVariantMap note(const int id) const; - Q_INVOKABLE bool setNote(const QVariantMap& note, int id = -1) const; + Q_INVOKABLE bool setNote(const QVariantMap& note, int id = -1); + Q_INVOKABLE bool deleteNote(const int id); public slots: - Q_INVOKABLE bool getAllNotes(const QStringList& exclude = QStringList()); - Q_INVOKABLE bool getNote(const int id, const QStringList& exclude = QStringList()); - Q_INVOKABLE bool createNote(const QJsonObject& note); - Q_INVOKABLE bool updateNote(const int id, const QJsonObject& note); - Q_INVOKABLE bool deleteNote(const int id); - Q_INVOKABLE bool syncNotes(); + void insert(int id, const QJsonObject& json); + void update(int id, const QJsonObject& json); + void remove(int id); - void insert(const int id, const QJsonObject& note); - void update(const int id, const QJsonObject& note); - void remove(const int id); - - Q_INVOKABLE void clear(); Q_INVOKABLE int indexOfNoteById(int id) const; + Q_INVOKABLE int idOfNoteByINdex(int index) const; signals: void accountChanged(const QString& account); - void allNotesChanged(const QList& ids); - void noteCreated(const int id, const QJsonObject& note); - void noteUpdated(const int id, const QJsonObject& note); - void noteDeleted(const int id); - void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles = QVector ()); private: - QMap m_notes; const static QHash m_roleNames; - QMap m_files; + //QMap m_files; QDir m_fileDir; const static QString m_fileSuffix; NotesApi* mp_notesApi; - NotesStore* mp_notesStore; }; #endif // NOTESMODEL_H diff --git a/src/notesstore.cpp b/src/notesstore.cpp deleted file mode 100644 index 89793dd..0000000 --- a/src/notesstore.cpp +++ /dev/null @@ -1,256 +0,0 @@ -#include "notesstore.h" -#include "note.h" - -#include -#include -#include - -const QString NotesStore::m_suffix = "json"; - -NotesStore::NotesStore(QString directory, QObject *parent) : QObject(parent) -{ - m_dir.setCurrent(directory); - m_dir.setPath(""); - m_dir.setFilter(QDir::Files); - m_dir.setNameFilters( { "*." + m_suffix } ); -} - -NotesStore::~NotesStore() { -} - -QString NotesStore::account() const { - if (m_dir != QDir(QStandardPaths::writableLocation(QStandardPaths::DataLocation))) { - return m_dir.path(); - } - return QString(); -} - -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 = QDir(QStandardPaths::writableLocation(QStandardPaths::DataLocation)); - } - if (!account.isEmpty()) { - m_dir.setPath(account); - 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(); - } -} - -const QList NotesStore::noteIds() { - QList ids; - if (m_dir.exists() && !account().isEmpty()) { - QFileInfoList files = m_dir.entryInfoList(); - for (int i = 0; i < files.size(); ++i) { - bool ok; - int id = files[i].baseName().toInt(&ok); - if (ok) { - ids << id; - } - } - } - else { - qDebug() << errorMessage(DirNotFoundError); - emit noteError(DirCannotReadError); - } - 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 Note::modified(readNoteFile(id, { "content" })); -} - -const QString NotesStore::errorMessage(ErrorCodes 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; -} - -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()); - if (file.exists()) { - if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { - QByteArray data = file.readAll(); - json = QJsonDocument::fromJson(data).object(); - file.close(); - 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 int id, const QJsonObject& note) { - bool success = false; - if (!account().isEmpty()) { - 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 = 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) { - bool success = false; - 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; - } - else { - emit noteError(FileCannotWriteError); - } - } - else { - emit noteError(FileNotFoundError); - } - } - return success; -} - -bool NotesStore::getAllNotes(const QStringList& exclude) { - qDebug() << "Getting all notes"; - const QList ids = noteIds(); - if (!ids.empty()) { - for (int i = 0; i < ids.size(); ++i) { - getNote(ids.at(i), exclude); - } - return true; - } - else { - return false; - } -} - -bool NotesStore::getNote(const int id, const QStringList& exclude) { - qDebug() << "Getting note: " << id; - QJsonObject note; - if (id >= 0) { - note = readNoteFile(id, exclude); - emit noteUpdated(id, note); - return true; - } - else { - qDebug() << "Skipping, invalid ID"; - } - return false; -} - -bool 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 (!noteExists(id)) { - if (writeNoteFile(id, note)) { - emit noteUpdated(id, note); - return true; - } - } - else { - qDebug() << "Note already exists"; - } - return false; -} - -bool 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"] = QJsonValue::fromVariant(QDateTime::currentDateTime().toTime_t()); - } - if (writeNoteFile(id, tmpNote)) { - emit noteUpdated(id, tmpNote); - return true; - } - } - else { - qDebug() << "Skipping, note is older" << QDateTime::fromTime_t(note.value("modified").toInt()) << QDateTime::fromTime_t(tmpNote.value("modified").toInt()); - } - } - else { - qDebug() << "Skipping, note is equal"; - } - } - else { - qDebug() << "Skipping, invalid ID"; - } - return false; -} - -bool NotesStore::deleteNote(const int id) { - qDebug() << "Deleting note: " << id; - if (removeNoteFile(id)) { - emit noteDeleted(id); - return true; - } - return false; -} diff --git a/src/notesstore.h b/src/notesstore.h deleted file mode 100644 index 090f5a0..0000000 --- a/src/notesstore.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef NOTESSTORE_H -#define NOTESSTORE_H - -#include -#include -#include -#include -#include - -class NotesStore : public QObject -{ - Q_OBJECT - -public: - explicit NotesStore( - QString directory = QStandardPaths::writableLocation(QStandardPaths::DataLocation), - QObject *parent = nullptr); - virtual ~NotesStore(); - - Q_PROPERTY(QString account READ account WRITE setAccount NOTIFY accountChanged) - - QString account() const; - void setAccount(const QString& account); - - const QList noteIds(); - bool noteExists(const int id); - int noteModified(const int id); - - enum ErrorCodes { - NoError, - FileNotFoundError, - FileCannotReadError, - FileCannotWriteError, - DirNotFoundError, - DirCannotReadError, - DirCannotWriteError - }; - Q_ENUM(ErrorCodes) - Q_INVOKABLE const QString errorMessage(ErrorCodes error) const; - - QJsonObject readNoteFile(const int id, const QStringList& exclude = QStringList()); - bool writeNoteFile(const int id, const QJsonObject& note); - bool removeNoteFile(const int id); - -public slots: - Q_INVOKABLE bool getAllNotes(const QStringList& exclude = QStringList()); - Q_INVOKABLE bool getNote(const int id, const QStringList& exclude = QStringList()); - Q_INVOKABLE bool createNote(const QJsonObject& note); - Q_INVOKABLE bool updateNote(const int id, const QJsonObject& note); - Q_INVOKABLE bool deleteNote(const int id); - -signals: - void accountChanged(const QString& account); - void allNotesChanged(const QList& ids); - void noteCreated(const int id, const QJsonObject& note); - void noteUpdated(const int id, const QJsonObject& note); - void noteDeleted(const int id); - void noteError(const ErrorCodes error); - -private: - QDir m_dir; - const static QString m_suffix; -}; - -#endif // NOTESSTORE_H diff --git a/translations/harbour-nextcloudnotes-de.ts b/translations/harbour-nextcloudnotes-de.ts index df3c953..55bcb14 100644 --- a/translations/harbour-nextcloudnotes-de.ts +++ b/translations/harbour-nextcloudnotes-de.ts @@ -362,41 +362,6 @@ 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 diff --git a/translations/harbour-nextcloudnotes-sv.ts b/translations/harbour-nextcloudnotes-sv.ts index 146a7e2..fae5dd2 100644 --- a/translations/harbour-nextcloudnotes-sv.ts +++ b/translations/harbour-nextcloudnotes-sv.ts @@ -362,41 +362,6 @@ - - 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 diff --git a/translations/harbour-nextcloudnotes.ts b/translations/harbour-nextcloudnotes.ts index bcf1d57..a92b47d 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 @@ -444,49 +444,6 @@ - - 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 @@ -883,27 +840,27 @@ You can also use other markdown syntax inside them. harbour-nextcloudnotes - + Notes - + Offline - + Synced - + API error - + File error