porymap/src/ui/updatepromoter.cpp

191 lines
7.3 KiB
C++
Raw Normal View History

2024-01-21 07:00:28 +00:00
#include "updatepromoter.h"
#include "ui_updatepromoter.h"
#include "log.h"
#include "config.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QDesktopServices>
2024-01-24 16:56:04 +00:00
#include <QTimer>
2024-01-21 07:00:28 +00:00
2024-01-24 16:56:04 +00:00
UpdatePromoter::UpdatePromoter(QWidget *parent, NetworkAccessManager *manager)
2024-01-21 07:00:28 +00:00
: QDialog(parent),
ui(new Ui::UpdatePromoter),
manager(manager)
{
ui->setupUi(this);
// Set up "Do not alert me" check box
this->updatePreferences();
2024-01-24 16:56:04 +00:00
ui->checkBox_StopAlerts->setVisible(false);
2024-01-21 07:00:28 +00:00
connect(ui->checkBox_StopAlerts, &QCheckBox::stateChanged, [this](int state) {
porymapConfig.checkForUpdates = (state != Qt::Checked);
2024-01-21 07:00:28 +00:00
emit this->changedPreferences();
});
// Set up button box
2024-01-24 16:56:04 +00:00
this->button_Retry = ui->buttonBox->button(QDialogButtonBox::Retry);
2024-01-21 07:00:28 +00:00
this->button_Downloads = ui->buttonBox->addButton("Go to Downloads...", QDialogButtonBox::ActionRole);
2024-01-24 16:56:04 +00:00
ui->buttonBox->button(QDialogButtonBox::Close)->setDefault(true);
2024-01-21 07:00:28 +00:00
connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &UpdatePromoter::dialogButtonClicked);
this->resetDialog();
}
UpdatePromoter::~UpdatePromoter() {
delete ui;
}
2024-01-21 07:00:28 +00:00
void UpdatePromoter::resetDialog() {
this->button_Downloads->setEnabled(false);
2024-01-24 16:56:04 +00:00
2024-01-21 07:00:28 +00:00
ui->text_Changelog->setVisible(false);
ui->label_Warning->setVisible(false);
2024-01-24 16:56:04 +00:00
ui->label_Status->setText("");
2024-01-21 07:00:28 +00:00
this->changelog = QString();
2024-01-24 16:56:04 +00:00
this->downloadUrl = QString();
this->newVersion = QVersionNumber();
2024-01-24 16:56:04 +00:00
this->foundReleases = false;
this->visitedUrls.clear();
2024-01-21 07:00:28 +00:00
}
void UpdatePromoter::checkForUpdates() {
2024-01-24 16:56:04 +00:00
// If the Retry button is disabled, making requests is disabled
if (!this->button_Retry->isEnabled())
2024-01-21 07:00:28 +00:00
return;
2024-01-24 16:56:04 +00:00
2024-01-21 07:00:28 +00:00
this->resetDialog();
2024-01-24 16:56:04 +00:00
this->button_Retry->setEnabled(false);
ui->label_Status->setText("Checking for updates...");
2024-01-21 07:00:28 +00:00
2024-01-24 16:56:04 +00:00
// We could use the URL ".../releases/latest" to retrieve less data, but this would run into problems if the
2024-01-21 07:00:28 +00:00
// most recent item on the releases page is not actually a new release (like the static windows build).
// By getting all releases we can also present a multi-version changelog of all changes since the host release.
2024-01-24 16:56:04 +00:00
static const QUrl url("https://api.github.com/repos/huderlem/porymap/releases");
this->get(url);
}
void UpdatePromoter::get(const QUrl &url) {
this->visitedUrls.insert(url);
auto reply = this->manager->get(url);
connect(reply, &NetworkReplyData::finished, [this, reply] () {
if (!reply->errorString().isEmpty()) {
this->error(reply->errorString(), reply->retryAfter());
2024-01-21 07:00:28 +00:00
} else {
2024-01-24 16:56:04 +00:00
this->processWebpage(QJsonDocument::fromJson(reply->body()), reply->nextUrl());
2024-01-21 07:00:28 +00:00
}
2024-01-24 16:56:04 +00:00
reply->deleteLater();
2024-01-21 07:00:28 +00:00
});
}
// Read all the items on the releases page, ignoring entries without a version identifier tag.
// Objects in the releases page data are sorted newest to oldest.
2024-01-24 16:56:04 +00:00
void UpdatePromoter::processWebpage(const QJsonDocument &data, const QUrl &nextUrl) {
2024-01-21 07:00:28 +00:00
const QJsonArray releases = data.array();
2024-01-24 16:56:04 +00:00
int i;
for (i = 0; i < releases.size(); i++) {
2024-01-21 07:00:28 +00:00
auto release = releases.at(i).toObject();
// Convert tag string to version numbers
const QString tagName = release.value("tag_name").toString();
const QVersionNumber version = QVersionNumber::fromString(tagName);
if (version.segmentCount() != 3) continue;
2024-01-21 07:00:28 +00:00
// We've found a valid release tag. If the version number is not newer than the host version then we can stop looking at releases.
2024-01-24 16:56:04 +00:00
this->foundReleases = true;
if (porymapVersion >= version)
2024-01-21 07:00:28 +00:00
break;
const QString description = release.value("body").toString();
2024-01-24 16:56:04 +00:00
if (description.isEmpty()) {
2024-01-21 07:00:28 +00:00
// If the release was published very recently it won't have a description yet, in which case don't tell the user about it yet.
continue;
}
2024-01-24 16:56:04 +00:00
if (this->downloadUrl.isEmpty()) {
2024-01-21 07:00:28 +00:00
// This is the first (newest) release we've found. Record its URL for download.
2024-01-24 16:56:04 +00:00
const QUrl url = QUrl(release.value("html_url").toString());
if (url.isEmpty()) {
// If there's no URL, something has gone wrong and we should skip this release.
continue;
}
this->downloadUrl = url;
this->newVersion = version;
2024-01-21 07:00:28 +00:00
}
// Record the changelog of this release so we can show all changes since the host release.
this->changelog.append(QString("## %1\n%2\n\n").arg(tagName).arg(description));
}
2024-01-24 16:56:04 +00:00
// If we read the entire page then we didn't find a release as old as the host version.
// Keep looking on the second page, there might still be new releases there.
if (i == releases.size() && !nextUrl.isEmpty() && !this->visitedUrls.contains(nextUrl)) {
this->get(nextUrl);
return;
}
if (!this->foundReleases) {
2024-01-21 07:00:28 +00:00
// We retrieved the webpage but didn't successfully parse any releases.
2024-01-24 16:56:04 +00:00
this->error("Error parsing releases webpage");
2024-01-21 07:00:28 +00:00
return;
}
2024-01-24 16:56:04 +00:00
// Populate dialog with result
ui->text_Changelog->setMarkdown(this->changelog);
ui->text_Changelog->setVisible(!this->changelog.isEmpty());
2024-01-24 16:56:04 +00:00
this->button_Downloads->setEnabled(!this->downloadUrl.isEmpty());
this->button_Retry->setEnabled(true);
if (!this->newVersion.isNull()) {
ui->label_Status->setText("A new version of Porymap is available!");
ui->label_Warning->setVisible(this->newVersion.majorVersion() > porymapVersion.majorVersion());
// Alert the user about the new version if the dialog wasn't already open.
// Show the window, but also show the option to turn off automatic alerts in the future.
// We only show this alert once for a given release.
if (!this->isVisible() && this->newVersion > porymapConfig.lastUpdateCheckVersion) {
ui->checkBox_StopAlerts->setVisible(true);
this->show();
}
porymapConfig.lastUpdateCheckVersion = this->newVersion;
} else {
ui->label_Status->setText("Your version of Porymap is up to date!");
ui->label_Warning->setVisible(false);
2024-01-24 16:56:04 +00:00
}
}
void UpdatePromoter::error(const QString &err, const QDateTime retryAfter) {
2024-01-21 07:00:28 +00:00
const QString message = QString("Failed to check for version update: %1").arg(err);
2024-01-24 16:56:04 +00:00
ui->label_Status->setText(message);
if (!this->isVisible())
2024-01-21 07:00:28 +00:00
logWarn(message);
// If a "retry after" date/time is provided, disable the Retry button until then.
// Otherwise users are allowed to retry after an error.
auto timeUntil = QDateTime::currentDateTime().msecsTo(retryAfter);
if (timeUntil > 0) {
this->button_Retry->setEnabled(false);
QTimer::singleShot(timeUntil, Qt::VeryCoarseTimer, [this]() {
this->button_Retry->setEnabled(true);
});
} else {
this->button_Retry->setEnabled(true);
}
2024-01-21 07:00:28 +00:00
}
void UpdatePromoter::updatePreferences() {
const QSignalBlocker blocker(ui->checkBox_StopAlerts);
ui->checkBox_StopAlerts->setChecked(!porymapConfig.checkForUpdates);
2024-01-21 07:00:28 +00:00
}
void UpdatePromoter::dialogButtonClicked(QAbstractButton *button) {
2024-01-24 16:56:04 +00:00
if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::RejectRole) {
2024-01-21 07:00:28 +00:00
this->close();
2024-01-24 16:56:04 +00:00
} else if (button == this->button_Retry) {
2024-01-21 07:00:28 +00:00
this->checkForUpdates();
2024-01-24 16:56:04 +00:00
} else if (button == this->button_Downloads) {
QDesktopServices::openUrl(this->downloadUrl);
2024-01-21 07:00:28 +00:00
}
}