From e16122d978a1bf3b18e564d139a585298db74846 Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Wed, 11 Jan 2017 00:28:51 +0100 Subject: [PATCH] Handle login frontend --- QtClient/JoplinQtClient/JoplinQtClient.pro | 6 ++- QtClient/JoplinQtClient/LoginPage.qml | 25 +++++++++ QtClient/JoplinQtClient/LoginPageForm.ui.qml | 30 +++++++++-- QtClient/JoplinQtClient/app.qml | 40 ++++++++++++-- QtClient/JoplinQtClient/application.cpp | 56 +++++++++++++++++--- QtClient/JoplinQtClient/application.h | 6 ++- QtClient/JoplinQtClient/constants.h | 1 - QtClient/JoplinQtClient/dispatcher.cpp | 16 ++++++ QtClient/JoplinQtClient/dispatcher.h | 11 ++++ QtClient/JoplinQtClient/synchronizer.cpp | 3 +- QtClient/JoplinQtClient/synchronizer.h | 2 +- QtClient/JoplinQtClient/webapi.cpp | 16 +++++- QtClient/JoplinQtClient/webapi.h | 3 +- QtClient/JoplinQtClient/window.cpp | 11 ++++ QtClient/JoplinQtClient/window.h | 21 ++++++++ 15 files changed, 220 insertions(+), 27 deletions(-) create mode 100755 QtClient/JoplinQtClient/window.cpp create mode 100755 QtClient/JoplinQtClient/window.h diff --git a/QtClient/JoplinQtClient/JoplinQtClient.pro b/QtClient/JoplinQtClient/JoplinQtClient.pro index eb62e989f..273beb3af 100755 --- a/QtClient/JoplinQtClient/JoplinQtClient.pro +++ b/QtClient/JoplinQtClient/JoplinQtClient.pro @@ -21,7 +21,8 @@ SOURCES += \ models/change.cpp \ models/basemodel.cpp \ models/setting.cpp \ - paths.cpp + paths.cpp \ + window.cpp RESOURCES += qml.qrc \ database.qrc @@ -57,7 +58,8 @@ HEADERS += \ enum.h \ models/setting.h \ paths.h \ - constants.h + constants.h \ + window.h DISTFILES += \ AndroidManifest.xml diff --git a/QtClient/JoplinQtClient/LoginPage.qml b/QtClient/JoplinQtClient/LoginPage.qml index ec8704c93..d825796df 100755 --- a/QtClient/JoplinQtClient/LoginPage.qml +++ b/QtClient/JoplinQtClient/LoginPage.qml @@ -1,4 +1,29 @@ import QtQuick 2.4 LoginPageForm { + + property Item appRoot + + id: root + + Connections { + target: root + onLoginButtonClicked: { + dispatcher.emitLoginClicked(root.apiBaseUrl, root.email, root.password); + } + } + + Connections { + target: dispatcher + onLoginStarted: { + root.enabled = false; + } + onLoginFailed: { + root.enabled = true; + } + onLoginSuccess: { + root.enabled = true; + } + } + } diff --git a/QtClient/JoplinQtClient/LoginPageForm.ui.qml b/QtClient/JoplinQtClient/LoginPageForm.ui.qml index cd01e5256..d392af9b7 100755 --- a/QtClient/JoplinQtClient/LoginPageForm.ui.qml +++ b/QtClient/JoplinQtClient/LoginPageForm.ui.qml @@ -3,8 +3,19 @@ import QtQuick.Controls 2.0 import QtQuick.Layouts 1.3 Item { + id: root width: 400 height: 400 + signal loginButtonClicked() + property alias apiBaseUrl: apiBaseUrlTF.text + property alias email: emailTF.text + property alias password: passwordTF.text + + Rectangle { + id: rectangle2 + color: "#ffffff" + anchors.fill: parent + } GridLayout { id: gridLayout1 @@ -15,11 +26,12 @@ Item { Label { id: label1 - text: qsTr("Domain") + text: qsTr("API base URL") } TextField { - id: textField1 + id: apiBaseUrlTF + text: "http://joplin.local" Layout.fillWidth: true } @@ -29,7 +41,8 @@ Item { } TextField { - id: textField2 + id: emailTF + text: "laurent@cozic.net" Layout.fillWidth: true } @@ -39,12 +52,13 @@ Item { } TextField { - id: textField3 + id: passwordTF + text: "12345678" Layout.fillWidth: true } Button { - id: button1 + id: loginButton text: qsTr("Login") Layout.fillWidth: true Layout.columnSpan: 2 @@ -65,4 +79,10 @@ Item { } + + Connections { + target: loginButton + onClicked: root.loginButtonClicked() + } + } diff --git a/QtClient/JoplinQtClient/app.qml b/QtClient/JoplinQtClient/app.qml index 05abbc1cf..c458389e5 100755 --- a/QtClient/JoplinQtClient/app.qml +++ b/QtClient/JoplinQtClient/app.qml @@ -12,17 +12,49 @@ Item { signal addNoteButtonClicked() signal addFolderButtonClicked() signal syncButtonClicked() + signal loginButtonClicked() property alias currentFolderIndex: mainPage.currentFolderIndex property alias currentNoteIndex: mainPage.currentNoteIndex + property var pages : ({}) + + function pageByName(pageName) { + if (root.pages[pageName]) return root.pages[pageName]; + + var page = null; + if (pageName === "main") { + page = mainPage + } else if (pageName === "login") { + var s = ' + LoginPage { + id: loginPage + anchors.fill: parent + visible: false + appRoot: root + }'; + page = Qt.createQmlObject(s, root); + } + + root.pages[pageName] = page; + + return page; + } + + function showPage(pageName) { + for (var n in root.pages) { + root.pages[n].visible = false; + } + + print("Switching to page: " + pageName); + var page = pageByName(pageName); + page.visible = true; + } + MainPage { id: mainPage anchors.fill: parent appRoot: root - } - - LoginPage { - + visible: false } } diff --git a/QtClient/JoplinQtClient/application.cpp b/QtClient/JoplinQtClient/application.cpp index 6c2969193..96868fc26 100755 --- a/QtClient/JoplinQtClient/application.cpp +++ b/QtClient/JoplinQtClient/application.cpp @@ -16,8 +16,7 @@ using namespace jop; Application::Application(int &argc, char **argv) : QGuiApplication(argc, argv), db_(jop::db()), - api_(jop::API_BASE_URL), - synchronizer_(api_.baseUrl(), db_), + synchronizer_(db_), folderModel_(db_) { @@ -43,6 +42,7 @@ Application::Application(int &argc, char **argv) : ctxt->setContextProperty("folderListModel", &folderModel_); ctxt->setContextProperty("noteListModel", ¬eModel_); ctxt->setContextProperty("noteModel", &selectedQmlNote_); + ctxt->setContextProperty("dispatcher", &dispatcher()); view_.setSource(QUrl("qrc:/app.qml")); @@ -56,6 +56,7 @@ Application::Application(int &argc, char **argv) : connect(&dispatcher(), SIGNAL(folderCreated(QString)), this, SLOT(dispatcher_folderCreated(QString))); connect(&dispatcher(), SIGNAL(folderUpdated(QString)), this, SLOT(dispatcher_folderUpdated(QString))); connect(&dispatcher(), SIGNAL(folderDeleted(QString)), this, SLOT(dispatcher_folderDeleted(QString))); + connect(&dispatcher(), SIGNAL(loginClicked(QString,QString,QString)), this, SLOT(dispatcher_loginClicked(QString,QString,QString))); view_.show(); @@ -66,6 +67,12 @@ Application::Application(int &argc, char **argv) : connect(&api_, SIGNAL(requestDone(const QJsonObject&, const QString&)), this, SLOT(api_requestDone(const QJsonObject&, const QString&))); + if (!settings.contains("user.email") || !settings.contains("session.id") || !settings.contains("api.baseUrl")) { + view_.showPage("login"); + } else { + view_.showPage("main"); + } + // Don't store password, store session ID // QString clientId = "B6E12222B6E12222"; // if (!settings.contains("user.email")) { @@ -77,7 +84,19 @@ Application::Application(int &argc, char **argv) : // postData.addQueryItem("email", settings.value("user.email").toString()); // postData.addQueryItem("password", settings.value("user.password").toString()); // postData.addQueryItem("client_id", clientId); -// api_.post("sessions", QUrlQuery(), postData, "getSession"); + // api_.post("sessions", QUrlQuery(), postData, "getSession"); +} + +void Application::login(const QString &email, const QString &password) { + QUrlQuery postData; + postData.addQueryItem("email", email); + postData.addQueryItem("password", password); + postData.addQueryItem("client_id", clientId()); + api_.post("sessions", QUrlQuery(), postData, "getSession"); +} + +QString Application::clientId() const { + return "2222222222222222"; } void Application::api_requestDone(const QJsonObject& response, const QString& tag) { @@ -85,10 +104,18 @@ void Application::api_requestDone(const QJsonObject& response, const QString& ta // Handle expired sessions if (tag == "getSession") { - QString sessionId = response.value("id").toString(); - Settings settings; - settings.setValue("sessionId", sessionId); - afterSessionInitialization(); + if (response.contains("error")) { + qCritical() << "Could not get session:" << response.value("error").toString(); + dispatcher().emitLoginFailed(); + } else { + QString sessionId = response.value("id").toString(); + qDebug() << "Got session" << sessionId; + Settings settings; + settings.setValue("session.id", sessionId); + afterSessionInitialization(); + dispatcher().emitLoginSuccess(); + } + view_.showPage("main"); return; } } @@ -108,6 +135,19 @@ void Application::dispatcher_folderDeleted(const QString &folderId) { //synchronizerTimer_.start(1000 * 3); } +void Application::dispatcher_loginClicked(const QString &apiBaseUrl, const QString &email, const QString &password) { + qDebug() << apiBaseUrl << email << password; + dispatcher().emitLoginStarted(); + + Settings settings; + settings.setValue("user.email", email); + settings.setValue("api.baseUrl", apiBaseUrl); + + api_.setBaseUrl(apiBaseUrl); + + login(email, password); +} + void Application::synchronizerTimer_timeout() { //synchronizerTimer_.start(1000 * 10); synchronizer_.start(); @@ -134,7 +174,7 @@ void Application::afterSessionInitialization() { // request a new session everytime on startup. Settings settings; - QString sessionId = settings.value("sessionId").toString(); + QString sessionId = settings.value("session.id").toString(); qDebug() << "Session:" << sessionId; api_.setSessionId(sessionId); synchronizer_.setSessionId(sessionId); diff --git a/QtClient/JoplinQtClient/application.h b/QtClient/JoplinQtClient/application.h index 03d3e2ca9..89bb18f41 100755 --- a/QtClient/JoplinQtClient/application.h +++ b/QtClient/JoplinQtClient/application.h @@ -10,6 +10,7 @@ #include "models/qmlnote.h" #include "webapi.h" #include "synchronizer.h" +#include "window.h" namespace jop { @@ -20,10 +21,12 @@ class Application : public QGuiApplication { public: Application(int &argc, char **argv); + void login(const QString& email, const QString& password); + QString clientId() const; private: - QQuickView view_; + Window view_; Database& db_; NoteCollection noteCollection_; FolderModel folderModel_; @@ -50,6 +53,7 @@ public slots: void dispatcher_folderCreated(const QString& folderId); void dispatcher_folderUpdated(const QString& folderId); void dispatcher_folderDeleted(const QString& folderId); + void dispatcher_loginClicked(const QString &domain, const QString &email, const QString &password); void synchronizerTimer_timeout(); diff --git a/QtClient/JoplinQtClient/constants.h b/QtClient/JoplinQtClient/constants.h index 4f2d15274..4cb9a485a 100755 --- a/QtClient/JoplinQtClient/constants.h +++ b/QtClient/JoplinQtClient/constants.h @@ -8,7 +8,6 @@ namespace jop { const QString ORG_NAME = "Cozic"; const QString ORG_DOMAIN = "cozic.net"; const QString APP_NAME = "Joplin"; -const QString API_BASE_URL = "https://joplin.cozic.net"; } diff --git a/QtClient/JoplinQtClient/dispatcher.cpp b/QtClient/JoplinQtClient/dispatcher.cpp index d14a67648..3857279db 100755 --- a/QtClient/JoplinQtClient/dispatcher.cpp +++ b/QtClient/JoplinQtClient/dispatcher.cpp @@ -16,6 +16,22 @@ void Dispatcher::emitFolderDeleted(const QString &folderId) { emit folderDeleted(folderId); } +void Dispatcher::emitLoginClicked(const QString &apiBaseUrl, const QString &email, const QString &password) { + emit loginClicked(apiBaseUrl, email, password); +} + +void Dispatcher::emitLoginStarted() { + emit loginStarted(); +} + +void Dispatcher::emitLoginFailed() { + emit loginFailed(); +} + +void Dispatcher::emitLoginSuccess() { + emit loginSuccess(); +} + Dispatcher dispatcherInstance_; Dispatcher& jop::dispatcher() { diff --git a/QtClient/JoplinQtClient/dispatcher.h b/QtClient/JoplinQtClient/dispatcher.h index 54193309a..610f901bd 100755 --- a/QtClient/JoplinQtClient/dispatcher.h +++ b/QtClient/JoplinQtClient/dispatcher.h @@ -12,15 +12,26 @@ class Dispatcher : public QObject { public: Dispatcher(); + +public slots: + void emitFolderCreated(const QString& folderId); void emitFolderUpdated(const QString& folderId); void emitFolderDeleted(const QString& folderId); + void emitLoginClicked(const QString& domain, const QString& email, const QString &password); + void emitLoginStarted(); + void emitLoginFailed(); + void emitLoginSuccess(); signals: void folderCreated(const QString& folderId); void folderUpdated(const QString& folderId); void folderDeleted(const QString& folderId); + void loginClicked(const QString& domain, const QString& email, const QString& password); + void loginStarted(); + void loginFailed(); + void loginSuccess(); }; diff --git a/QtClient/JoplinQtClient/synchronizer.cpp b/QtClient/JoplinQtClient/synchronizer.cpp index 1ed69e590..308b0fbdd 100755 --- a/QtClient/JoplinQtClient/synchronizer.cpp +++ b/QtClient/JoplinQtClient/synchronizer.cpp @@ -5,8 +5,7 @@ using namespace jop; -Synchronizer::Synchronizer(const QString &apiUrl, Database &database) : api_(apiUrl), db_(database) { - qDebug() << api_.baseUrl(); +Synchronizer::Synchronizer(Database &database) : db_(database) { state_ = Idle; uploadsRemaining_ = 0; connect(&api_, SIGNAL(requestDone(QJsonObject,QString)), this, SLOT(api_requestDone(QJsonObject,QString))); diff --git a/QtClient/JoplinQtClient/synchronizer.h b/QtClient/JoplinQtClient/synchronizer.h index 391c6c285..ebbf04f75 100755 --- a/QtClient/JoplinQtClient/synchronizer.h +++ b/QtClient/JoplinQtClient/synchronizer.h @@ -16,7 +16,7 @@ public: enum SynchronizationState { Idle, UploadingChanges, DownloadingChanges }; - Synchronizer(const QString& apiUrl, Database& database); + Synchronizer(Database& database); void start(); void setSessionId(const QString& v); diff --git a/QtClient/JoplinQtClient/webapi.cpp b/QtClient/JoplinQtClient/webapi.cpp index 007b718b2..50632a397 100755 --- a/QtClient/JoplinQtClient/webapi.cpp +++ b/QtClient/JoplinQtClient/webapi.cpp @@ -4,8 +4,8 @@ using namespace jop; -WebApi::WebApi(const QString &baseUrl) { - baseUrl_ = baseUrl; +WebApi::WebApi() { + baseUrl_ = ""; sessionId_ = ""; connect(&manager_, SIGNAL(finished(QNetworkReply*)), this, SLOT(request_finished(QNetworkReply*))); } @@ -15,6 +15,14 @@ QString WebApi::baseUrl() const { } void WebApi::execRequest(HttpMethod method, const QString &path, const QUrlQuery &query, const QUrlQuery &data, const QString& tag) { + if (baseUrl() == "") { + qCritical() << "Trying to execute request before base URL has been set"; + QJsonObject obj; + obj["error"] = "Trying to execute request before base URL has been set"; + emit requestDone(obj, tag); + return; + } + QueuedRequest r; r.method = method; r.path = path; @@ -136,3 +144,7 @@ void WebApi::request_finished(QNetworkReply *reply) { void WebApi::request_error(QNetworkReply::NetworkError e) { qDebug() << "Network error" << e; } + +void jop::WebApi::setBaseUrl(const QString &v) { + baseUrl_ = v; +} diff --git a/QtClient/JoplinQtClient/webapi.h b/QtClient/JoplinQtClient/webapi.h index 64fd5c663..749e90255 100755 --- a/QtClient/JoplinQtClient/webapi.h +++ b/QtClient/JoplinQtClient/webapi.h @@ -23,7 +23,8 @@ public: QBuffer* buffer; }; - WebApi(const QString& baseUrl); + WebApi(); + void setBaseUrl(const QString& v); QString baseUrl() const; void execRequest(HttpMethod method, const QString& path,const QUrlQuery& query = QUrlQuery(), const QUrlQuery& data = QUrlQuery(), const QString& tag = ""); void post(const QString& path,const QUrlQuery& query = QUrlQuery(), const QUrlQuery& data = QUrlQuery(), const QString& tag = ""); diff --git a/QtClient/JoplinQtClient/window.cpp b/QtClient/JoplinQtClient/window.cpp new file mode 100755 index 000000000..549ecccf4 --- /dev/null +++ b/QtClient/JoplinQtClient/window.cpp @@ -0,0 +1,11 @@ +#include "window.h" + +using namespace jop; + +Window::Window() : QQuickView() {} + +void Window::showPage(const QString &pageName) { + QVariant pageNameV(pageName); + QVariant returnedValue; + QMetaObject::invokeMethod((QObject*)rootObject(), "showPage", Q_RETURN_ARG(QVariant, returnedValue), Q_ARG(QVariant, pageNameV)); +} diff --git a/QtClient/JoplinQtClient/window.h b/QtClient/JoplinQtClient/window.h new file mode 100755 index 000000000..08809970c --- /dev/null +++ b/QtClient/JoplinQtClient/window.h @@ -0,0 +1,21 @@ +#ifndef WINDOW_H +#define WINDOW_H + +#include + +namespace jop { + +class Window : public QQuickView { + + Q_OBJECT + +public: + + Window(); + void showPage(const QString& pageName); + +}; + +} + +#endif // WINDOW_H