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
api.h
codes.h
)
set(SOURCES
api.cpp
codes.cpp
)
target_sources(magpie PRIVATE ${SOURCES})

View File

@ -4,6 +4,8 @@
#include <QJsonObject>
#include <QUrlQuery>
#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<QNetworkReply, NetworkReplyDeleter> 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<QString>() ||
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<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 {

View File

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

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"
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();
}

View File

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

View File

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

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
URI "qml.Forms"
URI magpie.Forms
VERSION 1.0
STATIC
RESOURCE_PREFIX /
OUTPUT_DIRECTORY ../magpie/Forms
NO_PLUGIN
QML_FILES
Login.qml

View File

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

View File

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

View File

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

View File

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