Only alert user at most once per new release

This commit is contained in:
GriffinR 2024-02-07 15:35:11 -05:00
parent e76729ce62
commit 73b5c0501d
8 changed files with 54 additions and 45 deletions

View file

@ -10,9 +10,12 @@
#include <QMultiMap> #include <QMultiMap>
#include <QDateTime> #include <QDateTime>
#include <QUrl> #include <QUrl>
#include <QVersionNumber>
#include "events.h" #include "events.h"
static const QVersionNumber porymapVersion = QVersionNumber::fromString(PORYMAP_VERSION);
// In both versions the default new map border is a generic tree // In both versions the default new map border is a generic tree
#define DEFAULT_BORDER_RSE (QList<uint16_t>{0x1D4, 0x1D5, 0x1DC, 0x1DD}) #define DEFAULT_BORDER_RSE (QList<uint16_t>{0x1D4, 0x1D5, 0x1DC, 0x1DD})
#define DEFAULT_BORDER_FRLG (QList<uint16_t>{0x14, 0x15, 0x1C, 0x1D}) #define DEFAULT_BORDER_FRLG (QList<uint16_t>{0x14, 0x15, 0x1C, 0x1D})
@ -78,6 +81,7 @@ public:
this->warpBehaviorWarningDisabled = false; this->warpBehaviorWarningDisabled = false;
this->checkForUpdates = true; this->checkForUpdates = true;
this->lastUpdateCheckTime = QDateTime(); this->lastUpdateCheckTime = QDateTime();
this->lastUpdateCheckVersion = porymapVersion;
this->rateLimitTimes.clear(); this->rateLimitTimes.clear();
} }
void addRecentProject(QString project); void addRecentProject(QString project);
@ -112,6 +116,7 @@ public:
void setWarpBehaviorWarningDisabled(bool disabled); void setWarpBehaviorWarningDisabled(bool disabled);
void setCheckForUpdates(bool enabled); void setCheckForUpdates(bool enabled);
void setLastUpdateCheckTime(QDateTime time); void setLastUpdateCheckTime(QDateTime time);
void setLastUpdateCheckVersion(QVersionNumber version);
void setRateLimitTimes(QMap<QUrl, QDateTime> map); void setRateLimitTimes(QMap<QUrl, QDateTime> map);
QString getRecentProject(); QString getRecentProject();
QStringList getRecentProjects(); QStringList getRecentProjects();
@ -145,6 +150,7 @@ public:
bool getWarpBehaviorWarningDisabled(); bool getWarpBehaviorWarningDisabled();
bool getCheckForUpdates(); bool getCheckForUpdates();
QDateTime getLastUpdateCheckTime(); QDateTime getLastUpdateCheckTime();
QVersionNumber getLastUpdateCheckVersion();
QMap<QUrl, QDateTime> getRateLimitTimes(); QMap<QUrl, QDateTime> getRateLimitTimes();
protected: protected:
virtual QString getConfigFilepath() override; virtual QString getConfigFilepath() override;
@ -196,6 +202,7 @@ private:
bool warpBehaviorWarningDisabled; bool warpBehaviorWarningDisabled;
bool checkForUpdates; bool checkForUpdates;
QDateTime lastUpdateCheckTime; QDateTime lastUpdateCheckTime;
QVersionNumber lastUpdateCheckVersion;
QMap<QUrl, QDateTime> rateLimitTimes; QMap<QUrl, QDateTime> rateLimitTimes;
}; };

View file

