summaryrefslogblamecommitdiffstats
path: root/src/gui.cpp
blob: e27086a3cf90b0105d6d7bd4b0a34c06f213ad43 (plain) (tree)
























































































































































































































                                                                                                                                                          
#include "gui.h"
#include "progress.h"

#include <QLabel>
#include <QVBoxLayout>
#include <QPushButton>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QMessageBox>
#include <QTimer>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>

static QString STATE_COPYING("COPYING"); // Still busy copying original file, new chunks are only accepted in already copied range
static QString STATE_WAITING_FOR_UPLOAD_DONE("WAITING_FOR_UPLOAD_DONE"); // Copying done, just waiting for new chunks until client signals that it's done
static QString STATE_WAITING_FOR_COPYING_DONE("WAITING_FOR_COPYING_DONE"); // Copying not done, but dnbd3-fuse already told us it's done, wait for copying
static QString STATE_UPLOAD_DONE("UPLOAD_DONE"); // dnbd3-fuse signaled merge request, upload is thus done and no new chunks are accepted
static QString STATE_PROCESSING("PROCESSING"); // Hashing, renaming, creating DB entry
static QString STATE_ERROR("ERROR");
static QString STATE_COMPLETELY_DONE("COMPLETELY_DONE");

Gui::Gui(const char *urlbase, const char *uuid, QWidget *parent)
    : QDialog(parent)
    , _nam(new QNetworkAccessManager(this))
    , _urlStatus(QString(urlbase).append("status/").append(uuid))
    , _urlAbort(QString(urlbase).append("abort/").append(uuid))
    , _urlFinish(QString(urlbase).append("finish/").append(uuid))
    , _denyInteraction(false)
    , _tmrStatus(new QTimer(this))
    , _status(nullptr)
{
    _nam->setAutoDeleteReplies(true);
    _nam->setTransferTimeout(5000);
    setupUi();
    auto flags = windowFlags();
    setWindowFlags(flags & ~(Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint));
    _tmrStatus->start(2500);
    QObject::connect(_tmrStatus, &QTimer::timeout, [this]() {
        QNetworkReply *reply = _nam->get(QNetworkRequest(_urlStatus));
        QObject::connect(reply, &QNetworkReply::finished, [reply, this]() {
            // JSON
            auto repData = reply->readAll();
            auto doc = QJsonDocument::fromJson(repData);
            if (doc.isEmpty() || !doc.isObject()) {
                QString str = QString::fromUtf8(repData);
                int code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
                if (code == 404) {
                    _remoteState = STATE_ERROR;
                    _status->setText(tr("Sitzung dem Server nicht bekannt"));
                    updateButtons();
                } else if (str.length() > 0 && str.length() < 160) {
                    _status->setText(QLatin1String("Server: ") + str);
                } else {
                    _status->setText(tr("Fehler beim Statusabruf"));
                }
                return;
            }
            // Got JSON
            auto outer = doc.object();
            QJsonValue state = outer[QLatin1String("state")];
            if (state.isString()) {
                auto ns = state.toString();
                if (ns != _remoteState) {
                    _status->setText(ns);
                    _remoteState = ns;
                    updateButtons();
                }
            }
            QJsonValue tasks = outer[QLatin1String("tasks")];
            if (tasks.isArray()) {
                auto arr = tasks.toArray();
                for (auto it : arr) {
                    if (!it.isObject())
                        continue;
                    auto o = it.toObject();
                    auto jt = o.value(QLatin1String("title"));
                    if (!jt.isString())
                        continue;
                    auto jp = o.value(QLatin1String("percent"));
                    auto je = o.value(QLatin1String("error"));
                    QString title = jt.toString();
                    QString err = je.isString() ? je.toString() : QLatin1String("");
                    int percent = err.isEmpty() && jp.isDouble() ? (int)jp.toDouble() : -1;
                    Progress *item = _items.value(title);
                    if (item == nullptr) {
                        if (_items.size() > 10)
                            continue;
                        item = new Progress(title, this);
                        _itemBox->addWidget(item);
                        _items.insert(title, item);
                    }
                    item->setProgress(percent);
                    item->setCaption(title + QLatin1String(" ") + err);
                }
            }
        });
    });
}

Gui::~Gui()
{
    _nam->blockSignals(true);
}

