polling testing some further mainscreen tinkering
This commit is contained in:
parent
4694f3838f
commit
74701e9431
69
API/api.cpp
69
API/api.cpp
@ -40,7 +40,8 @@ API::API(const QUrl& address, QObject* parent):
|
|||||||
state(NoServer),
|
state(NoServer),
|
||||||
accessToken(),
|
accessToken(),
|
||||||
renewToken(),
|
renewToken(),
|
||||||
firstPoll()
|
firstPoll(),
|
||||||
|
pollReply()
|
||||||
{
|
{
|
||||||
firstPoll.setSingleShot(true);
|
firstPoll.setSingleShot(true);
|
||||||
firstPoll.setInterval(2000);
|
firstPoll.setInterval(2000);
|
||||||
@ -67,21 +68,8 @@ void API::startPolling() {
|
|||||||
if (state != Authenticating)
|
if (state != Authenticating)
|
||||||
throw std::runtime_error("Can not start polling in this state: " + std::to_string(state));
|
throw std::runtime_error("Can not start polling in this state: " + std::to_string(state));
|
||||||
|
|
||||||
if (accessToken.isEmpty())
|
sendPoll();
|
||||||
throw std::runtime_error("Can not start polling: access token is empty");
|
|
||||||
|
|
||||||
QByteArray authorizationHeader = "Bearer " + accessToken.toUtf8();
|
|
||||||
QNetworkRequest request(createUrl("/poll"));
|
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, json);
|
|
||||||
request.setRawHeader(QByteArray("Authorization"), authorizationHeader);
|
|
||||||
request.setTransferTimeout(30000);
|
|
||||||
|
|
||||||
firstPoll.start();
|
firstPoll.start();
|
||||||
|
|
||||||
QNetworkReply* reply = network.get(request);
|
|
||||||
connect(reply, &QNetworkReply::finished,
|
|
||||||
std::bind(&API::onPollFinished, this, reply)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void API::setAddress(const QUrl& path) {
|
void API::setAddress(const QUrl& path) {
|
||||||
@ -146,6 +134,24 @@ QUrl API::createUrl(const QString &path) const {
|
|||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void API::sendPoll() {
|
||||||
|
if (accessToken.isEmpty())
|
||||||
|
throw std::runtime_error("Can not start polling: access token is empty");
|
||||||
|
|
||||||
|
QByteArray authorizationHeader = "Bearer " + accessToken.toUtf8();
|
||||||
|
QNetworkRequest request(createUrl("/poll"));
|
||||||
|
request.setHeader(QNetworkRequest::ContentTypeHeader, json);
|
||||||
|
request.setRawHeader("Authorization", authorizationHeader);
|
||||||
|
request.setTransferTimeout(30000);
|
||||||
|
|
||||||
|
pollReply = std::unique_ptr<QNetworkReply>(network.get(request));
|
||||||
|
connect(
|
||||||
|
pollReply.get(), &QNetworkReply::finished,
|
||||||
|
this, &API::onPollFinished,
|
||||||
|
Qt::QueuedConnection
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
@ -218,7 +224,7 @@ void API::sendLogin(const QString& login, const QString& password, const QJSValu
|
|||||||
setState(Authenticating);
|
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)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,19 +289,32 @@ void API::onLoginFinished(QNetworkReply* reply, const QJSValue& finished) {
|
|||||||
startPolling();
|
startPolling();
|
||||||
}
|
}
|
||||||
|
|
||||||
void API::onPollFinished(QNetworkReply *reply) {
|
void API::onPollFinished() {
|
||||||
firstPoll.stop();
|
firstPoll.stop();
|
||||||
std::unique_ptr<QNetworkReply, NetworkReplyDeleter> rpl(reply);
|
QNetworkReply::NetworkError error = pollReply->error();
|
||||||
QNetworkReply::NetworkError error = reply->error();
|
std::optional<QVariantMap> data = readResult(pollReply.get());
|
||||||
std::optional<QVariantMap> data = readResult(reply);
|
Codes::Poll code = Codes::Poll::unknownError;
|
||||||
|
if (validateResponse(data, resultStructure))
|
||||||
|
code = Codes::convertPoll(data->value("result").toInt());
|
||||||
|
|
||||||
|
QString detail = Codes::description(code);
|
||||||
|
|
||||||
if (error != QNetworkReply::NoError)
|
if (error != QNetworkReply::NoError)
|
||||||
qDebug() << reply->errorString();
|
qDebug() << pollReply->errorString() + ": " + detail;
|
||||||
|
else
|
||||||
|
qDebug() << "Poll finished: " + detail;
|
||||||
|
|
||||||
if (data)
|
switch (code) {
|
||||||
qDebug() << data.value();
|
case Codes::Poll::success:
|
||||||
|
//todo handle the result
|
||||||
setState(NotAuthenticated);
|
case Codes::Poll::timeout:
|
||||||
|
return sendPoll();
|
||||||
|
case Codes::Poll::tokenProblem:
|
||||||
|
case Codes::Poll::replace:
|
||||||
|
case Codes::Poll::unknownError: //todo this one doesn't actually mean that we can't work for now, the network may be temporarily down or something
|
||||||
|
pollReply.reset();
|
||||||
|
return setState(NotAuthenticated);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void API::onFirstPollSuccess() {
|
void API::onFirstPollSuccess() {
|
||||||
|
@ -56,7 +56,7 @@ 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);
|
void onLoginFinished(QNetworkReply* reply, const QJSValue& finished);
|
||||||
void onPollFinished(QNetworkReply* reply);
|
void onPollFinished();
|
||||||
|
|
||||||
void onFirstPollSuccess();
|
void onFirstPollSuccess();
|
||||||
|
|
||||||
@ -67,6 +67,7 @@ private:
|
|||||||
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);
|
void setState(State newState);
|
||||||
QUrl createUrl(const QString& path) const;
|
QUrl createUrl(const QString& path) const;
|
||||||
|
void sendPoll();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QUrl address;
|
QUrl address;
|
||||||
@ -75,4 +76,5 @@ private:
|
|||||||
QString accessToken;
|
QString accessToken;
|
||||||
QString renewToken;
|
QString renewToken;
|
||||||
QTimer firstPoll;
|
QTimer firstPoll;
|
||||||
|
std::unique_ptr<QNetworkReply> pollReply;
|
||||||
};
|
};
|
||||||
|
@ -27,6 +27,14 @@ constexpr std::array<const char*, uint8_t(Codes::Login::unknownError) + 1> login
|
|||||||
"Unknown login error"
|
"Unknown login error"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
constexpr std::array<const char*, uint8_t(Codes::Poll::unknownError) + 1> pollErrors({
|
||||||
|
"New data from poll",
|
||||||
|
"Couldn't authorize polling: token error",
|
||||||
|
"Other device was authorized with the same token",
|
||||||
|
"Poll timeout, no new data",
|
||||||
|
"Unknown poll error"
|
||||||
|
});
|
||||||
|
|
||||||
Codes::Register Codes::convertRegister (int source) {
|
Codes::Register Codes::convertRegister (int source) {
|
||||||
if (source < 0) {
|
if (source < 0) {
|
||||||
std::cerr << "Converting Register response code which is smaller than zero, falling back to unknownError" << std::endl;
|
std::cerr << "Converting Register response code which is smaller than zero, falling back to unknownError" << std::endl;
|
||||||
@ -62,3 +70,21 @@ Codes::Login Codes::convertLogin(int source) {
|
|||||||
QString Codes::description(Login code) {
|
QString Codes::description(Login code) {
|
||||||
return loginErrors[(uint8_t)code];
|
return loginErrors[(uint8_t)code];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Codes::Poll Codes::convertPoll(int source) {
|
||||||
|
if (source < 0) {
|
||||||
|
std::cerr << "Converting Poll response code which is smaller than zero, falling back to unknownError" << std::endl;
|
||||||
|
return Poll::unknownError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source > (int)Poll::unknownError) {
|
||||||
|
std::cerr << "Converting Poll response code which is bigger than biggest known, falling back to unknownError" << std::endl;
|
||||||
|
return Poll::unknownError;
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<Poll>(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Codes::description(Poll code) {
|
||||||
|
return pollErrors[(uint8_t)code];
|
||||||
|
}
|
||||||
|
10
API/codes.h
10
API/codes.h
@ -28,10 +28,20 @@ enum class Login {
|
|||||||
unknownError
|
unknownError
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class Poll {
|
||||||
|
success,
|
||||||
|
tokenProblem,
|
||||||
|
replace,
|
||||||
|
timeout,
|
||||||
|
unknownError
|
||||||
|
};
|
||||||
|
|
||||||
Register convertRegister (int source);
|
Register convertRegister (int source);
|
||||||
Login convertLogin (int source);
|
Login convertLogin (int source);
|
||||||
|
Poll convertPoll (int source);
|
||||||
|
|
||||||
QString description (Register code);
|
QString description (Register code);
|
||||||
QString description (Login code);
|
QString description (Login code);
|
||||||
|
QString description (Poll code);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
16
qml/Application/Assets.qml
Normal file
16
qml/Application/Assets.qml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 Yury Gubich <blue@macaw.me>
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Label {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: "This is Assets screen"
|
||||||
|
font {
|
||||||
|
pixelSize: 24
|
||||||
|
bold: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,10 @@ qt_add_qml_module(magpieApplication
|
|||||||
NO_PLUGIN
|
NO_PLUGIN
|
||||||
QML_FILES
|
QML_FILES
|
||||||
Root.qml
|
Root.qml
|
||||||
|
Main.qml
|
||||||
|
Home.qml
|
||||||
|
Assets.qml
|
||||||
|
Records.qml
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(magpie PRIVATE magpieApplication)
|
target_link_libraries(magpie PRIVATE magpieApplication)
|
||||||
|
16
qml/Application/Home.qml
Normal file
16
qml/Application/Home.qml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 Yury Gubich <blue@macaw.me>
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Label {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: "This is a Home screen"
|
||||||
|
font {
|
||||||
|
pixelSize: 24
|
||||||
|
bold: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
81
qml/Application/Main.qml
Normal file
81
qml/Application/Main.qml
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 Yury Gubich <blue@macaw.me>
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
Item {
|
||||||
|
property string currentPage: "home"
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: menu
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.minimumWidth: 50
|
||||||
|
Layout.preferredWidth: 100
|
||||||
|
Layout.maximumWidth: 100
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
|
||||||
|
MenuItem {
|
||||||
|
width: parent.width
|
||||||
|
text: qsTr("Home")
|
||||||
|
icon.name: "home"
|
||||||
|
highlighted: currentPage === "home"
|
||||||
|
onTriggered: {
|
||||||
|
if (!this.highlighted)
|
||||||
|
content.replace(home)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MenuItem {
|
||||||
|
width: parent.width
|
||||||
|
text: qsTr("Assets")
|
||||||
|
icon.name: "document-properties"
|
||||||
|
highlighted: currentPage === "assets"
|
||||||
|
onTriggered: {
|
||||||
|
if (!this.highlighted)
|
||||||
|
content.replace(assets)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MenuItem {
|
||||||
|
width: parent.width
|
||||||
|
text: qsTr("Records")
|
||||||
|
icon.name: "system-search"
|
||||||
|
highlighted: currentPage === "records"
|
||||||
|
onTriggered: {
|
||||||
|
if (!this.highlighted)
|
||||||
|
content.replace(records)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StackView {
|
||||||
|
id: content
|
||||||
|
initialItem: home
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: home
|
||||||
|
Home {
|
||||||
|
StackView.onActivating: currentPage = "home"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: assets
|
||||||
|
Assets {
|
||||||
|
StackView.onActivating: currentPage = "assets"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: records
|
||||||
|
Records {
|
||||||
|
StackView.onActivating: currentPage = "records"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
qml/Application/Records.qml
Normal file
16
qml/Application/Records.qml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 Yury Gubich <blue@macaw.me>
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Label {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: "This is Records screen"
|
||||||
|
font {
|
||||||
|
pixelSize: 24
|
||||||
|
bold: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,13 +4,17 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
|
|
||||||
Page {
|
Item {
|
||||||
Label {
|
StackView {
|
||||||
anchors.centerIn: parent
|
id: stack
|
||||||
text: "Here we go!"
|
initialItem: main
|
||||||
font {
|
anchors.fill: parent
|
||||||
pixelSize: 24
|
}
|
||||||
bold: true
|
|
||||||
|
Component {
|
||||||
|
id: main
|
||||||
|
Main {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
13
qml/main.qml
13
qml/main.qml
@ -13,17 +13,13 @@ import magpie.Application as Application
|
|||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
property int counter: 0
|
property int counter: 0
|
||||||
property bool pickingServer: false
|
property bool pickingServer: false
|
||||||
|
property bool runningApp: false
|
||||||
|
|
||||||
width: 640
|
width: 640
|
||||||
height: 480
|
height: 480
|
||||||
visible: true
|
visible: true
|
||||||
title: "Magpie"
|
title: "Magpie"
|
||||||
|
|
||||||
header: Label {
|
|
||||||
text: stack.currentItem.title
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StackView {
|
StackView {
|
||||||
id: stack
|
id: stack
|
||||||
initialItem: welcome
|
initialItem: welcome
|
||||||
@ -49,7 +45,8 @@ ApplicationWindow {
|
|||||||
Component {
|
Component {
|
||||||
id: app
|
id: app
|
||||||
Application.Root {
|
Application.Root {
|
||||||
|
StackView.onActivating: runningApp = true;
|
||||||
|
StackView.onDeactivating: runningApp = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -62,7 +59,9 @@ ApplicationWindow {
|
|||||||
}
|
}
|
||||||
function onStateChanged (state) {
|
function onStateChanged (state) {
|
||||||
if (state === API.Authenticated)
|
if (state === API.Authenticated)
|
||||||
stack.push(app)
|
stack.push(app);
|
||||||
|
else if (runningApp && state === API.NotAuthenticated)
|
||||||
|
stack.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user