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 QtQuick 2.5
import Sailfish.Silica 1.0 import Sailfish.Silica 1.0
import Nemo.Configuration 1.0 import Nemo.Configuration 1.0
import harbour.nextcloudnotes.notesapi 1.0
Page { Page {
id: loginPage id: loginPage

View file

@ -5,15 +5,9 @@ import "../js/showdown/dist/showdown.js" as ShowDown
Dialog { Dialog {
id: noteDialog id: noteDialog
property int id property int index
property int modified property var note: notesModel.getNote(notesModel.index(index, 0))
property string title
property string category
property string content
property bool favorite
property string etag
property bool error
property string errorMessage
property var showdown: ShowDown.showdown property var showdown: ShowDown.showdown
property var converter: new showdown.Converter( property var converter: new showdown.Converter(
@ -50,9 +44,21 @@ Dialog {
} }
} }
Component.onCompleted: { Component.onCompleted: {
console.log(title)
parseContent() 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() { function reloadContent() {
//notesApi.getNoteFromApi(id) //notesApi.getNoteFromApi(id)
@ -66,7 +72,7 @@ Dialog {
function parseContent() { function parseContent() {
//note = notesApi.getNoteFromApi(id, false) //note = notesApi.getNoteFromApi(id, false)
var convertedText = converter.makeHtml(content) var convertedText = converter.makeHtml(note["content"])
var occurence = -1 var occurence = -1
convertedText = convertedText.replace(/^<li>(<p>)?\[ \] (.*)(<.*)$/gmi, convertedText = convertedText.replace(/^<li>(<p>)?\[ \] (.*)(<.*)$/gmi,
function(match, p1, p2, p3, offset) { function(match, p1, p2, p3, offset) {
@ -109,12 +115,12 @@ Dialog {
MenuItem { MenuItem {
text: qsTr("Delete") text: qsTr("Delete")
onClicked: remorse.execute("Deleting", function() { notesApi.deleteNote(id) } ) onClicked: remorse.execute("Deleting", function() { notesApi.deleteNote(note["id"]) } )
} }
MenuItem { MenuItem {
text: enabled ? qsTr("Reload") : qsTr("Updating...") text: enabled ? qsTr("Reload") : qsTr("Updating...")
enabled: !notesApi.busy enabled: !notesApi.busy
onClicked: notesApi.getNote(id) onClicked: notesApi.getNote(note["id"])
} }
MenuLabel { MenuLabel {
visible: appSettings.currentAccount.length >= 0 visible: appSettings.currentAccount.length >= 0
@ -159,7 +165,7 @@ Dialog {
onLinkActivated: { onLinkActivated: {
//console.log(link) //console.log(link)
var occurence = -1 var occurence = -1
var newContent = content var newContent = note["content"]
if (/^tasklist:checkbox_(\d+)$/m.test(link)) { if (/^tasklist:checkbox_(\d+)$/m.test(link)) {
newContent = newContent.replace(/- \[ \] (.*)$/gm, newContent = newContent.replace(/- \[ \] (.*)$/gm,
function(match, p1, offset, string) { function(match, p1, offset, string) {
@ -237,18 +243,18 @@ Dialog {
width: parent.width - x width: parent.width - x
IconButton { IconButton {
id: favoriteButton id: favoriteButton
property bool selected: favorite property bool selected: note["favorite"]
width: Theme.iconSizeMedium width: Theme.iconSizeMedium
icon.source: (selected ? "image://theme/icon-m-favorite-selected?" : "image://theme/icon-m-favorite?") + icon.source: (selected ? "image://theme/icon-m-favorite-selected?" : "image://theme/icon-m-favorite?") +
(favoriteButton.highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor) (favoriteButton.highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor)
onClicked: { onClicked: {
notesApi.updateNote(id, {'favorite': !favorite, 'modified': new Date().valueOf() / 1000 }) notesApi.updateNote(note["id"], {'favorite': selected, 'modified': new Date().valueOf() / 1000 })
} }
} }
TextField { TextField {
id: categoryField id: categoryField
width: parent.width - favoriteButton.width width: parent.width - favoriteButton.width
text: category text: note["category"]
placeholderText: qsTr("No category") placeholderText: qsTr("No category")
label: qsTr("Category") label: qsTr("Category")
EnterKey.iconSource: "image://theme/icon-m-enter-accept" EnterKey.iconSource: "image://theme/icon-m-enter-accept"
@ -266,7 +272,7 @@ Dialog {
DetailItem { DetailItem {
id: modifiedDetail id: modifiedDetail
label: qsTr("Modified") 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 id: remorse
} }
onClicked: pageStack.push(Qt.resolvedUrl("../pages/NotePage.qml"), onClicked: pageStack.push(Qt.resolvedUrl("../pages/NotePage.qml"), { index: index } )
{ //note: noteListModel.get(index),
id: id,
modified: modified,
title: title,
category: category,
content: content,
favorite: favorite,
etag: etag,
error: error,
errorMessage: errorMessage
})
onPressAndHold: menu.open(note) onPressAndHold: menu.open(note)
Separator { Separator {
@ -131,6 +120,7 @@ Page {
icon.source: (favorite ? "image://theme/icon-m-favorite-selected?" : "image://theme/icon-m-favorite?") + icon.source: (favorite ? "image://theme/icon-m-favorite-selected?" : "image://theme/icon-m-favorite?") +
(note.highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor) (note.highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor)
onClicked: { onClicked: {
notesStore.updateNote(id, {'favorite': !favorite, 'modified': new Date().valueOf() / 1000 } )
notesApi.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") text: qsTr("Delete")
onClicked: { onClicked: {
remorse.execute(note, qsTr("Deleting note"), function() { remorse.execute(note, qsTr("Deleting note"), function() {
//notesStore.deleteNote(id)
notesApi.deleteNote(id) notesApi.deleteNote(id)
}) })
} }

View file

@ -126,6 +126,31 @@ const QJsonDocument Note::toJsonDocument() const {
return QJsonDocument(m_json); 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 { int Note::id() const {
return m_json.value(noteFieldName(Id)).toInt(-1); return m_json.value(noteFieldName(Id)).toInt(-1);
} }
@ -232,10 +257,14 @@ bool Note::favorite(const QJsonObject &jobj) {
return jobj.value(noteFieldName(Favorite)).toBool(); return jobj.value(noteFieldName(Favorite)).toBool();
} }
void Note::setFavorite(bool favorite) { void Note::setFavorite(bool favorite) {
if (favorite != this->favorite()) { if (favorite && favorite != this->favorite()) {
m_json.insert(noteFieldName(Favorite), QJsonValue(favorite)); m_json.insert(noteFieldName(Favorite), QJsonValue(favorite));
emit favoriteChanged(this->favorite()); emit favoriteChanged(this->favorite());
} }
else {
m_json.remove(noteFieldName(Favorite));
emit favoriteChanged(this->favorite());
}
} }
QString Note::etag() const { QString Note::etag() const {

View file

@ -57,6 +57,8 @@ public:
Q_INVOKABLE static QStringList noteFieldNames() { Q_INVOKABLE static QStringList noteFieldNames() {
return m_noteFieldNames.values(); 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) Q_PROPERTY(int id READ id WRITE setId NOTIFY idChanged)
int id() const; int id() const;

View file

@ -11,9 +11,6 @@ NotesApi::NotesApi(const QString statusEndpoint, const QString loginEndpoint, co
// TODO verify connections (also in destructor) // TODO verify connections (also in destructor)
m_loginPollTimer.setInterval(POLL_INTERVALL); m_loginPollTimer.setInterval(POLL_INTERVALL);
connect(&m_loginPollTimer, SIGNAL(timeout()), this, SLOT(pollLoginUrl())); connect(&m_loginPollTimer, SIGNAL(timeout()), this, SLOT(pollLoginUrl()));
m_statusReply = NULL;
m_loginReply = NULL;
m_pollReply = NULL;
setNcStatusStatus(NextcloudStatus::NextcloudUnknown); setNcStatusStatus(NextcloudStatus::NextcloudUnknown);
setLoginStatus(LoginStatus::LoginUnknown); setLoginStatus(LoginStatus::LoginUnknown);
m_ncStatusStatus = NextcloudStatus::NextcloudUnknown; 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) { void NotesApi::getAllNotes(Note::NoteField exclude) {
qDebug() << "Getting all notes"; qDebug() << "Getting all notes";
QUrl url = apiEndpointUrl(m_notesEndpoint); QUrl url = apiEndpointUrl(m_notesEndpoint);
@ -71,11 +72,15 @@ void NotesApi::getAllNotes(Note::NoteField exclude) {
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
qDebug() << "GET" << url.toDisplayString(); qDebug() << "GET" << url.toDisplayString();
m_authenticatedRequest.setUrl(url); m_authenticatedRequest.setUrl(url);
m_notesReplies << m_manager.get(m_authenticatedRequest); m_getAllNotesReplies << m_manager.get(m_authenticatedRequest);
emit busyChanged(true); 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) { void NotesApi::getNote(const int id, Note::NoteField exclude) {
qDebug() << "Getting note: " << id; qDebug() << "Getting note: " << id;
QUrl url = apiEndpointUrl(m_notesEndpoint + QString("/%1").arg(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()) { if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
qDebug() << "GET" << url.toDisplayString(); qDebug() << "GET" << url.toDisplayString();
m_authenticatedRequest.setUrl(url); m_authenticatedRequest.setUrl(url);
m_notesReplies << m_manager.get(m_authenticatedRequest); m_getNoteReplies << m_manager.get(m_authenticatedRequest);
emit busyChanged(true); emit busyChanged(true);
} }
} }
void NotesApi::createNote(const QVariantMap &note) {
createNote(Note(QJsonObject::fromVariantMap(note)));
}
void NotesApi::createNote(const Note &note) { void NotesApi::createNote(const Note &note) {
qDebug() << "Creating note"; qDebug() << "Creating note";
QUrl url = apiEndpointUrl(m_notesEndpoint); QUrl url = apiEndpointUrl(m_notesEndpoint);
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
qDebug() << "POST" << url.toDisplayString(); qDebug() << "POST" << url.toDisplayString();
m_authenticatedRequest.setUrl(url); 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); 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) { void NotesApi::updateNote(const Note &note) {
qDebug() << "Updating note: " << note.id(); qDebug() << "Updating note: " << note.id();
QUrl url = apiEndpointUrl(m_notesEndpoint + QString("/%1").arg(note.id())); QUrl url = apiEndpointUrl(m_notesEndpoint + QString("/%1").arg(note.id()));
if (note.isValid() && url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { if (note.isValid() && url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
qDebug() << "PUT" << url.toDisplayString(); qDebug() << "PUT" << url.toDisplayString();
m_authenticatedRequest.setUrl(url); 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); emit busyChanged(true);
} }
} }
@ -127,11 +142,23 @@ void NotesApi::deleteNote(const int id) {
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
qDebug() << "DELETE" << url.toDisplayString(); qDebug() << "DELETE" << url.toDisplayString();
m_authenticatedRequest.setUrl(url); m_authenticatedRequest.setUrl(url);
m_notesReplies << m_manager.deleteResource(m_authenticatedRequest); m_deleteNoteReplies << m_manager.deleteResource(m_authenticatedRequest);
emit busyChanged(true); 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) { const QByteArray NotesApi::noteApiData(const Note &note) {
QJsonObject json = note.toJsonObject(); QJsonObject json = note.toJsonObject();
json.remove(Note::noteFieldName(Note::Id)); json.remove(Note::noteFieldName(Note::Id));
@ -264,7 +291,7 @@ bool NotesApi::getNcStatus() {
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
setNcStatusStatus(NextcloudStatus::NextcloudBusy); setNcStatusStatus(NextcloudStatus::NextcloudBusy);
m_request.setUrl(url); m_request.setUrl(url);
m_statusReply = m_manager.post(m_request, QByteArray()); m_statusReplies << m_manager.post(m_request, QByteArray());
return true; return true;
} }
else { else {
@ -283,7 +310,7 @@ bool NotesApi::initiateFlowV2Login() {
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
setLoginStatus(LoginStatus::LoginFlowV2Initiating); setLoginStatus(LoginStatus::LoginFlowV2Initiating);
m_request.setUrl(url); m_request.setUrl(url);
m_loginReply = m_manager.post(m_request, QByteArray()); m_loginReplies << m_manager.post(m_request, QByteArray());
return true; return true;
} }
else { else {
@ -307,7 +334,7 @@ void NotesApi::pollLoginUrl() {
qDebug() << "POST" << m_pollUrl.toDisplayString(); qDebug() << "POST" << m_pollUrl.toDisplayString();
if (m_pollUrl.isValid() && !m_pollUrl.scheme().isEmpty() && !m_pollUrl.host().isEmpty() && !m_pollToken.isEmpty()) { if (m_pollUrl.isValid() && !m_pollUrl.scheme().isEmpty() && !m_pollUrl.host().isEmpty() && !m_pollToken.isEmpty()) {
m_request.setUrl(m_pollUrl); 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 { else {
qDebug() << "URL not valid!"; qDebug() << "URL not valid!";
@ -327,7 +354,7 @@ void NotesApi::verifyLogin(QString username, QString password) {
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) { if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
qDebug() << "GET" << url.toDisplayString(); qDebug() << "GET" << url.toDisplayString();
m_ocsRequest.setUrl(url); m_ocsRequest.setUrl(url);
m_ocsReply = m_manager.get(m_ocsRequest); m_ocsReplies << m_manager.get(m_ocsRequest);
emit busyChanged(true); emit busyChanged(true);
} }
} }
@ -343,12 +370,6 @@ const QString NotesApi::errorMessage(ErrorCodes error) const {
case CommunicationError: case CommunicationError:
message = tr("Failed to communicate with the Nextcloud server"); message = tr("Failed to communicate with the Nextcloud server");
break; 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: case SslHandshakeError:
message = tr("An error occured while establishing an encrypted connection"); message = tr("An error occured while establishing an encrypted connection");
break; break;
@ -380,90 +401,107 @@ void NotesApi::onNetworkAccessibleChanged(QNetworkAccessManager::NetworkAccessib
} }
void NotesApi::replyFinished(QNetworkReply *reply) { 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); emit error(NoError);
else if (reply->error() == QNetworkReply::AuthenticationRequiredError)
QByteArray data = reply->readAll();
if (reply == m_ocsReply) {
qDebug() << "OCS reply";
QString xml(data);
if (xml.contains("<status>ok</status>")) {
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) {
emit error(AuthenticationError); 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) { else if (m_getNoteReplies.contains(reply)) {
qDebug() << "Polling not finished yet" << reply->url().toDisplayString(); qDebug() << "Get note reply";
if (reply->error() == QNetworkReply::NoError && json.isObject())
updateApiNote(json.object());
m_getNoteReplies.removeOne(reply);
} }
else { else if (m_createNoteReplies.contains(reply)) {
if (reply == m_loginReply) { qDebug() << "Create note reply";
m_loginReply = NULL; 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; m_loginStatus = LoginStatus::LoginFailed;
emit loginStatusChanged(m_loginStatus); emit loginStatusChanged(m_loginStatus);
} }
else if (reply == m_pollReply) { m_loginReplies.removeOne(reply);
m_pollReply = NULL; }
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; m_loginStatus = LoginStatus::LoginFlowV2Polling;
emit loginStatusChanged(m_loginStatus); emit loginStatusChanged(m_loginStatus);
} }
else if (reply == m_statusReply) { else {
m_statusReply = NULL; m_loginStatus = LoginStatus::LoginFailed;
updateNcStatus(QJsonObject()); emit loginStatusChanged(m_loginStatus);
//m_statusStatus = RequestStatus::StatusError;
//emit statusStatusChanged(m_statusStatus);
} }
else if (m_notesReplies.contains(reply)) { m_pollReplies.removeOne(reply);
m_notesReplies.removeOne(reply); abortFlowV2Login();
emit busyChanged(busy());
}
if (reply != m_statusReply)
emit error(CommunicationError);
} }
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("<status>ok</status>")) {
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(); reply->deleteLater();
} }

View file

@ -91,7 +91,7 @@ public:
QDateTime lastSync() const { return m_lastSync; } QDateTime lastSync() const { return m_lastSync; }
bool busy() const { return !m_notesReplies.empty();; } bool busy() const;
enum NextcloudStatus { enum NextcloudStatus {
NextcloudUnknown, // Nothing known about the nextcloud server NextcloudUnknown, // Nothing known about the nextcloud server
@ -134,8 +134,6 @@ public:
NoError, NoError,
NoConnectionError, NoConnectionError,
CommunicationError, CommunicationError,
LocalFileReadError,
LocalFileWriteError,
SslHandshakeError, SslHandshakeError,
AuthenticationError AuthenticationError
}; };
@ -146,10 +144,14 @@ public:
void setAccount(const QString& account); void setAccount(const QString& account);
public slots: public slots:
Q_INVOKABLE void getAllNotes(Note::NoteField exclude = Note::None); Q_INVOKABLE void getAllNotes(const QStringList exclude = QStringList());
Q_INVOKABLE void getNote(const int id, Note::NoteField exclude = Note::None); void getAllNotes(Note::NoteField exclude);
Q_INVOKABLE void createNote(const Note& note); Q_INVOKABLE void getNote(const int id, const QStringList exclude = QStringList());
Q_INVOKABLE void updateNote(const Note& note); 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); Q_INVOKABLE void deleteNote(const int id);
signals: signals:
@ -205,7 +207,7 @@ private:
// Nextcloud status.php // Nextcloud status.php
const QString m_statusEndpoint; const QString m_statusEndpoint;
QNetworkReply* m_statusReply; QVector<QNetworkReply*> m_statusReplies;
void updateNcStatus(const QJsonObject &status); void updateNcStatus(const QJsonObject &status);
NextcloudStatus m_ncStatusStatus; NextcloudStatus m_ncStatusStatus;
void setNcStatusStatus(NextcloudStatus status, bool *changed = NULL); 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 // Nextcloud Login Flow v2 - https://docs.nextcloud.com/server/18/developer_manual/client_apis/LoginFlow/index.html#login-flow-v2
const QString m_loginEndpoint; const QString m_loginEndpoint;
QNetworkReply* m_loginReply; QVector<QNetworkReply*> m_loginReplies;
QNetworkReply* m_pollReply; QVector<QNetworkReply*> m_pollReplies;
bool updateLoginFlow(const QJsonObject &login); bool updateLoginFlow(const QJsonObject &login);
bool updateLoginCredentials(const QJsonObject &credentials); bool updateLoginCredentials(const QJsonObject &credentials);
LoginStatus m_loginStatus; 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 // Nextcloud OCS API - https://docs.nextcloud.com/server/18/developer_manual/client_apis/OCS/ocs-api-overview.html
const QString m_ocsEndpoint; const QString m_ocsEndpoint;
QNetworkReply* m_ocsReply; QVector<QNetworkReply*> m_ocsReplies;
// Nextcloud Notes API - https://github.com/nextcloud/notes/wiki/Notes-0.2 // Nextcloud Notes API - https://github.com/nextcloud/notes/wiki/Notes-0.2
const QString m_notesEndpoint; 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 updateApiNotes(const QJsonArray& json);
void updateApiNote(const QJsonObject& json); void updateApiNote(const QJsonObject& json);
QDateTime m_lastSync; QDateTime m_lastSync;

View file

@ -13,22 +13,29 @@ class NotesInterface : public QObject
Q_CLASSINFO("url", "https://github.com/scharel/harbour-nextcloudnotes") Q_CLASSINFO("url", "https://github.com/scharel/harbour-nextcloudnotes")
public: public:
explicit NotesInterface(QObject *parent = nullptr) : QObject(parent) { } explicit NotesInterface(QObject *parent = nullptr) : QObject(parent) {
}
virtual QString account() const = 0; virtual QString account() const = 0;
virtual void setAccount(const QString& account) = 0; virtual void setAccount(const QString& account) = 0;
public slots: public slots:
Q_INVOKABLE virtual void getAllNotes(Note::NoteField exclude = Note::None) = 0; Q_INVOKABLE virtual void getAllNotes(const QStringList exclude = QStringList()) = 0;
Q_INVOKABLE virtual void getNote(const int id, Note::NoteField exclude = Note::None) = 0; virtual void getAllNotes(Note::NoteField exclude) = 0;
Q_INVOKABLE virtual void createNote(const Note& note) = 0; Q_INVOKABLE virtual void getNote(const int id, const QStringList exclude = QStringList()) = 0;
Q_INVOKABLE virtual void updateNote(const Note& note) = 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; Q_INVOKABLE virtual void deleteNote(const int id) = 0;
signals: signals:
void accountChanged(const QString& account); void accountChanged(const QString& account);
void noteUpdated(const QVariantMap& note);
void noteUpdated(const Note& note); void noteUpdated(const Note& note);
void noteDeleted(const int id); void noteDeleted(const int id);
}; };
#endif // NOTESINTERFACE_H #endif // NOTESINTERFACE_H

View file

@ -27,6 +27,18 @@ int NotesProxyModel::roleFromName(const QString &name) const {
return roleNames().key(name.toLocal8Bit()); 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 { bool NotesProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const {
QAbstractItemModel* source = sourceModel(); QAbstractItemModel* source = sourceModel();
if (m_favoritesOnTop && source->data(source_left, NotesModel::FavoriteRole).toBool() != source->data(source_right, NotesModel::FavoriteRole).toBool()) 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); int position = m_notes.indexOf(note);
if (position >= 0) { if (position >= 0) {
if (m_notes.at(position).equal(note)) { if (m_notes.at(position).equal(note)) {
qDebug() << "Note already present unchanged."; qDebug() << "Note already present and unchanged.";
} }
else { else {
qDebug() << "Note already present, updating it."; qDebug() << "Note already present, updating it.";

View file

@ -20,6 +20,7 @@ public:
Q_INVOKABLE void sort(); Q_INVOKABLE void sort();
Q_INVOKABLE int roleFromName(const QString &name) const; Q_INVOKABLE int roleFromName(const QString &name) const;
Q_INVOKABLE const QVariantMap getNote(const QModelIndex &index) const;
protected: protected:
virtual bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const; 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) { void NotesStore::getAllNotes(Note::NoteField exclude) {
qDebug() << "Getting all notes"; qDebug() << "Getting all notes";
QFileInfoList files = m_dir.entryInfoList(); 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) { void NotesStore::getNote(const int id, Note::NoteField exclude) {
qDebug() << "Getting note: " << id; qDebug() << "Getting note: " << id;
if (id >= 0) { 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) { void NotesStore::createNote(const Note& note) {
qDebug() << "Creating note: " << note.id(); qDebug() << "Creating note: " << note.id();
if (!note.isValid()) { 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) { void NotesStore::updateNote(const Note& note) {
qDebug() << "Updating note: " << note.id(); qDebug() << "Updating note: " << note.id();
if (note.isValid()) { if (note.isValid()) {

View file

@ -21,10 +21,14 @@ public:
void setAccount(const QString& account); void setAccount(const QString& account);
public slots: public slots:
Q_INVOKABLE void getAllNotes(Note::NoteField exclude = Note::None); Q_INVOKABLE void getAllNotes(const QStringList exclude = QStringList());
Q_INVOKABLE void getNote(const int id, Note::NoteField exclude = Note::None); void getAllNotes(Note::NoteField exclude);
Q_INVOKABLE void createNote(const Note& note); Q_INVOKABLE void getNote(const int id, const QStringList exclude = QStringList());
Q_INVOKABLE void updateNote(const Note& note); 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); Q_INVOKABLE void deleteNote(const int id);
private: private:

View file

@ -262,14 +262,6 @@
<source>Failed to communicate with the Nextcloud server</source> <source>Failed to communicate with the Nextcloud server</source>
<translation>Fehler bei der Server-Kommunikation</translation> <translation>Fehler bei der Server-Kommunikation</translation>
</message> </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> <message>
<source>An error occured while establishing an encrypted connection</source> <source>An error occured while establishing an encrypted connection</source>
<translation>Fehler beim Aufbau einer verschlüsselten Kommunikation</translation> <translation>Fehler beim Aufbau einer verschlüsselten Kommunikation</translation>

View file

@ -262,14 +262,6 @@
<source>Failed to communicate with the Nextcloud server</source> <source>Failed to communicate with the Nextcloud server</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </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> <message>
<source>An error occured while establishing an encrypted connection</source> <source>An error occured while establishing an encrypted connection</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>

View file

@ -133,98 +133,98 @@
<context> <context>
<name>LoginPage</name> <name>LoginPage</name>
<message> <message>
<location filename="../qml/pages/LoginPage.qml" line="18"/> <location filename="../qml/pages/LoginPage.qml" line="17"/>
<source>Nextcloud Login</source> <source>Nextcloud Login</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/LoginPage.qml" line="168"/> <location filename="../qml/pages/LoginPage.qml" line="167"/>
<source>Nextcloud server</source> <source>Nextcloud server</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/LoginPage.qml" line="227"/> <location filename="../qml/pages/LoginPage.qml" line="226"/>
<source>Username</source> <source>Username</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/LoginPage.qml" line="238"/> <location filename="../qml/pages/LoginPage.qml" line="237"/>
<source>Password</source> <source>Password</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/LoginPage.qml" line="213"/> <location filename="../qml/pages/LoginPage.qml" line="212"/>
<source>Abort</source> <source>Abort</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<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> <source>Follow the instructions in the browser</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/LoginPage.qml" line="104"/> <location filename="../qml/pages/LoginPage.qml" line="103"/>
<source>Login successfull!</source> <source>Login successfull!</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/LoginPage.qml" line="101"/> <location filename="../qml/pages/LoginPage.qml" line="100"/>
<location filename="../qml/pages/LoginPage.qml" line="110"/> <location filename="../qml/pages/LoginPage.qml" line="109"/>
<source>Login failed!</source> <source>Login failed!</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/LoginPage.qml" line="90"/> <location filename="../qml/pages/LoginPage.qml" line="89"/>
<source>Enter your credentials</source> <source>Enter your credentials</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/LoginPage.qml" line="213"/> <location filename="../qml/pages/LoginPage.qml" line="212"/>
<source>Login</source> <source>Login</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/LoginPage.qml" line="213"/> <location filename="../qml/pages/LoginPage.qml" line="212"/>
<source>Re-Login</source> <source>Re-Login</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/LoginPage.qml" line="247"/> <location filename="../qml/pages/LoginPage.qml" line="246"/>
<source>Test Login</source> <source>Test Login</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/LoginPage.qml" line="253"/> <location filename="../qml/pages/LoginPage.qml" line="252"/>
<source>Note</source> <source>Note</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<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> <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> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/LoginPage.qml" line="265"/> <location filename="../qml/pages/LoginPage.qml" line="264"/>
<source>Security</source> <source>Security</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<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> <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> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/LoginPage.qml" line="276"/> <location filename="../qml/pages/LoginPage.qml" line="275"/>
<source>Do not check certificates</source> <source>Do not check certificates</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<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> <source>Enable this option to allow selfsigned certificates</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/LoginPage.qml" line="286"/> <location filename="../qml/pages/LoginPage.qml" line="285"/>
<source>Allow unencrypted connections</source> <source>Allow unencrypted connections</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -245,12 +245,12 @@
<context> <context>
<name>Note</name> <name>Note</name>
<message> <message>
<location filename="../src/note.cpp" line="306"/> <location filename="../src/note.cpp" line="335"/>
<source>Today</source> <source>Today</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../src/note.cpp" line="308"/> <location filename="../src/note.cpp" line="337"/>
<source>Yesterday</source> <source>Yesterday</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -258,52 +258,52 @@
<context> <context>
<name>NotePage</name> <name>NotePage</name>
<message> <message>
<location filename="../qml/pages/NotePage.qml" line="111"/> <location filename="../qml/pages/NotePage.qml" line="117"/>
<source>Delete</source> <source>Delete</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotePage.qml" line="115"/> <location filename="../qml/pages/NotePage.qml" line="121"/>
<source>Reload</source> <source>Reload</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotePage.qml" line="115"/> <location filename="../qml/pages/NotePage.qml" line="121"/>
<source>Updating...</source> <source>Updating...</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotePage.qml" line="121"/> <location filename="../qml/pages/NotePage.qml" line="127"/>
<source>Last update</source> <source>Last update</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotePage.qml" line="124"/> <location filename="../qml/pages/NotePage.qml" line="130"/>
<source>never</source> <source>never</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotePage.qml" line="131"/> <location filename="../qml/pages/NotePage.qml" line="137"/>
<source>Edit</source> <source>Edit</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotePage.qml" line="132"/> <location filename="../qml/pages/NotePage.qml" line="138"/>
<source>Notes</source> <source>Notes</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotePage.qml" line="252"/> <location filename="../qml/pages/NotePage.qml" line="258"/>
<source>No category</source> <source>No category</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotePage.qml" line="253"/> <location filename="../qml/pages/NotePage.qml" line="259"/>
<source>Category</source> <source>Category</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotePage.qml" line="268"/> <location filename="../qml/pages/NotePage.qml" line="274"/>
<source>Modified</source> <source>Modified</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -311,37 +311,27 @@
<context> <context>
<name>NotesApi</name> <name>NotesApi</name>
<message> <message>
<location filename="../src/notesapi.cpp" line="341"/> <location filename="../src/notesapi.cpp" line="368"/>
<source>No network connection available</source> <source>No network connection available</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<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> <source>Failed to communicate with the Nextcloud server</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../src/notesapi.cpp" line="347"/> <location filename="../src/notesapi.cpp" line="374"/>
<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"/>
<source>An error occured while establishing an encrypted connection</source> <source>An error occured while establishing an encrypted connection</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<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> <source>Could not authenticate to the Nextcloud instance</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../src/notesapi.cpp" line="359"/> <location filename="../src/notesapi.cpp" line="380"/>
<source>Unknown error</source> <source>Unknown error</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -349,97 +339,97 @@
<context> <context>
<name>NotesPage</name> <name>NotesPage</name>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="30"/> <location filename="../qml/pages/NotesPage.qml" line="29"/>
<source>Settings</source> <source>Settings</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="34"/> <location filename="../qml/pages/NotesPage.qml" line="33"/>
<source>Add note</source> <source>Add note</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="39"/> <location filename="../qml/pages/NotesPage.qml" line="38"/>
<source>Reload</source> <source>Reload</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="39"/> <location filename="../qml/pages/NotesPage.qml" line="38"/>
<source>Updating...</source> <source>Updating...</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="45"/> <location filename="../qml/pages/NotesPage.qml" line="44"/>
<source>Last update</source> <source>Last update</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="48"/> <location filename="../qml/pages/NotesPage.qml" line="47"/>
<source>never</source> <source>never</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="58"/> <location filename="../qml/pages/NotesPage.qml" line="57"/>
<source>Nextcloud Notes</source> <source>Nextcloud Notes</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="199"/> <location filename="../qml/pages/NotesPage.qml" line="188"/>
<source>Modified</source> <source>Modified</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="202"/> <location filename="../qml/pages/NotesPage.qml" line="191"/>
<source>Delete</source> <source>Delete</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="204"/> <location filename="../qml/pages/NotesPage.qml" line="193"/>
<source>Deleting note</source> <source>Deleting note</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="234"/> <location filename="../qml/pages/NotesPage.qml" line="224"/>
<source>Loading notes...</source> <source>Loading notes...</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="240"/> <location filename="../qml/pages/NotesPage.qml" line="230"/>
<source>No account yet</source> <source>No account yet</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<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> <source>Got to the settings to add an account</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="247"/> <location filename="../qml/pages/NotesPage.qml" line="237"/>
<source>No notes yet</source> <source>No notes yet</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<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> <source>Pull down to add a note</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="254"/> <location filename="../qml/pages/NotesPage.qml" line="244"/>
<source>No result</source> <source>No result</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="255"/> <location filename="../qml/pages/NotesPage.qml" line="245"/>
<source>Try another query</source> <source>Try another query</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../qml/pages/NotesPage.qml" line="261"/> <location filename="../qml/pages/NotesPage.qml" line="251"/>
<source>An error occurred</source> <source>An error occurred</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<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> <source>Open the settings to configure your Nextcloud accounts</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>