Making progess...

This commit is contained in:
Scharel Clemens 2020-04-12 20:27:54 +02:00
parent 34484748f5
commit 629d752dee
15 changed files with 313 additions and 226 deletions

View file

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

View file

@ -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(/^<li>(<p>)?\[ \] (.*)(<.*)$/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)
}
}

View file

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

View file

@ -126,6 +126,31 @@ const QJsonDocument Note::toJsonDocument() const {
return QJsonDocument(m_json);
}
Note::NoteField Note::noteFieldsFromStringList(QStringList fields) {
QList<NoteField> list = noteFields();
QFlags<NoteField> flags;
for (int i = 0; i < list.size(); ++i) {
if (fields.contains(noteFieldName(list[i]))) {
flags |= list[i];
}
}
return static_cast<NoteField>((int)flags);
}
QStringList Note::noteFieldsToStringList(NoteField fields) {
QList<NoteField> list = noteFields();
QFlags<NoteField> 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 {

View file

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

View file

@ -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 &note) {
createNote(Note(QJsonObject::fromVariantMap(note)));
}
void NotesApi::createNote(const Note &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_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 &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()) {
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 &note) {
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,16 +401,92 @@ 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);
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 (reply == m_ocsReply) {
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 (m_getNoteReplies.contains(reply)) {
qDebug() << "Get note reply";
if (reply->error() == QNetworkReply::NoError && json.isObject())
updateApiNote(json.object());
m_getNoteReplies.removeOne(reply);
}
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);
}
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 {
m_loginStatus = LoginStatus::LoginFailed;
emit loginStatusChanged(m_loginStatus);
}
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 (xml.contains("<status>ok</status>")) {
if (reply->error() == QNetworkReply::NoError && xml.contains("<status>ok</status>")) {
qDebug() << "Login Success!";
setLoginStatus(LoginSuccess);
}
@ -397,73 +494,14 @@ void NotesApi::replyFinished(QNetworkReply *reply) {
qDebug() << "Login Failed!";
setLoginStatus(LoginFailed);
}
m_ocsReplies.removeOne(reply);
}
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() << "Unknown reply";
}
//qDebug() << data;
}
}
else if (reply->error() == QNetworkReply::AuthenticationRequiredError) {
emit error(AuthenticationError);
}
else if (reply->error() == QNetworkReply::ContentNotFoundError && reply == m_pollReply) {
qDebug() << "Polling not finished yet" << reply->url().toDisplayString();
}
else {
if (reply == m_loginReply) {
m_loginReply = NULL;
m_loginStatus = LoginStatus::LoginFailed;
emit loginStatusChanged(m_loginStatus);
}
else if (reply == m_pollReply) {
m_pollReply = NULL;
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 if (m_notesReplies.contains(reply)) {
m_notesReplies.removeOne(reply);
emit busyChanged(busy());
}
if (reply != m_statusReply)
emit error(CommunicationError);
}
reply->deleteLater();
}

View file

@ -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<QNetworkReply*> 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<QNetworkReply*> m_loginReplies;
QVector<QNetworkReply*> 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<QNetworkReply*> m_ocsReplies;
// Nextcloud Notes API - https://github.com/nextcloud/notes/wiki/Notes-0.2
const QString m_notesEndpoint;
QVector<QNetworkReply*> m_notesReplies;
QVector<QNetworkReply*> m_getAllNotesReplies;
QVector<QNetworkReply*> m_getNoteReplies;
QVector<QNetworkReply*> m_createNoteReplies;
QVector<QNetworkReply*> m_updateNoteReplies;
QVector<QNetworkReply*> m_deleteNoteReplies;
void updateApiNotes(const QJsonArray& json);
void updateApiNote(const QJsonObject& json);
QDateTime m_lastSync;

View file

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

View file

