diff --git a/qml/harbour-nextcloudnotes.qml b/qml/harbour-nextcloudnotes.qml
index 24e01c8..585a648 100644
--- a/qml/harbour-nextcloudnotes.qml
+++ b/qml/harbour-nextcloudnotes.qml
@@ -10,9 +10,13 @@ ApplicationWindow
ConfigurationGroup {
id: appSettings
path: "/apps/harbour-nextcloudnotes/settings"
- synchronous: true
+ //synchronous: true
property int currentAccount: value("currentAccount", -1)
+ property int autoSyncInterval: value("autoSyncInterval", 0)
+ property int previewLineCount: value("previewLineCount", 4)
+ property string groupBy: value("groupBy", "date")
+ property bool showSeparator: value("showSeparator", false)
}
ConfigurationValue {
diff --git a/qml/pages/NotePage.qml b/qml/pages/NotePage.qml
index 895732d..031e8ab 100644
--- a/qml/pages/NotePage.qml
+++ b/qml/pages/NotePage.qml
@@ -4,22 +4,36 @@ import Sailfish.Silica 1.0
Dialog {
id: noteDialog
+ function reloadContent() {
+ dialogHeader.title = account.model.get(noteIndex).title
+ contentLabel.plainText = account.model.get(noteIndex).content
+ contentLabel.parse()
+ }
+
acceptDestination: Qt.resolvedUrl("EditPage.qml")
acceptDestinationProperties: { account: account; noteIndex: noteIndex }
Component.onCompleted: acceptDestinationProperties = { account: account, noteIndex: noteIndex }
onStatusChanged: {
if (status === PageStatus.Active) {
- dialogHeader.title = account.model.get(noteIndex).title
- contentLabel.plainText = account.model.get(noteIndex).content
- contentLabel.parse()
+ account.getNote(account.model.get(noteIndex).id)
+ reloadContent()
}
}
Connections {
- target: account.model.get(noteIndex)
- onTitleChanged: dialogHeader.title = account.model.get(noteIndex).title
+ target: account/*.model.get(noteIndex)
+ onTitleChanged: {
+ console.log("Title changed")
+ dialogHeader.title = account.model.get(noteIndex).title
+ }
onContentChanged: {
+ console.log("Content changed")
contentLabel.plainText = account.model.get(noteIndex).content
contentLabel.parse()
+ }*/
+ onBusyChanged: {
+ if (account.busy === false) {
+ reloadContent()
+ }
}
}
@@ -46,6 +60,34 @@ Dialog {
Column {
id: column
width: parent.width
+ RemorsePopup {
+ id: remorse
+ onTriggered: pageStack.pop()
+ }
+ PullDownMenu {
+ busy: account ? account.busy : false
+
+ MenuItem {
+ text: qsTr("Delete")
+ enabled: account ? true : false
+ //visible: appSettings.currentAccount >= 0
+ onClicked: remorse.execute("Deleting", function() { account.deleteNote(account.model.get(noteIndex).id) } )
+ }
+ MenuItem {
+ text: enabled ? qsTr("Reload") : qsTr("Updating...")
+ enabled: account ? !account.busy : false
+ //visible: appSettings.currentAccount >= 0
+ onClicked: account.getNote(account.model.get(noteIndex).id)
+ }
+ MenuLabel {
+ visible: appSettings.currentAccount >= 0
+ text: account ? (
+ qsTr("Last update") + ": " + (
+ new Date(account.update).valueOf() !== 0 ?
+ new Date(account.update).toLocaleString(Qt.locale(), Locale.ShortFormat) :
+ qsTr("never"))) : ""
+ }
+ }
DialogHeader {
id: dialogHeader
@@ -67,7 +109,7 @@ Dialog {
tmpText = tmpText.replace(markdown[i].regex, markdown[i].replace)
}
text = tmpText
- console.log(text)
+ //console.log(text)
}
}
}
diff --git a/qml/pages/NotesApi.qml b/qml/pages/NotesApi.qml
index 915dc8e..04675de 100644
--- a/qml/pages/NotesApi.qml
+++ b/qml/pages/NotesApi.qml
@@ -6,6 +6,8 @@ Item {
property string uuid
property string name
property url server
+ property url url
+ property string version: "v0.2"
property string username
property string password
property date update
@@ -13,9 +15,13 @@ Item {
property bool unencryptedConnection
property var model: ListModel { }
+ property var json: [ ]
//property string file: StandardPaths.data + "/" + uuid + ".json"
//property bool saveFile: false
property bool busy: false
+ property int status: 204
+ property string statusText: "No Content"
+ //TODO: put content into an array
ConfigurationGroup {
id: account
@@ -25,6 +31,7 @@ Item {
Component.onCompleted: {
name = account.value("name", "", String)
server = account.value("server", "", String)
+ url = server + "/index.php/apps/notes/api/" + version + "/notes"
username = account.value("username", "", String)
password = account.value("password", "", String)
update = account.value("update", "", Date)
@@ -49,7 +56,7 @@ Item {
function callApi(method, data) {
busy = true
- var endpoint = server + "/index.php/apps/notes/api/v0.2/notes"
+ var endpoint = url
if (data && (method === "GET" || method === "PUT" || method === "DELETE")) {
if (data.id) {
endpoint = endpoint + "/" + data.id
@@ -65,37 +72,39 @@ Item {
apiReq.onreadystatechange = function() {
if (apiReq.readyState === XMLHttpRequest.DONE) {
if (apiReq.status === 200) {
- console.log("Successfull API request!")
- //console.log(apiReq.responseText)
+ //console.log("Successfull API request!")
+ console.log("Network status: " + apiReq.statusText + " (" + apiReq.status + ")")
- var json = JSON.parse(apiReq.responseText)
+ json = JSON.parse(apiReq.responseText)
switch(method) {
case "GET":
if (Array.isArray(json)) {
- console.log("Got all notes")
+ console.log("Received all notes via API: " + endpoint)
model.clear()
for (var element in json) {
model.append(json[element])
+ model.setProperty(element, "date", getDisplayDate(json[element].modified))
}
update = new Date()
}
else {
- console.log("Got a single note")
+ console.log("Received a single note via API: " + endpoint)
for (var i = 0; i < model.count; i++) {
var listItem = model.get(i)
if (listItem.id === json.id){
model.set(i, json)
+ model.setProperty(i, "date", getDisplayDate(json.modified))
}
}
}
break;
case "POST":
- console.log("Created a note")
+ console.log("Created a note via API: " + endpoint)
model.append(json)
model.move(model.count-1, 0, 1)
break;
case "PUT":
- console.log("Updated a note")
+ console.log("Updated a note via API: " + endpoint)
for (var i = 0; i < model.count; i++) {
var listItem = model.get(i)
if (listItem.id === json.id){
@@ -104,7 +113,7 @@ Item {
}
break;
case "DELETE":
- console.log("Deleted a note")
+ console.log("Deleted a note via API: " + endpoint)
for (var i = 0; i < model.count; i++) {
var listItem = model.get(i)
if (listItem.id === data.id){
@@ -127,13 +136,13 @@ Item {
console.log("Note does not exist!")
}*/
else {
- console.log("Networking error: " + apiReq.statusText + " (" + apiReq.status + ")")
+ console.log("Network error: " + apiReq.statusText + " (" + apiReq.status + ")")
}
+ status = apiReq.status
+ statusText = apiReq.statusText
+ //model.sync()
busy = false
}
- else {
- //console.log("HTTP ready state: " + apiReq.readyState)
- }
}
if (method === "GET") {
apiReq.send()
@@ -192,6 +201,27 @@ Item {
}
}
+ // source: https://stackoverflow.com/a/14339782
+ function getDisplayDate(date) {
+ var today = new Date()
+ today.setHours(0)
+ today.setMinutes(0)
+ today.setSeconds(0)
+ today.setMilliseconds(0)
+ var compDate = new Date(date*1000)
+ compDate.setHours(0)
+ compDate.setMinutes(0)
+ compDate.setSeconds(0)
+ compDate.setMilliseconds(0)
+ if (compDate.getTime() === today.getTime()) {
+ return qsTr("Today")
+ } else if ((today.getTime() - compDate.getTime()) <= (24 * 60 * 60 *1000)) {
+ return qsTr("Yesterday")
+ } else {
+ return compDate.toLocaleDateString(Qt.locale(), Locale.ShortFormat)
+ }
+ }
+
/*Component.onCompleted: {
if (saveFile) {
if (account.name === "") {
diff --git a/qml/pages/NotesPage.qml b/qml/pages/NotesPage.qml
index 4f66f45..564d0f1 100644
--- a/qml/pages/NotesPage.qml
+++ b/qml/pages/NotesPage.qml
@@ -4,10 +4,20 @@ import Sailfish.Silica 1.0
Page {
id: page
+ Timer {
+ id: autoSyncTimer
+ interval: appSettings.autoSyncInterval * 1000
+ repeat: true
+ running: interval > 0 && appWindow.visible
+ triggeredOnStart: true
+ onTriggered: nextcloudAccounts.itemAt(appSettings.currentAccount).getNotes()
+ onIntervalChanged: console.log("Auto-Sync every " + interval / 1000 + " seconds")
+ }
+
onStatusChanged: {
if (status === PageStatus.Active) {
if (nextcloudAccounts.count > 0) {
- nextcloudAccounts.itemAt(appSettings.currentAccount).getNotes()
+ autoSyncTimer.restart()
}
else {
addAccountHint.restart()
@@ -18,7 +28,6 @@ Page {
SilicaListView {
id: notesList
anchors.fill: parent
- spacing: Theme.paddingLarge
PullDownMenu {
busy: nextcloudAccounts.itemAt(appSettings.currentAccount) ? nextcloudAccounts.itemAt(appSettings.currentAccount).busy : false
@@ -29,12 +38,12 @@ Page {
}
MenuItem {
text: qsTr("Add note")
- enabled: nextcloudAccounts.itemAt(appSettings.currentAccount) ? !nextcloudAccounts.itemAt(appSettings.currentAccount).busy : false
+ enabled: nextcloudAccounts.itemAt(appSettings.currentAccount) ? true : false
visible: appSettings.currentAccount >= 0
onClicked: nextcloudAccounts.itemAt(appSettings.currentAccount).createNote()
}
MenuItem {
- text: qsTr("Reload")
+ text: enabled ? qsTr("Reload") : qsTr("Updating...")
enabled: nextcloudAccounts.itemAt(appSettings.currentAccount) ? !nextcloudAccounts.itemAt(appSettings.currentAccount).busy : false
visible: appSettings.currentAccount >= 0
onClicked: nextcloudAccounts.itemAt(appSettings.currentAccount).getNotes()
@@ -52,6 +61,12 @@ Page {
header: PageHeader {
title: nextcloudAccounts.itemAt(appSettings.currentAccount).name //qsTr("Nextclound Notes")
description: nextcloudAccounts.itemAt(appSettings.currentAccount).username + "@" + nextcloudAccounts.itemAt(appSettings.currentAccount).server
+ /*BusyIndicator {
+ running: nextcloudAccounts.itemAt(appSettings.currentAccount) ? nextcloudAccounts.itemAt(appSettings.currentAccount).busy : false
+ x: Theme.horizontalPageMargin
+ anchors.verticalCenter: parent.verticalCenter
+ }*/
+
/*SearchField {
width: parent.width
placeholderText: qsTr("Nextcloud Notes")
@@ -74,12 +89,17 @@ Page {
id: note
contentHeight: titleLabel.height + previewLabel.height + 2*Theme.paddingSmall
+ Separator {
+ width: parent.width
+ color: Theme.primaryColor
+ anchors.top: titleLabel.top
+ visible: appSettings.showSeparator && index !== 0
+ }
+
IconButton {
id: isFavoriteIcon
anchors.left: parent.left
- anchors.leftMargin: Theme.paddingSmall
anchors.top: parent.top
- width: Theme.iconSizeMedium
icon.source: (favorite ? "image://theme/icon-m-favorite-selected?" : "image://theme/icon-m-favorite?") +
(note.highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor)
onClicked: {
@@ -92,7 +112,7 @@ Page {
id: titleLabel
anchors.left: isFavoriteIcon.right
anchors.leftMargin: Theme.paddingSmall
- anchors.right: parent.right
+ anchors.right: categoryRectangle.left
anchors.rightMargin: Theme.horizontalPageMargin
anchors.top: parent.top
text: title
@@ -100,6 +120,27 @@ Page {
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: appSettings.groupBy !== "category" && categoryLabel.text.length > 0
+ Label {
+ id: categoryLabel
+ anchors.centerIn: parent
+ text: category
+ color: Theme.secondaryColor
+ font.pixelSize: Theme.fontSizeExtraSmall
+ }
+ }
+
Label {
id: previewLabel
anchors.left: isFavoriteIcon.right
@@ -107,14 +148,20 @@ Page {
anchors.right: parent.right
anchors.rightMargin: Theme.horizontalPageMargin
anchors.top: titleLabel.bottom
- anchors.topMargin: Theme.paddingMedium
- height: Theme.itemSizeExtraLarge
text: content
font.pixelSize: Theme.fontSizeExtraSmall
textFormat: Text.PlainText
wrapMode: Text.Wrap
elide: Text.ElideRight
+ maximumLineCount: appSettings.previewLineCount > 0 ? appSettings.previewLineCount : 1
+ visible: appSettings.previewLineCount > 0
color: note.highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor
+ Component.onCompleted: {
+ var lines = text.split('\n')
+ lines.splice(0,1);
+ text = lines.join('\n');
+ text = text.replace(/^\s*$(?:\r\n?|\n)/gm, "")
+ }
}
onClicked: pageStack.push(Qt.resolvedUrl("NotePage.qml"), { account: nextcloudAccounts.itemAt(appSettings.currentAccount), noteIndex: index } )
@@ -138,7 +185,14 @@ Page {
}
}
- section.property: "category"
+ /*section.property: "category"
+ section.criteria: ViewSection.FullString
+ section.labelPositioning: ViewSection.InlineLabels
+ section.delegate: SectionHeader {
+ text: section
+ }*/
+
+ section.property: appSettings.groupBy
section.criteria: ViewSection.FullString
section.labelPositioning: ViewSection.InlineLabels
section.delegate: SectionHeader {
@@ -149,16 +203,10 @@ Page {
id: busyIndicator
anchors.centerIn: parent
size: BusyIndicatorSize.Large
- visible: nextcloudAccounts.itemAt(appSettings.currentAccount) ? nextcloudAccounts.itemAt(appSettings.currentAccount).busy && notesList.count === 0 : false
+ visible: nextcloudAccounts.itemAt(appSettings.currentAccount) ? (notesList.count === 0 && nextcloudAccounts.itemAt(appSettings.currentAccount).busy) : false
running: visible
}
- ViewPlaceholder {
- enabled: notesList.count === 0 && !busyIndicator.running && !noLoginPlaceholder.enabled
- text: qsTr("No notes yet")
- hintText: qsTr("Pull down to add a note")
- }
-
ViewPlaceholder {
id: noLoginPlaceholder
enabled: nextcloudUUIDs.value.length <= 0
@@ -166,6 +214,20 @@ Page {
hintText: qsTr("Got to the settings to add an account")
}
+ ViewPlaceholder {
+ id: noNotesPlaceholder
+ enabled: nextcloudAccounts.itemAt(appSettings.currentAccount) ? (nextcloudAccounts.itemAt(appSettings.currentAccount).status === 204 && !busyIndicator.running && !noLoginPlaceholder.enabled) : false
+ text: qsTr("No notes yet")
+ hintText: qsTr("Pull down to add a note")
+ }
+
+ ViewPlaceholder {
+ id: errorPlaceholder
+ enabled: notesList.count === 0 && !busyIndicator.running && !noNotesPlaceholder.enabled && !noLoginPlaceholder.enabled
+ text: qsTr("An error occurred")
+ hintText: nextcloudAccounts.itemAt(appSettings.currentAccount).statusText
+ }
+
TouchInteractionHint {
id: addAccountHint
interactionMode: TouchInteraction.Pull
diff --git a/qml/pages/SettingsPage.qml b/qml/pages/SettingsPage.qml
index 95d6fee..f417049 100644
--- a/qml/pages/SettingsPage.qml
+++ b/qml/pages/SettingsPage.qml
@@ -29,7 +29,6 @@ Page {
SectionHeader {
text: qsTr("Accounts")
}
-
Label {
id: noAccountsLabel
visible: nextcloudAccounts.count <= 0
@@ -38,16 +37,15 @@ Page {
color: Theme.secondaryHighlightColor
anchors.horizontalCenter: parent.horizontalCenter
}
-
Repeater {
model: nextcloudAccounts.count
delegate: ListItem {
id: accountListItem
- contentHeight: textSwitch.height
- highlighted: textSwitch.down
+ contentHeight: accountTextSwitch.height
+ highlighted: accountTextSwitch.down
TextSwitch {
- id: textSwitch
+ id: accountTextSwitch
automaticCheck: false
checked: index === appSettings.currentAccount
text: nextcloudAccounts.itemAt(index).name.length <= 0 ? qsTr("Unnamed account") : nextcloudAccounts.itemAt(index).name
@@ -79,7 +77,6 @@ Page {
}
}
}
-
Button {
text: qsTr("Add account")
anchors.horizontalCenter: parent.horizontalCenter
@@ -97,6 +94,76 @@ Page {
})
}
}
+
+ SectionHeader {
+ text: qsTr("Appearance")
+ }
+ ComboBox {
+ id: groupByComboBox
+ property var names: [qsTr("Date"), qsTr("Category")]
+ label: qsTr("Group notes by")
+ menu: ContextMenu {
+ Repeater {
+ id: groupByRepeater
+ model: ["date", "category"]
+ MenuItem {
+ text: groupByComboBox.names[index]
+ Component.onCompleted: {
+ if (modelData === appSettings.groupBy) {
+ groupByComboBox.currentIndex = index
+ }
+ }
+ }
+ }
+ }
+ onCurrentIndexChanged: {
+ appSettings.groupBy = groupByRepeater.model[currentIndex]
+ }
+ }
+ TextSwitch {
+ text: qsTr("Show separator")
+ description: qsTr("Show a separator line between the notes")
+ checked: appSettings.showSeparator
+ onCheckedChanged: appSettings.showSeparator = checked
+ }
+ Slider {
+ width: parent.width
+ minimumValue: 0
+ maximumValue: 20
+ stepSize: 1
+ value: appSettings.previewLineCount
+ valueText: sliderValue + " " + qsTr("lines")
+ label: qsTr("Number of lines to preview in the list view")
+ onSliderValueChanged: appSettings.previewLineCount = sliderValue
+ }
+
+ SectionHeader {
+ text: qsTr("Synchronization")
+ }
+ ComboBox {
+ id: autoSyncComboBox
+ label: qsTr("Auto-Sync")
+ description: qsTr("Periodically pull notes from the server")
+ menu: ContextMenu {
+ Repeater {
+ id: autoSyncIntervalRepeater
+ model: [0, 3, 5, 10, 20, 30, 60, 120, 300, 600]
+ MenuItem {
+ text: modelData === 0 ?
+ qsTr("Disabled") : (qsTr("every") + " " +
+ (parseInt(modelData / 60) ?
+ (parseInt(modelData / 60) + " " + qsTr("Minutes")) :
+ (modelData + " " + qsTr("Seconds"))))
+ Component.onCompleted: {
+ if (modelData === appSettings.autoSyncInterval) {
+ autoSyncComboBox.currentIndex = index
+ }
+ }
+ }
+ }
+ }
+ onCurrentIndexChanged: appSettings.autoSyncInterval = autoSyncIntervalRepeater.model[currentIndex]
+ }
}
VerticalScrollDecorator {}
diff --git a/translations/harbour-nextcloudnotes.ts b/translations/harbour-nextcloudnotes.ts
index ffe4cfa..ce33f37 100644
--- a/translations/harbour-nextcloudnotes.ts
+++ b/translations/harbour-nextcloudnotes.ts
@@ -137,10 +137,6 @@
-
-
-
-
@@ -149,6 +145,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
SettingsPage
@@ -188,6 +196,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
UnencryptedDialog