fix: Autoupdater

This commit is contained in:
collecting
2025-10-24 10:39:04 +00:00
parent d5f1589341
commit 1a29259e33

View File

@@ -2,17 +2,17 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "citron/updater/updater_dialog.h" #include "citron/updater/updater_dialog.h"
#ifdef _WIN32
#include "ui_updater_dialog.h" #include "ui_updater_dialog.h"
#include <QApplication> #include <QApplication>
#include <QMessageBox>
#include <QCloseEvent> #include <QCloseEvent>
#include <QDesktopServices> #include <QDesktopServices>
#include <QUrl> #include <QMessageBox>
#include <QTimer>
#include <QProcess> #include <QProcess>
#include <QTimer>
#include <QUrl>
namespace Updater {
UpdaterDialog::UpdaterDialog(QWidget* parent) UpdaterDialog::UpdaterDialog(QWidget* parent)
: QDialog(parent), ui(std::make_unique<Ui::UpdaterDialog>()), : QDialog(parent), ui(std::make_unique<Ui::UpdaterDialog>()),
@@ -23,16 +23,16 @@ UpdaterDialog::UpdaterDialog(QWidget* parent)
ui->setupUi(this); ui->setupUi(this);
// Set up connections // Set up connections
connect(updater_service.get(), &Updater::UpdaterService::UpdateCheckCompleted, connect(updater_service.get(), &Updater::UpdaterService::UpdateCheckCompleted, this,
this, &UpdaterDialog::OnUpdateCheckCompleted); &UpdaterDialog::OnUpdateCheckCompleted);
connect(updater_service.get(), &Updater::UpdaterService::UpdateDownloadProgress, connect(updater_service.get(), &Updater::UpdaterService::UpdateDownloadProgress, this,
this, &UpdaterDialog::OnUpdateDownloadProgress); &UpdaterDialog::OnUpdateDownloadProgress);
connect(updater_service.get(), &Updater::UpdaterService::UpdateInstallProgress, connect(updater_service.get(), &Updater::UpdaterService::UpdateInstallProgress, this,
this, &UpdaterDialog::OnUpdateInstallProgress); &UpdaterDialog::OnUpdateInstallProgress);
connect(updater_service.get(), &Updater::UpdaterService::UpdateCompleted, connect(updater_service.get(), &Updater::UpdaterService::UpdateCompleted, this,
this, &UpdaterDialog::OnUpdateCompleted); &UpdaterDialog::OnUpdateCompleted);
connect(updater_service.get(), &Updater::UpdaterService::UpdateError, connect(updater_service.get(), &Updater::UpdaterService::UpdateError, this,
this, &UpdaterDialog::OnUpdateError); &UpdaterDialog::OnUpdateError);
// Set up UI connections // Set up UI connections
connect(ui->downloadButton, &QPushButton::clicked, this, &UpdaterDialog::OnDownloadButtonClicked); connect(ui->downloadButton, &QPushButton::clicked, this, &UpdaterDialog::OnDownloadButtonClicked);
@@ -48,9 +48,8 @@ UpdaterDialog::UpdaterDialog(QWidget* parent)
if (current_state == State::Downloading) { if (current_state == State::Downloading) {
ui->downloadInfoLabel->setText( ui->downloadInfoLabel->setText(
QStringLiteral("Downloaded: %1 / %2") QStringLiteral("Downloaded: %1 / %2")
.arg(FormatBytes(downloaded_bytes)) .arg(FormatBytes(downloaded_bytes))
.arg(FormatBytes(total_download_size)) .arg(FormatBytes(total_download_size)));
);
} }
}); });
} }
@@ -62,15 +61,6 @@ void UpdaterDialog::CheckForUpdates(const std::string& update_url) {
updater_service->CheckForUpdates(update_url); updater_service->CheckForUpdates(update_url);
} }
void UpdaterDialog::ShowUpdateAvailable(const Updater::UpdateInfo& update_info) {
current_update_info = update_info;
ShowUpdateAvailableState();
}
void UpdaterDialog::ShowUpdateChecking() {
ShowCheckingState();
}
void UpdaterDialog::OnUpdateCheckCompleted(bool has_update, const Updater::UpdateInfo& update_info) { void UpdaterDialog::OnUpdateCheckCompleted(bool has_update, const Updater::UpdateInfo& update_info) {
if (has_update) { if (has_update) {
current_update_info = update_info; current_update_info = update_info;
@@ -80,7 +70,8 @@ void UpdaterDialog::OnUpdateCheckCompleted(bool has_update, const Updater::Updat
} }
} }
void UpdaterDialog::OnUpdateDownloadProgress(int percentage, qint64 bytes_received, qint64 bytes_total) { void UpdaterDialog::OnUpdateDownloadProgress(int percentage, qint64 bytes_received,
qint64 bytes_total) {
downloaded_bytes = bytes_received; downloaded_bytes = bytes_received;
total_download_size = bytes_total; total_download_size = bytes_total;
@@ -100,20 +91,21 @@ void UpdaterDialog::OnUpdateInstallProgress(int percentage, const QString& curre
ui->downloadInfoLabel->setText(current_file); ui->downloadInfoLabel->setText(current_file);
} }
void UpdaterDialog::OnUpdateCompleted(Updater::UpdaterService::UpdateResult result, const QString& message) { void UpdaterDialog::OnUpdateCompleted(Updater::UpdaterService::UpdateResult result,
const QString& message) {
progress_timer->stop(); progress_timer->stop();
switch (result) { switch (result) {
case Updater::UpdaterService::UpdateResult::Success: case Updater::UpdaterService::UpdateResult::Success:
ShowCompletedState(); ShowCompletedState();
break; break;
case Updater::UpdaterService::UpdateResult::Cancelled: case Updater::UpdaterService::UpdateResult::Cancelled:
close(); close();
break; break;
default: default:
ShowErrorState(); ShowErrorState();
ui->statusLabel->setText(GetUpdateMessage(result) + QStringLiteral("\n\n") + message); ui->statusLabel->setText(GetUpdateMessage(result) + QStringLiteral("\n\n") + message);
break; break;
} }
} }
@@ -141,22 +133,16 @@ void UpdaterDialog::OnCloseButtonClicked() {
} }
void UpdaterDialog::OnRestartButtonClicked() { void UpdaterDialog::OnRestartButtonClicked() {
// Ask for confirmation
int ret = QMessageBox::question(this, QStringLiteral("Restart Citron"), int ret = QMessageBox::question(this, QStringLiteral("Restart Citron"),
QStringLiteral("Are you sure you want to restart Citron now?"), QStringLiteral("Are you sure you want to restart Citron now?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
QMessageBox::Yes);
if (ret == QMessageBox::Yes) { if (ret == QMessageBox::Yes) {
// Get the current executable path
QString program = QApplication::applicationFilePath(); QString program = QApplication::applicationFilePath();
QStringList arguments = QApplication::arguments(); QStringList arguments = QApplication::arguments();
arguments.removeFirst(); // Remove the program name arguments.removeFirst();
// Start the new instance
QProcess::startDetached(program, arguments); QProcess::startDetached(program, arguments);
// Close the current instance
QApplication::quit(); QApplication::quit();
} }
} }
@@ -164,41 +150,31 @@ void UpdaterDialog::OnRestartButtonClicked() {
void UpdaterDialog::SetupUI() { void UpdaterDialog::SetupUI() {
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
setFixedSize(size()); setFixedSize(size());
// Set current version
ui->currentVersionValue->setText(QString::fromStdString(updater_service->GetCurrentVersion())); ui->currentVersionValue->setText(QString::fromStdString(updater_service->GetCurrentVersion()));
ShowCheckingState(); ShowCheckingState();
} }
void UpdaterDialog::ShowCheckingState() { void UpdaterDialog::ShowCheckingState() {
current_state = State::Checking; current_state = State::Checking;
ui->titleLabel->setText(QStringLiteral("Checking for updates...")); ui->titleLabel->setText(QStringLiteral("Checking for updates..."));
ui->statusLabel->setText(QStringLiteral("Please wait while we check for available updates...")); ui->statusLabel->setText(QStringLiteral("Please wait while we check for available updates..."));
ui->updateInfoGroup->setVisible(false); ui->updateInfoGroup->setVisible(false);
ui->changelogGroup->setVisible(false); ui->changelogGroup->setVisible(false);
ui->progressGroup->setVisible(false); ui->progressGroup->setVisible(false);
ui->downloadButton->setVisible(false); ui->downloadButton->setVisible(false);
ui->cancelButton->setVisible(true); ui->cancelButton->setVisible(true);
ui->closeButton->setVisible(false); ui->closeButton->setVisible(false);
ui->restartButton->setVisible(false); ui->restartButton->setVisible(false);
ui->cancelButton->setText(QStringLiteral("Cancel")); ui->cancelButton->setText(QStringLiteral("Cancel"));
} }
void UpdaterDialog::ShowNoUpdateState() { void UpdaterDialog::ShowNoUpdateState() {
current_state = State::NoUpdate; current_state = State::NoUpdate;
ui->titleLabel->setText(QStringLiteral("No updates available")); ui->titleLabel->setText(QStringLiteral("No updates available"));
ui->statusLabel->setText(QStringLiteral("You are running the latest version of Citron.")); ui->statusLabel->setText(QStringLiteral("You are running the latest version of Citron."));
ui->updateInfoGroup->setVisible(true); ui->updateInfoGroup->setVisible(true);
ui->changelogGroup->setVisible(false); ui->changelogGroup->setVisible(false);
ui->progressGroup->setVisible(false); ui->progressGroup->setVisible(false);
ui->downloadButton->setVisible(false); ui->downloadButton->setVisible(false);
ui->cancelButton->setVisible(false); ui->cancelButton->setVisible(false);
ui->closeButton->setVisible(true); ui->closeButton->setVisible(true);
@@ -207,105 +183,84 @@ void UpdaterDialog::ShowNoUpdateState() {
void UpdaterDialog::ShowUpdateAvailableState() { void UpdaterDialog::ShowUpdateAvailableState() {
current_state = State::UpdateAvailable; current_state = State::UpdateAvailable;
ui->titleLabel->setText(QStringLiteral("Update available")); ui->titleLabel->setText(QStringLiteral("Update available"));
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."));
// Fill in update information
ui->latestVersionValue->setText(QString::fromStdString(current_update_info.version)); ui->latestVersionValue->setText(QString::fromStdString(current_update_info.version));
ui->releaseDateValue->setText(QString::fromStdString(current_update_info.release_date)); ui->releaseDateValue->setText(QString::fromStdString(current_update_info.release_date));
// Show changelog if available
if (!current_update_info.changelog.empty()) { if (!current_update_info.changelog.empty()) {
ui->changelogText->setPlainText(QString::fromStdString(current_update_info.changelog)); ui->changelogText->setPlainText(QString::fromStdString(current_update_info.changelog));
ui->changelogGroup->setVisible(true); ui->changelogGroup->setVisible(true);
} else { } else {
ui->changelogGroup->setVisible(false); ui->changelogGroup->setVisible(false);
} }
ui->updateInfoGroup->setVisible(true); ui->updateInfoGroup->setVisible(true);
ui->progressGroup->setVisible(false); ui->progressGroup->setVisible(false);
ui->downloadButton->setVisible(true); ui->downloadButton->setVisible(true);
ui->cancelButton->setVisible(true); ui->cancelButton->setVisible(true);
ui->closeButton->setVisible(false); ui->closeButton->setVisible(false);
ui->restartButton->setVisible(false); ui->restartButton->setVisible(false);
ui->cancelButton->setText(QStringLiteral("Later")); ui->cancelButton->setText(QStringLiteral("Later"));
} }
void UpdaterDialog::ShowDownloadingState() { void UpdaterDialog::ShowDownloadingState() {
current_state = State::Downloading; current_state = State::Downloading;
ui->titleLabel->setText(QStringLiteral("Downloading update...")); ui->titleLabel->setText(QStringLiteral("Downloading update..."));
ui->statusLabel->setText(QStringLiteral("Please wait while the update is being downloaded and installed.")); ui->statusLabel->setText(
QStringLiteral("Please wait while the update is being downloaded and installed."));
ui->updateInfoGroup->setVisible(false); ui->updateInfoGroup->setVisible(false);
ui->changelogGroup->setVisible(false); ui->changelogGroup->setVisible(false);
ui->progressGroup->setVisible(true); ui->progressGroup->setVisible(true);
ui->progressLabel->setText(QStringLiteral("Preparing download...")); ui->progressLabel->setText(QStringLiteral("Preparing download..."));
ui->progressBar->setValue(0); ui->progressBar->setValue(0);
ui->downloadInfoLabel->setText(QStringLiteral("")); ui->downloadInfoLabel->setText(QStringLiteral(""));
ui->downloadButton->setVisible(false); ui->downloadButton->setVisible(false);
ui->cancelButton->setVisible(true); ui->cancelButton->setVisible(true);
ui->closeButton->setVisible(false); ui->closeButton->setVisible(false);
ui->restartButton->setVisible(false); ui->restartButton->setVisible(false);
ui->cancelButton->setText(QStringLiteral("Cancel")); ui->cancelButton->setText(QStringLiteral("Cancel"));
progress_timer->start(); progress_timer->start();
} }
void UpdaterDialog::ShowInstallingState() { void UpdaterDialog::ShowInstallingState() {
current_state = State::Installing; current_state = State::Installing;
ui->titleLabel->setText(QStringLiteral("Installing update...")); ui->titleLabel->setText(QStringLiteral("Installing update..."));
ui->statusLabel->setText(QStringLiteral("Please wait while the update is being installed. Do not close the application.")); ui->statusLabel->setText(QStringLiteral(
"Please wait while the update is being installed. Do not close the application."));
ui->progressLabel->setText(QStringLiteral("Installing...")); ui->progressLabel->setText(QStringLiteral("Installing..."));
ui->downloadInfoLabel->setText(QStringLiteral("")); ui->downloadInfoLabel->setText(QStringLiteral(""));
ui->cancelButton->setVisible(false); ui->cancelButton->setVisible(false);
} }
void UpdaterDialog::ShowCompletedState() { void UpdaterDialog::ShowCompletedState() {
current_state = State::Completed; current_state = State::Completed;
ui->titleLabel->setText(QStringLiteral("Update ready!")); ui->titleLabel->setText(QStringLiteral("Update ready!"));
ui->statusLabel->setText(QStringLiteral("The update has been downloaded and prepared successfully. The update will be applied when you restart Citron.")); ui->statusLabel->setText(QStringLiteral("The update has been downloaded and prepared "
"successfully. The update will be applied when you "
"restart Citron."));
ui->progressGroup->setVisible(false); ui->progressGroup->setVisible(false);
ui->downloadButton->setVisible(false); ui->downloadButton->setVisible(false);
ui->cancelButton->setVisible(false); ui->cancelButton->setVisible(false);
ui->closeButton->setVisible(true); ui->closeButton->setVisible(true);
ui->restartButton->setVisible(true); ui->restartButton->setVisible(true);
ui->progressBar->setValue(100); ui->progressBar->setValue(100);
} }
void UpdaterDialog::ShowErrorState() { void UpdaterDialog::ShowErrorState() {
current_state = State::Error; current_state = State::Error;
ui->titleLabel->setText(QStringLiteral("Update failed")); ui->titleLabel->setText(QStringLiteral("Update failed"));
// statusLabel text is set by the caller
ui->updateInfoGroup->setVisible(false); ui->updateInfoGroup->setVisible(false);
ui->changelogGroup->setVisible(false); ui->changelogGroup->setVisible(false);
ui->progressGroup->setVisible(false); ui->progressGroup->setVisible(false);
ui->downloadButton->setVisible(false); ui->downloadButton->setVisible(false);
ui->cancelButton->setVisible(false); ui->cancelButton->setVisible(false);
ui->closeButton->setVisible(true); ui->closeButton->setVisible(true);
ui->restartButton->setVisible(false); ui->restartButton->setVisible(false);
} }
void UpdaterDialog::UpdateDownloadProgress(int percentage, qint64 bytes_received, qint64 bytes_total) { void UpdaterDialog::UpdateDownloadProgress(int percentage, qint64 bytes_received,
qint64 bytes_total) {
downloaded_bytes = bytes_received; downloaded_bytes = bytes_received;
total_download_size = bytes_total; total_download_size = bytes_total;
ui->progressBar->setValue(percentage); ui->progressBar->setValue(percentage);
ui->progressLabel->setText(QStringLiteral("Downloading update... %1%").arg(percentage)); ui->progressLabel->setText(QStringLiteral("Downloading update... %1%").arg(percentage));
} }
@@ -317,90 +272,42 @@ void UpdaterDialog::UpdateInstallProgress(int percentage, const QString& current
} }
QString UpdaterDialog::FormatBytes(qint64 bytes) const { QString UpdaterDialog::FormatBytes(qint64 bytes) const {
const QStringList units = {QStringLiteral("B"), QStringLiteral("KB"), QStringLiteral("MB"), QStringLiteral("GB")}; const QStringList units = {QStringLiteral("B"), QStringLiteral("KB"), QStringLiteral("MB"),
QStringLiteral("GB")};
double size = bytes; double size = bytes;
int unit = 0; int unit = 0;
while (size >= 1024.0 && unit < units.size() - 1) { while (size >= 1024.0 && unit < units.size() - 1) {
size /= 1024.0; size /= 1024.0;
unit++; unit++;
} }
return QStringLiteral("%1 %2")
return QStringLiteral("%1 %2").arg(QString::number(size, 'f', unit == 0 ? 0 : 1)).arg(units[unit]); .arg(QString::number(size, 'f', unit == 0 ? 0 : 1))
.arg(units[unit]);
} }
QString UpdaterDialog::GetUpdateMessage(Updater::UpdaterService::UpdateResult result) const { QString UpdaterDialog::GetUpdateMessage(Updater::UpdaterService::UpdateResult result) const {
switch (result) { switch (result) {
case Updater::UpdaterService::UpdateResult::Success: case Updater::UpdaterService::UpdateResult::Success:
return QStringLiteral("Update completed successfully!"); return QStringLiteral("Update completed successfully!");
case Updater::UpdaterService::UpdateResult::Failed: case Updater::UpdaterService::UpdateResult::Failed:
return QStringLiteral("Update failed due to an unknown error."); return QStringLiteral("Update failed due to an unknown error.");
case Updater::UpdaterService::UpdateResult::Cancelled: case Updater::UpdaterService::UpdateResult::Cancelled:
return QStringLiteral("Update was cancelled."); return QStringLiteral("Update was cancelled.");
case Updater::UpdaterService::UpdateResult::NetworkError: case Updater::UpdaterService::UpdateResult::NetworkError:
return QStringLiteral("Update failed due to a network error."); return QStringLiteral("Update failed due to a network error.");
case Updater::UpdaterService::UpdateResult::ExtractionError: case Updater::UpdaterService::UpdateResult::ExtractionError:
return QStringLiteral("Failed to extract the update archive."); return QStringLiteral("Failed to extract the update archive.");
case Updater::UpdaterService::UpdateResult::PermissionError: case Updater::UpdaterService::UpdateResult::PermissionError:
return QStringLiteral("Update failed due to insufficient permissions."); return QStringLiteral("Update failed due to insufficient permissions.");
case Updater::UpdaterService::UpdateResult::InvalidArchive: case Updater::UpdaterService::UpdateResult::InvalidArchive:
return QStringLiteral("The downloaded update archive is invalid."); return QStringLiteral("The downloaded update archive is invalid.");
case Updater::UpdaterService::UpdateResult::NoUpdateAvailable: case Updater::UpdaterService::UpdateResult::NoUpdateAvailable:
return QStringLiteral("No update is available."); return QStringLiteral("No update is available.");
default: default:
return QStringLiteral("Unknown error occurred."); return QStringLiteral("Unknown error occurred.");
} }
} }
#include "updater_dialog.moc" } // namespace Updater
#else // _WIN32
// Forward declarations for non-Windows platforms
namespace Updater {
struct UpdateInfo {};
class UpdaterService {
public:
enum class UpdateResult { Success };
};
}
// Stub implementations for non-Windows platforms
UpdaterDialog::UpdaterDialog(QWidget* parent) : QDialog(parent) {}
UpdaterDialog::~UpdaterDialog() = default;
void UpdaterDialog::CheckForUpdates(const std::string&) {}
void UpdaterDialog::ShowUpdateAvailable(const Updater::UpdateInfo&) {}
void UpdaterDialog::ShowUpdateChecking() {}
void UpdaterDialog::OnUpdateCheckCompleted(bool, const Updater::UpdateInfo&) {}
void UpdaterDialog::OnUpdateDownloadProgress(int, qint64, qint64) {}
void UpdaterDialog::OnUpdateInstallProgress(int, const QString&) {}
#ifdef _WIN32
void UpdaterDialog::OnUpdateCompleted(Updater::UpdaterService::UpdateResult, const QString&) {}
#else
void UpdaterDialog::OnUpdateCompleted(int, const QString&) {}
#endif
void UpdaterDialog::OnUpdateError(const QString&) {}
void UpdaterDialog::OnDownloadButtonClicked() {}
void UpdaterDialog::OnCancelButtonClicked() {}
void UpdaterDialog::OnCloseButtonClicked() {}
void UpdaterDialog::OnRestartButtonClicked() {}
void UpdaterDialog::SetupUI() {}
void UpdaterDialog::ShowCheckingState() {}
void UpdaterDialog::ShowNoUpdateState() {}
void UpdaterDialog::ShowUpdateAvailableState() {}
void UpdaterDialog::ShowDownloadingState() {}
void UpdaterDialog::ShowInstallingState() {}
void UpdaterDialog::ShowCompletedState() {}
void UpdaterDialog::ShowErrorState() {}
void UpdaterDialog::UpdateDownloadProgress(int, qint64, qint64) {}
void UpdaterDialog::UpdateInstallProgress(int, const QString&) {}
QString UpdaterDialog::FormatBytes(qint64) const { return QString(); }
#ifdef _WIN32
QString UpdaterDialog::GetUpdateMessage(Updater::UpdaterService::UpdateResult) const { return QString(); }
#else
QString UpdaterDialog::GetUpdateMessage(int) const { return QString(); }
#endif
#include "updater_dialog.moc" #include "updater_dialog.moc"
#endif // _WIN32