@ -5,6 +5,7 @@
#include <QDialog> #include <QDialog>
#include <QPushButton> #include <QPushButton>
#include <QVersionNumber>
namespace Ui { namespace Ui {
class UpdatePromoter; class UpdatePromoter;
@ -29,7 +30,7 @@ private:
QString changelog; QString changelog;
QUrl downloadUrl; QUrl downloadUrl;
bool breakingChanges; QVersionNumber newVersion;
bool foundReleases; bool foundReleases;
QSet<QUrl> visitedUrls; // Prevent infinite redirection QSet<QUrl> visitedUrls; // Prevent infinite redirection
@ -38,7 +39,6 @@ private:
void get(const QUrl &url); void get(const QUrl &url);
void processWebpage(const QJsonDocument &data, const QUrl &nextUrl); void processWebpage(const QJsonDocument &data, const QUrl &nextUrl);
void error(const QString &err, const QDateTime time = QDateTime()); void error(const QString &err, const QDateTime time = QDateTime());
bool isNewerVersion(int major, int minor, int patch);
private slots: private slots:
void dialogButtonClicked(QAbstractButton *button); void dialogButtonClicked(QAbstractButton *button);

View file

@ -14,14 +14,8 @@ RC_ICONS = resources/icons/porymap-icon-2.ico
ICON = resources/icons/porymap.icns ICON = resources/icons/porymap.icns
QMAKE_CXXFLAGS += -std=c++17 -Wall QMAKE_CXXFLAGS += -std=c++17 -Wall
QMAKE_TARGET_BUNDLE_PREFIX = com.pret QMAKE_TARGET_BUNDLE_PREFIX = com.pret
VERSION_MAJOR = 5 VERSION = 5.3.0
VERSION_MINOR = 3 DEFINES += PORYMAP_VERSION=\\\"$$VERSION\\\"
VERSION_PATCH = 0
VERSION = $${VERSION_MAJOR}.$${VERSION_MINOR}.$${VERSION_PATCH}
DEFINES += PORYMAP_VERSION_MAJOR=$$VERSION_MAJOR \
PORYMAP_VERSION_MINOR=$$VERSION_MINOR \
PORYMAP_VERSION_PATCH=$$VERSION_PATCH \
PORYMAP_VERSION=\\\"$$VERSION\\\"
SOURCES += src/core/block.cpp \ SOURCES += src/core/block.cpp \
src/core/bitpacker.cpp \ src/core/bitpacker.cpp \

View file

@ -411,6 +411,14 @@ void PorymapConfig::parseConfigKeyValue(QString key, QString value) {
this->checkForUpdates = getConfigBool(key, value); this->checkForUpdates = getConfigBool(key, value);
} else if (key == "last_update_check_time") { } else if (key == "last_update_check_time") {
this->lastUpdateCheckTime = QDateTime::fromString(value).toLocalTime(); this->lastUpdateCheckTime = QDateTime::fromString(value).toLocalTime();
} else if (key == "last_update_check_version") {
auto version = QVersionNumber::fromString(value);
if (version.segmentCount() != 3) {
logWarn(QString("Invalid config value for %1: '%2'. Must be 3 numbers separated by '.'").arg(key).arg(value));
this->lastUpdateCheckVersion = porymapVersion;
} else {
this->lastUpdateCheckVersion = version;
}
} else if (key.startsWith("rate_limit_time/")) { } else if (key.startsWith("rate_limit_time/")) {
static const QRegularExpression regex("\\brate_limit_time/(?<url>.+)"); static const QRegularExpression regex("\\brate_limit_time/(?<url>.+)");
QRegularExpressionMatch match = regex.match(key); QRegularExpressionMatch match = regex.match(key);
@ -465,6 +473,7 @@ QMap<QString, QString> PorymapConfig::getKeyValueMap() {
map.insert("warp_behavior_warning_disabled", QString::number(this->warpBehaviorWarningDisabled)); map.insert("warp_behavior_warning_disabled", QString::number(this->warpBehaviorWarningDisabled));
map.insert("check_for_updates", QString::number(this->checkForUpdates)); map.insert("check_for_updates", QString::number(this->checkForUpdates));
map.insert("last_update_check_time", this->lastUpdateCheckTime.toUTC().toString()); map.insert("last_update_check_time", this->lastUpdateCheckTime.toUTC().toString());
map.insert("last_update_check_version", this->lastUpdateCheckVersion.toString());
for (auto i = this->rateLimitTimes.cbegin(), end = this->rateLimitTimes.cend(); i != end; i++){ for (auto i = this->rateLimitTimes.cbegin(), end = this->rateLimitTimes.cend(); i != end; i++){
// Only include rate limit times that are still active (i.e., in the future) // Only include rate limit times that are still active (i.e., in the future)
const QDateTime time = i.value(); const QDateTime time = i.value();
@ -664,6 +673,11 @@ void PorymapConfig::setLastUpdateCheckTime(QDateTime time) {
this->save(); this->save();
} }
void PorymapConfig::setLastUpdateCheckVersion(QVersionNumber version) {
this->lastUpdateCheckVersion = version;
this->save();
}
void PorymapConfig::setRateLimitTimes(QMap<QUrl, QDateTime> map) { void PorymapConfig::setRateLimitTimes(QMap<QUrl, QDateTime> map) {
this->rateLimitTimes = map; this->rateLimitTimes = map;
this->save(); this->save();
@ -831,6 +845,10 @@ QDateTime PorymapConfig::getLastUpdateCheckTime() {
return this->lastUpdateCheckTime; return this->lastUpdateCheckTime;
} }
QVersionNumber PorymapConfig::getLastUpdateCheckVersion() {
return this->lastUpdateCheckVersion;
}
QMap<QUrl, QDateTime> PorymapConfig::getRateLimitTimes() { QMap<QUrl, QDateTime> PorymapConfig::getRateLimitTimes() {
return this->rateLimitTimes; return this->rateLimitTimes;
} }

View file

@ -65,7 +65,7 @@ MainWindow::MainWindow(QWidget *parent) :
ui->setupUi(this); ui->setupUi(this);
cleanupLargeLog(); cleanupLargeLog();
logInfo(QString("Launching Porymap v%1").arg(PORYMAP_VERSION)); logInfo(QString("Launching Porymap v%1").arg(QCoreApplication::applicationVersion()));
this->initWindow(); this->initWindow();
if (porymapConfig.getReopenOnLaunch() && this->openProject(porymapConfig.getRecentProject(), true)) if (porymapConfig.getReopenOnLaunch() && this->openProject(porymapConfig.getRecentProject(), true))

View file

@ -78,9 +78,9 @@ void Scripting::populateGlobalObject(MainWindow *mainWindow) {
// Get version numbers // Get version numbers
QJSValue version = instance->engine->newObject(); QJSValue version = instance->engine->newObject();
version.setProperty("major", PORYMAP_VERSION_MAJOR); version.setProperty("major", porymapVersion.majorVersion());
version.setProperty("minor", PORYMAP_VERSION_MINOR); version.setProperty("minor", porymapVersion.minorVersion());
version.setProperty("patch", PORYMAP_VERSION_PATCH); version.setProperty("patch", porymapVersion.microVersion());
constants.setProperty("version", version); constants.setProperty("version", version);
// Get basic tileset information // Get basic tileset information

View file

@ -8,7 +8,7 @@ AboutPorymap::AboutPorymap(QWidget *parent) :
{ {
ui->setupUi(this); ui->setupUi(this);
this->ui->label_Version->setText(QString("Version %1 - %2").arg(PORYMAP_VERSION).arg(QStringLiteral(__DATE__))); this->ui->label_Version->setText(QString("Version %1 - %2").arg(QCoreApplication::applicationVersion()).arg(QStringLiteral(__DATE__)));
this->ui->textBrowser->setSource(QUrl("qrc:/CHANGELOG.md")); this->ui->textBrowser->setSource(QUrl("qrc:/CHANGELOG.md"));
} }

View file

@ -43,7 +43,7 @@ void UpdatePromoter::resetDialog() {
this->changelog = QString(); this->changelog = QString();
this->downloadUrl = QString(); this->downloadUrl = QString();
this->breakingChanges = false; this->newVersion = QVersionNumber();
this->foundReleases = false; this->foundReleases = false;
this->visitedUrls.clear(); this->visitedUrls.clear();
} }
@ -87,19 +87,12 @@ void UpdatePromoter::processWebpage(const QJsonDocument &data, const QUrl &nextU
// Convert tag string to version numbers // Convert tag string to version numbers
const QString tagName = release.value("tag_name").toString(); const QString tagName = release.value("tag_name").toString();
const QStringList tag = tagName.split("."); const QVersionNumber version = QVersionNumber::fromString(tagName);
if (tag.length() != 3) continue; if (version.segmentCount() != 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;
// 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. // 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.
this->foundReleases = true; this->foundReleases = true;
if (!this->isNewerVersion(major, minor, patch)) if (porymapVersion >= version)
break; break;
const QString description = release.value("body").toString(); const QString description = release.value("body").toString();
@ -116,7 +109,7 @@ void UpdatePromoter::processWebpage(const QJsonDocument &data, const QUrl &nextU
continue; continue;
} }
this->downloadUrl = url; this->downloadUrl = url;
this->breakingChanges = (major > PORYMAP_VERSION_MAJOR); this->newVersion = version;
} }
// Record the changelog of this release so we can show all changes since the host release. // Record the changelog of this release so we can show all changes since the host release.
@ -137,21 +130,26 @@ void UpdatePromoter::processWebpage(const QJsonDocument &data, const QUrl &nextU
} }
// Populate dialog with result // Populate dialog with result
bool updateAvailable = !this->changelog.isEmpty();
ui->label_Status->setText(updateAvailable ? "A new version of Porymap is available!"
: "Your version of Porymap is up to date!");
ui->label_Warning->setVisible(this->breakingChanges);
ui->text_Changelog->setMarkdown(this->changelog); ui->text_Changelog->setMarkdown(this->changelog);
ui->text_Changelog->setVisible(updateAvailable); ui->text_Changelog->setVisible(!this->changelog.isEmpty());
this->button_Downloads->setEnabled(!this->downloadUrl.isEmpty()); this->button_Downloads->setEnabled(!this->downloadUrl.isEmpty());
this->button_Retry->setEnabled(true); 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 if there's a new update available and the dialog wasn't already open. // 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. // Show the window, but also show the option to turn off automatic alerts in the future.
if (updateAvailable && !this->isVisible()) { // We only show this alert once for a given release.
if (!this->isVisible() && this->newVersion > porymapConfig.getLastUpdateCheckVersion()) {
ui->checkBox_StopAlerts->setVisible(true); ui->checkBox_StopAlerts->setVisible(true);
this->show(); this->show();
} }
porymapConfig.setLastUpdateCheckVersion(this->newVersion);
} else {
ui->label_Status->setText("Your version of Porymap is up to date!");
ui->label_Warning->setVisible(false);
}
} }
void UpdatePromoter::error(const QString &err, const QDateTime retryAfter) { void UpdatePromoter::error(const QString &err, const QDateTime retryAfter) {
@ -173,14 +171,6 @@ void UpdatePromoter::error(const QString &err, const QDateTime retryAfter) {
} }
} }
bool UpdatePromoter::isNewerVersion(int major, int minor, int patch) {
if (major != PORYMAP_VERSION_MAJOR)
return major > PORYMAP_VERSION_MAJOR;
if (minor != PORYMAP_VERSION_MINOR)
return minor > PORYMAP_VERSION_MINOR;
return patch > PORYMAP_VERSION_PATCH;
}
void UpdatePromoter::updatePreferences() { void UpdatePromoter::updatePreferences() {
const QSignalBlocker blocker(ui->checkBox_StopAlerts); const QSignalBlocker blocker(ui->checkBox_StopAlerts);
ui->checkBox_StopAlerts->setChecked(!porymapConfig.getCheckForUpdates()); ui->checkBox_StopAlerts->setChecked(!porymapConfig.getCheckForUpdates());