@ -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<int, QVariant> item = sourceModel()->itemData(mapToSource(index));
QHash<int, QByteArray> names = roleNames();
QVariantMap note;
QMapIterator<int, QVariant> 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 &note) {
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.";

View file

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

View file

@ -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 &note) {
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 &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()) {

View file

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

View file

@ -262,14 +262,6 @@
<source>Failed to communicate with the Nextcloud server</source>
<translation>Fehler bei der Server-Kommunikation</translation>
</message>
<message>
<source>An error happened while reading from the local storage</source>
<translation>Fehler beim Lesen der lokalen Datei</translation>
</message>
<message>
<source>An error happened while writing to the local storage</source>
<translation>Fehler beim Schreiben der lokalen Datei</translation>
</message>
<message>
<source>An error occured while establishing an encrypted connection</source>
<translation>Fehler beim Aufbau einer verschlüsselten Kommunikation</translation>

View file

@ -262,14 +262,6 @@
<source>Failed to communicate with the Nextcloud server</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>An error happened while reading from the local storage</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>An error happened while writing to the local storage</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>An error occured while establishing an encrypted connection</source>
<translation type="unfinished"></translation>

View file

@ -133,98 +133,98 @@
<context>
<name>LoginPage</name>
<message>
<location filename="../qml/pages/LoginPage.qml" line="18"/>
<location filename="../qml/pages/LoginPage.qml" line="17"/>
<source>Nextcloud Login</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/LoginPage.qml" line="168"/>
<location filename="../qml/pages/LoginPage.qml" line="167"/>
<source>Nextcloud server</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/LoginPage.qml" line="227"/>
<location filename="../qml/pages/LoginPage.qml" line="226"/>
<source>Username</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/LoginPage.qml" line="238"/>
<location filename="../qml/pages/LoginPage.qml" line="237"/>
<source>Password</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/LoginPage.qml" line="213"/>
<location filename="../qml/pages/LoginPage.qml" line="212"/>
<source>Abort</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/LoginPage.qml" line="95"/>
<location filename="../qml/pages/LoginPage.qml" line="94"/>
<source>Follow the instructions in the browser</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/LoginPage.qml" line="104"/>
<location filename="../qml/pages/LoginPage.qml" line="103"/>
<source>Login successfull!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/LoginPage.qml" line="101"/>
<location filename="../qml/pages/LoginPage.qml" line="110"/>
<location filename="../qml/pages/LoginPage.qml" line="100"/>
<location filename="../qml/pages/LoginPage.qml" line="109"/>
<source>Login failed!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/LoginPage.qml" line="90"/>
<location filename="../qml/pages/LoginPage.qml" line="89"/>
<source>Enter your credentials</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/LoginPage.qml" line="213"/>
<location filename="../qml/pages/LoginPage.qml" line="212"/>
<source>Login</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/LoginPage.qml" line="213"/>
<location filename="../qml/pages/LoginPage.qml" line="212"/>
<source>Re-Login</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/LoginPage.qml" line="247"/>
<location filename="../qml/pages/LoginPage.qml" line="246"/>
<source>Test Login</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/LoginPage.qml" line="253"/>
<location filename="../qml/pages/LoginPage.qml" line="252"/>
<source>Note</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/LoginPage.qml" line="261"/>
<location filename="../qml/pages/LoginPage.qml" line="260"/>
<source>The &lt;a href=&quot;https://apps.nextcloud.com/apps/notes&quot;&gt;Notes&lt;/a&gt; app needs to be installed on the Nextcloud server for this app to work.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/LoginPage.qml" line="265"/>
<location filename="../qml/pages/LoginPage.qml" line="264"/>
<source>Security</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/LoginPage.qml" line="272"/>
<location filename="../qml/pages/LoginPage.qml" line="271"/>
<source>&lt;strong&gt;CAUTION: Your password will be saved without any encryption on the device!&lt;/strong&gt;&lt;br&gt;Please consider creating a dedicated app password! Open your Nextcloud in a browser and go to &lt;i&gt;Settings&lt;/i&gt; &lt;i&gt;Security&lt;/i&gt;.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/LoginPage.qml" line="276"/>
<location filename="../qml/pages/LoginPage.qml" line="275"/>
<source>Do not check certificates</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/LoginPage.qml" line="277"/>
<location filename="../qml/pages/LoginPage.qml" line="276"/>
<source>Enable this option to allow selfsigned certificates</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/LoginPage.qml" line="286"/>
<location filename="../qml/pages/LoginPage.qml" line="285"/>
<source>Allow unencrypted connections</source>
<translation type="unfinished"></translation>
</message>
@ -245,12 +245,12 @@
<context>
<name>Note</name>
<message>
<location filename="../src/note.cpp" line="306"/>
<location filename="../src/note.cpp" line="335"/>
<source>Today</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/note.cpp" line="308"/>
<location filename="../src/note.cpp" line="337"/>
<source>Yesterday</source>
<translation type="unfinished"></translation>
</message>
@ -258,52 +258,52 @@
<context>
<name>NotePage</name>
<message>
<location filename="../qml/pages/NotePage.qml" line="111"/>
<location filename="../qml/pages/NotePage.qml" line="117"/>
<source>Delete</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotePage.qml" line="115"/>
<location filename="../qml/pages/NotePage.qml" line="121"/>
<source>Reload</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotePage.qml" line="115"/>
<location filename="../qml/pages/NotePage.qml" line="121"/>
<source>Updating...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotePage.qml" line="121"/>
<location filename="../qml/pages/NotePage.qml" line="127"/>
<source>Last update</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotePage.qml" line="124"/>
<location filename="../qml/pages/NotePage.qml" line="130"/>
<source>never</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotePage.qml" line="131"/>
<location filename="../qml/pages/NotePage.qml" line="137"/>
<source>Edit</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotePage.qml" line="132"/>
<location filename="../qml/pages/NotePage.qml" line="138"/>
<source>Notes</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotePage.qml" line="252"/>
<location filename="../qml/pages/NotePage.qml" line="258"/>
<source>No category</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotePage.qml" line="253"/>
<location filename="../qml/pages/NotePage.qml" line="259"/>
<source>Category</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotePage.qml" line="268"/>
<location filename="../qml/pages/NotePage.qml" line="274"/>
<source>Modified</source>
<translation type="unfinished"></translation>
</message>
@ -311,37 +311,27 @@
<context>
<name>NotesApi</name>
<message>
<location filename="../src/notesapi.cpp" line="341"/>
<location filename="../src/notesapi.cpp" line="368"/>
<source>No network connection available</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/notesapi.cpp" line="344"/>
<location filename="../src/notesapi.cpp" line="371"/>
<source>Failed to communicate with the Nextcloud server</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/notesapi.cpp" line="347"/>
<source>An error happened while reading from the local storage</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/notesapi.cpp" line="350"/>
<source>An error happened while writing to the local storage</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/notesapi.cpp" line="353"/>
<location filename="../src/notesapi.cpp" line="374"/>
<source>An error occured while establishing an encrypted connection</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/notesapi.cpp" line="356"/>
<location filename="../src/notesapi.cpp" line="377"/>
<source>Could not authenticate to the Nextcloud instance</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/notesapi.cpp" line="359"/>
<location filename="../src/notesapi.cpp" line="380"/>
<source>Unknown error</source>
<translation type="unfinished"></translation>
</message>
@ -349,97 +339,97 @@
<context>
<name>NotesPage</name>
<message>
<location filename="../qml/pages/NotesPage.qml" line="30"/>
<location filename="../qml/pages/NotesPage.qml" line="29"/>
<source>Settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="34"/>
<location filename="../qml/pages/NotesPage.qml" line="33"/>
<source>Add note</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="39"/>
<location filename="../qml/pages/NotesPage.qml" line="38"/>
<source>Reload</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="39"/>
<location filename="../qml/pages/NotesPage.qml" line="38"/>
<source>Updating...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="45"/>
<location filename="../qml/pages/NotesPage.qml" line="44"/>
<source>Last update</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="48"/>
<location filename="../qml/pages/NotesPage.qml" line="47"/>
<source>never</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="58"/>
<location filename="../qml/pages/NotesPage.qml" line="57"/>
<source>Nextcloud Notes</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="199"/>
<location filename="../qml/pages/NotesPage.qml" line="188"/>
<source>Modified</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="202"/>
<location filename="../qml/pages/NotesPage.qml" line="191"/>
<source>Delete</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="204"/>
<location filename="../qml/pages/NotesPage.qml" line="193"/>
<source>Deleting note</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="234"/>
<location filename="../qml/pages/NotesPage.qml" line="224"/>
<source>Loading notes...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="240"/>
<location filename="../qml/pages/NotesPage.qml" line="230"/>
<source>No account yet</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="241"/>
<location filename="../qml/pages/NotesPage.qml" line="231"/>
<source>Got to the settings to add an account</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="247"/>
<location filename="../qml/pages/NotesPage.qml" line="237"/>
<source>No notes yet</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="248"/>
<location filename="../qml/pages/NotesPage.qml" line="238"/>
<source>Pull down to add a note</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="254"/>
<location filename="../qml/pages/NotesPage.qml" line="244"/>
<source>No result</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="255"/>
<location filename="../qml/pages/NotesPage.qml" line="245"/>
<source>Try another query</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="261"/>
<location filename="../qml/pages/NotesPage.qml" line="251"/>
<source>An error occurred</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/pages/NotesPage.qml" line="272"/>
<location filename="../qml/pages/NotesPage.qml" line="262"/>
<source>Open the settings to configure your Nextcloud accounts</source>
<translation type="unfinished"></translation>
</message>