diff --git a/qml/pages/LoginPage.qml b/qml/pages/LoginPage.qml index 6543528..36c7306 100644 --- a/qml/pages/LoginPage.qml +++ b/qml/pages/LoginPage.qml @@ -1,7 +1,6 @@ import QtQuick 2.5 import Sailfish.Silica 1.0 import Nemo.Configuration 1.0 -import harbour.nextcloudnotes.notesapi 1.0 Page { id: loginPage diff --git a/qml/pages/NotePage.qml b/qml/pages/NotePage.qml index be4d1d3..9983f65 100644 --- a/qml/pages/NotePage.qml +++ b/qml/pages/NotePage.qml @@ -5,15 +5,9 @@ import "../js/showdown/dist/showdown.js" as ShowDown Dialog { id: noteDialog - property int id - property int modified - property string title - property string category - property string content - property bool favorite - property string etag - property bool error - property string errorMessage + property int index + property var note: notesModel.getNote(notesModel.index(index, 0)) + property var showdown: ShowDown.showdown property var converter: new showdown.Converter( @@ -50,9 +44,21 @@ Dialog { } } Component.onCompleted: { - console.log(title) parseContent() } + Connections { + target: notesModel + onDataChanged: { + console.log(topLeft, bottomRight, index) + if (notesModel.index(topLeft, bottomRight) === index) { + console.log("This note changed") + } + else { + console.log("Another note changed") + } + } + } + function reloadContent() { //notesApi.getNoteFromApi(id) @@ -66,7 +72,7 @@ Dialog { function parseContent() { //note = notesApi.getNoteFromApi(id, false) - var convertedText = converter.makeHtml(content) + var convertedText = converter.makeHtml(note["content"]) var occurence = -1 convertedText = convertedText.replace(/^
  • (

    )?\[ \] (.*)(<.*)$/gmi, function(match, p1, p2, p3, offset) { @@ -109,12 +115,12 @@ Dialog { MenuItem { text: qsTr("Delete") - onClicked: remorse.execute("Deleting", function() { notesApi.deleteNote(id) } ) + onClicked: remorse.execute("Deleting", function() { notesApi.deleteNote(note["id"]) } ) } MenuItem { text: enabled ? qsTr("Reload") : qsTr("Updating...") enabled: !notesApi.busy - onClicked: notesApi.getNote(id) + onClicked: notesApi.getNote(note["id"]) } MenuLabel { visible: appSettings.currentAccount.length >= 0 @@ -159,7 +165,7 @@ Dialog { onLinkActivated: { //console.log(link) var occurence = -1 - var newContent = content + var newContent = note["content"] if (/^tasklist:checkbox_(\d+)$/m.test(link)) { newContent = newContent.replace(/- \[ \] (.*)$/gm, function(match, p1, offset, string) { @@ -237,18 +243,18 @@ Dialog { width: parent.width - x IconButton { id: favoriteButton - property bool selected: favorite + property bool selected: note["favorite"] width: Theme.iconSizeMedium icon.source: (selected ? "image://theme/icon-m-favorite-selected?" : "image://theme/icon-m-favorite?") + (favoriteButton.highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor) onClicked: { - notesApi.updateNote(id, {'favorite': !favorite, 'modified': new Date().valueOf() / 1000 }) + notesApi.updateNote(note["id"], {'favorite': selected, 'modified': new Date().valueOf() / 1000 }) } } TextField { id: categoryField width: parent.width - favoriteButton.width - text: category + text: note["category"] placeholderText: qsTr("No category") label: qsTr("Category") EnterKey.iconSource: "image://theme/icon-m-enter-accept" @@ -266,7 +272,7 @@ Dialog { DetailItem { id: modifiedDetail label: qsTr("Modified") - value: new Date(noteDialog.modified * 1000).toLocaleString(Qt.locale(), Locale.ShortFormat) + value: new Date(note["modified"] * 1000).toLocaleString(Qt.locale(), Locale.ShortFormat) } } diff --git a/qml/pages/NotesPage.qml b/qml/pages/NotesPage.qml index 28eb247..dbec369 100644 --- a/qml/pages/NotesPage.qml +++ b/qml/pages/NotesPage.qml @@ -103,18 +103,7 @@ Page { id: remorse } - onClicked: pageStack.push(Qt.resolvedUrl("../pages/NotePage.qml"), - { //note: noteListModel.get(index), - id: id, - modified: modified, - title: title, - category: category, - content: content, - favorite: favorite, - etag: etag, - error: error, - errorMessage: errorMessage - }) + onClicked: pageStack.push(Qt.resolvedUrl("../pages/NotePage.qml"), { index: index } ) onPressAndHold: menu.open(note) Separator { @@ -131,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: { + notesStore.updateNote(id, {'favorite': !favorite, 'modified': new Date().valueOf() / 1000 } ) notesApi.updateNote(id, {'favorite': !favorite, 'modified': new Date().valueOf() / 1000 } ) } } @@ -201,6 +191,7 @@ Page { text: qsTr("Delete") onClicked: { remorse.execute(note, qsTr("Deleting note"), function() { + //notesStore.deleteNote(id) notesApi.deleteNote(id) }) } diff --git a/src/note.cpp b/src/note.cpp index a0f59de..ba24455 100644 --- a/src/note.cpp +++ b/src/note.cpp @@ -126,6 +126,31 @@ const QJsonDocument Note::toJsonDocument() const { return QJsonDocument(m_json); } +Note::NoteField Note::noteFieldsFromStringList(QStringList fields) { + QList list = noteFields(); + QFlags flags; + + for (int i = 0; i < list.size(); ++i) { + if (fields.contains(noteFieldName(list[i]))) { + flags |= list[i]; + } + } + return static_cast((int)flags); +} + +QStringList Note::noteFieldsToStringList(NoteField fields) { + QList list = noteFields(); + QFlags flags(fields); + QStringList stringList; + + for (int i = 0; i < list.size(); ++i) { + if (flags.testFlag(list[i])) { + stringList << noteFieldName(list[i]); + } + } + return stringList; +} + int Note::id() const { return m_json.value(noteFieldName(Id)).toInt(-1); } @@ -232,10 +257,14 @@ bool Note::favorite(const QJsonObject &jobj) { return jobj.value(noteFieldName(Favorite)).toBool(); } void Note::setFavorite(bool favorite) { - if (favorite != this->favorite()) { + if (favorite && favorite != this->favorite()) { m_json.insert(noteFieldName(Favorite), QJsonValue(favorite)); emit favoriteChanged(this->favorite()); } + else { + m_json.remove(noteFieldName(Favorite)); + emit favoriteChanged(this->favorite()); + } } QString Note::etag() const { diff --git a/src/note.h b/src/note.h index 7402df8..07607b0 100644 --- a/src/note.h +++ b/src/note.h @@ -57,6 +57,8 @@ public: Q_INVOKABLE static QStringList noteFieldNames() { return m_noteFieldNames.values(); } + Q_INVOKABLE static NoteField noteFieldsFromStringList(QStringList fields); + Q_INVOKABLE static QStringList noteFieldsToStringList(Note::NoteField fields); Q_PROPERTY(int id READ id WRITE setId NOTIFY idChanged) int id() const; diff --git a/src/notesapi.cpp b/src/notesapi.cpp index 490ee2c..47b9de3 100644 --- a/src/notesapi.cpp +++ b/src/notesapi.cpp @@ -11,9 +11,6 @@ NotesApi::NotesApi(const QString statusEndpoint, const QString loginEndpoint, co // TODO verify connections (also in destructor) m_loginPollTimer.setInterval(POLL_INTERVALL); connect(&m_loginPollTimer, SIGNAL(timeout()), this, SLOT(pollLoginUrl())); - m_statusReply = NULL; - m_loginReply = NULL; - m_pollReply = NULL; setNcStatusStatus(NextcloudStatus::NextcloudUnknown); setLoginStatus(LoginStatus::LoginUnknown); m_ncStatusStatus = NextcloudStatus::NextcloudUnknown; @@ -53,6 +50,10 @@ void NotesApi::setAccount(const QString &account) { } } +void NotesApi::getAllNotes(const QStringList exclude) { + getAllNotes(Note::noteFieldsFromStringList(exclude)); +} + void NotesApi::getAllNotes(Note::NoteField exclude) { qDebug() << "Getting all notes"; QUrl url = apiEndpointUrl(m_notesEndpoint); @@ -71,11 +72,15 @@ void NotesApi::getAllNotes(Note::NoteField exclude) { if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { qDebug() << "GET" << url.toDisplayString(); m_authenticatedRequest.setUrl(url); - m_notesReplies << m_manager.get(m_authenticatedRequest); + m_getAllNotesReplies << m_manager.get(m_authenticatedRequest); emit busyChanged(true); } } +void NotesApi::getNote(const int id, const QStringList exclude) { + getNote(id, Note::noteFieldsFromStringList(exclude)); +} + void NotesApi::getNote(const int id, Note::NoteField exclude) { qDebug() << "Getting note: " << id; QUrl url = apiEndpointUrl(m_notesEndpoint + QString("/%1").arg(id)); @@ -94,29 +99,39 @@ void NotesApi::getNote(const int id, Note::NoteField exclude) { if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { qDebug() << "GET" << url.toDisplayString(); m_authenticatedRequest.setUrl(url); - m_notesReplies << m_manager.get(m_authenticatedRequest); + m_getNoteReplies << m_manager.get(m_authenticatedRequest); emit busyChanged(true); } } +void NotesApi::createNote(const QVariantMap ¬e) { + createNote(Note(QJsonObject::fromVariantMap(note))); +} + void NotesApi::createNote(const Note ¬e) { 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_notesReplies << m_manager.post(m_authenticatedRequest, noteApiData(note)); + m_createNoteReplies << m_manager.post(m_authenticatedRequest, noteApiData(note)); 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()) { qDebug() << "PUT" << url.toDisplayString(); m_authenticatedRequest.setUrl(url); - m_notesReplies << m_manager.put(m_authenticatedRequest, noteApiData(note)); + m_updateNoteReplies << m_manager.put(m_authenticatedRequest, noteApiData(note)); emit busyChanged(true); } } @@ -127,11 +142,23 @@ void NotesApi::deleteNote(const int id) { if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { qDebug() << "DELETE" << url.toDisplayString(); m_authenticatedRequest.setUrl(url); - m_notesReplies << m_manager.deleteResource(m_authenticatedRequest); + m_deleteNoteReplies << m_manager.deleteResource(m_authenticatedRequest); emit busyChanged(true); } } +bool NotesApi::busy() const { + return !(m_getAllNotesReplies.empty() || + m_getNoteReplies.empty() || + m_createNoteReplies.empty() || + m_updateNoteReplies.empty() || + m_deleteNoteReplies.empty() || + m_statusReplies.empty() || + m_loginReplies.empty() || + m_pollReplies.empty() || + m_ocsReplies.empty()); +} + const QByteArray NotesApi::noteApiData(const Note ¬e) { QJsonObject json = note.toJsonObject(); json.remove(Note::noteFieldName(Note::Id)); @@ -264,7 +291,7 @@ bool NotesApi::getNcStatus() { if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { setNcStatusStatus(NextcloudStatus::NextcloudBusy); m_request.setUrl(url); - m_statusReply = m_manager.post(m_request, QByteArray()); + m_statusReplies << m_manager.post(m_request, QByteArray()); return true; } else { @@ -283,7 +310,7 @@ bool NotesApi::initiateFlowV2Login() { if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { setLoginStatus(LoginStatus::LoginFlowV2Initiating); m_request.setUrl(url); - m_loginReply = m_manager.post(m_request, QByteArray()); + m_loginReplies << m_manager.post(m_request, QByteArray()); return true; } else { @@ -307,7 +334,7 @@ void NotesApi::pollLoginUrl() { qDebug() << "POST" << m_pollUrl.toDisplayString(); if (m_pollUrl.isValid() && !m_pollUrl.scheme().isEmpty() && !m_pollUrl.host().isEmpty() && !m_pollToken.isEmpty()) { m_request.setUrl(m_pollUrl); - m_pollReply = m_manager.post(m_request, QByteArray("token=").append(m_pollToken)); + m_pollReplies << m_manager.post(m_request, QByteArray("token=").append(m_pollToken)); } else { qDebug() << "URL not valid!"; @@ -327,7 +354,7 @@ void NotesApi::verifyLogin(QString username, QString password) { if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { qDebug() << "GET" << url.toDisplayString(); m_ocsRequest.setUrl(url); - m_ocsReply = m_manager.get(m_ocsRequest); + m_ocsReplies << m_manager.get(m_ocsRequest); emit busyChanged(true); } } @@ -343,12 +370,6 @@ const QString NotesApi::errorMessage(ErrorCodes error) const { case CommunicationError: message = tr("Failed to communicate with the Nextcloud server"); break; - case LocalFileReadError: - message = tr("An error happened while reading from the local storage"); - break; - case LocalFileWriteError: - message = tr("An error happened while writing to the local storage"); - break; case SslHandshakeError: message = tr("An error occured while establishing an encrypted connection"); break; @@ -380,90 +401,107 @@ void NotesApi::onNetworkAccessibleChanged(QNetworkAccessManager::NetworkAccessib } void NotesApi::replyFinished(QNetworkReply *reply) { - //qDebug() << reply->error() << reply->errorString(); - if (reply->error() == QNetworkReply::NoError) { + if (reply->error() != QNetworkReply::NoError) + qDebug() << reply->error() << reply->errorString(); + + if (reply->error() == QNetworkReply::NoError) emit error(NoError); - - QByteArray data = reply->readAll(); - - if (reply == m_ocsReply) { - qDebug() << "OCS reply"; - QString xml(data); - if (xml.contains("ok")) { - qDebug() << "Login Success!"; - setLoginStatus(LoginSuccess); - } - else { - qDebug() << "Login Failed!"; - setLoginStatus(LoginFailed); - } - } - else { - QJsonDocument json = QJsonDocument::fromJson(data); - if (reply == m_loginReply) { - qDebug() << "Login reply"; - if (json.isObject()) - updateLoginFlow(json.object()); - m_loginReply = NULL; - } - else if (reply == m_pollReply) { - qDebug() << "Poll reply, finished"; - if (json.isObject()) - updateLoginCredentials(json.object()); - m_pollReply = NULL; - abortFlowV2Login(); - } - else if (reply == m_statusReply) { - qDebug() << "Status reply"; - if (json.isObject()) - updateNcStatus(json.object()); - m_statusReply = NULL; - } - else if (m_notesReplies.contains(reply)) { - qDebug() << "Notes reply"; - if (json.isArray()) - updateApiNotes(json.array()); - else if (json.isObject()) - updateApiNote(json.object()); - m_notesReplies.removeOne(reply); - emit busyChanged(busy()); - } - else { - qDebug() << "Unknown or double reply"; - } - //qDebug() << data; - } - } - else if (reply->error() == QNetworkReply::AuthenticationRequiredError) { + else if (reply->error() == QNetworkReply::AuthenticationRequiredError) emit error(AuthenticationError); + else if (reply->error() == QNetworkReply::ContentNotFoundError && m_pollReplies.contains(reply)) + emit error(NoError); + else + emit error(CommunicationError); + + QByteArray data = reply->readAll(); + QJsonDocument json = QJsonDocument::fromJson(data); + + if (m_getAllNotesReplies.contains(reply)) { + qDebug() << "Get all notes reply"; + if (reply->error() == QNetworkReply::NoError && json.isArray()) + updateApiNotes(json.array()); + m_getAllNotesReplies.removeOne(reply); } - else if (reply->error() == QNetworkReply::ContentNotFoundError && reply == m_pollReply) { - qDebug() << "Polling not finished yet" << reply->url().toDisplayString(); + else if (m_getNoteReplies.contains(reply)) { + qDebug() << "Get note reply"; + if (reply->error() == QNetworkReply::NoError && json.isObject()) + updateApiNote(json.object()); + m_getNoteReplies.removeOne(reply); } - else { - if (reply == m_loginReply) { - m_loginReply = NULL; + else if (m_createNoteReplies.contains(reply)) { + qDebug() << "Create note reply"; + if (reply->error() == QNetworkReply::NoError && json.isObject()) + updateApiNote(json.object()); + m_createNoteReplies.removeOne(reply); + } + else if (m_updateNoteReplies.contains(reply)) { + qDebug() << "Update note reply"; + if (reply->error() == QNetworkReply::NoError && json.isObject()) + updateApiNote(json.object()); + m_updateNoteReplies.removeOne(reply); + } + else if (m_deleteNoteReplies.contains(reply)) { + qDebug() << "Delete note reply"; + bool ok; + QString idString = reply->url().path().split('/', QString::SkipEmptyParts).last(); + int id = idString.toInt(&ok); + if (reply->error() == QNetworkReply::NoError && ok) + emit noteDeleted(id); + m_deleteNoteReplies.removeOne(reply); + } + else if (m_loginReplies.contains(reply)) { + qDebug() << "Login reply"; + if (reply->error() == QNetworkReply::NoError && json.isObject()) + updateLoginFlow(json.object()); + else { m_loginStatus = LoginStatus::LoginFailed; emit loginStatusChanged(m_loginStatus); } - else if (reply == m_pollReply) { - m_pollReply = NULL; + m_loginReplies.removeOne(reply); + } + else if (m_pollReplies.contains(reply)) { + qDebug() << "Poll reply, finished"; + if (reply->error() == QNetworkReply::NoError && json.isObject()) + updateLoginCredentials(json.object()); + else if (reply->error() == QNetworkReply::ContentNotFoundError) { + qDebug() << "Polling not finished yet" << reply->url().toDisplayString(); m_loginStatus = LoginStatus::LoginFlowV2Polling; emit loginStatusChanged(m_loginStatus); } - else if (reply == m_statusReply) { - m_statusReply = NULL; - updateNcStatus(QJsonObject()); - //m_statusStatus = RequestStatus::StatusError; - //emit statusStatusChanged(m_statusStatus); + else { + m_loginStatus = LoginStatus::LoginFailed; + emit loginStatusChanged(m_loginStatus); } - else if (m_notesReplies.contains(reply)) { - m_notesReplies.removeOne(reply); - emit busyChanged(busy()); - } - if (reply != m_statusReply) - emit error(CommunicationError); + m_pollReplies.removeOne(reply); + abortFlowV2Login(); } + else if (m_statusReplies.contains(reply)) { + qDebug() << "Status reply"; + if (reply->error() == QNetworkReply::NoError && json.isObject()) + updateNcStatus(json.object()); + else + updateNcStatus(QJsonObject()); + m_statusReplies.removeOne(reply); + } + else if (m_ocsReplies.contains(reply)) { + qDebug() << "OCS reply"; + QString xml(data); + if (reply->error() == QNetworkReply::NoError && xml.contains("ok")) { + qDebug() << "Login Success!"; + setLoginStatus(LoginSuccess); + } + else { + qDebug() << "Login Failed!"; + setLoginStatus(LoginFailed); + } + m_ocsReplies.removeOne(reply); + } + else { + qDebug() << "Unknown reply"; + } + //qDebug() << data; + + emit busyChanged(busy()); reply->deleteLater(); } diff --git a/src/notesapi.h b/src/notesapi.h index b51bd10..a144904 100644 --- a/src/notesapi.h +++ b/src/notesapi.h @@ -91,7 +91,7 @@ public: QDateTime lastSync() const { return m_lastSync; } - bool busy() const { return !m_notesReplies.empty();; } + bool busy() const; enum NextcloudStatus { NextcloudUnknown, // Nothing known about the nextcloud server @@ -134,8 +134,6 @@ public: NoError, NoConnectionError, CommunicationError, - LocalFileReadError, - LocalFileWriteError, SslHandshakeError, AuthenticationError }; @@ -146,10 +144,14 @@ public: void setAccount(const QString& account); public slots: - Q_INVOKABLE void getAllNotes(Note::NoteField exclude = Note::None); - Q_INVOKABLE void getNote(const int id, Note::NoteField exclude = Note::None); - Q_INVOKABLE void createNote(const Note& note); - Q_INVOKABLE void updateNote(const Note& note); + Q_INVOKABLE void 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 deleteNote(const int id); signals: @@ -205,7 +207,7 @@ private: // Nextcloud status.php const QString m_statusEndpoint; - QNetworkReply* m_statusReply; + QVector m_statusReplies; void updateNcStatus(const QJsonObject &status); NextcloudStatus m_ncStatusStatus; void setNcStatusStatus(NextcloudStatus status, bool *changed = NULL); @@ -220,8 +222,8 @@ private: // Nextcloud Login Flow v2 - https://docs.nextcloud.com/server/18/developer_manual/client_apis/LoginFlow/index.html#login-flow-v2 const QString m_loginEndpoint; - QNetworkReply* m_loginReply; - QNetworkReply* m_pollReply; + QVector m_loginReplies; + QVector m_pollReplies; bool updateLoginFlow(const QJsonObject &login); bool updateLoginCredentials(const QJsonObject &credentials); LoginStatus m_loginStatus; @@ -233,11 +235,15 @@ private: // Nextcloud OCS API - https://docs.nextcloud.com/server/18/developer_manual/client_apis/OCS/ocs-api-overview.html const QString m_ocsEndpoint; - QNetworkReply* m_ocsReply; + QVector m_ocsReplies; // Nextcloud Notes API - https://github.com/nextcloud/notes/wiki/Notes-0.2 const QString m_notesEndpoint; - QVector m_notesReplies; + QVector m_getAllNotesReplies; + QVector m_getNoteReplies; + QVector m_createNoteReplies; + QVector m_updateNoteReplies; + QVector m_deleteNoteReplies; void updateApiNotes(const QJsonArray& json); void updateApiNote(const QJsonObject& json); QDateTime m_lastSync; diff --git a/src/notesinterface.h b/src/notesinterface.h index 994d532..c05aec8 100644 --- a/src/notesinterface.h +++ b/src/notesinterface.h @@ -13,22 +13,29 @@ class NotesInterface : public QObject Q_CLASSINFO("url", "https://github.com/scharel/harbour-nextcloudnotes") public: - explicit NotesInterface(QObject *parent = nullptr) : QObject(parent) { } + explicit NotesInterface(QObject *parent = nullptr) : QObject(parent) { + } virtual QString account() const = 0; virtual void setAccount(const QString& account) = 0; public slots: - Q_INVOKABLE virtual void getAllNotes(Note::NoteField exclude = Note::None) = 0; - Q_INVOKABLE virtual void getNote(const int id, Note::NoteField exclude = Note::None) = 0; - Q_INVOKABLE virtual void createNote(const Note& note) = 0; - Q_INVOKABLE virtual void updateNote(const Note& note) = 0; + Q_INVOKABLE virtual void 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 deleteNote(const int id) = 0; signals: void accountChanged(const QString& account); + void noteUpdated(const QVariantMap& note); void noteUpdated(const Note& note); void noteDeleted(const int id); + }; #endif // NOTESINTERFACE_H diff --git a/src/notesmodel.cpp b/src/notesmodel.cpp index 5a60e43..39be5cd 100644 --- a/src/notesmodel.cpp +++ b/src/notesmodel.cpp @@ -27,6 +27,18 @@ 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()) @@ -63,7 +75,7 @@ int NotesModel::insertNote(const Note ¬e) { int position = m_notes.indexOf(note); if (position >= 0) { if (m_notes.at(position).equal(note)) { - qDebug() << "Note already present unchanged."; + qDebug() << "Note already present and unchanged."; } else { qDebug() << "Note already present, updating it."; diff --git a/src/notesmodel.h b/src/notesmodel.h index 1744353..ad587b7 100644 --- a/src/notesmodel.h +++ b/src/notesmodel.h @@ -20,6 +20,7 @@ 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; diff --git a/src/notesstore.cpp b/src/notesstore.cpp index 4abdfc2..8cdb308 100644 --- a/src/notesstore.cpp +++ b/src/notesstore.cpp @@ -39,6 +39,10 @@ void NotesStore::setAccount(const QString& account) { } } +void NotesStore::getAllNotes(const QStringList exclude) { + getAllNotes(Note::noteFieldsFromStringList(exclude)); +} + void NotesStore::getAllNotes(Note::NoteField exclude) { qDebug() << "Getting all notes"; QFileInfoList files = m_dir.entryInfoList(); @@ -51,6 +55,10 @@ void NotesStore::getAllNotes(Note::NoteField 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) { @@ -60,6 +68,10 @@ void NotesStore::getNote(const int id, Note::NoteField exclude) { } } +void NotesStore::createNote(const QVariantMap ¬e) { + createNote(Note(QJsonObject::fromVariantMap(note))); +} + void NotesStore::createNote(const Note& note) { qDebug() << "Creating note: " << note.id(); if (!note.isValid()) { @@ -76,6 +88,12 @@ 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()) { diff --git a/src/notesstore.h b/src/notesstore.h index 82b4e09..b9a0c64 100644 --- a/src/notesstore.h +++ b/src/notesstore.h @@ -21,10 +21,14 @@ public: void setAccount(const QString& account); public slots: - Q_INVOKABLE void getAllNotes(Note::NoteField exclude = Note::None); - Q_INVOKABLE void getNote(const int id, Note::NoteField exclude = Note::None); - Q_INVOKABLE void createNote(const Note& note); - Q_INVOKABLE void updateNote(const Note& note); + Q_INVOKABLE void 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 deleteNote(const int id); private: diff --git a/translations/harbour-nextcloudnotes-de.ts b/translations/harbour-nextcloudnotes-de.ts index 163705a..bc012af 100644 --- a/translations/harbour-nextcloudnotes-de.ts +++ b/translations/harbour-nextcloudnotes-de.ts @@ -262,14 +262,6 @@ Failed to communicate with the Nextcloud server Fehler bei der Server-Kommunikation - - An error happened while reading from the local storage - Fehler beim Lesen der lokalen Datei - - - An error happened while writing to the local storage - Fehler beim Schreiben der lokalen Datei - An error occured while establishing an encrypted connection Fehler beim Aufbau einer verschlüsselten Kommunikation diff --git a/translations/harbour-nextcloudnotes-sv.ts b/translations/harbour-nextcloudnotes-sv.ts index d851a61..7e0daa2 100644 --- a/translations/harbour-nextcloudnotes-sv.ts +++ b/translations/harbour-nextcloudnotes-sv.ts @@ -262,14 +262,6 @@ Failed to communicate with the Nextcloud server - - An error happened while reading from the local storage - - - - An error happened while writing to the local storage - - An error occured while establishing an encrypted connection diff --git a/translations/harbour-nextcloudnotes.ts b/translations/harbour-nextcloudnotes.ts index e2dc511..02d02ba 100644 --- a/translations/harbour-nextcloudnotes.ts +++ b/translations/harbour-nextcloudnotes.ts @@ -133,98 +133,98 @@ LoginPage - + Nextcloud Login - + Nextcloud server - + Username - + Password - + Abort - + Follow the instructions in the browser - + Login successfull! - - + + Login failed! - + Enter your credentials - + Login - + Re-Login - + Test Login - + Note - + The <a href="https://apps.nextcloud.com/apps/notes">Notes</a> app needs to be installed on the Nextcloud server for this app to work. - + Security - + <strong>CAUTION: Your password will be saved without any encryption on the device!</strong><br>Please consider creating a dedicated app password! Open your Nextcloud in a browser and go to <i>Settings</i> → <i>Security</i>. - + Do not check certificates - + Enable this option to allow selfsigned certificates - + Allow unencrypted connections @@ -245,12 +245,12 @@ Note - + Today - + Yesterday @@ -258,52 +258,52 @@ NotePage - + Delete - + Reload - + Updating... - + Last update - + never - + Edit - + Notes - + No category - + Category - + Modified @@ -311,37 +311,27 @@ NotesApi - + No network connection available - + Failed to communicate with the Nextcloud server - - An error happened while reading from the local storage - - - - - An error happened while writing to the local storage - - - - + An error occured while establishing an encrypted connection - + Could not authenticate to the Nextcloud instance - + Unknown error @@ -349,97 +339,97 @@ NotesPage - + Settings - + Add note - + Reload - + Updating... - + Last update - + never - + Nextcloud Notes - + 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