Worked on multi account settings. The app is still not usable!

This commit is contained in:
Scharel Clemens 2018-11-14 22:13:47 +01:00
parent e03d822b94
commit 3f95d6ed1b
9 changed files with 329 additions and 173 deletions

View file

@ -31,7 +31,8 @@ DISTFILES += qml/harbour-nextcloudnotes.qml \
qml/pages/EditPage.qml \
qml/pages/SettingsPage.qml \
qml/pages/AboutPage.qml \
qml/pages/MarkdownPage.qml
qml/pages/MarkdownPage.qml \
qml/pages/UnencryptedDialog.qml
SAILFISHAPP_ICONS = 86x86 108x108 128x128 172x172

View file

@ -10,6 +10,7 @@ CoverBackground {
CoverActionList {
id: coverAction
enabled: appSettings.currentAccount >= 0
CoverAction {
iconSource: "image://theme/icon-cover-new"

View file

@ -10,35 +10,50 @@ ApplicationWindow
ConfigurationGroup {
id: appSettings
path: "/apps/harbour-nextcloudnotes/settings"
property var accounts: [ ] // FIXME
property int currentAccount: 0 // FIXME
// For testing
Component.onCompleted: {
//appSettings.clear()
//accounts[0] = { server: "127.0.0.1", username: "fu", password: "bar", lastUpdate: new Date(0) }
//accounts[1] = { server: "127.0.0.2", username: "fu", password: "bar", lastUpdate: new Date(0) }
//accounts[2] = { server: "127.0.0.3", username: "fu", password: "bar", lastUpdate: new Date(0) }
console.log("Configured accounts: " + accounts.length)
for(var i=0; i<accounts.length; i++) {
console.log("Account " + i + (i === currentAccount ? " (active):" : ":"))
console.log("- Server: " + accounts[i].server)
console.log("- Username: " + accounts[i].username)
console.log("- Password: " + accounts[i].password)
}
if (typeof(accounts[currentAccount]) !== 'undefined') {
notes.account = appSettings.accounts[appSettings.currentAccount]
}
else {
currentAccount = 0
notes.account = appSettings.accounts[0]
property int currentAccount: value("currentAccount", -1)
property var accountIDs: value("accountIDs", [])
//Component.onCompleted: clear()
}
ConfigurationGroup {
id: accounts
path: "/apps/harbour-nextcloudnotes/accounts"
ConfigurationGroup {
id: account
path: appSettings.accountIDs[appSettings.currentAccount]
property string name
property url server
property string username
property string password
property date update
property bool unsecureConnection: false
property bool unencryptedConnection: false
onPathChanged: {
console.log(scope.path + "/" + path + ": " + name)
console.log(name)
}
}
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
function add() {
var uuid = uuidv4()
return uuid
}
//Component.onCompleted: clear()
}
property var notes: NotesApi {
name: "notes"
//account: appSettings.accounts[appSettings.currentAccount]
saveFile: false
NotesApi {
id: notes
}
initialPage: Component { NotesPage { } }

View file

@ -1,74 +1,147 @@
import QtQuick 2.0
import Sailfish.Silica 1.0
import Nemo.Configuration 1.0
Dialog {
id: loginDialog
property var account
canAccept: (serverField.acceptableInput && usernameField.text.length > 0 && passwordField.text.length > 0)
onAccepted: {
account = {
server: serverField.text,
username: usernameField.text,
password: passwordField.text,
lastUpdate: new Date(0)
}
property var accountID
ConfigurationGroup {
id: account
path: "/apps/harbour-nextcloudnotes/accounts/" + accountID
}
Column {
width: parent.width
canAccept: (nameField.text.length > 0 && serverField.acceptableInput && usernameField.text.length > 0 && passwordField.text.length > 0)
onAccepted: {
account.setValue("name", nameField.text)
account.setValue("server", serverField.text)
account.setValue("username", usernameField.text)
account.setValue("password", passwordField.text)
//accounts.itemAt(iAccount).unsecureConnection = unsecureConnectionTextSwitch.checked
//accounts.itemAt(iAccount).unencryptedConnection = unencryptedConnectionTextSwitch.checked
account.setValue("valid", true)
account.sync()
}
DialogHeader {
id: header
//title: qsTr("Nextcloud Login")
acceptText: qsTr("Login")
}
SilicaFlickable {
anchors.fill: parent
contentHeight: column.height
Image {
anchors.horizontalCenter: parent.horizontalCenter
height: Theme.itemSizeHuge
fillMode: Image.PreserveAspectFit
source: "../img/nextcloud-logo-transparent.png"
}
TextField {
id: serverField
focus: true
Column {
id: column
width: parent.width
text: (typeof(account) !== 'undefined' && account.server.toString().length > 0) ? account.server : "https://"
placeholderText: qsTr("Nextcloud server")
label: placeholderText + " " + qsTr("(starting with \"https://\")")
inputMethodHints: Qt.ImhUrlCharactersOnly
// regExp from https://stackoverflow.com/a/3809435 (EDIT: removed ? after https to force SSL)
validator: RegExpValidator { regExp: /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/ } // TODO disable unencrypted communication
EnterKey.enabled: acceptableInput
EnterKey.iconSource: "image://theme/icon-m-enter-next"
EnterKey.onClicked: usernameField.focus = true
}
TextField {
id: usernameField
width: parent.width
text: (typeof(account) !== 'undefined' && account.username.length > 0) ? account.username : ""
placeholderText: qsTr("Username")
label: placeholderText
inputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase
errorHighlight: text.length === 0 && focus === true
EnterKey.enabled: text.length > 0
EnterKey.iconSource: "image://theme/icon-m-enter-next"
EnterKey.onClicked: passwordField.focus = true
}
DialogHeader {
id: header
//title: qsTr("Nextcloud Login")
acceptText: qsTr("Login")
}
PasswordField {
id: passwordField
width: parent.width
text: (typeof(account) !== 'undefined' && account.password.length > 0) ? account.password : ""
label: placeholderText
errorHighlight: text.length === 0 && focus === true
EnterKey.enabled: text.length > 0
EnterKey.iconSource: "image://theme/icon-m-enter-accept"
EnterKey.onClicked: loginDialog.accept()
Image {
anchors.horizontalCenter: parent.horizontalCenter
height: Theme.itemSizeHuge
fillMode: Image.PreserveAspectFit
source: "../img/nextcloud-logo-transparent.png"
}
TextField {
id: nameField
focus: true
width: parent.width
text: account.value("name", qsTr("My Nextcloud account"), String)
placeholderText: qsTr("Account name")
label: placeholderText
errorHighlight: text.length === 0// && focus === true
EnterKey.enabled: text.length > 0
EnterKey.iconSource: "image://theme/icon-m-enter-next"
EnterKey.onClicked: serverField.focus = true
}
TextField {
id: serverField
width: parent.width
text: account.value("server", "https://", String)
placeholderText: qsTr("Nextcloud server")
label: placeholderText + " " + qsTr("(starting with \"https://\")")
inputMethodHints: Qt.ImhUrlCharactersOnly
// regExp from https://stackoverflow.com/a/3809435 (EDIT: removed ? after https to force SSL)
validator: RegExpValidator { regExp: /https:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/ }
errorHighlight: !acceptableInput// && focus === true
EnterKey.enabled: acceptableInput
EnterKey.iconSource: "image://theme/icon-m-enter-next"
EnterKey.onClicked: usernameField.focus = true
}
TextField {
id: usernameField
width: parent.width
text: account.value("username", "", String)
placeholderText: qsTr("Username")
label: placeholderText
inputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase
errorHighlight: text.length === 0// && focus === true
EnterKey.enabled: text.length > 0
EnterKey.iconSource: "image://theme/icon-m-enter-next"
EnterKey.onClicked: passwordField.focus = true
}
PasswordField {
id: passwordField
width: parent.width
text: account.value("password", "", String)
label: placeholderText
errorHighlight: text.length === 0// && focus === true
EnterKey.enabled: text.length > 0
EnterKey.iconSource: "image://theme/icon-m-enter-accept"
EnterKey.onClicked: loginDialog.accept()
}
SectionHeader {
text: qsTr("Security")
}
Label {
x: Theme.horizontalPageMargin
width: parent.width - 2*x
wrapMode: Text.Wrap
color: Theme.secondaryColor
text: qsTr("Please consider creating a dedicated app password! Open your Nextcloud in a browser and go to <i>Settings</i> → <i>Security</i>.")
}
/*TextSwitch {
id: unsecureConnectionTextSwitch
checked: appSettings.unsecureConnection
automaticCheck: true
text: qsTr("Do not check certificates")
description: qsTr("Enable this option to allow selfsigned certificates")
onCheckedChanged: {
if (checked) {
}
else {
unencryptedConnection.checked = false
}
}
}
TextSwitch {
id: unencryptedConnectionTextSwitch
enabled: unsecureConnectionTextSwitch.checked
checked: appSettings.unencryptedConnection
automaticCheck: false
text: qsTr("Allow unencrypted connection")
description: qsTr("")
onClicked: {
if (!checked) {
var dialog = pageStack.push(Qt.resolvedUrl("UnencryptedDialog.qml"))
dialog.accepted.connect(function() {
checked = true
})
dialog.rejected.connect(function() {
checked = false
})
}
else
checked = false
}
}*/
}
}
}

View file

@ -2,12 +2,8 @@ import QtQuick 2.0
import Sailfish.Silica 1.0
Item {
property string name
property var account
property var model: ListModel { }
property string json
property string file: StandardPaths.data + "/" + name + ".json"
property string file: StandardPaths.data + "/" + account.name + ".json"
property bool saveFile: false
property bool busy: false
//property date lastUpdate: new Date(0)
@ -92,7 +88,7 @@ Item {
callApi("DELETE", { 'id': id } )
}
onJsonChanged: refresh()
//onJsonChanged: refresh()
function flush() {
json = ""
@ -100,7 +96,7 @@ Item {
filePut.open("PUT", file)
filePut.send(json)
model.clear()
account.lastUpdate = new Date(0)
account.update = new Date(0)
status = 200
}
@ -127,7 +123,7 @@ Item {
function parseJson() {
var elements = JSON.parse(json)
if (elements === null) {
console.log("Error parsing " + name + "-JSON")
console.log("Error parsing " + account.name + "-JSON")
elements = ""
json = ""
return null
@ -140,7 +136,7 @@ Item {
/*Component.onCompleted: {
if (saveFile) {
if (name === "") {
if (account.name === "") {
saveFile = false
}
else {
@ -153,7 +149,7 @@ Item {
update()
}
else {
console.log("Loaded " + name + " from local JSON file")
console.log("Loaded " + account.name + " from local JSON file")
json = fileReq.responseText
busy = false
}

View file

@ -12,28 +12,32 @@ Page {
PullDownMenu {
busy: notes.busy
MenuLabel {
visible: appSettings.accounts.length > 0
text: appSettings.accounts.length > 0 ?
(qsTr("Last update") + ": " +
(appSettings.accounts[appSettings.currentAccount].lastUpdate.value === 0 ?
appSettings.accounts[appSettings.currentAccount].lastUpdate.toLocaleString(Qt.locale(), Locale.ShortFormat) :
qsTr("never"))) : ""
}
MenuItem {
text: qsTr("Reload")
visible: appSettings.accounts.length > 0
onClicked: notes.getNotes()
}
MenuItem {
text: qsTr("Settings")
onClicked: pageStack.push(Qt.resolvedUrl("SettingsPage.qml"))
}
MenuItem {
text: qsTr("Add note")
enabled: appSettings.accounts.length > 0
enabled: !notes.busy
visible: account.server.length > 0
onClicked: console.log("Add note")
}
MenuItem {
text: qsTr("Reload")
enabled: !notes.busy
visible: account.server.length > 0
onClicked: notes.getNotes()
}
MenuLabel {
visible: account.server.length > 0
text: qsTr("Last update") + ": " +
account.update.value !== 0 ?
new Date(account.update).toLocaleString(Qt.locale(), Locale.ShortFormat) :
qsTr("never")
//(new Date(appSettings.value("accountUpdates", [appSettings.currentAccount])).value === 0 ?
//new Date(appSettings.value("accountUpdates", [appSettings.currentAccount])).toLocaleString(Qt.locale(), Locale.ShortFormat) :
//qsTr("never"))
}
}
header: SearchField {
@ -43,12 +47,12 @@ Page {
EnterKey.iconSource: "image://theme/icon-m-enter-close"
EnterKey.onClicked: focus = false
enabled: false //notesList.count > 0 // TODO
enabled: notesList.count > 0
}
currentIndex: -1
Component.onCompleted: {
if (appSettings.accounts.length > 0) {
if (account.valid) {
notes.getNotes()
}
}
@ -141,33 +145,33 @@ Page {
running: visible
}
ViewPlaceholder {
id: noLoginPlaceholder
enabled: (appSettings.accounts.length === 0)
text: qsTr("No accounts yet")
}
ViewPlaceholder {
enabled: notesList.count === 0 && !notes.busy && !noLoginPlaceholder.enabled
text: qsTr("No notes yet")
hintText: qsTr("Pull down to add a note")
}
ViewPlaceholder {
id: noLoginPlaceholder
enabled: appSettings.accountIDs.length <= 0
text: qsTr("No account yet")
hintText: qsTr("Got to the settings to add an account")
}
TouchInteractionHint {
id: addAccountHint
Component.onCompleted: if(!account.valid) restart()
interactionMode: TouchInteraction.Pull
direction: TouchInteraction.Down
}
InteractionHintLabel {
anchors.fill: parent
text: qsTr("Open the settings to configure your Nextcloud accounts")
opacity: addAccountHint.running ? 1.0 : 0.0
Behavior on opacity { FadeAnimation {} }
width: parent.width
}
VerticalScrollDecorator { flickable: notesList }
}
TouchInteractionHint {
id: addAccountHint
Component.onCompleted: if (appSettings.accounts.length === 0) restart()
interactionMode: TouchInteraction.Pull
direction: TouchInteraction.Down
}
InteractionHintLabel {
anchors.fill: parent
text: qsTr("Open the settings to add a Nextcloud account")
opacity: addAccountHint.running ? 1.0 : 0.0
Behavior on opacity { FadeAnimation {} }
width: parent.width
}
}

View file

@ -1,5 +1,6 @@
import QtQuick 2.0
import Sailfish.Silica 1.0
import Nemo.Configuration 1.0
Page {
id: page
@ -30,40 +31,51 @@ Page {
}
Label {
id: noAccountsLabel
visible: appSettings.accounts.length === 0
visible: appSettings.accountIDs.length <= 0
text: qsTr("No Nextcloud account yet")
font.pixelSize: Theme.fontSizeLarge
color: Theme.secondaryHighlightColor
anchors.horizontalCenter: parent.horizontalCenter
}
Repeater {
model: appSettings.accounts
model: appSettings.accountIDs.length
delegate: ListItem {
id: listItem
ConfigurationGroup {
id: account
path: "/apps/harbour-nextcloudnotes/accounts/" + appSettings.accountIDs[index]
}
TextSwitch {
anchors.verticalCenter: parent.verticalCenter
automaticCheck: false
checked: index === appSettings.currentAccount
text: appSettings.accounts[index].username + "@" + appSettings.accounts[index].server
description: checked ? qsTr("Press and hold to edit") : qsTr("Click to choose as active account")
onClicked: appSettings.currentAccount = index
text: account.value("name", qsTr("Account") + " " + (index+1), String)
//enabled: account.value("valid", false, Boolean)
description: account.value("valid", false, Boolean) ? account.value("username", qsTr("user"), String) + "@" + account.value("server", qsTr("server"), String) : qsTr("Press and hold to configure")
onClicked: if (account.value("valid", false, Boolean)) appSettings.currentAccount = index
onPressAndHold: listItem.openMenu()
}
menu: ContextMenu {
MenuItem {
text: qsTr("Edit")
text: qsTr("Configure")
onClicked: {
var login = pageStack.push(Qt.resolvedUrl("LoginDialog.qml"), { account: appSettings.accounts[index] } )
var login = pageStack.push(Qt.resolvedUrl("LoginDialog.qml"), { accountID: appSettings.accountIDs[index] })
login.accepted.connect(function() {
console.log(login.account.username + ":" + login.account.password + "@" + login.account.server.toString())
appSettings.accounts[index] = login.account
update()
})
login.rejected.connect(function() {
})
}
}
MenuItem {
/*MenuItem {
text: qsTr("Delete")
visible: false // TODO
}
onClicked: {
accounts.itemAt(index).clear()
// TODO reorder items
}
}*/
}
}
}
@ -71,23 +83,16 @@ Page {
text: qsTr("Add account")
anchors.horizontalCenter: parent.horizontalCenter
onClicked: {
var login = pageStack.push(Qt.resolvedUrl("LoginDialog.qml"))
var login = pageStack.push(Qt.resolvedUrl("LoginDialog.qml"), { accountID: accounts.add() })
login.accepted.connect(function() {
var list = appSettings.accounts
list.push(login.account)
appSettings.accounts = list
appSettings.sync()
appSettings.currentAccount = appSettings.accounts.length
appSettings.sync()
notes.account = appSettings.accounts[appSettings.currentAccount]
notes.getNotes()
var tmpIDs = appSettings.accountIDs
tmpIDs.push(login.accountID)
appSettings.accountIDs = tmpIDs
})
login.rejected.connect(function() {
})
}
}
SectionHeader {
text: qsTr("Security")
}
}
VerticalScrollDecorator {}

View file

@ -0,0 +1,34 @@
import QtQuick 2.0
import Sailfish.Silica 1.0
Dialog {
id: unencryptedDialog
canAccept: textSwitch.checked
SilicaFlickable {
anchors.fill: parent
contentHeight: column.height
Column {
id: column
width: parent.width
DialogHeader {
}
Label {
x: Theme.horizontalPageMargin
width: parent.width - 2*x
wrapMode: Text.Wrap
linkColor: Theme.highlightColor
text: qsTr("<strong>Your username and password will be transferred unencrypted over the network when you enable this option.<br>Do not accept unless you know exactly what you are doing!</strong ><br><a href=\"https://github.com/nextcloud/notes/wiki/API-0.2\">More information...</a>")
}
TextSwitch {
id: textSwitch
text: qsTr("I do understand")
}
}
}
}

View file

@ -48,6 +48,26 @@
<source>Nextcloud server</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Account name</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Security</source>
<translation type="unfinished"></translation>
</message>
<message>
<source></source>
<translation></translation>
</message>
<message>
<source>Please consider creating a dedicated app password! Open your Nextcloud in a browser and go to &lt;i&gt;Settings&lt;/i&gt; → &lt;i&gt;Security&lt;/i&gt;.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>My Nextcloud account</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MarkdownPage</name>
@ -105,10 +125,6 @@
<source>Reload</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No accounts yet</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>never</source>
<translation type="unfinished"></translation>
@ -117,6 +133,14 @@
<source>Open the settings to add a Nextcloud account</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No account yet</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Got to the settings to add an account</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsPage</name>
@ -128,36 +152,39 @@
<source>Accounts</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Add account</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No Nextcloud account yet</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>About</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Press and hold to edit</source>
<source>user</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Click to choose as active account</source>
<source>server</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Edit</source>
<source>Account</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete</source>
<source>Press and hold to configure</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Security</source>
<source>Configure</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>UnencryptedDialog</name>
<message>
<source>I do understand</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;strong&gt;Your username and password will be transferred unencrypted over the network when you enable this option.&lt;br&gt;Do not accept unless you know exactly what you are doing!&lt;/strong &gt;&lt;br&gt;&lt;a href=&quot;https://github.com/nextcloud/notes/wiki/API-0.2&quot;&gt;More information...&lt;/a&gt;</source>
<translation type="unfinished"></translation>
</message>
</context>