From 4e2a06bcded9e5b3785a3929149f4aa42a5c547c Mon Sep 17 00:00:00 2001 From: Scharel Clemens Date: Tue, 5 Jan 2021 21:40:53 +0100 Subject: [PATCH] Further work on Login workflow --- README.md | 5 ++ qml/harbour-nextcloudnotes.qml | 25 ++++++- qml/pages/LoginPage.qml | 87 ++++++++++------------- qml/pages/NotesPage.qml | 6 +- qml/pages/SettingsPage.qml | 2 +- src/notesapi.cpp | 8 +-- translations/harbour-nextcloudnotes-de.ts | 24 +++++-- translations/harbour-nextcloudnotes-sv.ts | 24 +++++-- translations/harbour-nextcloudnotes.ts | 80 +++++++++++++-------- 9 files changed, 162 insertions(+), 99 deletions(-) diff --git a/README.md b/README.md index 239389d..1fc6eae 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,11 @@ Currently the app can be downloaded from [OpenRepos](https://openrepos.net/conte You can preview some screenshots [here](https://cloud.scharel.name/apps/gallery/s/harbour-nextcloudnotes#Screenshots). +## Cloning the repo + +If you want to clone the repo add `--recurse-submodules` to the git clone command. +This will also clone the ShowdownJS repo to the corresponding directory. + ## Features ### Implemented diff --git a/qml/harbour-nextcloudnotes.qml b/qml/harbour-nextcloudnotes.qml index 0ad005e..71b821b 100644 --- a/qml/harbour-nextcloudnotes.qml +++ b/qml/harbour-nextcloudnotes.qml @@ -15,7 +15,7 @@ ApplicationWindow property bool initialized: false property var accounts: value("accounts", [], Array) - property string currentAccountIndex: value("currentAccountIndex", -1, Number) + property int currentAccountIndex: value("currentAccountIndex", -1, Number) property int autoSyncInterval: value("autoSyncInterval", 0, Number) property int previewLineCount: value("previewLineCount", 4, Number) property bool favoritesOnTop: value("favoritesOnTop", true, Boolean) @@ -30,6 +30,9 @@ ApplicationWindow account = accounts[currentAccountIndex] console.log("Current account: " + account.username + "@" + account.url) } + else { + account = null + } } onSortByChanged: { @@ -42,17 +45,30 @@ ApplicationWindow notesProxyModel.favoritesOnTop = favoritesOnTop } - function createAccount(user, url) { + function createAccount(user, password, url) { var hash = accountHash.hash(user, url) console.log("Hash(" + user + "@" + url + ") = " + hash) return hash } function removeAccount(hash) { accounts[hash] = null - currentAccount = -1 } + currentAccount = -1 + } } property var account + onAccountChanged: { + if (account) { + notesApi.server = server + notesApi.username = username + notesApi.password = password + } + else { + notesApi.server = "" + notesApi.username = "" + notesApi.password = "" + } + } Notification { id: offlineNotification @@ -90,6 +106,9 @@ ApplicationWindow if (interval > 0) { console.log("Auto-Sync every " + interval / 1000 + " seconds") } + else { + console.log("Auto-Sync disabled") + } } } diff --git a/qml/pages/LoginPage.qml b/qml/pages/LoginPage.qml index 61e92c6..935aff6 100644 --- a/qml/pages/LoginPage.qml +++ b/qml/pages/LoginPage.qml @@ -8,22 +8,23 @@ Dialog { canAccept: false + property int peviousAccountIndex: appSettings.currentAccountIndex + property bool legacyLoginPossible: false property bool flowLoginV2Possible: false - property url server - property string username - property string password property bool doNotVerifySsl: false property bool allowUnecrypted: false - property string productName - property string version + Component.onCompleted: { + appSettings.currentAccountIndex = -1 + } onRejected: { + appSettings.currentAccountIndex = peviousAccountIndex } onAccepted: { - appSettings.createAccount(username, server) + appSettings.createAccount(notesApi.username, notesApi.password, notesApi.server) } Timer { @@ -55,16 +56,13 @@ Dialog { flowLoginV2Possible = false } } - onStatusVersionStringChanged: { - if (notesApi.statusVersionString) { - version = notesApi.statusVersionString - console.log(notesApi.statusVersionString) - } - } onStatusProductNameChanged: { if (notesApi.statusProductName) { productName = notesApi.statusProductName - console.log(notesApi.statusProductName) + console.log(productName) + } + else { + productName = null } } onLoginStatusChanged: { @@ -111,24 +109,6 @@ Dialog { Qt.openUrlExternally(notesApi.loginUrl) } } - onServerChanged: { - if (notesApi.server) { - console.log(notesApi.server) - server = notesApi.server - } - } - onUsernameChanged: { - if (notesApi.username) { - console.log(notesApi.username) - username = notesApi.username - } - } - onPasswordChanged: { - if (notesApi.password) { - console.log("***") - password = notesApi.password - } - } } SilicaFlickable { @@ -157,6 +137,10 @@ Dialog { id: apiProgressBar anchors.horizontalCenter: parent.horizontalCenter width: parent.width + label: verifyServerTimer.running ? qsTr("Verifying address") : " " + indeterminate: notesApi.loginStatus === NotesApi.LoginFlowV2Initiating || + notesApi.loginStatus === NotesApi.LoginFlowV2Polling || + notesApi.ncStatusStatus === notesApi.NextcloudBusy || (verifyServerTimer.running) } Row { @@ -164,18 +148,18 @@ Dialog { TextField { id: serverField width: parent.width - statusIcon.width - Theme.horizontalPageMargin - text: server - placeholderText: productName ? productName : qsTr("Nextcloud server") - label: placeholderText - validator: RegExpValidator { regExp: unencryptedConnectionTextSwitch.checked ? /^https?:\/\/([-a-zA-Z0-9@:%._\+~#=].*)/: /^https:\/\/([-a-zA-Z0-9@:%._\+~#=].*)/ } + text: notesApi.server + placeholderText: qsTr("Enter Nextcloud address") + label: notesApi.statusProductName ? notesApi.statusProductName : qsTr("Nextcloud address") + validator: RegExpValidator { regExp: allowUnecrypted ? /^https?:\/\/([-a-zA-Z0-9@:%._\+~#=].*)/: /^https:\/\/([-a-zA-Z0-9@:%._\+~#=].*)/ } inputMethodHints: Qt.ImhUrlCharactersOnly onClicked: if (text === "") text = allowUnecrypted ? "http://" : "https://" onTextChanged: { + loginDialog.canAccept = false if (acceptableInput) { notesApi.server = text + verifyServerTimer.restart() } - verifyServerTimer.restart() - notesApi.getNcStatus() } //EnterKey.enabled: text.length > 0 EnterKey.iconSource: legacyLoginPossible ? "image://theme/icon-m-enter-next" : flowLoginV2Possible ? "image://theme/icon-m-enter-accept" : "image://theme/icon-m-enter-close" @@ -189,13 +173,8 @@ Dialog { } Icon { id: statusIcon - highlighted: serverField.highlighted - source: notesApi.statusInstalled ? "image://theme/icon-m-acknowledge" : "image://theme/icon-m-question" - BusyIndicator { - anchors.centerIn: parent - size: BusyIndicatorSize.Medium - running: notesApi.ncStatusStatus === notesApi.NextcloudBusy || (verifyServerTimer.running) - } + source: notesApi.statusInstalled ? "image://theme/icon-m-accept" : "image://theme/icon-m-cancel" + color: notesApi.statusInstalled ? "green" : Theme.errorColor } } @@ -234,11 +213,15 @@ Dialog { TextField { id: usernameField width: parent.width - text: username - placeholderText: qsTr("Username") - label: placeholderText + text: notesApi.username + placeholderText: qsTr("Enter Username") + label: qsTr("Username") inputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase errorHighlight: text.length === 0// && focus === true + onTextChanged: { + loginDialog.canAccept = false + notesApi.username = text + } EnterKey.enabled: text.length > 0 EnterKey.iconSource: "image://theme/icon-m-enter-next" EnterKey.onClicked: passwordField.focus = true @@ -246,10 +229,14 @@ Dialog { PasswordField { id: passwordField width: parent.width - text: password - placeholderText: qsTr("Password") - label: placeholderText + text: notesApi.password + placeholderText: qsTr("Enter Password") + label: qsTr("Password") errorHighlight: text.length === 0// && focus === true + onTextChanged: { + loginDialog.canAccept = false + notesApi.password = text + } EnterKey.enabled: text.length > 0 EnterKey.iconSource: "image://theme/icon-m-enter-accept" EnterKey.onClicked: notesApi.verifyLogin(usernameField.text, passwordField.text) diff --git a/qml/pages/NotesPage.qml b/qml/pages/NotesPage.qml index d8fcb5f..1ba99dd 100644 --- a/qml/pages/NotesPage.qml +++ b/qml/pages/NotesPage.qml @@ -32,16 +32,16 @@ Page { } MenuItem { text: qsTr("Add note") - enabled: account != null && notesApi.networkAccessible + enabled: account !== null && notesApi.networkAccessible onClicked: notesApi.createNote( { 'content': "", 'modified': new Date().valueOf() / 1000 } ) } MenuItem { text: notesApi.networkAccessible && !notesApi.busy ? qsTr("Reload") : qsTr("Updating...") - enabled: account != null && notesApi.networkAccessible && !notesApi.busy + enabled: account !== null && notesApi.networkAccessible && !notesApi.busy onClicked: notes.getAllNotes() } MenuLabel { - visible: account != null + visible: account !== null text: qsTr("Last update") + ": " + ( new Date(account.update).valueOf() !== 0 ? new Date(account.update).toLocaleString(Qt.locale(), Locale.ShortFormat) : diff --git a/qml/pages/SettingsPage.qml b/qml/pages/SettingsPage.qml index d9481a8..eb31287 100644 --- a/qml/pages/SettingsPage.qml +++ b/qml/pages/SettingsPage.qml @@ -91,7 +91,7 @@ Page { text: qsTr("Add account") anchors.horizontalCenter: parent.horizontalCenter onClicked: { - var login = pageStack.push(Qt.resolvedUrl("LoginPage.qml"), { accountId: "" }) + var login = pageStack.push(Qt.resolvedUrl("LoginPage.qml")) } } diff --git a/src/notesapi.cpp b/src/notesapi.cpp index 670ce26..0c865a5 100644 --- a/src/notesapi.cpp +++ b/src/notesapi.cpp @@ -387,7 +387,7 @@ void NotesApi::replyFinished(QNetworkReply *reply) { emit noteError(CommunicationError); QByteArray data = reply->readAll(); - //qDebug() << data; + qDebug() << data; //qDebug() << reply->rawHeader("X-Notes-API-Versions"); QJsonDocument json = QJsonDocument::fromJson(data); @@ -470,10 +470,9 @@ void NotesApi::replyFinished(QNetworkReply *reply) { } else if (m_ocsReplies.contains(reply)) { qDebug() << "OCS reply"; - QString xml(data); - if (reply->error() == QNetworkReply::NoError && xml.contains("ok")) { - qDebug() << "Login Success!"; + if (reply->error() == QNetworkReply::NoError && updateCapabilities(json.object())) { setLoginStatus(LoginSuccess); + qDebug() << "Login Succcessfull!"; } else { qDebug() << "Login Failed!"; @@ -653,6 +652,7 @@ bool NotesApi::updateLoginCredentials(const QJsonObject &credentials) { setPassword(appPassword); } if (!serverAddr.isEmpty() && !loginName.isEmpty() && !appPassword.isEmpty()) { + abortFlowV2Login(); qDebug() << "Login successfull for user" << loginName << "on" << serverAddr; setLoginStatus(LoginStatus::LoginFlowV2Success); return true; diff --git a/translations/harbour-nextcloudnotes-de.ts b/translations/harbour-nextcloudnotes-de.ts index 97f83c0..05fd1d4 100644 --- a/translations/harbour-nextcloudnotes-de.ts +++ b/translations/harbour-nextcloudnotes-de.ts @@ -118,10 +118,6 @@ Nextcloud Login Nextcloud Login - - Nextcloud server - Nextcloud Server - Abort Abbrechen @@ -198,6 +194,26 @@ Enforce legacy login + + Nextcloud address + + + + Verifying address + + + + Enter Nextcloud address + + + + Enter Username + + + + Enter Password + + MITLicense diff --git a/translations/harbour-nextcloudnotes-sv.ts b/translations/harbour-nextcloudnotes-sv.ts index 3fdef37..3c37cc3 100644 --- a/translations/harbour-nextcloudnotes-sv.ts +++ b/translations/harbour-nextcloudnotes-sv.ts @@ -118,10 +118,6 @@ Nextcloud Login - - Nextcloud server - - Abort @@ -198,6 +194,26 @@ Enforce legacy login + + Nextcloud address + + + + Verifying address + + + + Enter Nextcloud address + + + + Enter Username + + + + Enter Password + + MITLicense diff --git a/translations/harbour-nextcloudnotes.ts b/translations/harbour-nextcloudnotes.ts index 8cf0af8..24eb40a 100644 --- a/translations/harbour-nextcloudnotes.ts +++ b/translations/harbour-nextcloudnotes.ts @@ -138,103 +138,123 @@ LoginPage - + Nextcloud Login - - Nextcloud server - - - - + Username - + Password - + Abort - + Follow the instructions in the browser - + Login successfull! - - + + Login failed! - + Enter your credentials - + + Nextcloud address + + + + + Verifying address + + + + + Enter Nextcloud address + + + + Enforce legacy login - + Login - + Re-Login - + + Enter Username + + + + + Enter Password + + + + Test Login - + Note - + The <a href="https://apps.nextcloud.com/apps/notes">Notes</a> app needs to be installed on the Nextcloud server for this app to work. - + Security - + <strong>CAUTION: Your password will be saved without any encryption on the device!</strong><br>Please consider creating a dedicated app password! Open your Nextcloud in a browser and go to <i>Settings</i> → <i>Security</i>. - + Do not check certificates - + Enable this option to allow selfsigned certificates - + Allow unencrypted connections @@ -860,27 +880,27 @@ You can also use other markdown syntax inside them. harbour-nextcloudnotes - + Notes - + Offline - + Synced - + API error - + File error