From b38ed2107b94f4fcb8f9222d85907aac2ff276ac Mon Sep 17 00:00:00 2001 From: blue Date: Tue, 26 Dec 2023 20:31:55 -0300 Subject: [PATCH] some more thoughts about state management --- API/CMakeLists.txt | 2 ++ API/api.cpp | 32 ++++++++++++++++++++++++-------- API/api.h | 1 + API/finalaction.cpp | 10 ++++++++++ API/finalaction.h | 19 +++++++++++++++++++ qml/Application/CMakeLists.txt | 12 ++++++++++++ qml/Application/Root.qml | 13 +++++++++++++ qml/CMakeLists.txt | 1 + qml/Components/Modal.qml | 7 ++++--- qml/Forms/Login.qml | 3 +++ qml/Welcome.qml | 19 ++++++++++++++++++- qml/main.qml | 12 ++++++++++++ root.cpp | 7 ++++++- 13 files changed, 125 insertions(+), 13 deletions(-) create mode 100644 API/finalaction.cpp create mode 100644 API/finalaction.h create mode 100644 qml/Application/CMakeLists.txt create mode 100644 qml/Application/Root.qml diff --git a/API/CMakeLists.txt b/API/CMakeLists.txt index d9a627f..7e2b135 100644 --- a/API/CMakeLists.txt +++ b/API/CMakeLists.txt @@ -1,11 +1,13 @@ set(HEADERS api.h codes.h + finalaction.h ) set(SOURCES api.cpp codes.cpp + finalaction.cpp ) target_sources(magpie PRIVATE ${SOURCES}) diff --git a/API/api.cpp b/API/api.cpp index 81c5e6e..70f6a57 100644 --- a/API/api.cpp +++ b/API/api.cpp @@ -3,8 +3,10 @@ #include #include #include +#include #include "codes.h" +#include "finalaction.h" constexpr const char* json = "application/json"; constexpr const char* urlEncoded = "application/x-www-form-urlencoded"; @@ -15,7 +17,7 @@ const std::map testStructure({ }); const std::map resultStructure({ - {"result", QMetaType::Double}, + {"result", QMetaType::LongLong}, }); const std::map tokensStructure({ @@ -47,8 +49,12 @@ API::State API::getState() const { void API::setTokens(const QString access, const QString &renew) { accessToken = access; renewToken = renew; - state = Authenticating; - emit stateChanged(state); + setState(Authenticating); + + //dont forget to remove + QTimer::singleShot(1000, this, [this] () { + setState(Authenticated); + }); } void API::setAddress(const QUrl& path) { @@ -60,10 +66,8 @@ void API::setAddress(const QUrl& path) { } address = path; - state = address.isEmpty() ? NoServer : NotAuthenticated; - emit addressChanged(address); - emit stateChanged(state); + setState(address.isEmpty() ? NoServer : NotAuthenticated); } std::optional API::readResult(QNetworkReply* reply) { @@ -100,6 +104,14 @@ bool API::validateResponse(const std::optional& data, const std::ma 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) { qDebug() << "Testing" << path; if (state == Offline) @@ -177,7 +189,7 @@ void API::sendLogin(const QString& login, const QString& password, const QJSValu QNetworkRequest request(url); request.setHeader(QNetworkRequest::ContentTypeHeader, urlEncoded); - + setState(Authenticating); QNetworkReply* reply = network.post(request, params.toString(QUrl::FullyEncoded).toUtf8()); connect(reply, &QNetworkReply::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) { + 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 rpl(reply); QNetworkReply::NetworkError error = reply->error(); std::optional data = readResult(reply); @@ -237,7 +254,6 @@ void API::onLoginFinished(QNetworkReply* reply, const QJSValue& finished) { accessToken = data->value("accessToken").toString(); renewToken = data->value("renewToken").toString(); emit storeTokens(accessToken, renewToken); - emit stateChanged(state); } void API::callCallback(const QJSValue& callback, const QString& error, const QJSValueList& arguments) const { diff --git a/API/api.h b/API/api.h index 1f12713..b0e60e6 100644 --- a/API/api.h +++ b/API/api.h @@ -57,6 +57,7 @@ private: void setAddress(const QUrl& path); static std::optional readResult(QNetworkReply* reply); static bool validateResponse(const std::optional& data, const std::map& structure); + void setState(State newState); private: QUrl address; diff --git a/API/finalaction.cpp b/API/finalaction.cpp new file mode 100644 index 0000000..e9c5f2d --- /dev/null +++ b/API/finalaction.cpp @@ -0,0 +1,10 @@ +#include "finalaction.h" + +FinalAction::FinalAction(const std::function& action): + action(action) +{} + +FinalAction::~FinalAction() { + action(); +} + diff --git a/API/finalaction.h b/API/finalaction.h new file mode 100644 index 0000000..db78dcb --- /dev/null +++ b/API/finalaction.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +class FinalAction { +public: + FinalAction(const std::function& action); + ~FinalAction(); + + FinalAction() = delete; + FinalAction(const FinalAction&) = delete; + FinalAction(FinalAction&&) = delete; + FinalAction& operator = (const FinalAction&) = delete; + FinalAction& operator = (FinalAction&&) = delete; + +private: + std::function action; +}; + diff --git a/qml/Application/CMakeLists.txt b/qml/Application/CMakeLists.txt new file mode 100644 index 0000000..0ab269f --- /dev/null +++ b/qml/Application/CMakeLists.txt @@ -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) diff --git a/qml/Application/Root.qml b/qml/Application/Root.qml new file mode 100644 index 0000000..81f05aa --- /dev/null +++ b/qml/Application/Root.qml @@ -0,0 +1,13 @@ +import QtQuick +import QtQuick.Controls + +Page { + Label { + anchors.centerIn: parent + text: "Here we go!" + font { + pixelSize: 24 + bold: true + } + } +} diff --git a/qml/CMakeLists.txt b/qml/CMakeLists.txt index 801a3a9..20c6cf9 100644 --- a/qml/CMakeLists.txt +++ b/qml/CMakeLists.txt @@ -18,3 +18,4 @@ target_link_libraries(magpie PRIVATE magpieQml) add_subdirectory(Forms) add_subdirectory(Components) +add_subdirectory(Application) diff --git a/qml/Components/Modal.qml b/qml/Components/Modal.qml index 538313a..54aa64f 100644 --- a/qml/Components/Modal.qml +++ b/qml/Components/Modal.qml @@ -2,6 +2,7 @@ import QtQuick import QtQuick.Controls Popup { + property bool closable: true property bool inProgress: false property string status: "" @@ -11,7 +12,7 @@ Popup { focus: true width: column.width + 60 height: column.height + 60 - closePolicy: Popup.CloseOnEscape + closePolicy: closable ? Popup.CloseOnEscape : Popup.NoAutoClose onClosed: inProgress = false Column { @@ -32,11 +33,11 @@ Popup { Button { text: qsTr("Close") anchors.horizontalCenter: parent.horizontalCenter - visible: !inProgress + visible: closable && !inProgress onClicked: modal.close() focus: true Keys.onReturnPressed: { - if (!inProgress) + if (closable && !inProgress) modal.close() } } diff --git a/qml/Forms/Login.qml b/qml/Forms/Login.qml index 8155132..685c35c 100644 --- a/qml/Forms/Login.qml +++ b/qml/Forms/Login.qml @@ -6,11 +6,13 @@ import magpie.Components as Components Column { signal register() + signal loggingIn(value: bool) function login(login, password) { if (modal.inProgress) return; + loggingIn(true); loginField.text = login; passwordField.text = password; @@ -28,6 +30,7 @@ Column { else modal.status = qsTr("Success"); + loggingIn(false); if (!!result) modal.close(); }); diff --git a/qml/Welcome.qml b/qml/Welcome.qml index a167cdd..98b3c6d 100644 --- a/qml/Welcome.qml +++ b/qml/Welcome.qml @@ -4,9 +4,15 @@ import QtQuick.Layouts import magpie.API import magpie.Forms as Forms +import magpie.Components as Components Page { id: page + QtObject { + id: priv + property bool loggingIn: false + } + signal pickServer(address: string) // 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 { - visible: API.state === API.NotAuthenticated + visible: priv.loggingIn || API.state === API.NotAuthenticated || API.state === API.Authenticating width: page.width height: stack.currentItem ? stack.currentItem.implicitHeight: 0 @@ -67,6 +80,9 @@ Page { id: loginForm Forms.Login { onRegister: stack.replace(registerForm) + onLoggingIn: function (value) { + priv.loggingIn = value; + } Component.onCompleted: { if (stack.pendingLogin && stack.pendingPassword) this.login(stack.pendingLogin, stack.pendingPassword) @@ -74,6 +90,7 @@ Page { stack.pendingLogin = ""; stack.pendingPassword = ""; } + Component.onDestruction: priv.loggingIn = false } } diff --git a/qml/main.qml b/qml/main.qml index b1c591d..64ded1b 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -5,6 +5,7 @@ import QtQuick.Layouts import QtCore import magpie.API +import magpie.Application as Application ApplicationWindow { property int counter: 0 @@ -41,6 +42,13 @@ ApplicationWindow { StackView.onDeactivating: pickingServer = false; } } + + Component { + id: app + Application.Root { + + } + } } Connections { @@ -49,5 +57,9 @@ ApplicationWindow { if (pickingServer && url.toString().length > 0) stack.pop() } + function onStateChanged (state) { + if (state === API.Authenticated) + stack.push(app) + } } } diff --git a/root.cpp b/root.cpp index 7950ce2..3f7810e 100644 --- a/root.cpp +++ b/root.cpp @@ -26,6 +26,11 @@ Root::Root(const QUrl& root, int& argc, char* argv[]) : QSettings settings; 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"); connect(&api, &API::addressChanged, this, &Root::onAPIAddressChanged); 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) { QSettings settings; - settings.setValue("assessToken", access); + settings.setValue("accessToken", access); settings.setValue("renewToken", renew); }