void Gui::setupUi()
{
    QVBoxLayout *outerVert = new QVBoxLayout(this);
    outerVert->setMargin(3);

    _itemBox = new QVBoxLayout;
    _itemBox->setObjectName("ItemLayout");
    outerVert->addLayout(_itemBox);

    QLayout *buttons = new QHBoxLayout;
    buttons->setObjectName("ButtonLayout");
    outerVert->addLayout(buttons);

    _status = new QLabel("-", this);
    _itemBox->addWidget(_status);

    _btnAbort = new QPushButton(tr("Abbrechen/Löschen"), this);
    buttons->addWidget(_btnAbort);
    QObject::connect(_btnAbort, &QPushButton::clicked, this, &Gui::pushedCancel);

    _btnOk = new QPushButton(tr("Änderungen permanent behalten"), this);
    buttons->addWidget(_btnOk);
    QObject::connect(_btnOk, &QPushButton::clicked, this, &Gui::pushedOk);
}

void Gui::updateButtons()
{
    if (_remoteState == STATE_COMPLETELY_DONE || _remoteState == STATE_PROCESSING) {
        if (_remoteState == STATE_COMPLETELY_DONE) {
            _tmrStatus->stop();
        }
        _btnAbort->setEnabled(false);
        _btnOk->setEnabled(true);
        _btnOk->setText(tr("Schließen"));
    } else if (_remoteState == STATE_UPLOAD_DONE) {
        _btnAbort->setEnabled(true);
        _btnOk->setEnabled(true);
    } else if (_remoteState == STATE_ERROR) {
        _btnAbort->setEnabled(true);
        _btnOk->setEnabled(false);
        _tmrStatus->stop();
        _btnAbort->setText(tr("Schließen"));
    } else {
        _btnAbort->setEnabled(true);
        _btnOk->setEnabled(false);
    }
}

void Gui::pushedCancel(bool)
{
    if (_remoteState == STATE_ERROR) {
        this->close();
        return;
    }
    if (_denyInteraction)
        return;
    if (_remoteState != STATE_ERROR) {
        int ret = QMessageBox::question(this, tr("Bestätigung"), tr("Wollen Sie diesen Bearbeitungsvorgang wirklich abbrechen?"));
        if (ret == QMessageBox::No)
            return;
    }
    if (_remoteState == STATE_COMPLETELY_DONE) {
        _btnAbort->setEnabled(false);
        return;
    }
    _denyInteraction = true;
    QNetworkReply *reply = _nam->post(QNetworkRequest(_urlAbort), QByteArray());
    connect(reply, &QNetworkReply::finished, [reply, this]() {
        QString msg = QString::fromUtf8(reply->readAll());
        if (reply->error() != QNetworkReply::NoError) {
            QMessageBox::critical(this, tr("Fehler"), msg);
            _denyInteraction = false;
        } else {
            _btnAbort->setEnabled(false);
            _btnOk->setEnabled(false);
            _remoteState = STATE_ERROR;
            QMessageBox::information(this, tr("Ergebnis"), msg);
            QTimer::singleShot(2000, [this]() {
                this->close();
            });
        }
    });
}

void Gui::pushedOk(bool)
{
    if (_remoteState == STATE_COMPLETELY_DONE || _remoteState == STATE_PROCESSING) {
        this->close();
        return;
    }
    if (_denyInteraction)
        return;
    if (_remoteState == STATE_UPLOAD_DONE) {
        int ret = QMessageBox::question(this, tr("Bestätigung"), tr("Die Änderungen an der VM werden dauerhaft beibehalten. Fortfahren?"));
        if (ret == QMessageBox::No)
            return;
    }
    _denyInteraction = true;
    QNetworkReply *reply = _nam->get(QNetworkRequest(_urlFinish));
    connect(reply, &QNetworkReply::finished, [reply, this]() {
        QString msg = QString::fromUtf8(reply->readAll());
        int code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
        if (reply->error() != QNetworkReply::NoError || (code < 200 || code >= 300)) {
            QMessageBox::critical(this, tr("Fehler"), msg);
            _denyInteraction = false;
        } else {
            _remoteState = STATE_PROCESSING;
            _status->setText(tr("Finalisiere..."));
            updateButtons();
        }
    });
}