Merge branch 'fix/auto-update-link' into 'main'

fix(updater): Link to Commits

See merge request citron/emulator!119
This commit is contained in:
Zephyron
2025-10-26 15:36:21 +10:00
2 changed files with 69 additions and 53 deletions

View File

@@ -10,26 +10,36 @@
#include <QDesktopServices> #include <QDesktopServices>
#include <QMessageBox> #include <QMessageBox>
#include <QProcess> #include <QProcess>
#include <QRegularExpression>
#include <QTimer> #include <QTimer>
#include <QUrl> #include <QUrl>
namespace Updater { namespace Updater {
// Helper function to format the date and time // Helper function to format the date and time nicely.
QString FormatDateTimeString(const std::string& iso_string) { QString FormatDateTimeString(const std::string& iso_string) {
if (iso_string.empty() || iso_string == "Unknown") { if (iso_string.empty() || iso_string == "Unknown") {
return QStringLiteral("Unknown"); return QStringLiteral("Unknown");
} }
// Parse the ISO 8601 date string provided by the GitHub API.
QDateTime date_time = QDateTime::fromString(QString::fromStdString(iso_string), Qt::ISODate); QDateTime date_time = QDateTime::fromString(QString::fromStdString(iso_string), Qt::ISODate);
if (!date_time.isValid()) { if (!date_time.isValid()) {
return QString::fromStdString(iso_string); // Fallback to original if parsing fails. return QString::fromStdString(iso_string);
} }
// Convert from UTC (which the 'Z' indicates) to the user's local time
// and format it in a friendly, readable way.
return date_time.toLocalTime().toString(QStringLiteral("MMMM d, yyyy 'at' hh:mm AP")); return date_time.toLocalTime().toString(QStringLiteral("MMMM d, yyyy 'at' hh:mm AP"));
} }
// Helper function to reformat the changelog with the correct commit link.
QString FormatChangelog(const std::string& raw_changelog) {
QString changelog = QString::fromStdString(raw_changelog);
const QString new_url = QStringLiteral("https://git.citron-emu.org/citron/emulator/-/commits/main");
QRegularExpression regex(QStringLiteral("\\[\\`([0-9a-fA-F]{7,40})\\`\\]\\(.*?\\)"));
QString replacement = QStringLiteral("[`\\1`](%1)").arg(new_url);
changelog.replace(regex, replacement);
return changelog;
}
UpdaterDialog::UpdaterDialog(QWidget* parent) UpdaterDialog::UpdaterDialog(QWidget* parent)
: QDialog(parent), ui(std::make_unique<Ui::UpdaterDialog>()), : QDialog(parent), ui(std::make_unique<Ui::UpdaterDialog>()),
updater_service(std::make_unique<Updater::UpdaterService>(this)), updater_service(std::make_unique<Updater::UpdaterService>(this)),
@@ -38,6 +48,15 @@ UpdaterDialog::UpdaterDialog(QWidget* parent)
ui->setupUi(this); ui->setupUi(this);
// NEW: Disable the default link handling behavior of the QTextBrowser.
// This prevents it from trying to load the URL internally.
ui->changelogText->setOpenLinks(false);
// Manually handle link clicks to ensure they always open in an external browser.
connect(ui->changelogText, &QTextBrowser::anchorClicked, this, [](const QUrl& link) {
QDesktopServices::openUrl(link);
});
// Set up connections // Set up connections
connect(updater_service.get(), &Updater::UpdaterService::UpdateCheckCompleted, this, connect(updater_service.get(), &Updater::UpdaterService::UpdateCheckCompleted, this,
&UpdaterDialog::OnUpdateCheckCompleted); &UpdaterDialog::OnUpdateCheckCompleted);
@@ -185,7 +204,6 @@ void UpdaterDialog::OnRestartButtonClicked() {
void UpdaterDialog::SetupUI() { void UpdaterDialog::SetupUI() {
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
// MODIFIED: Use setMinimumSize to allow the window to be resized larger.
setMinimumSize(size()); setMinimumSize(size());
ui->currentVersionValue->setText(QString::fromStdString(updater_service->GetCurrentVersion())); ui->currentVersionValue->setText(QString::fromStdString(updater_service->GetCurrentVersion()));
@@ -218,7 +236,6 @@ void UpdaterDialog::ShowNoUpdateState(const Updater::UpdateInfo& update_info) {
ui->latestVersionValue->setText(QString::fromStdString(update_info.version)); ui->latestVersionValue->setText(QString::fromStdString(update_info.version));
// MODIFIED: Use the new helper function to format the release date.
ui->releaseDateValue->setText(FormatDateTimeString(update_info.release_date)); ui->releaseDateValue->setText(FormatDateTimeString(update_info.release_date));
ui->changelogGroup->setVisible(false); ui->changelogGroup->setVisible(false);
@@ -237,16 +254,14 @@ void UpdaterDialog::ShowUpdateAvailableState() {
ui->statusLabel->setText(QStringLiteral("A new version of Citron is available for download.")); ui->statusLabel->setText(QStringLiteral("A new version of Citron is available for download."));
ui->latestVersionValue->setText(QString::fromStdString(current_update_info.version)); ui->latestVersionValue->setText(QString::fromStdString(current_update_info.version));
// MODIFIED: Use the new helper function to format the release date.
ui->releaseDateValue->setText(FormatDateTimeString(current_update_info.release_date)); ui->releaseDateValue->setText(FormatDateTimeString(current_update_info.release_date));
if (!current_update_info.changelog.empty()) { if (!current_update_info.changelog.empty()) {
// MODIFIED: Use setMarkdown to render the changelog with formatting and links. ui->changelogText->setMarkdown(FormatChangelog(current_update_info.changelog));
ui->changelogText->setMarkdown(QString::fromStdString(current_update_info.changelog));
ui->changelogGroup->setVisible(true);
} else { } else {
ui->changelogGroup->setVisible(false); ui->changelogText->setText(tr("No changelog information was provided for this update."));
} }
ui->changelogGroup->setVisible(true);
#ifdef __linux__ #ifdef __linux__
if (current_update_info.download_options.size() > 1) { if (current_update_info.download_options.size() > 1) {

View File

@@ -7,28 +7,29 @@
#include <memory> #include <memory>
#include "citron/updater/updater_service.h" #include "citron/updater/updater_service.h"
// Forward declare QString for the helper function.
class QString; class QString;
namespace Ui { namespace Ui {
class UpdaterDialog; class UpdaterDialog;
} }
namespace Updater { namespace Updater {
// Add the declaration for the date formatting helper function. // Declarations for helper functions
QString FormatDateTimeString(const std::string& iso_string); QString FormatDateTimeString(const std::string& iso_string);
class UpdaterDialog : public QDialog { QString FormatChangelog(const std::string& raw_changelog);
class UpdaterDialog : public QDialog {
Q_OBJECT Q_OBJECT
public: public:
explicit UpdaterDialog(QWidget* parent = nullptr); explicit UpdaterDialog(QWidget* parent = nullptr);
~UpdaterDialog() override; ~UpdaterDialog() override;
void CheckForUpdates(const std::string& update_url); void CheckForUpdates(const std::string& update_url);
private slots: private slots:
void OnUpdateCheckCompleted(bool has_update, const Updater::UpdateInfo& update_info); void OnUpdateCheckCompleted(bool has_update, const Updater::UpdateInfo& update_info);
void OnUpdateDownloadProgress(int percentage, qint64 bytes_received, qint64 bytes_total); void OnUpdateDownloadProgress(int percentage, qint64 bytes_received, qint64 bytes_total);
void OnUpdateInstallProgress(int percentage, const QString& current_file); void OnUpdateInstallProgress(int percentage, const QString& current_file);
@@ -39,7 +40,7 @@ private slots:
void OnCloseButtonClicked(); void OnCloseButtonClicked();
void OnRestartButtonClicked(); void OnRestartButtonClicked();
private: private:
enum class State { Checking, NoUpdate, UpdateAvailable, Downloading, Installing, Completed, Error }; enum class State { Checking, NoUpdate, UpdateAvailable, Downloading, Installing, Completed, Error };
void SetupUI(); void SetupUI();
@@ -60,6 +61,6 @@ private:
qint64 total_download_size; qint64 total_download_size;
qint64 downloaded_bytes; qint64 downloaded_bytes;
QTimer* progress_timer; QTimer* progress_timer;
}; };
} // namespace Updater } // namespace Updater