diff --git a/forms/mainwindow.ui b/forms/mainwindow.ui index d70180d4..ea7d340a 100644 --- a/forms/mainwindow.ui +++ b/forms/mainwindow.ui @@ -1715,7 +1715,7 @@ 0 0 100 - 30 + 16 @@ -1809,7 +1809,7 @@ 0 0 100 - 30 + 16 @@ -1903,7 +1903,7 @@ 0 0 100 - 30 + 16 @@ -2003,7 +2003,7 @@ 0 0 100 - 30 + 16 @@ -2097,7 +2097,7 @@ 0 0 100 - 30 + 16 @@ -3112,6 +3112,7 @@ + @@ -3406,6 +3407,14 @@ Custom Scripts... + + + Check for Updates... + + + QAction::ApplicationSpecificRole + + diff --git a/include/mainwindow.h b/include/mainwindow.h index e95db003..7d512ef5 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -12,6 +12,7 @@ #include #include #include +#include #include "project.h" #include "orderedjson.h" #include "config.h" @@ -288,6 +289,7 @@ private slots: void on_spinBox_SelectedCollision_valueChanged(int collision); void on_actionRegion_Map_Editor_triggered(); void on_actionPreferences_triggered(); + void on_actionCheck_for_Updates_triggered(); void togglePreferenceSpecificUi(); void on_actionProject_Settings_triggered(); void on_actionCustom_Scripts_triggered(); @@ -296,6 +298,7 @@ private slots: public: Ui::MainWindow *ui; Editor *editor = nullptr; + QPointer networkAccessManager = nullptr; private: QLabel *label_MapRulerStatus = nullptr; @@ -396,6 +399,8 @@ private: QObjectList shortcutableObjects() const; void addCustomHeaderValue(QString key, QJsonValue value, bool isNew = false); int insertTilesetLabel(QStringList * list, QString label); + + void checkForUpdates(); }; enum MapListUserRoles { diff --git a/porymap.pro b/porymap.pro index ddb3f621..90072830 100644 --- a/porymap.pro +++ b/porymap.pro @@ -4,7 +4,7 @@ # #------------------------------------------------- -QT += core gui qml +QT += core gui qml network greaterThan(QT_MAJOR_VERSION, 4): QT += widgets diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 3a649062..7027ebca 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -242,6 +242,63 @@ void MainWindow::initExtraSignals() { label_MapRulerStatus->setAlignment(Qt::AlignCenter); label_MapRulerStatus->setTextFormat(Qt::PlainText); label_MapRulerStatus->setTextInteractionFlags(Qt::TextSelectableByMouse); + + // TODO: (if enabled) queue an automatic "Check for Updates" +} + +// TODO: Relocate +#include +#include +void MainWindow::on_actionCheck_for_Updates_triggered() { + checkForUpdates(); +} + +void MainWindow::checkForUpdates() { + if (!this->networkAccessManager) + this->networkAccessManager = new QNetworkAccessManager(this); + + // We could get ".../releases/latest" to retrieve less data, but this would run into problems if the + // most recent item on the releases page is not actually a new release (like the static windows build). + QNetworkRequest request(QUrl("https://api.github.com/repos/huderlem/porymap/releases")); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QNetworkReply * reply = this->networkAccessManager->get(request); + + connect(reply, &QNetworkReply::finished, [this, reply] { + QJsonDocument data = QJsonDocument::fromJson(reply->readAll()); + QJsonArray releases = data.array(); + + // Read all the items on the releases page, stopping when we find a tag that parses as a version identifier. + // Objects in the releases page data are sorted newest to oldest. Although I can't find a guarantee of this in + // GitHub's API documentation, this seems unlikely to change. + for (int i = 0; i < releases.size(); i++) { + auto release = releases.at(i).toObject(); + + const QStringList tag = release.value("tag_name").toString().split("."); + if (tag.length() != 3) continue; + + bool ok; + int major = tag.at(0).toInt(&ok); + if (!ok) continue; + int minor = tag.at(1).toInt(&ok); + if (!ok) continue; + int patch = tag.at(2).toInt(&ok); + if (!ok) continue; + + const QString downloadLink = release.value("html_url").toString(); + if (downloadLink.isEmpty()) continue; + + // We've found a valid release tag, we can stop reading. + logInfo(QString("Newest release is %1.%2.%3\n%4").arg(major).arg(minor).arg(patch).arg(downloadLink)); + + // If the release was published very recently it won't have a description yet, in which case don't tell the user. + const QString changelog = release.value("body").toString(); + if (changelog.isEmpty()) break; + + // TODO: Compare version to host version, then show appropriate dialog + break; + } + }); } void MainWindow::initEditor() {