Sending register, handling register result, sending login

This commit is contained in:
Blue 2023-12-21 15:03:44 -03:00
parent 89aa830bfa
commit 73e91e658f
Signed by: blue
GPG Key ID: 9B203B252A63EE38
15 changed files with 295 additions and 90 deletions

View File

@ -1,9 +1,11 @@
set(HEADERS set(HEADERS
api.h api.h
codes.h
) )
set(SOURCES set(SOURCES
api.cpp api.cpp
codes.cpp
) )
target_sources(magpie PRIVATE ${SOURCES}) target_sources(magpie PRIVATE ${SOURCES})

View File

@ -4,6 +4,8 @@
#include <QJsonObject> #include <QJsonObject>
#include <QUrlQuery> #include <QUrlQuery>
#include "codes.h"
constexpr const char* json = "application/json"; constexpr const char* json = "application/json";
constexpr const char* urlEncoded = "application/x-www-form-urlencoded"; constexpr const char* urlEncoded = "application/x-www-form-urlencoded";
@ -96,15 +98,18 @@ void API::onTestFinished(QNetworkReply* reply, const QUrl& addr, const QJSValue&
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..."; qDebug() << "Registering...";
if (state != NotAuthenticated) if (state != NotAuthenticated)
callCallback(finished, "Can not register in current state"); return callCallback(finished, "Can not register in current state");
return;
QUrlQuery params({ QUrlQuery params({
{"login", login}, {"login", login},
{"password", password} {"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); request.setHeader(QNetworkRequest::ContentTypeHeader, urlEncoded);
QNetworkReply* reply = network.post(request, params.toString(QUrl::FullyEncoded).toUtf8()); QNetworkReply* reply = network.post(request, params.toString(QUrl::FullyEncoded).toUtf8());
@ -113,6 +118,29 @@ void API::sendRegister(const QString& login, const QString& password, const QJSV
); );
} }
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 { void API::onRegisterFinished(QNetworkReply* reply, const QJSValue& finished) const {
std::unique_ptr<QNetworkReply, NetworkReplyDeleter> rpl(reply); std::unique_ptr<QNetworkReply, NetworkReplyDeleter> rpl(reply);
QNetworkReply::NetworkError error = reply->error(); QNetworkReply::NetworkError error = reply->error();
@ -125,24 +153,50 @@ void API::onRegisterFinished(QNetworkReply *reply, const QJSValue &finished) con
!contentType.canConvert<QString>() || !contentType.canConvert<QString>() ||
contentType.toString() != json contentType.toString() != json
) { ) {
QString err("wrong response content type"); return callCallback(finished, "wrong response content type");
qDebug() << "Register failed:" << err;
callCallback(finished, err);
return;
} }
QByteArray data = reply->readAll(); QByteArray data = reply->readAll();
QJsonDocument document = QJsonDocument::fromJson(data); QJsonDocument document = QJsonDocument::fromJson(data);
QJsonObject rootObj = document.object(); QJsonObject rootObj = document.object();
QJsonValue result = rootObj.value("result"); QJsonValue res = rootObj.value("result");
if (!result.isString()) if (!res.isDouble())
return callCallback(finished, "malformed json"); return callCallback(finished, "malformed json");
if (result.toString() != "ok") Codes::Register result = Codes::convertRegister(res.toInt());
return callCallback(finished, "Registration result was not okay"); 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<QNetworkReply, NetworkReplyDeleter> 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<QString>() ||
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 { void API::callCallback(const QJSValue& callback, const QString& error, const QJSValueList& arguments) const {

View File

@ -34,10 +34,12 @@ signals:
public slots: public slots:
void test(const QString& path, const QJSValue& finished = QJSValue()); void test(const QString& path, const QJSValue& finished = QJSValue());
void sendRegister(const QString& login, const QString& password, 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: private slots:
void onTestFinished(QNetworkReply* reply, const QUrl& addr, const QJSValue& finished); void onTestFinished(QNetworkReply* reply, const QUrl& addr, const QJSValue& finished);
void onRegisterFinished(QNetworkReply* reply, const QJSValue& finished) const; void onRegisterFinished(QNetworkReply* reply, const QJSValue& finished) const;
void onLoginFinished(QNetworkReply* reply, const QJSValue& finished);
private: private:
void callCallback(const QJSValue& callback, const QString& error = QString(), const QJSValueList& arguments = QJSValueList()) const; void callCallback(const QJSValue& callback, const QString& error = QString(), const QJSValueList& arguments = QJSValueList()) const;

33
API/codes.cpp Normal file
View File

@ -0,0 +1,33 @@
#include "codes.h"
#include <iostream>
constexpr std::array<const char*, uint8_t(Codes::Register::unknownError) + 1> 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<Register>(source);
}
QString Codes::description(Register code) {
return registerErrors[(uint8_t)code];
}

22
API/codes.h Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include <QString>
namespace Codes {
enum class Register {
success,
noLogin,
emptyLogin,
loginExists,
loginPolicyViolation,
noPassword,
emptyPassword,
passwordPolicyViolation,
unknownError
};
Register convertRegister (int source);
QString description (Register code);
}

View File

@ -4,6 +4,6 @@
#include "root.h" #include "root.h"
int main(int argc, char *argv[]) { 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(); return app.exec();
} }

View File

@ -7,7 +7,7 @@ int main(int argc, char *argv[]) {
Root* app; Root* app;
try { 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) { } catch (const std::exception& exception) {
std::cerr << "Couldn't start megpie: " << exception.what() << std::endl; std::cerr << "Couldn't start megpie: " << exception.what() << std::endl;
} }

View File

@ -1,8 +1,9 @@
qt_add_qml_module(magpieQml qt_add_qml_module(magpieQml
URI qml URI magpie
VERSION 1.0 VERSION 1.0
STATIC STATIC
RESOURCE_PREFIX / RESOURCE_PREFIX /
OUTPUT_DIRECTORY magpie
NO_PLUGIN NO_PLUGIN
QML_FILES QML_FILES
main.qml main.qml
@ -10,6 +11,10 @@ qt_add_qml_module(magpieQml
Welcome.qml 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) target_link_libraries(magpie PRIVATE magpieQml)
add_subdirectory(Forms) add_subdirectory(Forms)
add_subdirectory(Components)

View File

@ -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)

61
qml/Components/Modal.qml Normal file
View File

@ -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
}
}
}

View File

@ -1,8 +1,9 @@
qt_add_qml_module(magpieForms qt_add_qml_module(magpieForms
URI "qml.Forms" URI magpie.Forms
VERSION 1.0 VERSION 1.0
STATIC STATIC
RESOURCE_PREFIX / RESOURCE_PREFIX /
OUTPUT_DIRECTORY ../magpie/Forms
NO_PLUGIN NO_PLUGIN
QML_FILES QML_FILES
Login.qml Login.qml

View File

@ -2,10 +2,37 @@ import QtQuick
import QtQuick.Controls import QtQuick.Controls
import magpie.API import magpie.API
import magpie.Components as Components
Column { Column {
signal register() 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 spacing: 5
Label { Label {
@ -29,7 +56,7 @@ Column {
} }
TextField { TextField {
id: login id: loginField
} }
Label { Label {
@ -37,7 +64,7 @@ Column {
} }
TextField { TextField {
id: password id: passwordField
echoMode: TextField.Password echoMode: TextField.Password
} }
} }
@ -45,9 +72,11 @@ Column {
Button { Button {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
text: qsTr("Login") text: qsTr("Login")
onClicked: function () { onClicked: login(loginField.text, passwordField.text)
console.log("Not implemented");
} }
Components.Modal {
id: modal
} }
Row { Row {

View File

@ -2,9 +2,11 @@ import QtQuick
import QtQuick.Controls import QtQuick.Controls
import magpie.API import magpie.API
import magpie.Components as Components
Column { Column {
signal login() signal login()
signal success(login: string, password: string)
spacing: 5 spacing: 5
@ -45,12 +47,35 @@ Column {
Button { Button {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
text: qsTr("Register") text: qsTr("Register")
onClicked: API.sendRegister(newLogin.text, newPassword.text, function (err, result) { onClicked: modal.check()
if (err) }
console.error("err")
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 { Row {

View File

@ -3,6 +3,7 @@ import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import magpie.API import magpie.API
import magpie.Components as Components
Page { Page {
property string address property string address
@ -47,71 +48,14 @@ Page {
} }
} }
Popup { Components.Modal {
property bool inProgress: false
id: 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 () { function check () {
if (modal.inProgress) if (modal.inProgress)
return; return;
modal.inProgress = true; modal.inProgress = true;
status.text = qsTr("Checking") + " " + address + "..."; modal.status = qsTr("Checking") + " " + address + "...";
modal.open() modal.open()
API.test(input.text, function (err, success) { API.test(input.text, function (err, success) {
@ -120,9 +64,9 @@ Page {
modal.inProgress = false; modal.inProgress = false;
if (err) if (err)
status.text = err; modal.status = err;
else else
status.text = qsTr("Success"); modal.status = qsTr("Success");
if (!!success) if (!!success)
modal.close() modal.close()

View File

@ -3,7 +3,7 @@ import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import magpie.API import magpie.API
import "Forms" as Forms import magpie.Forms as Forms
Page { Page {
id: page id: page
@ -55,6 +55,9 @@ Page {
height: stack.currentItem ? stack.currentItem.implicitHeight: 0 height: stack.currentItem ? stack.currentItem.implicitHeight: 0
StackView { StackView {
property string pendingLogin: ""
property string pendingPassword: ""
id: stack id: stack
initialItem: loginForm initialItem: loginForm
anchors.fill: parent anchors.fill: parent
@ -64,6 +67,13 @@ Page {
id: loginForm id: loginForm
Forms.Login { Forms.Login {
onRegister: stack.replace(registerForm) 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 id: registerForm
Forms.Register { Forms.Register {
onLogin: stack.replace(loginForm, StackView.PopTransition) onLogin: stack.replace(loginForm, StackView.PopTransition)
onSuccess: function(login, password) {
stack.pendingLogin = login;
stack.pendingPassword = password;
stack.replace(loginForm, StackView.PopTransition);
}
} }
} }
} }