some more thoughts about state management
This commit is contained in:
parent
437e76067f
commit
b38ed2107b
@ -1,11 +1,13 @@
|
|||||||
set(HEADERS
|
set(HEADERS
|
||||||
api.h
|
api.h
|
||||||
codes.h
|
codes.h
|
||||||
|
finalaction.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
api.cpp
|
api.cpp
|
||||||
codes.cpp
|
codes.cpp
|
||||||
|
finalaction.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_sources(magpie PRIVATE ${SOURCES})
|
target_sources(magpie PRIVATE ${SOURCES})
|
||||||
|
32
API/api.cpp
32
API/api.cpp
@ -3,8 +3,10 @@
|
|||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QUrlQuery>
|
#include <QUrlQuery>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
#include "codes.h"
|
#include "codes.h"
|
||||||
|
#include "finalaction.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";
|
||||||
@ -15,7 +17,7 @@ const std::map<QString, QMetaType::Type> testStructure({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const std::map<QString, QMetaType::Type> resultStructure({
|
const std::map<QString, QMetaType::Type> resultStructure({
|
||||||
{"result", QMetaType::Double},
|
{"result", QMetaType::LongLong},
|
||||||
});
|
});
|
||||||
|
|
||||||
const std::map<QString, QMetaType::Type> tokensStructure({
|
const std::map<QString, QMetaType::Type> tokensStructure({
|
||||||
@ -47,8 +49,12 @@ API::State API::getState() const {
|
|||||||
void API::setTokens(const QString access, const QString &renew) {
|
void API::setTokens(const QString access, const QString &renew) {
|
||||||
accessToken = access;
|
accessToken = access;
|
||||||
renewToken = renew;
|
renewToken = renew;
|
||||||
state = Authenticating;
|
setState(Authenticating);
|
||||||
emit stateChanged(state);
|
|
||||||
|
//dont forget to remove
|
||||||
|
QTimer::singleShot(1000, this, [this] () {
|
||||||
|
setState(Authenticated);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void API::setAddress(const QUrl& path) {
|
void API::setAddress(const QUrl& path) {
|
||||||
@ -60,10 +66,8 @@ void API::setAddress(const QUrl& path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
address = path;
|
address = path;
|
||||||
state = address.isEmpty() ? NoServer : NotAuthenticated;
|
|
||||||
|
|
||||||
emit addressChanged(address);
|
emit addressChanged(address);
|
||||||
emit stateChanged(state);
|
setState(address.isEmpty() ? NoServer : NotAuthenticated);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<QVariantMap> API::readResult(QNetworkReply* reply) {
|
std::optional<QVariantMap> API::readResult(QNetworkReply* reply) {
|
||||||
@ -100,6 +104,14 @@ bool API::validateResponse(const std::optional<QVariantMap>& data, const std::ma
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void API::setState(State newState) {
|
||||||
|
if (newState == state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
state = newState;
|
||||||
|
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)
|
if (state == Offline)
|
||||||
@ -177,7 +189,7 @@ void API::sendLogin(const QString& login, const QString& password, const QJSValu
|
|||||||
|
|
||||||
QNetworkRequest request(url);
|
QNetworkRequest request(url);
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, urlEncoded);
|
request.setHeader(QNetworkRequest::ContentTypeHeader, urlEncoded);
|
||||||
|
setState(Authenticating);
|
||||||
QNetworkReply* reply = network.post(request, params.toString(QUrl::FullyEncoded).toUtf8());
|
QNetworkReply* reply = network.post(request, params.toString(QUrl::FullyEncoded).toUtf8());
|
||||||
connect(reply, &QNetworkReply::finished,
|
connect(reply, &QNetworkReply::finished,
|
||||||
std::bind(&API::onLoginFinished, this, reply, finished)
|
std::bind(&API::onLoginFinished, this, reply, finished)
|
||||||
@ -208,6 +220,11 @@ void API::onRegisterFinished(QNetworkReply* reply, const QJSValue& finished) con
|
|||||||
}
|
}
|
||||||
|
|
||||||
void API::onLoginFinished(QNetworkReply* reply, const QJSValue& finished) {
|
void API::onLoginFinished(QNetworkReply* reply, const QJSValue& finished) {
|
||||||
|
State state = NotAuthenticated;
|
||||||
|
FinalAction action([this, &state]() { //this should be executed on leaving the function in any way
|
||||||
|
setState(state); //setting the state as a result of this object destruction
|
||||||
|
}); //this way any error will result in NotAuthenticated, and on success it should be Authenticated
|
||||||
|
|
||||||
std::unique_ptr<QNetworkReply, NetworkReplyDeleter> rpl(reply);
|
std::unique_ptr<QNetworkReply, NetworkReplyDeleter> rpl(reply);
|
||||||
QNetworkReply::NetworkError error = reply->error();
|
QNetworkReply::NetworkError error = reply->error();
|
||||||
std::optional<QVariantMap> data = readResult(reply);
|
std::optional<QVariantMap> data = readResult(reply);
|
||||||
@ -237,7 +254,6 @@ void API::onLoginFinished(QNetworkReply* reply, const QJSValue& finished) {
|
|||||||
accessToken = data->value("accessToken").toString();
|
accessToken = data->value("accessToken").toString();
|
||||||
renewToken = data->value("renewToken").toString();
|
renewToken = data->value("renewToken").toString();
|
||||||
emit storeTokens(accessToken, renewToken);
|
emit storeTokens(accessToken, renewToken);
|
||||||
emit stateChanged(state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
|
@ -57,6 +57,7 @@ private:
|
|||||||
void setAddress(const QUrl& path);
|
void setAddress(const QUrl& path);
|
||||||
static std::optional<QVariantMap> readResult(QNetworkReply* reply);
|
static std::optional<QVariantMap> readResult(QNetworkReply* reply);
|
||||||
static bool validateResponse(const std::optional<QVariantMap>& data, const std::map<QString, QMetaType::Type>& structure);
|
static bool validateResponse(const std::optional<QVariantMap>& data, const std::map<QString, QMetaType::Type>& structure);
|
||||||
|
void setState(State newState);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QUrl address;
|
QUrl address;
|
||||||
|
10
API/finalaction.cpp
Normal file
10
API/finalaction.cpp
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#include "finalaction.h"
|
||||||
|
|
||||||
|
FinalAction::FinalAction(const std::function<void()>& action):
|
||||||
|
action(action)
|
||||||
|
{}
|
||||||
|
|
||||||
|
FinalAction::~FinalAction() {
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
|
19
API/finalaction.h
Normal file
19
API/finalaction.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
class FinalAction {
|
||||||
|
public:
|
||||||
|
FinalAction(const std::function<void()>& action);
|
||||||
|
~FinalAction();
|
||||||
|
|
||||||
|
FinalAction() = delete;
|
||||||
|
FinalAction(const FinalAction&) = delete;
|
||||||
|
FinalAction(FinalAction&&) = delete;
|
||||||
|
FinalAction& operator = (const FinalAction&) = delete;
|
||||||
|
FinalAction& operator = (FinalAction&&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::function<void()> action;
|
||||||
|
};
|
||||||
|
|
12
qml/Application/CMakeLists.txt
Normal file
12
qml/Application/CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
qt_add_qml_module(magpieApplication
|
||||||
|
URI magpie.Application
|
||||||
|
VERSION 1.0
|
||||||
|
STATIC
|
||||||
|
RESOURCE_PREFIX /
|
||||||
|
OUTPUT_DIRECTORY ../magpie/Application
|
||||||
|
NO_PLUGIN
|
||||||
|
QML_FILES
|
||||||
|
Root.qml
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(magpie PRIVATE magpieApplication)
|
13
qml/Application/Root.qml
Normal file
13
qml/Application/Root.qml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
|
||||||
|
Page {
|
||||||
|
Label {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: "Here we go!"
|
||||||
|
font {
|
||||||
|
pixelSize: 24
|
||||||
|
bold: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -18,3 +18,4 @@ target_link_libraries(magpie PRIVATE magpieQml)
|
|||||||
|
|
||||||
add_subdirectory(Forms)
|
add_subdirectory(Forms)
|
||||||
add_subdirectory(Components)
|
add_subdirectory(Components)
|
||||||
|
add_subdirectory(Application)
|
||||||
|
@ -2,6 +2,7 @@ import QtQuick
|
|||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
|
|
||||||
Popup {
|
Popup {
|
||||||
|
property bool closable: true
|
||||||
property bool inProgress: false
|
property bool inProgress: false
|
||||||
property string status: ""
|
property string status: ""
|
||||||
|
|
||||||
@ -11,7 +12,7 @@ Popup {
|
|||||||
focus: true
|
focus: true
|
||||||
width: column.width + 60
|
width: column.width + 60
|
||||||
height: column.height + 60
|
height: column.height + 60
|
||||||
closePolicy: Popup.CloseOnEscape
|
closePolicy: closable ? Popup.CloseOnEscape : Popup.NoAutoClose
|
||||||
onClosed: inProgress = false
|
onClosed: inProgress = false
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
@ -32,11 +33,11 @@ Popup {
|
|||||||
Button {
|
Button {
|
||||||
text: qsTr("Close")
|
text: qsTr("Close")
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
visible: !inProgress
|
visible: closable && !inProgress
|
||||||
onClicked: modal.close()
|
onClicked: modal.close()
|
||||||
focus: true
|
focus: true
|
||||||
Keys.onReturnPressed: {
|
Keys.onReturnPressed: {
|
||||||
if (!inProgress)
|
if (closable && !inProgress)
|
||||||
modal.close()
|
modal.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,13 @@ import magpie.Components as Components
|
|||||||
|
|
||||||
Column {
|
Column {
|
||||||
signal register()
|
signal register()
|
||||||
|
signal loggingIn(value: bool)
|
||||||
|
|
||||||
function login(login, password) {
|
function login(login, password) {
|
||||||
if (modal.inProgress)
|
if (modal.inProgress)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
loggingIn(true);
|
||||||
loginField.text = login;
|
loginField.text = login;
|
||||||
passwordField.text = password;
|
passwordField.text = password;
|
||||||
|
|
||||||
@ -28,6 +30,7 @@ Column {
|
|||||||
else
|
else
|
||||||
modal.status = qsTr("Success");
|
modal.status = qsTr("Success");
|
||||||
|
|
||||||
|
loggingIn(false);
|
||||||
if (!!result)
|
if (!!result)
|
||||||
modal.close();
|
modal.close();
|
||||||
});
|
});
|
||||||
|
@ -4,9 +4,15 @@ import QtQuick.Layouts
|
|||||||
|
|
||||||
import magpie.API
|
import magpie.API
|
||||||
import magpie.Forms as Forms
|
import magpie.Forms as Forms
|
||||||
|
import magpie.Components as Components
|
||||||
|
|
||||||
Page {
|
Page {
|
||||||
id: page
|
id: page
|
||||||
|
QtObject {
|
||||||
|
id: priv
|
||||||
|
property bool loggingIn: false
|
||||||
|
}
|
||||||
|
|
||||||
signal pickServer(address: string)
|
signal pickServer(address: string)
|
||||||
|
|
||||||
// title: qsTr("Welcome")
|
// title: qsTr("Welcome")
|
||||||
@ -48,8 +54,15 @@ Page {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Components.Modal {
|
||||||
|
inProgress: API.state === API.Authenticating
|
||||||
|
visible: !priv.loggingIn && API.state === API.Authenticating
|
||||||
|
closable: API.state === API.NotAuthenticated
|
||||||
|
status: "Logging into " + API.address + "..."
|
||||||
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
visible: API.state === API.NotAuthenticated
|
visible: priv.loggingIn || API.state === API.NotAuthenticated || API.state === API.Authenticating
|
||||||
|
|
||||||
width: page.width
|
width: page.width
|
||||||
height: stack.currentItem ? stack.currentItem.implicitHeight: 0
|
height: stack.currentItem ? stack.currentItem.implicitHeight: 0
|
||||||
@ -67,6 +80,9 @@ Page {
|
|||||||
id: loginForm
|
id: loginForm
|
||||||
Forms.Login {
|
Forms.Login {
|
||||||
onRegister: stack.replace(registerForm)
|
onRegister: stack.replace(registerForm)
|
||||||
|
onLoggingIn: function (value) {
|
||||||
|
priv.loggingIn = value;
|
||||||
|
}
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (stack.pendingLogin && stack.pendingPassword)
|
if (stack.pendingLogin && stack.pendingPassword)
|
||||||
this.login(stack.pendingLogin, stack.pendingPassword)
|
this.login(stack.pendingLogin, stack.pendingPassword)
|
||||||
@ -74,6 +90,7 @@ Page {
|
|||||||
stack.pendingLogin = "";
|
stack.pendingLogin = "";
|
||||||
stack.pendingPassword = "";
|
stack.pendingPassword = "";
|
||||||
}
|
}
|
||||||
|
Component.onDestruction: priv.loggingIn = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
12
qml/main.qml
12
qml/main.qml
@ -5,6 +5,7 @@ import QtQuick.Layouts
|
|||||||
import QtCore
|
import QtCore
|
||||||
|
|
||||||
import magpie.API
|
import magpie.API
|
||||||
|
import magpie.Application as Application
|
||||||
|
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
property int counter: 0
|
property int counter: 0
|
||||||
@ -41,6 +42,13 @@ ApplicationWindow {
|
|||||||
StackView.onDeactivating: pickingServer = false;
|
StackView.onDeactivating: pickingServer = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: app
|
||||||
|
Application.Root {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
@ -49,5 +57,9 @@ ApplicationWindow {
|
|||||||
if (pickingServer && url.toString().length > 0)
|
if (pickingServer && url.toString().length > 0)
|
||||||
stack.pop()
|
stack.pop()
|
||||||
}
|
}
|
||||||
|
function onStateChanged (state) {
|
||||||
|
if (state === API.Authenticated)
|
||||||
|
stack.push(app)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
7
root.cpp
7
root.cpp
@ -26,6 +26,11 @@ Root::Root(const QUrl& root, int& argc, char* argv[]) :
|
|||||||
QSettings settings;
|
QSettings settings;
|
||||||
api.setAddress(settings.value("address").toUrl());
|
api.setAddress(settings.value("address").toUrl());
|
||||||
|
|
||||||
|
QString acc = settings.value("accessToken").toString();
|
||||||
|
QString ren = settings.value("renewToken").toString();
|
||||||
|
if (!acc.isEmpty() && !ren.isEmpty())
|
||||||
|
api.setTokens(acc, ren);
|
||||||
|
|
||||||
qRegisterMetaType<API>("API");
|
qRegisterMetaType<API>("API");
|
||||||
connect(&api, &API::addressChanged, this, &Root::onAPIAddressChanged);
|
connect(&api, &API::addressChanged, this, &Root::onAPIAddressChanged);
|
||||||
connect(&api, &API::storeTokens, this, &Root::onStoreTokens);
|
connect(&api, &API::storeTokens, this, &Root::onStoreTokens);
|
||||||
@ -72,6 +77,6 @@ void Root::onAPIAddressChanged(const QUrl& url) {
|
|||||||
|
|
||||||
void Root::onStoreTokens(const QString& access, const QString& renew) {
|
void Root::onStoreTokens(const QString& access, const QString& renew) {
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
settings.setValue("assessToken", access);
|
settings.setValue("accessToken", access);
|
||||||
settings.setValue("renewToken", renew);
|
settings.setValue("renewToken", renew);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user