Improved communication between the modules. Offline mode seems to work fine now. Some kind of synchronisation of offline changes is now needed

This commit is contained in:
Scharel Clemens 2020-04-13 19:13:14 +02:00
parent 629d752dee
commit 4ff667e3d1
15 changed files with 432 additions and 224 deletions

View file

@ -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

View file

@ -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 } )
}

View file

@ -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();

View file

@ -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();
}

View file

@ -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;

View file

@ -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<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 (!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<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 (!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 &note) {
createNote(Note(QJsonObject::fromVariantMap(note)));
}
void NotesApi::createNote(const Note &note) {
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 &note) {
Note newNote(QJsonObject::fromVariantMap(note));
newNote.setId(id);
updateNote(newNote);
}
void NotesApi::updateNote(const Note &note) {
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 &note) {
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<QSslError> &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);
}

View file

@ -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;

View file

@ -2,8 +2,7 @@
#define NOTESINTERFACE_H
#include <QObject>
#include "note.h"
#include <QJsonObject>
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);
};

View file

@ -70,16 +70,17 @@ QList<int> NotesModel::ids() const {
return ids;
}
int NotesModel::insertNote(const Note &note) {
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 &note) {
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 &note) {
qDebug() << "Removing note: " << note.id();
int position = m_notes.indexOf(note);
bool NotesModel::removeNote(const QJsonObject &note) {
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 &note) {
}
bool NotesModel::removeNote(int id) {
qDebug() << "Removing note: " << id;
return removeNote(Note(QJsonObject{ {"id", id} } ));
return removeNote(QJsonObject{ {"id", id} } );
}
void NotesModel::clear() {

View file

@ -66,8 +66,8 @@ public:
virtual bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles);
public slots:
int insertNote(const Note &note);
bool removeNote(const Note &note);
int insertNote(const int id, const QJsonObject& note);
bool removeNote(const QJsonObject& note);
bool removeNote(int id);
Q_INVOKABLE void clear();
Q_INVOKABLE QList<int> ids() const;

View file

@ -1,5 +1,7 @@
#include "notesstore.h"
#include <QJsonDocument>
#include <QDateTime>
#include <QDebug>
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 &note) {
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.<NUMBER>.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 &note) {
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<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]));
}
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 &note) 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;

View file

@ -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

View file

@ -274,6 +274,10 @@
<source>Unknown error</source>
<translation>Unbekannter Fehler</translation>
</message>
<message>
<source>No error</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>NotesPage</name>
@ -354,6 +358,41 @@
<translation>Ein Fehler ist aufgetreten</translation>
</message>
</context>
<context>
<name>NotesStore</name>
<message>
<source>File not found</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cannot read from the file</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cannot write to the file</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Directory not found</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cannot read from directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cannot create or write to directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unknown error</source>
<translation type="unfinished">Unbekannter Fehler</translation>
</message>
<message>
<source>No error</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsPage</name>
<message>
@ -686,8 +725,12 @@ You can also use other markdown syntax inside them.</source>
<translation>Synchronisiert</translation>
</message>
<message>
<source>Error</source>
<translation>Fehler</translation>
<source>API error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>File error</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>

View file

@ -274,6 +274,10 @@
<source>Unknown error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No error</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>NotesPage</name>
@ -354,6 +358,41 @@
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>NotesStore</name>
<message>
<source>File not found</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cannot read from the file</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cannot write to the file</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Directory not found</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cannot read from directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cannot create or write to directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unknown error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No error</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsPage</name>
<message>
@ -686,7 +725,11 @@ You can also use other markdown syntax inside them.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Error</source>
<source>API error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>File error</source>
<translation type="unfinished"></translation>
</message>
</context>

View file

@ -245,12 +245,12 @@
<context>
<name>Note</name>
<message>
<location filename="../src/note.cpp" line="335"/>
<location filename="../src/note.cpp" line="357"/>
<source>Today</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/note.cpp" line="337"/>
<location filename="../src/note.cpp" line="359"/>
<source>Yesterday</source>
<translation type="unfinished"></translation>
</message>
@ -311,27 +311,32 @@
<context>
<name>NotesApi</name>
<message>
<location filename="../src/notesapi.cpp" line="368"/>
<location filename="../src/notesapi.cpp" line="311"/>
<source>No error</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/notesapi.cpp" line="314"/>
<source>No network connection available</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/notesapi.cpp" line="371"/>
<location filename="../src/notesapi.cpp" line="317"/>
<source>Failed to communicate with the Nextcloud server</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/notesapi.cpp" line="374"/>
<location filename="../src/notesapi.cpp" line="320"/>
<source>An error occured while establishing an encrypted connection</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/notesapi.cpp" line="377"/>
<location filename="../src/notesapi.cpp" line="323"/>
<source>Could not authenticate to the Nextcloud instance</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/notesapi.cpp" line="380"/>
<location filename="../src/notesapi.cpp" line="326"/>
<source>Unknown error</source>
<translation type="unfinished"></translation>
</message>
@ -374,66 +379,109 @@
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="188"/>
<location filename="../qml/pages/NotesPage.qml" line="189"/>
<source>Modified</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="191"/>
<location filename="../qml/pages/NotesPage.qml" line="192"/>
<source>Delete</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="193"/>
<location filename="../qml/pages/NotesPage.qml" line="194"/>
<source>Deleting note</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="224"/>
<location filename="../qml/pages/NotesPage.qml" line="225"/>
<source>Loading notes...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="230"/>
<location filename="../qml/pages/NotesPage.qml" line="231"/>
<source>No account yet</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="231"/>
<location filename="../qml/pages/NotesPage.qml" line="232"/>
<source>Got to the settings to add an account</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="237"/>
<location filename="../qml/pages/NotesPage.qml" line="238"/>
<source>No notes yet</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="238"/>
<location filename="../qml/pages/NotesPage.qml" line="239"/>
<source>Pull down to add a note</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="244"/>
<location filename="../qml/pages/NotesPage.qml" line="245"/>
<source>No result</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="245"/>
<location filename="../qml/pages/NotesPage.qml" line="246"/>
<source>Try another query</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="251"/>
<location filename="../qml/pages/NotesPage.qml" line="252"/>
<source>An error occurred</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="262"/>
<location filename="../qml/pages/NotesPage.qml" line="263"/>
<source>Open the settings to configure your Nextcloud accounts</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>NotesStore</name>
<message>
<location filename="../src/notesstore.cpp" line="52"/>
<source>No error</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/notesstore.cpp" line="55"/>
<source>File not found</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/notesstore.cpp" line="58"/>
<source>Cannot read from the file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/notesstore.cpp" line="61"/>
<source>Cannot write to the file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/notesstore.cpp" line="64"/>
<source>Directory not found</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/notesstore.cpp" line="67"/>
<source>Cannot read from directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/notesstore.cpp" line="70"/>
<source>Cannot create or write to directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/notesstore.cpp" line="73"/>
<source>Unknown error</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsPage</name>
<message>
@ -844,9 +892,14 @@ You can also use other markdown syntax inside them.</source>
<source>Synced</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/harbour-nextcloudnotes.qml" line="141"/>
<source>API error</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/harbour-nextcloudnotes.qml" line="134"/>
<source>Error</source>
<source>File error</source>
<translation type="unfinished"></translation>
</message>
</context>