Implemented the API in C++. Still plenty bugs.
This commit is contained in:
parent
85b33a9e37
commit
a026c2ee8d
17 changed files with 488 additions and 515 deletions
|
@ -17,11 +17,13 @@ CONFIG += sailfishapp
|
|||
DEFINES += APP_VERSION=\\\"$$VERSION\\\"
|
||||
|
||||
HEADERS += \
|
||||
src/notesapi.h \
|
||||
src/sslconfiguration.h \
|
||||
src/notesmodel.h \
|
||||
src/note.h
|
||||
|
||||
SOURCES += src/harbour-nextcloudnotes.cpp \
|
||||
src/notesapi.cpp \
|
||||
src/sslconfiguration.cpp \
|
||||
src/notesmodel.cpp \
|
||||
src/note.cpp
|
||||
|
@ -45,8 +47,7 @@ DISTFILES += qml/harbour-nextcloudnotes.qml \
|
|||
qml/pages/MITLicense.qml \
|
||||
qml/pages/GPLLicense.qml \
|
||||
qml/pages/SyntaxPage.qml \
|
||||
qml/components/NotesApi.qml \
|
||||
qml/components/NoteDelegateModel.qml
|
||||
qml/components/NotesApi.qml
|
||||
|
||||
SAILFISHAPP_ICONS = 86x86 108x108 128x128 172x172
|
||||
|
||||
|
|
|
@ -1,243 +0,0 @@
|
|||
import QtQuick 2.5
|
||||
import Sailfish.Silica 1.0
|
||||
import QtQml.Models 2.2
|
||||
|
||||
DelegateModel {
|
||||
id: noteListModel
|
||||
property string searchText: ""
|
||||
property bool favoritesOnTop
|
||||
property string sortBy
|
||||
property bool showSeparator
|
||||
property int previewLineCount
|
||||
|
||||
onSearchTextChanged: reload()
|
||||
onSortByChanged: reload()
|
||||
Connections {
|
||||
target: api
|
||||
onModelChanged: {
|
||||
console.log("API model changed!")
|
||||
reload()
|
||||
}
|
||||
onNoteCreated: {
|
||||
console.log("New note created: " + id)
|
||||
}
|
||||
onNoteRemoved: {
|
||||
console.log("Note removed: " + id)
|
||||
}
|
||||
onNoteChanged: {
|
||||
console.log("Note changed: " + id)
|
||||
}
|
||||
}
|
||||
|
||||
function reload() {
|
||||
if (items.count > 0)
|
||||
items.setGroups(0, items.count, "unsorted")
|
||||
if (searchItems.count > 0)
|
||||
searchItems.setGroups(0, searchItems.count, "unsorted")
|
||||
}
|
||||
|
||||
items.includeByDefault: false
|
||||
groups: [
|
||||
DelegateModelGroup {
|
||||
id: searchItems
|
||||
name: "search"
|
||||
},
|
||||
DelegateModelGroup {
|
||||
id: unsortedItems
|
||||
name: "unsorted"
|
||||
includeByDefault: true
|
||||
onChanged: {
|
||||
switch(sortBy) {
|
||||
case "date":
|
||||
noteListModel.sort(function(left, right) {
|
||||
if (favoritesOnTop) {
|
||||
if (left.favorite === right.favorite)
|
||||
return left.modified > right.modified
|
||||
else
|
||||
return left.favorite
|
||||
}
|
||||
else
|
||||
return left.modified > right.modified
|
||||
})
|
||||
break
|
||||
case "category":
|
||||
noteListModel.sort(function(left, right) {
|
||||
if (favoritesOnTop) {
|
||||
if (left.favorite === right.favorite)
|
||||
return left.category < right.category
|
||||
else
|
||||
return left.favorite
|
||||
}
|
||||
else
|
||||
return left.category < right.category
|
||||
})
|
||||
break
|
||||
case "title":
|
||||
noteListModel.sort(function(left, right) {
|
||||
if (favoritesOnTop) {
|
||||
if (left.favorite === right.favorite)
|
||||
return left.title < right.title
|
||||
else
|
||||
return left.favorite
|
||||
}
|
||||
else
|
||||
return left.title < right.title
|
||||
})
|
||||
break
|
||||
default:
|
||||
setGroups(0, unsortedItems.count, "items")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
Connections {
|
||||
target: items
|
||||
onCountChanged: console.log(count)
|
||||
}
|
||||
|
||||
function insertPosition(lessThan, item) {
|
||||
var lower = 0
|
||||
var upper = items.count
|
||||
while (lower < upper) {
|
||||
var middle = Math.floor(lower + (upper - lower) / 2)
|
||||
var result = lessThan(item.model, items.get(middle).model);
|
||||
if (result) {
|
||||
upper = middle
|
||||
} else {
|
||||
lower = middle + 1
|
||||
}
|
||||
}
|
||||
return lower
|
||||
}
|
||||
function sort(lessThan) {
|
||||
while (unsortedItems.count > 0) {
|
||||
var item = unsortedItems.get(0)
|
||||
var index = insertPosition(lessThan, item)
|
||||
|
||||
if (searchText === "" ||
|
||||
item.model.title.toLowerCase().indexOf(searchText.toLowerCase()) >= 0 ||
|
||||
item.model.content.toLowerCase().indexOf(searchText.toLowerCase()) >= 0 ||
|
||||
item.model.category.toLowerCase().indexOf(searchText.toLowerCase()) >= 0) {
|
||||
//console.log("Adding " + item.model.title + " to model")
|
||||
item.groups = "items"
|
||||
items.move(item.itemsIndex, index)
|
||||
}
|
||||
else if (searchText !== "") {
|
||||
item.groups = "search"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delegate: BackgroundItem {
|
||||
id: note
|
||||
|
||||
contentHeight: titleLabel.height + previewLabel.height + 2*Theme.paddingSmall
|
||||
height: contentHeight + menu.height
|
||||
width: parent.width
|
||||
highlighted: down || menu.active
|
||||
/*ListView.onAdd: AddAnimation {
|
||||
target: note //searchText !== "" ? null : note
|
||||
}
|
||||
ListView.onRemove: RemoveAnimation {
|
||||
target: note //searchText !== "" ? null : note
|
||||
}*/
|
||||
RemorseItem {
|
||||
id: remorse
|
||||
}
|
||||
|
||||
onClicked: pageStack.push(Qt.resolvedUrl("../pages/NotePage.qml"),
|
||||
{ note: api.model.get(index) })
|
||||
onPressAndHold: menu.open(note)
|
||||
|
||||
Separator {
|
||||
width: parent.width
|
||||
color: Theme.primaryColor
|
||||
anchors.top: titleLabel.top
|
||||
visible: showSeparator && index !== 0
|
||||
}
|
||||
|
||||
IconButton {
|
||||
id: isFavoriteIcon
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
icon.source: (favorite ? "image://theme/icon-m-favorite-selected?" : "image://theme/icon-m-favorite?") +
|
||||
(note.highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor)
|
||||
onClicked: {
|
||||
api.updateNote(id, {'favorite': !favorite} )
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
id: titleLabel
|
||||
anchors.left: isFavoriteIcon.right
|
||||
anchors.leftMargin: Theme.paddingSmall
|
||||
anchors.right: categoryRectangle.visible ? categoryRectangle.left : parent.right
|
||||
anchors.top: parent.top
|
||||
text: title
|
||||
truncationMode: TruncationMode.Fade
|
||||
color: note.highlighted ? Theme.highlightColor : Theme.primaryColor
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: categoryRectangle
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Theme.horizontalPageMargin
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: Theme.paddingSmall
|
||||
width: categoryLabel.width + Theme.paddingLarge
|
||||
height: categoryLabel.height + Theme.paddingSmall
|
||||
color: "transparent"
|
||||
border.color: Theme.highlightColor
|
||||
radius: height / 4
|
||||
visible: sortBy !== "category" && categoryLabel.text.length > 0
|
||||
Label {
|
||||
id: categoryLabel
|
||||
anchors.centerIn: parent
|
||||
text: category
|
||||
color: note.highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
id: previewLabel
|
||||
anchors.left: isFavoriteIcon.right
|
||||
anchors.leftMargin: Theme.paddingSmall
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Theme.horizontalPageMargin
|
||||
anchors.top: titleLabel.bottom
|
||||
text: parseText(content)
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
textFormat: Text.PlainText
|
||||
wrapMode: Text.Wrap
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: previewLineCount > 0 ? previewLineCount : 1
|
||||
visible: previewLineCount > 0
|
||||
color: note.highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor
|
||||
function parseText (preText) {
|
||||
var lines = preText.split('\n')
|
||||
lines.splice(0,1);
|
||||
var newText = lines.join('\n');
|
||||
return newText.replace(/^\s*$(?:\r\n?|\n)/gm, "")
|
||||
}
|
||||
}
|
||||
|
||||
ContextMenu {
|
||||
id: menu
|
||||
MenuLabel {
|
||||
id: modifiedLabel
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: qsTr("Modified") + ": " + new Date(modified * 1000).toLocaleString(Qt.locale(), Locale.ShortFormat)
|
||||
}
|
||||
MenuItem {
|
||||
text: qsTr("Delete")
|
||||
onClicked: {
|
||||
remorse.execute(note, qsTr("Deleting note"), function() {
|
||||
api.deleteNote(id)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +1,11 @@
|
|||
import QtQuick 2.0
|
||||
import Sailfish.Silica 1.0
|
||||
import Nemo.Configuration 1.0
|
||||
import harbour.nextcloudnotes.notesapi 1.0
|
||||
import harbour.nextcloudnotes.notesmodel 1.0
|
||||
import harbour.nextcloudnotes.sslconfiguration 1.0
|
||||
import "pages"
|
||||
import "components"
|
||||
//import "components"
|
||||
|
||||
ApplicationWindow
|
||||
{
|
||||
|
@ -47,8 +48,9 @@ ApplicationWindow
|
|||
property bool useCapitalX: value("useCapitalX", false, Boolean)
|
||||
onCurrentAccountChanged: {
|
||||
account.path = "/apps/harbour-nextcloudnotes/accounts/" + currentAccount
|
||||
noteListModel.clear()
|
||||
api.getNotesFromApi()
|
||||
//noteListModel.clear()
|
||||
//api.getNotesFromApi()
|
||||
api.getAllNotes();
|
||||
}
|
||||
|
||||
function addAccount() {
|
||||
|
@ -86,10 +88,10 @@ ApplicationWindow
|
|||
}
|
||||
}
|
||||
|
||||
SslConfiguration {
|
||||
/*SslConfiguration {
|
||||
id: ssl
|
||||
checkCert: !account.unsecureConnection
|
||||
}
|
||||
}*/
|
||||
|
||||
Timer {
|
||||
id: autoSyncTimer
|
||||
|
@ -99,7 +101,8 @@ ApplicationWindow
|
|||
triggeredOnStart: true
|
||||
onTriggered: {
|
||||
if (!api.busy) {
|
||||
api.getNotesFromApi()
|
||||
//api.getNotesFromApi()
|
||||
api.getAllNotes();
|
||||
}
|
||||
else {
|
||||
triggeredOnStart = false
|
||||
|
@ -115,15 +118,32 @@ ApplicationWindow
|
|||
}
|
||||
|
||||
NotesApi {
|
||||
id: api
|
||||
/*scheme: "https"
|
||||
host: account.server
|
||||
path: "/index.php/apps/notes/api/" + account.version
|
||||
username: account.username
|
||||
password: account.password*/
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
api.scheme = "https"
|
||||
api.host = account.server
|
||||
api.path = "/index.php/apps/notes/api/" + account.version
|
||||
api.username = account.username
|
||||
api.password = account.password
|
||||
}
|
||||
|
||||
/*NotesApi {
|
||||
id: api
|
||||
onResponseChanged: noteListModel.applyJSON(response)
|
||||
}
|
||||
|
||||
NotesModel {
|
||||
/*NotesModel {
|
||||
id: noteListModel
|
||||
sortBy: appSettings.sortBy
|
||||
sortBy: appSettisignangs.sortBy
|
||||
favoritesOnTop: appSettings.favoritesOnTop
|
||||
}
|
||||
}*/
|
||||
|
||||
initialPage: Component { NotesPage { } }
|
||||
cover: Qt.resolvedUrl("cover/CoverPage.qml")
|
||||
|
|
|
@ -13,7 +13,7 @@ Dialog {
|
|||
path: "/apps/harbour-nextcloudnotes/accounts/" + accountId
|
||||
Component.onCompleted: {
|
||||
nameField.text = value("name", "", String)
|
||||
serverField.text = value("server", "https://", String)
|
||||
serverField.text = value("server", "", String)
|
||||
usernameField.text = value("username", "", String)
|
||||
passwordField.text = value("password", "", String)
|
||||
unsecureConnectionTextSwitch.checked = value("unsecureConnection", false, Boolean)
|
||||
|
@ -72,14 +72,14 @@ Dialog {
|
|||
TextField {
|
||||
id: serverField
|
||||
// regExp combined from https://stackoverflow.com/a/3809435 (EDIT: removed ? after https to force SSL) and https://www.regextester.com/22
|
||||
property var encryptedRegEx: /^https:\/\/(((www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b|((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))))([-a-zA-Z0-9@:%_\+.~#?&//=]*)$/
|
||||
property var unencryptedRegEx : /^https?:\/\/(((www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b|((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))))([-a-zA-Z0-9@:%_\+.~#?&//=]*)$/
|
||||
//property var encryptedRegEx: /^https:\/\/(((www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b|((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))))([-a-zA-Z0-9@:%_\+.~#?&//=]*)$/
|
||||
//property var unencryptedRegEx : /^https?:\/\/(((www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b|((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))))([-a-zA-Z0-9@:%_\+.~#?&//=]*)$/
|
||||
width: parent.width
|
||||
//text: account.value("server", "https://", String)
|
||||
placeholderText: qsTr("Nextcloud server")
|
||||
label: placeholderText + " " + qsTr("(starting with \"https://\")")
|
||||
label: placeholderText// + " " + qsTr("(starting with \"https://\")")
|
||||
inputMethodHints: Qt.ImhUrlCharactersOnly
|
||||
validator: RegExpValidator { regExp: unencryptedConnectionTextSwitch.checked ? serverField.unencryptedRegEx : serverField.encryptedRegEx }
|
||||
//validator: RegExpValidator { regExp: unencryptedConnectionTextSwitch.checked ? serverField.unencryptedRegEx : serverField.encryptedRegEx }
|
||||
errorHighlight: !acceptableInput// && focus === true
|
||||
EnterKey.enabled: acceptableInput
|
||||
EnterKey.iconSource: "image://theme/icon-m-enter-next"
|
||||
|
|
|
@ -83,7 +83,7 @@ Page {
|
|||
|
||||
currentIndex: -1
|
||||
|
||||
model: noteListModel
|
||||
model: api.model()
|
||||
|
||||
delegate: BackgroundItem {
|
||||
id: note
|
||||
|
@ -232,7 +232,7 @@ Page {
|
|||
horizontalAlignment: Qt.AlignHCenter
|
||||
text: qsTr("Loading notes...")
|
||||
}
|
||||
|
||||
/*
|
||||
ViewPlaceholder {
|
||||
id: noLoginPlaceholder
|
||||
enabled: appSettings.accountIDs.length <= 0
|
||||
|
@ -260,7 +260,7 @@ Page {
|
|||
text: qsTr("An error occurred")
|
||||
hintText: api.statusText
|
||||
}
|
||||
|
||||
*/
|
||||
TouchInteractionHint {
|
||||
id: addAccountHint
|
||||
interactionMode: TouchInteraction.Pull
|
||||
|
|
|
@ -13,7 +13,7 @@ Name: harbour-nextcloudnotes
|
|||
%{!?qtc_make:%define qtc_make make}
|
||||
%{?qtc_builddir:%define _builddir %qtc_builddir}
|
||||
Summary: Nextcloud Notes
|
||||
Version: 0.3
|
||||
Version: 0.4
|
||||
Release: 0
|
||||
Group: Applications/Editors
|
||||
License: MIT
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include <sailfishapp.h>
|
||||
#include <QtQml>
|
||||
#include <QObject>
|
||||
#include "notesapi.h"
|
||||
#include "note.h"
|
||||
#include "notesmodel.h"
|
||||
#include "sslconfiguration.h"
|
||||
|
@ -16,6 +17,7 @@ int main(int argc, char *argv[])
|
|||
app->setOrganizationName("harbour-nextcloudnotes");
|
||||
|
||||
qDebug() << app->applicationDisplayName() << app->applicationVersion();
|
||||
qmlRegisterType<NotesApi>("harbour.nextcloudnotes.notesapi", 1, 0, "NotesApi");
|
||||
qmlRegisterType<Note>("harbour.nextcloudnotes.note", 1, 0, "Note");
|
||||
qmlRegisterType<NotesModel>("harbour.nextcloudnotes.notesmodel", 1, 0, "NotesModel");
|
||||
qmlRegisterType<SslConfiguration>("harbour.nextcloudnotes.sslconfiguration", 1, 0, "SslConfiguration");
|
||||
|
|
86
src/note.cpp
86
src/note.cpp
|
@ -41,14 +41,80 @@ Note& Note::operator=(const Note& note) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
bool Note::equal(const Note& n) const {
|
||||
return m_id == n.id() &&
|
||||
m_modified == n.modified() &&
|
||||
m_title == n.title() &&
|
||||
m_category == n.category() &&
|
||||
m_content == n.content() &&
|
||||
m_favorite == n.favorite() &&
|
||||
m_etag == n.etag() &&
|
||||
m_error == n.error() &&
|
||||
m_errorMessage == n.errorMessage();
|
||||
bool Note::operator==(const Note& note) const {
|
||||
return same(note);
|
||||
}
|
||||
|
||||
bool Note::same(const Note& note) const {
|
||||
return m_id == note.id();
|
||||
}
|
||||
|
||||
bool Note::same(const int id) const {
|
||||
return m_id == id;
|
||||
}
|
||||
|
||||
bool Note::equal(const Note& note) const {
|
||||
return m_id == note.id() &&
|
||||
m_modified == note.modified() &&
|
||||
m_title == note.title() &&
|
||||
m_category == note.category() &&
|
||||
m_content == note.content() &&
|
||||
m_favorite == note.favorite() &&
|
||||
m_etag == note.etag() &&
|
||||
m_error == note.error() &&
|
||||
m_errorMessage == note.errorMessage();
|
||||
}
|
||||
|
||||
bool Note::newer(const Note& note) const {
|
||||
return same(note) && note.modified() > m_modified;
|
||||
}
|
||||
|
||||
bool Note::older(const Note& note) const {
|
||||
return same(note) && note.modified() < m_modified;
|
||||
}
|
||||
|
||||
QString Note::dateString() const {
|
||||
QDateTime date;
|
||||
QString dateString;
|
||||
date.setTime_t(m_modified);
|
||||
qint64 diff = date.daysTo(QDateTime::currentDateTime());
|
||||
if (diff == 0)
|
||||
dateString = tr("Today");
|
||||
else if (diff == 1)
|
||||
dateString = tr("Yesterday");
|
||||
else if (diff < 7)
|
||||
dateString = date.toLocalTime().toString("dddd");
|
||||
else if (date.toLocalTime().toString("yyyy") == QDateTime::currentDateTime().toString("yyyy"))
|
||||
dateString = date.toLocalTime().toString("MMMM");
|
||||
else
|
||||
dateString = date.toLocalTime().toString("MMMM yyyy");
|
||||
return dateString;
|
||||
}
|
||||
|
||||
Note Note::fromjson(const QJsonObject& jobj) {
|
||||
Note note = new Note;
|
||||
note.setId(jobj.value("id").toInt());
|
||||
note.setModified(jobj.value("modified").toInt());
|
||||
note.setTitle(jobj.value("title").toString());
|
||||
note.setCategory(jobj.value("category").toString());
|
||||
note.setContent(jobj.value("content").toString());
|
||||
note.setFavorite(jobj.value("favorite").toBool());
|
||||
note.setEtag(jobj.value("etag").toString());
|
||||
note.setError(jobj.value("error").toBool(true));
|
||||
note.setErrorMessage(jobj.value("errorMessage").toString());
|
||||
return note;
|
||||
}
|
||||
|
||||
bool Note::searchInNote(const QString &query, const Note ¬e, SearchAttributes criteria, Qt::CaseSensitivity cs) {
|
||||
bool queryFound = false;
|
||||
if (criteria.testFlag(SearchInTitle)) {
|
||||
queryFound |= note.title().contains(query, cs);
|
||||
}
|
||||
if (criteria.testFlag(SearchInContent)) {
|
||||
queryFound |= note.content().contains(query, cs);
|
||||
}
|
||||
if (criteria.testFlag(SearchInCategory)) {
|
||||
queryFound |= note.category().contains(query, cs);
|
||||
}
|
||||
return queryFound;
|
||||
}
|
||||
|
|
76
src/note.h
76
src/note.h
|
@ -24,6 +24,23 @@ public:
|
|||
Note(QObject *parent = NULL);
|
||||
Note(const Note& note, QObject *parent = NULL);
|
||||
|
||||
Note& operator=(const Note& note);
|
||||
bool operator==(const Note& note) const;
|
||||
bool same(const Note& note) const;
|
||||
bool same(const int id) const;
|
||||
bool equal(const Note& note) const;
|
||||
bool newer(const Note& note) const;
|
||||
bool older(const Note& note) const;
|
||||
|
||||
enum SearchAttribute {
|
||||
NoSearchAttribute = 0x0,
|
||||
SearchInTitle = 0x1,
|
||||
SearchInCategory = 0x2,
|
||||
SearchInContent = 0x4,
|
||||
SearchAll = 0x7
|
||||
};
|
||||
Q_DECLARE_FLAGS(SearchAttributes, SearchAttribute)
|
||||
|
||||
int id() const { return m_id; }
|
||||
uint modified() const { return m_modified; }
|
||||
QString title() const { return m_title; }
|
||||
|
@ -33,23 +50,7 @@ public:
|
|||
QString etag() const { return m_etag; }
|
||||
bool error() const { return m_error; }
|
||||
QString errorMessage() const { return m_errorMessage; }
|
||||
QString dateString() const {
|
||||
QDateTime date;
|
||||
QString dateString;
|
||||
date.setTime_t(m_modified);
|
||||
qint64 diff = date.daysTo(QDateTime::currentDateTime());
|
||||
if (diff == 0)
|
||||
dateString = tr("Today");
|
||||
else if (diff == 1)
|
||||
dateString = tr("Yesterday");
|
||||
else if (diff < 7)
|
||||
dateString = date.toLocalTime().toString("dddd");
|
||||
else if (date.toLocalTime().toString("yyyy") == QDateTime::currentDateTime().toString("yyyy"))
|
||||
dateString = date.toLocalTime().toString("MMMM");
|
||||
else
|
||||
dateString = date.toLocalTime().toString("MMMM yyyy");
|
||||
return dateString;
|
||||
}
|
||||
QString dateString() const;
|
||||
|
||||
void setId(int id) { if (id != m_id) { m_id = id; emit idChanged(id); } }
|
||||
void setModified(uint modified) { if (modified != m_modified) { m_modified = modified; emit modifiedChanged(modified); emit dateStringChanged(dateString()); } }
|
||||
|
@ -61,45 +62,8 @@ public:
|
|||
void setError(bool error) { if (error != m_error) { m_error = error; emit errorChanged(error); } }
|
||||
void setErrorMessage(QString errorMessage) { if (errorMessage != m_errorMessage) { m_errorMessage = errorMessage; emit errorMessageChanged(errorMessage); } }
|
||||
|
||||
Note& operator=(const Note& note);
|
||||
bool operator==(const Note& note) const {
|
||||
return m_id == note.id();
|
||||
}
|
||||
bool equal(const Note& n) const;
|
||||
enum SearchAttribute {
|
||||
NoSearchAttribute = 0x0,
|
||||
SearchInTitle = 0x1,
|
||||
SearchInCategory = 0x2,
|
||||
SearchInContent = 0x4,
|
||||
SearchAll = 0x7
|
||||
};
|
||||
Q_DECLARE_FLAGS(SearchAttributes, SearchAttribute)
|
||||
static Note fromjson(const QJsonObject& jobj) {
|
||||
Note note = new Note;
|
||||
note.setId(jobj.value("id").toInt());
|
||||
note.setModified(jobj.value("modified").toInt());
|
||||
note.setTitle(jobj.value("title").toString());
|
||||
note.setCategory(jobj.value("category").toString());
|
||||
note.setContent(jobj.value("content").toString());
|
||||
note.setFavorite(jobj.value("favorite").toBool());
|
||||
note.setEtag(jobj.value("etag").toString());
|
||||
note.setError(jobj.value("error").toBool(true));
|
||||
note.setErrorMessage(jobj.value("errorMessage").toString());
|
||||
return note;
|
||||
}
|
||||
static bool searchInNote(const QString &query, const Note ¬e, SearchAttributes criteria = QFlag(SearchAll), Qt::CaseSensitivity cs = Qt::CaseInsensitive) {
|
||||
bool queryFound = false;
|
||||
if (criteria.testFlag(SearchInTitle)) {
|
||||
queryFound |= note.title().contains(query, cs);
|
||||
}
|
||||
if (criteria.testFlag(SearchInContent)) {
|
||||
queryFound |= note.content().contains(query, cs);
|
||||
}
|
||||
if (criteria.testFlag(SearchInCategory)) {
|
||||
queryFound |= note.category().contains(query, cs);
|
||||
}
|
||||
return queryFound;
|
||||
}
|
||||
static Note fromjson(const QJsonObject& jobj);
|
||||
static bool searchInNote(const QString &query, const Note ¬e, SearchAttributes criteria = QFlag(SearchAll), Qt::CaseSensitivity cs = Qt::CaseInsensitive);
|
||||
|
||||
signals:
|
||||
void idChanged(int id);
|
||||
|
|
212
src/notesapi.cpp
Normal file
212
src/notesapi.cpp
Normal file
|
@ -0,0 +1,212 @@
|
|||
#include "notesapi.h"
|
||||
#include <QGuiApplication>
|
||||
#include <QAuthenticator>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
NotesApi::NotesApi(QObject *parent) : QObject(parent)
|
||||
{
|
||||
mp_model = new NotesModel(this);
|
||||
connect(this, SIGNAL(urlChanged(QUrl)), this, SLOT(verifyUrl(QUrl)));
|
||||
connect(&m_manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), this, SLOT(requireAuthentication(QNetworkReply*,QAuthenticator*)));
|
||||
connect(&m_manager, SIGNAL(networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility)), this, SLOT(onNetworkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility)));
|
||||
connect(&m_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
|
||||
connect(&m_manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)), this, SLOT(sslError(QNetworkReply*,QList<QSslError>)));
|
||||
m_request.setSslConfiguration(QSslConfiguration::defaultConfiguration());
|
||||
m_request.setHeader(QNetworkRequest::UserAgentHeader, QGuiApplication::applicationDisplayName() + " / " + QGuiApplication::applicationVersion());
|
||||
m_request.setRawHeader("OCS-APIREQUEST", "true");
|
||||
m_request.setHeader(QNetworkRequest::ContentTypeHeader, QString("application/json").toUtf8());
|
||||
}
|
||||
|
||||
NotesApi::~NotesApi() {
|
||||
delete mp_model;
|
||||
}
|
||||
|
||||
void NotesApi::setSslVerify(bool verify) {
|
||||
if (verify != (m_request.sslConfiguration().peerVerifyMode() == QSslSocket::VerifyPeer)) {
|
||||
m_request.sslConfiguration().setPeerVerifyMode(verify ? QSslSocket::VerifyPeer : QSslSocket::VerifyNone);
|
||||
emit sslVerifyChanged(verify);
|
||||
}
|
||||
}
|
||||
|
||||
void NotesApi::requireAuthentication(QNetworkReply *reply, QAuthenticator *authenticator) {
|
||||
if (reply && authenticator) {
|
||||
authenticator->setUser(username());
|
||||
authenticator->setPassword(password());
|
||||
}
|
||||
}
|
||||
|
||||
void NotesApi::setUrl(QUrl url) {
|
||||
if (url != m_url) {
|
||||
QUrl oldUrl = m_url;
|
||||
m_url = url;
|
||||
emit urlChanged(m_url);
|
||||
if (m_url.scheme() != oldUrl.scheme())
|
||||
emit schemeChanged(m_url.scheme());
|
||||
if (m_url.host() != oldUrl.host())
|
||||
emit hostChanged(m_url.host());
|
||||
if (m_url.port() != oldUrl.port())
|
||||
emit portChanged(m_url.port());
|
||||
if (m_url.userName() != oldUrl.userName())
|
||||
emit usernameChanged(m_url.userName());
|
||||
if (m_url.password() != oldUrl.password())
|
||||
emit passwordChanged(m_url.password());
|
||||
if (m_url.path() != oldUrl.path())
|
||||
emit pathChanged(m_url.path());
|
||||
qDebug() << "API URL changed:" << m_url.toDisplayString();
|
||||
}
|
||||
}
|
||||
|
||||
void NotesApi::setScheme(QString scheme) {
|
||||
if (scheme == "http" || scheme == "https") {
|
||||
QUrl url = m_url;
|
||||
url.setScheme(scheme);
|
||||
setUrl(url);
|
||||
}
|
||||
}
|
||||
|
||||
void NotesApi::setHost(QString host) {
|
||||
if (!host.isEmpty()) {
|
||||
QUrl url = m_url;
|
||||
url.setHost(host);
|
||||
setUrl(url);
|
||||
}
|
||||
}
|
||||
|
||||
void NotesApi::setPort(int port) {
|
||||
if (port >= -1 && port <= 65535) {
|
||||
QUrl url = m_url;
|
||||
url.setPort(port);
|
||||
setUrl(url);
|
||||
}
|
||||
}
|
||||
|
||||
void NotesApi::setUsername(QString username) {
|
||||
if (!username.isEmpty()) {
|
||||
QUrl url = m_url;
|
||||
url.setUserName(username);
|
||||
QString concatenated = username + ":" + password();
|
||||
QByteArray data = concatenated.toLocal8Bit().toBase64();
|
||||
QString headerData = "Basic " + data;
|
||||
m_request.setRawHeader("Authorization", headerData.toLocal8Bit());
|
||||
setUrl(url);
|
||||
}
|
||||
}
|
||||
|
||||
void NotesApi::setPassword(QString password) {
|
||||
if (!password.isEmpty()) {
|
||||
QUrl url = m_url;
|
||||
url.setPassword(password);
|
||||
QString concatenated = username() + ":" + password;
|
||||
QByteArray data = concatenated.toLocal8Bit().toBase64();
|
||||
QString headerData = "Basic " + data;
|
||||
m_request.setRawHeader("Authorization", headerData.toLocal8Bit());
|
||||
setUrl(url);
|
||||
}
|
||||
}
|
||||
|
||||
void NotesApi::setPath(QString path) {
|
||||
if (!path.isEmpty()) {
|
||||
QUrl url = m_url;
|
||||
url.setPath(path);
|
||||
setUrl(url);
|
||||
}
|
||||
}
|
||||
|
||||
bool NotesApi::busy() const {
|
||||
bool busy = false;
|
||||
for (int i = 0; i < m_replies.size(); ++i) {
|
||||
busy |=m_replies[i]->isRunning();
|
||||
}
|
||||
return busy;
|
||||
}
|
||||
|
||||
void NotesApi::getAllNotes(QStringList excludeFields) {
|
||||
QUrl url = m_url;
|
||||
url.setPath(url.path() + "/notes");
|
||||
if (!excludeFields.isEmpty())
|
||||
url.setQuery(QString("exclude=").append(excludeFields.join(",")));
|
||||
if (url.isValid()) {
|
||||
qDebug() << "GET" << url.toDisplayString();
|
||||
m_request.setUrl(url);
|
||||
m_replies << m_manager.get(m_request);
|
||||
emit busyChanged(busy());
|
||||
}
|
||||
}
|
||||
|
||||
void NotesApi::getNote(int noteId, QStringList excludeFields) {
|
||||
QUrl url = m_url;
|
||||
url.setPath(url.path() + QString("/notes/%1").arg(noteId));
|
||||
if (!excludeFields.isEmpty())
|
||||
url.setQuery(QString("exclude=").append(excludeFields.join(",")));
|
||||
if (url.isValid()) {
|
||||
qDebug() << "GET" << url.toDisplayString();
|
||||
m_request.setUrl(url);
|
||||
m_replies << m_manager.get(m_request);
|
||||
emit busyChanged(busy());
|
||||
}
|
||||
}
|
||||
|
||||
void NotesApi::createNote(QVariantHash fields) {
|
||||
QUrl url = m_url;
|
||||
url.setPath(url.path() + "/notes");
|
||||
if (url.isValid()) {
|
||||
qDebug() << "POST" << url.toDisplayString();
|
||||
m_request.setUrl(url);
|
||||
m_replies << m_manager.post(m_request, QJsonDocument(QJsonObject::fromVariantHash(fields)).toJson());
|
||||
emit busyChanged(busy());
|
||||
}
|
||||
}
|
||||
|
||||
void NotesApi::updateNote(int noteId, QVariantHash fields) {
|
||||
QUrl url = m_url;
|
||||
url.setPath(url.path() + QString("/notes/%1").arg(noteId));
|
||||
if (url.isValid()) {
|
||||
qDebug() << "PUT" << url.toDisplayString();
|
||||
m_request.setUrl(url);
|
||||
m_replies << m_manager.put(m_request, QJsonDocument(QJsonObject::fromVariantHash(fields)).toJson());
|
||||
emit busyChanged(busy());
|
||||
}
|
||||
}
|
||||
|
||||
void NotesApi::deleteNote(int noteId) {
|
||||
QUrl url = m_url;
|
||||
url.setPath(url.path() + QString("/notes/%1").arg(noteId));
|
||||
if (url.isValid()) {
|
||||
qDebug() << "DELETE" << url.toDisplayString();
|
||||
m_request.setUrl(url);
|
||||
m_replies << m_manager.deleteResource(m_request);
|
||||
emit busyChanged(busy());
|
||||
}
|
||||
}
|
||||
|
||||
void NotesApi::verifyUrl(QUrl url) {
|
||||
emit urlValidChanged(url.isValid());
|
||||
}
|
||||
|
||||
void NotesApi::onNetworkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible) {
|
||||
qDebug() << m_manager.networkAccessible();
|
||||
emit networkAccessibleChanged(accessible == QNetworkAccessManager::Accessible);
|
||||
}
|
||||
|
||||
void NotesApi::replyFinished(QNetworkReply *reply) {
|
||||
if (reply->error() == QNetworkReply::NoError) {
|
||||
QJsonDocument json = QJsonDocument::fromJson(reply->readAll());
|
||||
if (mp_model)
|
||||
mp_model->applyJSON(json);
|
||||
//qDebug() << json;
|
||||
}
|
||||
else {
|
||||
qDebug() << reply->errorString();
|
||||
}
|
||||
m_replies.removeAll(reply);
|
||||
reply->deleteLater();
|
||||
emit busyChanged(busy());
|
||||
}
|
||||
|
||||
void NotesApi::sslError(QNetworkReply *reply, const QList<QSslError> &errors) {
|
||||
qDebug() << "SSL errors accured while calling" << reply->url().toDisplayString();
|
||||
for (int i = 0; i < errors.size(); ++i) {
|
||||
qDebug() << errors[i].errorString();
|
||||
}
|
||||
}
|
96
src/notesapi.h
Normal file
96
src/notesapi.h
Normal file
|
@ -0,0 +1,96 @@
|
|||
#ifndef NOTESAPI_H
|
||||
#define NOTESAPI_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
#include <QDebug>
|
||||
#include "notesmodel.h"
|
||||
|
||||
class NotesApi : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit NotesApi(QObject *parent = nullptr);
|
||||
virtual ~NotesApi();
|
||||
|
||||
Q_PROPERTY(bool sslVerify READ sslVerify WRITE setSslVerify NOTIFY sslVerifyChanged)
|
||||
bool sslVerify() const { return m_request.sslConfiguration().peerVerifyMode() == QSslSocket::VerifyPeer; }
|
||||
void setSslVerify(bool verify);
|
||||
|
||||
Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged)
|
||||
QUrl url() const { return m_url; }
|
||||
void setUrl(QUrl url);
|
||||
|
||||
Q_PROPERTY(bool urlValid READ urlValid NOTIFY urlValidChanged)
|
||||
bool urlValid() const { return m_url.isValid(); }
|
||||
|
||||
Q_PROPERTY(QString scheme READ scheme WRITE setScheme NOTIFY schemeChanged)
|
||||
QString scheme() const { return m_url.scheme(); }
|
||||
void setScheme(QString scheme);
|
||||
|
||||
Q_PROPERTY(QString host READ host WRITE setHost NOTIFY hostChanged)
|
||||
QString host() const { return m_url.host(); }
|
||||
void setHost(QString host);
|
||||
|
||||
Q_PROPERTY(int port READ port WRITE setPort NOTIFY portChanged)
|
||||
int port() const { return m_url.port(); }
|
||||
void setPort(int port);
|
||||
|
||||
Q_PROPERTY(QString username READ username WRITE setUsername NOTIFY usernameChanged)
|
||||
QString username() const { return m_url.userName(); }
|
||||
void setUsername(QString username);
|
||||
|
||||
Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged)
|
||||
QString password() const { return m_url.password(); }
|
||||
void setPassword(QString password);
|
||||
|
||||
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
|
||||
QString path() const { return m_url.path(); }
|
||||
void setPath(QString path);
|
||||
|
||||
Q_PROPERTY(bool networkAccessible READ networkAccessible NOTIFY networkAccessibleChanged)
|
||||
bool networkAccessible() const { return m_manager.networkAccessible() == QNetworkAccessManager::Accessible; }
|
||||
|
||||
Q_PROPERTY(bool busy READ busy NOTIFY busyChanged)
|
||||
bool busy() const;
|
||||
|
||||
Q_INVOKABLE void getAllNotes(QStringList excludeFields = QStringList());
|
||||
Q_INVOKABLE void getNote(int noteId, QStringList excludeFields = QStringList());
|
||||
Q_INVOKABLE void createNote(QVariantHash fields = QVariantHash());
|
||||
Q_INVOKABLE void updateNote(int noteId, QVariantHash fields = QVariantHash());
|
||||
Q_INVOKABLE void deleteNote(int noteId);
|
||||
Q_INVOKABLE NotesModel& model() const { return *mp_model; }
|
||||
|
||||
signals:
|
||||
void sslVerifyChanged(bool verify);
|
||||
void urlChanged(QUrl url);
|
||||
void urlValidChanged(bool valid);
|
||||
void schemeChanged(QString scheme);
|
||||
void hostChanged(QString host);
|
||||
void portChanged(int port);
|
||||
void usernameChanged(QString username);
|
||||
void passwordChanged(QString password);
|
||||
void pathChanged(QString path);
|
||||
void networkAccessibleChanged(bool accessible);
|
||||
void busyChanged(bool busy);
|
||||
|
||||
public slots:
|
||||
|
||||
private slots:
|
||||
void verifyUrl(QUrl url);
|
||||
void requireAuthentication(QNetworkReply * reply, QAuthenticator * authenticator);
|
||||
void onNetworkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible);
|
||||
void replyFinished(QNetworkReply* reply);
|
||||
void sslError(QNetworkReply* reply, const QList<QSslError> &errors);
|
||||
|
||||
private:
|
||||
QUrl m_url;
|
||||
QNetworkAccessManager m_manager;
|
||||
QNetworkRequest m_request;
|
||||
QVector<QNetworkReply*> m_replies;
|
||||
NotesModel* mp_model;
|
||||
};
|
||||
|
||||
#endif // NOTESAPI_H
|
|
@ -86,11 +86,9 @@ bool NotesModel::applyJSONobject(const QJsonObject &jobj) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool NotesModel::applyJSON(const QString &json) {
|
||||
bool NotesModel::applyJSON(const QJsonDocument &jdoc) {
|
||||
qDebug() << "Applying new JSON input";// << json;
|
||||
QJsonParseError error;
|
||||
QJsonDocument jdoc = QJsonDocument::fromJson(json.toUtf8(), &error);
|
||||
if (!jdoc.isNull() && error.error == QJsonParseError::NoError) {
|
||||
if (!jdoc.isNull()) {
|
||||
if (jdoc.isArray()) {
|
||||
qDebug() << "- It's an array...";
|
||||
QJsonArray jarr = jdoc.array();
|
||||
|
@ -110,12 +108,20 @@ bool NotesModel::applyJSON(const QString &json) {
|
|||
}
|
||||
else {
|
||||
qDebug() << "Unknown JSON document. This message should never occure!";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "Unable to parse the JSON input:" << error.errorString();
|
||||
qDebug() << "JSON document is empty!";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NotesModel::applyJSON(const QString &json) {
|
||||
QJsonParseError error;
|
||||
QJsonDocument jdoc = QJsonDocument::fromJson(json.toUtf8(), &error);
|
||||
if (!jdoc.isNull() && error.error == QJsonParseError::NoError) {
|
||||
return applyJSON(jdoc);
|
||||
}
|
||||
return error.error == QJsonParseError::NoError;
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ public:
|
|||
Q_INVOKABLE void search(QString searchText = QString());
|
||||
Q_INVOKABLE void clearSearch();
|
||||
|
||||
Q_INVOKABLE bool applyJSON(const QJsonDocument &jdoc);
|
||||
Q_INVOKABLE bool applyJSON(const QString &json);
|
||||
Q_INVOKABLE int insertNote(const Note ¬e);
|
||||
Q_INVOKABLE bool removeNote(const Note ¬e);
|
||||
|
|
|
@ -16,7 +16,7 @@ void SslConfiguration::setCheckCert(bool check) {
|
|||
if (_checkCert != check) {
|
||||
qDebug() << "Changing SSL Cert check to" << check;
|
||||
_checkCert = check;
|
||||
emit checkCertChanged(_checkCert);
|
||||
QSslConfiguration::setDefaultConfiguration(_checkCert ? checkCertConfig : noCheckConfig);
|
||||
emit checkCertChanged(_checkCert);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,10 +119,6 @@
|
|||
<source>Nextcloud server</source>
|
||||
<translation>Nextcloud Server URL</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>(starting with "https://")</source>
|
||||
<translation>(beginnend mit "https://")</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Username</source>
|
||||
<translation>Benutzername</translation>
|
||||
|
@ -174,21 +170,6 @@
|
|||
<translation>Gestern</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>NoteDelegateModel</name>
|
||||
<message>
|
||||
<source>Modified</source>
|
||||
<translation>Geändert</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete</source>
|
||||
<translation>Löschen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Deleting note</source>
|
||||
<translation>Lösche Notiz</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>NotePage</name>
|
||||
<message>
|
||||
|
@ -243,80 +224,52 @@
|
|||
<name>NotesPage</name>
|
||||
<message>
|
||||
<source>Settings</source>
|
||||
<translation>Einstellungen</translation>
|
||||
<translation type="unfinished">Einstellungen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Add note</source>
|
||||
<translation>Notiz hinzufügen</translation>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reload</source>
|
||||
<translation>Neu laden</translation>
|
||||
<translation type="unfinished">Neu laden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Updating...</source>
|
||||
<translation>Aktualisiere...</translation>
|
||||
<translation type="unfinished">Aktualisiere...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Last update</source>
|
||||
<translation>Zuletzt aktualisiert</translation>
|
||||
<translation type="unfinished">Zuletzt aktualisiert</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>never</source>
|
||||
<translation>noch nie</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>No account yet</source>
|
||||
<translation>Noch kein Konto eingerichtet</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Got to the settings to add an account</source>
|
||||
<translation>Gehe in die Einstellungen um ein Konto hinzuzufügen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>No notes yet</source>
|
||||
<translation>Keine Notizen vorhanden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Pull down to add a note</source>
|
||||
<translation>Ziehe nach unten um eine Notiz zu erstellen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>No result</source>
|
||||
<translation>Nichts gefunden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Try another query</source>
|
||||
<translation>Probiere eine andere Suche</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>An error occurred</source>
|
||||
<translation>Ein Fehler ist aufgetreten</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Open the settings to configure your Nextcloud accounts</source>
|
||||
<translation>Gehe in die Einstellungen um deine Nextcloud Konten zu verwalten</translation>
|
||||
<translation type="unfinished">noch nie</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Nextcloud Notes</source>
|
||||
<translation>Nextcloud Notizen</translation>
|
||||
<translation type="unfinished">Nextcloud Notizen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Modified</source>
|
||||
<translation>Geändert</translation>
|
||||
<translation type="unfinished">Geändert</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete</source>
|
||||
<translation>Löschen</translation>
|
||||
<translation type="unfinished">Löschen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Deleting note</source>
|
||||
<translation>Lösche Notiz</translation>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Loading notes...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Open the settings to configure your Nextcloud accounts</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsPage</name>
|
||||
|
|
|
@ -107,10 +107,6 @@
|
|||
<source>Login</source>
|
||||
<translation>Logga in</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>(starting with "https://")</source>
|
||||
<translation>(börjar med "https://")</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Username</source>
|
||||
<translation>Användarnamn</translation>
|
||||
|
@ -174,21 +170,6 @@
|
|||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>NoteDelegateModel</name>
|
||||
<message>
|
||||
<source>Modified</source>
|
||||
<translation type="unfinished">Ändrad</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete</source>
|
||||
<translation type="unfinished">Ta bort</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Deleting note</source>
|
||||
<translation type="unfinished">Tar bort anteckning</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>NotePage</name>
|
||||
<message>
|
||||
|
@ -243,59 +224,27 @@
|
|||
<name>NotesPage</name>
|
||||
<message>
|
||||
<source>Settings</source>
|
||||
<translation>Inställningar</translation>
|
||||
<translation type="unfinished">Inställningar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Add note</source>
|
||||
<translation>Lägg till anteckning</translation>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reload</source>
|
||||
<translation>Uppdatera</translation>
|
||||
<translation type="unfinished">Uppdatera</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Updating...</source>
|
||||
<translation>Uppdaterar...</translation>
|
||||
<translation type="unfinished">Uppdaterar...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Last update</source>
|
||||
<translation>Senaste uppdatering</translation>
|
||||
<translation type="unfinished">Senaste uppdatering</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>never</source>
|
||||
<translation>aldrig</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>No account yet</source>
|
||||
<translation>Inget konto ännu</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Got to the settings to add an account</source>
|
||||
<translation>Gå till inställningarna för att lägga till ett konto</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>No notes yet</source>
|
||||
<translation>Inga anteckningar ännu</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Pull down to add a note</source>
|
||||
<translation>Dra neråt för att lägga till anteckning</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>No result</source>
|
||||
<translation>Inget resultat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Try another query</source>
|
||||
<translation>Försök med en annan söksträng</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>An error occurred</source>
|
||||
<translation>Ett fel inträffade</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Open the settings to configure your Nextcloud accounts</source>
|
||||
<translation>Öppna inställningarna för att konfigurera dina Nextcloud-konton</translation>
|
||||
<translation type="unfinished">aldrig</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Nextcloud Notes</source>
|
||||
|
@ -311,12 +260,16 @@
|
|||
</message>
|
||||
<message>
|
||||
<source>Deleting note</source>
|
||||
<translation type="unfinished">Tar bort anteckning</translation>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Loading notes...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Open the settings to configure your Nextcloud accounts</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsPage</name>
|
||||
|
|
|
@ -144,11 +144,6 @@
|
|||
<source>Nextcloud server</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/pages/LoginDialog.qml" line="80"/>
|
||||
<source>(starting with "https://")</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/pages/LoginDialog.qml" line="93"/>
|
||||
<source>Username</source>
|
||||
|
@ -201,34 +196,16 @@
|
|||
<context>
|
||||
<name>Note</name>
|
||||
<message>
|
||||
<location filename="../src/note.h" line="42"/>
|
||||
<location filename="../src/note.cpp" line="82"/>
|
||||
<source>Today</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/note.h" line="44"/>
|
||||
<location filename="../src/note.cpp" line="84"/>
|
||||
<source>Yesterday</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>NoteDelegateModel</name>
|
||||
<message>
|
||||
<location filename="../qml/components/NoteDelegateModel.qml" line="231"/>
|
||||
<source>Modified</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/components/NoteDelegateModel.qml" line="234"/>
|
||||
<source>Delete</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/components/NoteDelegateModel.qml" line="236"/>
|
||||
<source>Deleting note</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>NotePage</name>
|
||||
<message>
|
||||
|
@ -347,41 +324,6 @@
|
|||
<source>Loading notes...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="239"/>
|
||||
<source>No account yet</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="240"/>
|
||||
<source>Got to the settings to add an account</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="246"/>
|
||||
<source>No notes yet</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="247"/>
|
||||
<source>Pull down to add a note</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="253"/>
|
||||
<source>No result</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="254"/>
|
||||
<source>Try another query</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="260"/>
|
||||
<source>An error occurred</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/pages/NotesPage.qml" line="271"/>
|
||||
<source>Open the settings to configure your Nextcloud accounts</source>
|
||||
|
|
Loading…
Reference in a new issue