some experiments about sending form

This commit is contained in:
Blue 2023-12-15 22:44:25 -03:00
parent be3d8b0e77
commit c966d95058
Signed by: blue
GPG Key ID: 9B203B252A63EE38
7 changed files with 305 additions and 21 deletions

View File

@ -2,8 +2,10 @@
#include <QDebug> #include <QDebug>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include <QUrlQuery>
constexpr const char* json = "application/json"; constexpr const char* json = "application/json";
constexpr const char* urlEncoded = "application/x-www-form-urlencoded";
struct NetworkReplyDeleter { struct NetworkReplyDeleter {
void operator () (QNetworkReply* reply) { void operator () (QNetworkReply* reply) {
@ -14,23 +16,53 @@ struct NetworkReplyDeleter {
API::API(const QUrl& address, QObject* parent): API::API(const QUrl& address, QObject* parent):
QObject(parent), QObject(parent),
address(address), address(address),
network() network(),
{ state(NoServer)
{}
QUrl API::getAddress() const {
return address;
}
API::State API::getState() const {
return state;
}
void API::setAddress(const QUrl& path) {
if (address == path)
return;
if (state == Authenticated) {
//do something
}
address = path;
state = address.isEmpty() ? NoServer : NotAuthenticated;
emit addressChanged(address);
emit stateChanged(state);
} }
void API::test(const QString& path, const QJSValue& finished) { void API::test(const QString& path, const QJSValue& finished) {
qDebug() << "Testing" << path; qDebug() << "Testing" << path;
if (state == Offline) {
QString err = "Need to be online to test";
qDebug() << "Test for" << path << "failed:" << err;
callCallback(finished, err);
return;
}
QNetworkRequest request(path + "/info"); QNetworkRequest request(path + "/info");
request.setHeader(QNetworkRequest::ContentTypeHeader, json); request.setHeader(QNetworkRequest::ContentTypeHeader, json);
QNetworkReply* reply = network.get(request); QNetworkReply* reply = network.get(request);
connect(reply, &QNetworkReply::finished, connect(reply, &QNetworkReply::finished,
std::bind(&API::onTestSuccess, this, reply, finished) std::bind(&API::onTestFinished, this, reply, finished)
); );
} }
void API::onTestSuccess(QNetworkReply* reply, const QJSValue& finished) const { void API::onTestFinished(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();
if (error != QNetworkReply::NoError) { if (error != QNetworkReply::NoError) {
@ -65,7 +97,7 @@ void API::onTestSuccess(QNetworkReply* reply, const QJSValue& finished) const {
return; return;
} }
if (type.toString() != "Pica") { if (type.toString() != "pica") {
QString err("server of this type (" + type.toString() + ") is not supported"); QString err("server of this type (" + type.toString() + ") is not supported");
qDebug() << "Test for" << reply->url() << "failed:" << err; qDebug() << "Test for" << reply->url() << "failed:" << err;
callCallback(finished, err); callCallback(finished, err);
@ -82,6 +114,74 @@ void API::onTestSuccess(QNetworkReply* reply, const QJSValue& finished) const {
callCallback(finished, QString(), {QJSValue(true)}); callCallback(finished, QString(), {QJSValue(true)});
} }
void API::sendRegister(const QString& login, const QString& password, const QJSValue &finished) {
qDebug() << "Registering...";
if (state != NotAuthenticated) {
QString err = "Can not register in current state";
qDebug() << "Register failed:" << err;
callCallback(finished, err);
return;
}
QUrlQuery params({
{"login", login},
{"password", password}
});
QNetworkRequest request(address.path() + "/register");
request.setHeader(QNetworkRequest::ContentTypeHeader, urlEncoded);
QNetworkReply* reply = network.post(request, params.toString(QUrl::FullyEncoded).toUtf8());
connect(reply, &QNetworkReply::finished,
std::bind(&API::onRegisterFinished, 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) {
QString err = reply->errorString();
qDebug() << "Register failed:" << err;
callCallback(finished, err);
return;
}
QVariant contentType = reply->header(QNetworkRequest::ContentTypeHeader);
if (!
contentType.isValid() ||
!contentType.canConvert<QString>() ||
contentType.toString() != json
) {
QString err("wrong response content type");
qDebug() << "Register failed:" << err;
callCallback(finished, err);
return;
}
QByteArray data = reply->readAll();
QJsonDocument document = QJsonDocument::fromJson(data);
QJsonObject rootObj = document.object();
QJsonValue result = rootObj.value("result");
if (!result.isString()) {
QString err("malformed json");
qDebug() << "Register failed:" << err;
callCallback(finished, err);
return;
}
if (result.toString() != "ok") {
QString err("Registration result was not okay");
qDebug() << "Register failed:" << err;
callCallback(finished, err);
return;
}
callCallback(finished, QString(), {QJSValue(true)});
}
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 {
if (callback.isCallable()) { if (callback.isCallable()) {
if (error.isEmpty()) if (error.isEmpty())

View File

@ -11,13 +11,33 @@
class API : public QObject { class API : public QObject {
Q_OBJECT Q_OBJECT
public:
enum State {Offline, NoServer, NotAuthenticated, Authenticated};
Q_ENUM(State)
private:
Q_PROPERTY(QUrl address READ getAddress WRITE setAddress NOTIFY addressChanged)
Q_PROPERTY(State state READ getState NOTIFY stateChanged)
public: public:
explicit API(const QUrl& path = QString(), QObject* parent = nullptr); explicit API(const QUrl& path = QString(), QObject* parent = nullptr);
Q_INVOKABLE void test(const QString& path, const QJSValue& finished = QJSValue()); QUrl getAddress() const;
State getState() const;
void setAddress(const QUrl& path);
signals:
void addressChanged(const QUrl& path);
void stateChanged(State state);
public slots:
void test(const QString& path, const QJSValue& finished = QJSValue());
void sendRegister(const QString& login, const QString& password, const QJSValue& finished = QJSValue());
private slots: private slots:
void onTestSuccess(QNetworkReply* reply, const QJSValue& finished) const; void onTestFinished(QNetworkReply* reply, const QJSValue& finished) const;
void onRegisterFinished(QNetworkReply* reply, const QJSValue& finished) const;
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;
@ -25,4 +45,5 @@ private:
private: private:
QUrl address; QUrl address;
QNetworkAccessManager network; QNetworkAccessManager network;
State state;
}; };

View File

@ -2,6 +2,8 @@ import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import API
Page { Page {
property string address property string address
property bool valid: false property bool valid: false

View File

@ -2,9 +2,9 @@ import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
Page { import API
property string serverAddress
Page {
signal pickServer(address: string) signal pickServer(address: string)
title: qsTr("Welcome") title: qsTr("Welcome")
@ -32,7 +32,7 @@ Page {
} }
Label { Label {
horizontalAlignment: Label.AlignLeft horizontalAlignment: Label.AlignLeft
text: serverAddress || qsTr("choose") text: API.state === API.NoServer ? qsTr("choose") : API.address
font { font {
italic: true italic: true
underline: true underline: true
@ -40,7 +40,157 @@ Page {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: pickServer(serverAddress) onClicked: pickServer(API.address)
}
}
}
Row {
id: forms
property bool registering: false
visible: API.state === API.NotAuthenticated
anchors.horizontalCenter: parent.horizontalCenter
topPadding: 10
Column {
visible: forms.registering === false
spacing: 10
Label {
anchors.horizontalCenter: parent.horizontalCenter
text: qsTr("Please, log in to your account")
font {
pixelSize: 14
}
}
Grid {
anchors.horizontalCenter: parent.horizontalCenter
columns: 2
columnSpacing: 10
rowSpacing: 5
verticalItemAlignment: Grid.AlignVCenter
horizontalItemAlignment: Grid.AlignRight
Label {
text: qsTr("Login") + ":";
}
TextField {
id: login
}
Label {
text: qsTr("Password") + ":";
}
TextField {
id: password
echoMode: TextField.Password
}
}
Button {
anchors.horizontalCenter: parent.horizontalCenter
text: qsTr("Login")
onClicked: function () {
console.log("Not implemented");
}
}
Row {
anchors.horizontalCenter: parent.horizontalCenter
spacing: 5
topPadding: 10
Label {
text: qsTr("Don't have account?")
}
Label {
text: qsTr("Sign up") + "!"
font {
italic: true
underline: true
}
MouseArea {
anchors.fill: parent
onClicked: forms.registering = true
}
}
}
}
Column {
visible: forms.registering === true
spacing: 10
Label {
anchors.horizontalCenter: parent.horizontalCenter
text: qsTr("Please, chose login and password")
font {
pixelSize: 14
}
}
Grid {
anchors.horizontalCenter: parent.horizontalCenter
columns: 2
columnSpacing: 10
rowSpacing: 5
verticalItemAlignment: Grid.AlignVCenter
horizontalItemAlignment: Grid.AlignRight
Label {
text: qsTr("Login") + ":";
}
TextField {
id: newLogin
}
Label {
text: qsTr("Password") + ":";
}
TextField {
id: newPassword
echoMode: TextField.Password
}
}
Button {
anchors.horizontalCenter: parent.horizontalCenter
text: qsTr("Register")
onClicked: API.sendRegister(newLogin.text, newPassword.text, function (err, result) {
if (err)
console.error("err")
console.log(result);
})
}
Row {
anchors.horizontalCenter: parent.horizontalCenter
spacing: 5
topPadding: 10
Label {
text: qsTr("Already have an account?")
}
Label {
text: qsTr("Log in") + "!"
font {
italic: true
underline: true
}
MouseArea {
anchors.fill: parent
onClicked: forms.registering = false
}
}
} }
} }
} }

View File

@ -4,6 +4,7 @@ import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import QtCore import QtCore
import API
ApplicationWindow { ApplicationWindow {
property int counter: 0 property int counter: 0
@ -27,25 +28,18 @@ ApplicationWindow {
Welcome { Welcome {
id: welcome id: welcome
serverAddress: settings.serverAddress
onPickServer: function (address) { onPickServer: function (address) {
pick.address = address; pick.address = address;
stack.push(pick) stack.push(pick)
} }
} }
Settings {
id: settings
property string serverAddress
}
ServerPick { ServerPick {
visible: false visible: false
id: pick id: pick
onBack: stack.pop() onBack: stack.pop()
onSuccess: function (address) { onSuccess: function (address) {
settings.serverAddress = address; API.address = address
stack.pop(); stack.pop();
} }
} }

View File

@ -1,5 +1,7 @@
#include "root.h" #include "root.h"
#include <QSettings>
Root::Root(const QUrl& root, int& argc, char* argv[]) : Root::Root(const QUrl& root, int& argc, char* argv[]) :
QGuiApplication(argc, argv), QGuiApplication(argc, argv),
root(root), root(root),
@ -21,11 +23,19 @@ Root::Root(const QUrl& root, int& argc, char* argv[]) :
Qt::QueuedConnection Qt::QueuedConnection
); );
QSettings settings;
api.setAddress(settings.value("address").toUrl());
qRegisterMetaType<API>("API");
connect(&api, &API::addressChanged, this, &Root::onAPIAddressChanged);
qmlRegisterSingletonType<API>("API", 1, 0, "API", [this] (QQmlEngine *engine, QJSEngine *scriptEngine) {
return &api;
});
engine.load(root); engine.load(root);
if (engine.rootObjects().isEmpty()) if (engine.rootObjects().isEmpty())
throw std::runtime_error("Couldn't looad root qml object"); throw std::runtime_error("Couldn't looad root qml object");
context->setContextProperty("API", &api);
} }
Root::~Root() { Root::~Root() {
@ -52,3 +62,8 @@ void Root::onObjectCreated(QObject* obj, const QUrl& objUrl) {
if (!obj && objUrl == root) if (!obj && objUrl == root)
exit(-2); exit(-2);
} }
void Root::onAPIAddressChanged(const QUrl& url) {
QSettings settings;
settings.setValue("address", url);
}

2
root.h
View File

@ -4,6 +4,7 @@
#include <stdexcept> #include <stdexcept>
#include <QString> #include <QString>
#include <QUrl>
#include <QGuiApplication> #include <QGuiApplication>
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include <QQmlContext> #include <QQmlContext>
@ -19,6 +20,7 @@ public:
private slots: private slots:
void onObjectCreated(QObject* obj, const QUrl& objUrl); void onObjectCreated(QObject* obj, const QUrl& objUrl);
void onAPIAddressChanged(const QUrl& url);
private: private:
QUrl root; QUrl root;