From 73e91e658fd324cfae6139fa0120026f474ca911 Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 21 Dec 2023 15:03:44 -0300 Subject: [PATCH] Sending register, handling register result, sending login --- API/CMakeLists.txt | 2 + API/api.cpp | 82 +++++++++++++++++++++++++++++------ API/api.h | 2 + API/codes.cpp | 33 ++++++++++++++ API/codes.h | 22 ++++++++++ main.cpp | 2 +- main_web.cpp | 2 +- qml/CMakeLists.txt | 7 ++- qml/Components/CMakeLists.txt | 12 +++++ qml/Components/Modal.qml | 61 ++++++++++++++++++++++++++ qml/Forms/CMakeLists.txt | 3 +- qml/Forms/Login.qml | 39 ++++++++++++++--- qml/Forms/Register.qml | 35 ++++++++++++--- qml/ServerPick.qml | 66 +++------------------------- qml/Welcome.qml | 17 +++++++- 15 files changed, 295 insertions(+), 90 deletions(-) create mode 100644 API/codes.cpp create mode 100644 API/codes.h create mode 100644 qml/Components/CMakeLists.txt create mode 100644 qml/Components/Modal.qml diff --git a/API/CMakeLists.txt b/API/CMakeLists.txt index df80941..d9a627f 100644 --- a/API/CMakeLists.txt +++ b/API/CMakeLists.txt @@ -1,9 +1,11 @@ set(HEADERS api.h + codes.h ) set(SOURCES api.cpp + codes.cpp ) target_sources(magpie PRIVATE ${SOURCES}) diff --git a/API/api.cpp b/API/api.cpp index 883656a..79821b7 100644 --- a/API/api.cpp +++ b/API/api.cpp @@ -4,6 +4,8 @@ #include #include +#include "codes.h" + constexpr const char* json = "application/json"; constexpr const char* urlEncoded = "application/x-www-form-urlencoded"; @@ -93,18 +95,21 @@ void API::onTestFinished(QNetworkReply* reply, const QUrl& addr, const QJSValue& setAddress(addr); } -void API::sendRegister(const QString& login, const QString& password, const QJSValue &finished) { +void API::sendRegister(const QString& login, const QString& password, const QJSValue& finished) { qDebug() << "Registering..."; if (state != NotAuthenticated) - callCallback(finished, "Can not register in current state"); - return; + return callCallback(finished, "Can not register in current state"); QUrlQuery params({ {"login", login}, {"password", password} }); - QNetworkRequest request(address.path() + "/register"); + QString path = address.path(); + QUrl regUrl = address; + regUrl.setPath(path + "/register"); + + QNetworkRequest request(regUrl); request.setHeader(QNetworkRequest::ContentTypeHeader, urlEncoded); QNetworkReply* reply = network.post(request, params.toString(QUrl::FullyEncoded).toUtf8()); @@ -113,7 +118,30 @@ void API::sendRegister(const QString& login, const QString& password, const QJSV ); } -void API::onRegisterFinished(QNetworkReply *reply, const QJSValue &finished) const { +void API::sendLogin(const QString& login, const QString& password, const QJSValue& finished) { + qDebug() << "Logging in..."; + if (state != NotAuthenticated) + return callCallback(finished, "Can not register in current state"); + + QUrlQuery params({ + {"login", login}, + {"password", password} + }); + + QString path = address.path(); + QUrl url = address; + url.setPath(path + "/login"); + + QNetworkRequest request(url); + request.setHeader(QNetworkRequest::ContentTypeHeader, urlEncoded); + + QNetworkReply* reply = network.post(request, params.toString(QUrl::FullyEncoded).toUtf8()); + connect(reply, &QNetworkReply::finished, + std::bind(&API::onLoginFinished, this, reply, finished) + ); +} + +void API::onRegisterFinished(QNetworkReply* reply, const QJSValue& finished) const { std::unique_ptr rpl(reply); QNetworkReply::NetworkError error = reply->error(); if (error != QNetworkReply::NoError) @@ -125,24 +153,50 @@ void API::onRegisterFinished(QNetworkReply *reply, const QJSValue &finished) con !contentType.canConvert() || contentType.toString() != json ) { - QString err("wrong response content type"); - qDebug() << "Register failed:" << err; - callCallback(finished, err); - return; + return callCallback(finished, "wrong response content type"); } QByteArray data = reply->readAll(); QJsonDocument document = QJsonDocument::fromJson(data); QJsonObject rootObj = document.object(); - QJsonValue result = rootObj.value("result"); - if (!result.isString()) + QJsonValue res = rootObj.value("result"); + if (!res.isDouble()) return callCallback(finished, "malformed json"); - if (result.toString() != "ok") - return callCallback(finished, "Registration result was not okay"); + Codes::Register result = Codes::convertRegister(res.toInt()); + bool success = result == Codes::Register::success; + QString err; + if (!success) + err = Codes::description(result); - callCallback(finished, QString(), {QJSValue(true)}); + callCallback(finished, err, {QJSValue(success)}); +} + +void API::onLoginFinished(QNetworkReply* reply, const QJSValue& finished) { + std::unique_ptr rpl(reply); + QNetworkReply::NetworkError error = reply->error(); + if (error != QNetworkReply::NoError) + return callCallback(finished, reply->errorString()); + + QVariant contentType = reply->header(QNetworkRequest::ContentTypeHeader); + if (! + contentType.isValid() || + !contentType.canConvert() || + contentType.toString() != json + ) { + return callCallback(finished, "wrong response content type"); + } + + QByteArray data = reply->readAll(); + QJsonDocument document = QJsonDocument::fromJson(data); + QJsonObject rootObj = document.object(); + + QJsonValue res = rootObj.value("result"); + if (!res.isDouble()) + return callCallback(finished, "malformed json"); + + //todo } void API::callCallback(const QJSValue& callback, const QString& error, const QJSValueList& arguments) const { diff --git a/API/api.h b/API/api.h index 6ba0df8..19be01d 100644 --- a/API/api.h +++ b/API/api.h @@ -34,10 +34,12 @@ signals: public slots: void test(const QString& path, const QJSValue& finished = QJSValue()); void sendRegister(const QString& login, const QString& password, const QJSValue& finished = QJSValue()); + void sendLogin(const QString& login, const QString& password, const QJSValue& finished = QJSValue()); private slots: void onTestFinished(QNetworkReply* reply, const QUrl& addr, const QJSValue& finished); void onRegisterFinished(QNetworkReply* reply, const QJSValue& finished) const; + void onLoginFinished(QNetworkReply* reply, const QJSValue& finished); private: void callCallback(const QJSValue& callback, const QString& error = QString(), const QJSValueList& arguments = QJSValueList()) const; diff --git a/API/codes.cpp b/API/codes.cpp new file mode 100644 index 0000000..7411f6f --- /dev/null +++ b/API/codes.cpp @@ -0,0 +1,33 @@ +#include "codes.h" + +#include + +constexpr std::array registerErrors({ + "Successfully registered an account", + "Login have not been provided", + "The login can not be empty", + "User with provided login already exists", + "Provided login doesn't comply with server policies", + "Password have not been provided", + "Password can not be empty", + "Provided password doesn't comply with server policies", + "Unknown registration error" +}); + +Codes::Register Codes::convertRegister (int source) { + if (source < 0) { + std::cerr << "Converting Register response code which is smaller than zero, falling back to unknownError" << std::endl; + return Register::unknownError; + } + + if (source > (int)Register::unknownError) { + std::cerr << "Converting Register response code which is bigger than biggest known, falling back to unknownError" << std::endl; + return Register::unknownError; + } + + return static_cast(source); +} + +QString Codes::description(Register code) { + return registerErrors[(uint8_t)code]; +} diff --git a/API/codes.h b/API/codes.h new file mode 100644 index 0000000..4d22dad --- /dev/null +++ b/API/codes.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +namespace Codes { +enum class Register { + success, + noLogin, + emptyLogin, + loginExists, + loginPolicyViolation, + noPassword, + emptyPassword, + passwordPolicyViolation, + unknownError +}; + +Register convertRegister (int source); + +QString description (Register code); + +} diff --git a/main.cpp b/main.cpp index 19554f3..715eb31 100644 --- a/main.cpp +++ b/main.cpp @@ -4,6 +4,6 @@ #include "root.h" int main(int argc, char *argv[]) { - Root app(u"qrc:qml/main.qml"_qs, argc, argv); + Root app(u"qrc:magpie/main.qml"_qs, argc, argv); return app.exec(); } diff --git a/main_web.cpp b/main_web.cpp index ba01824..3ceb29b 100644 --- a/main_web.cpp +++ b/main_web.cpp @@ -7,7 +7,7 @@ int main(int argc, char *argv[]) { Root* app; try { - app = new Root(u"qrc:qml/main.qml"_qs, argc, argv); + app = new Root(u"qrc:magpie/main.qml"_qs, argc, argv); } catch (const std::exception& exception) { std::cerr << "Couldn't start megpie: " << exception.what() << std::endl; } diff --git a/qml/CMakeLists.txt b/qml/CMakeLists.txt index 49f5936..801a3a9 100644 --- a/qml/CMakeLists.txt +++ b/qml/CMakeLists.txt @@ -1,8 +1,9 @@ qt_add_qml_module(magpieQml - URI qml + URI magpie VERSION 1.0 STATIC RESOURCE_PREFIX / + OUTPUT_DIRECTORY magpie NO_PLUGIN QML_FILES main.qml @@ -10,6 +11,10 @@ qt_add_qml_module(magpieQml Welcome.qml ) +list(APPEND QML_DIRS ${CMAKE_CURRENT_BINARY_DIR}) +set(QML_IMPORT_PATH "${QML_DIRS}" CACHE STRING "Qt Creator 11 extra qml import paths") + target_link_libraries(magpie PRIVATE magpieQml) add_subdirectory(Forms) +add_subdirectory(Components) diff --git a/qml/Components/CMakeLists.txt b/qml/Components/CMakeLists.txt new file mode 100644 index 0000000..f783a4d --- /dev/null +++ b/qml/Components/CMakeLists.txt @@ -0,0 +1,12 @@ +qt_add_qml_module(magpieComponents + URI magpie.Components + VERSION 1.0 + STATIC + RESOURCE_PREFIX / + OUTPUT_DIRECTORY ../magpie/Components + NO_PLUGIN + QML_FILES + Modal.qml +) + +target_link_libraries(magpie PRIVATE magpieComponents) diff --git a/qml/Components/Modal.qml b/qml/Components/Modal.qml new file mode 100644 index 0000000..538313a --- /dev/null +++ b/qml/Components/Modal.qml @@ -0,0 +1,61 @@ +import QtQuick +import QtQuick.Controls + +Popup { + property bool inProgress: false + property string status: "" + + id: modal + anchors.centerIn: parent + modal: true + focus: true + width: column.width + 60 + height: column.height + 60 + closePolicy: Popup.CloseOnEscape + onClosed: inProgress = false + + Column { + id: column + anchors.centerIn: parent + spacing: 10 + Label { + text: status + width: 300 + wrapMode: Label.WordWrap + horizontalAlignment: Label.AlignHCenter + } + BusyIndicator { + anchors.horizontalCenter: parent.horizontalCenter + visible: inProgress + running: inProgress + } + Button { + text: qsTr("Close") + anchors.horizontalCenter: parent.horizontalCenter + visible: !inProgress + onClicked: modal.close() + focus: true + Keys.onReturnPressed: { + if (!inProgress) + modal.close() + } + } + } + + enter: Transition { + NumberAnimation { + property: "opacity" + from: 0.0 + to: 1.0 + duration: 200 + } + } + exit: Transition { + NumberAnimation { + property: "opacity" + from: 1.0 + to: 0.0 + duration: 200 + } + } +} diff --git a/qml/Forms/CMakeLists.txt b/qml/Forms/CMakeLists.txt index eb7aa3e..f8eb81b 100644 --- a/qml/Forms/CMakeLists.txt +++ b/qml/Forms/CMakeLists.txt @@ -1,8 +1,9 @@ qt_add_qml_module(magpieForms - URI "qml.Forms" + URI magpie.Forms VERSION 1.0 STATIC RESOURCE_PREFIX / + OUTPUT_DIRECTORY ../magpie/Forms NO_PLUGIN QML_FILES Login.qml diff --git a/qml/Forms/Login.qml b/qml/Forms/Login.qml index c4e4d35..8155132 100644 --- a/qml/Forms/Login.qml +++ b/qml/Forms/Login.qml @@ -2,10 +2,37 @@ import QtQuick import QtQuick.Controls import magpie.API +import magpie.Components as Components Column { signal register() + function login(login, password) { + if (modal.inProgress) + return; + + loginField.text = login; + passwordField.text = password; + + modal.inProgress = true; + modal.status = qsTr("Logging in as") + " " + login + "..."; + modal.open(); + + API.sendLogin(login, password, function (err, result) { + if (!modal.inProgress) + return; + + modal.inProgress = false; + if (err) + modal.status = err; + else + modal.status = qsTr("Success"); + + if (!!result) + modal.close(); + }); + } + spacing: 5 Label { @@ -29,7 +56,7 @@ Column { } TextField { - id: login + id: loginField } Label { @@ -37,7 +64,7 @@ Column { } TextField { - id: password + id: passwordField echoMode: TextField.Password } } @@ -45,9 +72,11 @@ Column { Button { anchors.horizontalCenter: parent.horizontalCenter text: qsTr("Login") - onClicked: function () { - console.log("Not implemented"); - } + onClicked: login(loginField.text, passwordField.text) + } + + Components.Modal { + id: modal } Row { diff --git a/qml/Forms/Register.qml b/qml/Forms/Register.qml index f63941f..802a9f1 100644 --- a/qml/Forms/Register.qml +++ b/qml/Forms/Register.qml @@ -2,9 +2,11 @@ import QtQuick import QtQuick.Controls import magpie.API +import magpie.Components as Components Column { signal login() + signal success(login: string, password: string) spacing: 5 @@ -45,12 +47,35 @@ Column { Button { anchors.horizontalCenter: parent.horizontalCenter text: qsTr("Register") - onClicked: API.sendRegister(newLogin.text, newPassword.text, function (err, result) { - if (err) - console.error("err") + onClicked: modal.check() + } - console.log(result); - }) + Components.Modal { + id: modal + function check () { + if (inProgress) + return; + + inProgress = true; + status = qsTr("Registering") + " " + newLogin.text + "..."; + open() + + API.sendRegister(newLogin.text, newPassword.text, function (err, result) { + if (!modal.inProgress) + return; + + modal.inProgress = false; + if (err) + modal.status = err; + else + modal.status = qsTr("Success"); + + if (!!result) { + modal.close(); + success(newLogin.text, newPassword.text); + } + }); + } } Row { diff --git a/qml/ServerPick.qml b/qml/ServerPick.qml index 83413f9..6df2e3f 100644 --- a/qml/ServerPick.qml +++ b/qml/ServerPick.qml @@ -3,6 +3,7 @@ import QtQuick.Controls import QtQuick.Layouts import magpie.API +import magpie.Components as Components Page { property string address @@ -47,71 +48,14 @@ Page { } } - Popup { - property bool inProgress: false - + Components.Modal { id: modal - anchors.centerIn: parent - modal: true - focus: true - width: column.width + 60 - height: column.height + 60 - closePolicy: Popup.CloseOnEscape - onClosed: modal.inProgress = false - - Column { - id: column - anchors.centerIn: parent - spacing: 10 - Label { - id: status - width: 300 - wrapMode: Label.WordWrap - horizontalAlignment: Label.AlignHCenter - } - BusyIndicator { - id: indicator - anchors.horizontalCenter: parent.horizontalCenter - visible: modal.inProgress - running: modal.inProgress - } - Button { - id: button - text: qsTr("Close") - anchors.horizontalCenter: parent.horizontalCenter - visible: !modal.inProgress - onClicked: modal.close() - focus: true - Keys.onReturnPressed: { - if (!modal.inProgress) - modal.close() - } - } - } - - enter: Transition { - NumberAnimation { - property: "opacity" - from: 0.0 - to: 1.0 - duration: 200 - } - } - exit: Transition { - NumberAnimation { - property: "opacity" - from: 1.0 - to: 0.0 - duration: 200 - } - } - function check () { if (modal.inProgress) return; modal.inProgress = true; - status.text = qsTr("Checking") + " " + address + "..."; + modal.status = qsTr("Checking") + " " + address + "..."; modal.open() API.test(input.text, function (err, success) { @@ -120,9 +64,9 @@ Page { modal.inProgress = false; if (err) - status.text = err; + modal.status = err; else - status.text = qsTr("Success"); + modal.status = qsTr("Success"); if (!!success) modal.close() diff --git a/qml/Welcome.qml b/qml/Welcome.qml index c0380ab..b6783e2 100644 --- a/qml/Welcome.qml +++ b/qml/Welcome.qml @@ -3,7 +3,7 @@ import QtQuick.Controls import QtQuick.Layouts import magpie.API -import "Forms" as Forms +import magpie.Forms as Forms Page { id: page @@ -55,6 +55,9 @@ Page { height: stack.currentItem ? stack.currentItem.implicitHeight: 0 StackView { + property string pendingLogin: "" + property string pendingPassword: "" + id: stack initialItem: loginForm anchors.fill: parent @@ -64,6 +67,13 @@ Page { id: loginForm Forms.Login { onRegister: stack.replace(registerForm) + Component.onCompleted: { + if (stack.pendingLogin && stack.pendingPassword) + this.login(stack.pendingLogin, stack.pendingPassword) + + stack.pendingLogin = ""; + stack.pendingPassword = ""; + } } } @@ -71,6 +81,11 @@ Page { id: registerForm Forms.Register { onLogin: stack.replace(loginForm, StackView.PopTransition) + onSuccess: function(login, password) { + stack.pendingLogin = login; + stack.pendingPassword = password; + stack.replace(loginForm, StackView.PopTransition); + } } } }