Skip to content

Commit bb01d33

Browse files
etude11kelson42
authored andcommitted
automatic check for updates
1 parent 1cd8168 commit bb01d33

6 files changed

Lines changed: 87 additions & 29 deletions

File tree

src/kiwixapp.cpp

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#include <QMessageBox>
1818
#include <QProgressDialog>
1919
#include <QPushButton>
20+
#include <QGuiApplication>
21+
#include <QScreen>
2022
#if defined(Q_OS_WIN)
2123
#include <QWindow>
2224
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
@@ -119,6 +121,13 @@ void KiwixApp::init()
119121
});
120122

121123
restoreWindowState();
124+
125+
// Initialize and run update checker if enabled
126+
if (m_settingsManager.getAutoCheckUpdates()) {
127+
QTimer::singleShot(1000, this, [this]() {
128+
checkForUpdates(false); // Auto-check
129+
});
130+
}
122131
}
123132

124133
void KiwixApp::setupDirectoryMonitoring()
@@ -483,7 +492,7 @@ void KiwixApp::createActions()
483492

484493
CREATE_ACTION_ICON_SHORTCUT(CheckUpdatesAction, "update", gt("check-update-title"), QKeySequence(Qt::CTRL | Qt::Key_U));
485494
connect(mpa_actions[CheckUpdatesAction], &QAction::triggered,
486-
this, &KiwixApp::checkForUpdates);
495+
this, [this]() { checkForUpdates(true); });
487496

488497
CREATE_ACTION(FeedbackAction, gt("feedback"));
489498
HIDE_ACTION(FeedbackAction);
@@ -627,57 +636,76 @@ QString KiwixApp::getPrevSaveDir() const
627636
return dir.exists() ? prevSaveDir : DEFAULT_SAVE_DIR;
628637
}
629638

630-
void KiwixApp::checkForUpdates()
639+
void KiwixApp::checkForUpdates(bool manualCheck)
631640
{
632641
if (!mp_versionChecker) {
633642
mp_versionChecker = std::make_unique<VersionChecker>();
634643
connect(mp_versionChecker.get(), &VersionChecker::updateAvailable,
635644
this, &KiwixApp::handleUpdateCheckResult);
636645
connect(mp_versionChecker.get(), &VersionChecker::noUpdateAvailable,
637-
this, &KiwixApp::handleNoUpdateAvailable);
646+
this, [this](){ handleNoUpdateAvailable(false); });
638647
connect(mp_versionChecker.get(), &VersionChecker::checkFailed,
639648
this, &KiwixApp::handleUpdateCheckFailed);
640649
}
650+
651+
if (manualCheck) {
652+
// Reconnect signal for manual check to show "no update" message
653+
disconnect(mp_versionChecker.get(), &VersionChecker::noUpdateAvailable, nullptr, nullptr);
654+
connect(mp_versionChecker.get(), &VersionChecker::noUpdateAvailable,
655+
this, [this](){ handleNoUpdateAvailable(true); });
656+
}
657+
641658
mp_versionChecker->checkForUpdates();
642659
}
643660

644661
void KiwixApp::handleUpdateCheckResult(const QString& latestVersion)
645662
{
646-
QMessageBox msgBox;
663+
// Create a non-intrusive notification
664+
QMessageBox msgBox(getMainWindow());
665+
msgBox.setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
647666
msgBox.setIcon(QMessageBox::Information);
648667
msgBox.setWindowTitle(gt("update-available-title"));
649-
msgBox.setText(gt("update-available").replace("{{VERSION}}", latestVersion) +
650-
"\n" + gt("current-version").replace("{{VERSION}}", version));
651-
msgBox.setInformativeText(gt("update-available-message"));
668+
msgBox.setText(gt("update-available").replace("{{VERSION}}", latestVersion));
652669

653670
auto installButton = msgBox.addButton(gt("install-update"), QMessageBox::ActionRole);
654-
msgBox.addButton(gt("remind-later"), QMessageBox::ActionRole); // Remove variable since it's unused
671+
msgBox.addButton(gt("remind-later"), QMessageBox::ActionRole);
655672
msgBox.addButton(QMessageBox::Close);
656-
673+
674+
// Position the notification in the bottom right corner
675+
if (QScreen* screen = QGuiApplication::primaryScreen()) {
676+
QRect screenGeometry = screen->geometry();
677+
msgBox.show(); // We need to show it first to get its size
678+
msgBox.hide();
679+
int x = screenGeometry.width() - msgBox.width() - 20;
680+
int y = screenGeometry.height() - msgBox.height() - 20;
681+
msgBox.move(x, y);
682+
}
683+
657684
msgBox.exec();
658685

659686
if (msgBox.clickedButton() == installButton) {
660-
// Create QProgressDialog as a member variable so it stays in scope
687+
// Create QProgressDialog for download progress
661688
auto* progressDialog = new QProgressDialog(gt("downloading-update"),
662689
gt("cancel"),
663690
0, 100,
664691
getMainWindow());
665692
progressDialog->setWindowModality(Qt::WindowModal);
666693

667-
// Connect with progressDialog instead of progress
694+
// Connect progress updates
668695
connect(mp_versionChecker.get(), &VersionChecker::downloadProgress,
669696
progressDialog, [progressDialog](qint64 received, qint64 total) {
670697
progressDialog->setValue((received * 100) / total);
671698
});
672699

700+
// Handle installation failures
673701
connect(mp_versionChecker.get(), &VersionChecker::installationFailed,
674702
this, [this](const QString& error) {
675703
QMessageBox::critical(getMainWindow(),
676704
gt("update-error-title"),
677705
gt("update-error-message").replace("{{ERROR}}", error));
678706
});
679707

680-
// Let's get the download URL for the latest version
708+
// Get download URL and start update process
681709
QString downloadUrl = mp_versionChecker->getDownloadUrl(latestVersion);
682710
mp_versionChecker->downloadAndInstallUpdate(downloadUrl, latestVersion);
683711

@@ -689,12 +717,14 @@ void KiwixApp::handleUpdateCheckResult(const QString& latestVersion)
689717
}
690718
}
691719

692-
void KiwixApp::handleNoUpdateAvailable()
720+
void KiwixApp::handleNoUpdateAvailable(bool showMessage)
693721
{
694-
QMessageBox::information(nullptr,
695-
gt("check-update-title"),
696-
gt("no-update-available") + "\n" +
697-
gt("current-version").replace("{{VERSION}}", version));
722+
if (showMessage) {
723+
QMessageBox::information(nullptr,
724+
gt("check-update-title"),
725+
gt("no-update-available") + "\n" +
726+
gt("current-version").replace("{{VERSION}}", version));
727+
}
698728
}
699729

700730
void KiwixApp::handleUpdateCheckFailed(const QString& error)

src/kiwixapp.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,9 @@ public slots:
124124
void handleItemsState(TabType);
125125
void updateNameMapper();
126126
void printVersions(std::ostream& out = std::cout);
127-
void checkForUpdates();
127+
void checkForUpdates(bool manualCheck = true);
128128
void handleUpdateCheckResult(const QString& latestVersion);
129-
void handleNoUpdateAvailable();
129+
void handleNoUpdateAvailable(bool showMessage);
130130
void handleUpdateCheckFailed(const QString& error);
131131

132132
protected:

src/settingsmanager.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,8 @@ void SettingsManager::setContentType(FilterList contentTypeList)
184184
emit(contentTypeChanged(m_contentTypeList));
185185
}
186186

187+
const QString SettingsManager::SETTING_AUTO_CHECK_UPDATES = "autoCheckUpdates";
188+
187189
void SettingsManager::initSettings()
188190
{
189191
if(isPortableMode()) {
@@ -225,4 +227,11 @@ void SettingsManager::initSettings()
225227
setCategory(m_categoryList.filter(QRegularExpression(R"(^[^|]*$)")));
226228

227229
m_contentTypeList = m_settings.value("contentType", {}).toList();
230+
m_autoCheckUpdates = m_settings.value(SETTING_AUTO_CHECK_UPDATES, true).toBool();
231+
}
232+
233+
void SettingsManager::setAutoCheckUpdates(bool enabled)
234+
{
235+
m_autoCheckUpdates = enabled;
236+
m_settings.setValue(SETTING_AUTO_CHECK_UPDATES, enabled);
228237
}

src/settingsmanager.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class SettingsManager : public QObject
3333
FilterList getLanguageList() { return deducePair(m_langList); }
3434
QStringList getCategoryList() { return m_categoryList; }
3535
FilterList getContentType() { return deducePair(m_contentTypeList); }
36+
bool getAutoCheckUpdates() const { return m_autoCheckUpdates; }
3637

3738
public slots:
3839
void setKiwixServerPort(int port);
@@ -45,6 +46,7 @@ public slots:
4546
void setLanguage(FilterList langList);
4647
void setCategory(QStringList categoryList);
4748
void setContentType(FilterList contentTypeList);
49+
void setAutoCheckUpdates(bool enabled);
4850

4951
private:
5052
void initSettings();
@@ -75,6 +77,8 @@ public slots:
7577
QList<QVariant> m_langList;
7678
QStringList m_categoryList;
7779
QList<QVariant> m_contentTypeList;
80+
bool m_autoCheckUpdates;
81+
static const QString SETTING_AUTO_CHECK_UPDATES;
7882
};
7983

8084
QString getDataDirectory();

src/versionchecker.cpp

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,8 @@ QString VersionChecker::getPlatformSpecificPattern()
9595
return "kiwix-desktop_.*_windows-x86_64\\.zip";
9696
#elif defined(Q_OS_LINUX)
9797
return "kiwix-desktop_.*_linux-x86_64\\.appimage";
98-
#elif defined(Q_OS_MAC)
99-
return "kiwix-desktop_.*_macos-x86_64\\.(dmg|pkg)";
10098
#else
101-
return "kiwix-desktop_.*\\.(appimage|zip|dmg|pkg)";
99+
return "kiwix-desktop_.*\\.(appimage|zip)";
102100
#endif
103101
}
104102

@@ -109,15 +107,13 @@ QString VersionChecker::getDownloadUrl(const QString& version) const
109107
filename += QString("_windows-x86_64_%1.zip").arg(version);
110108
#elif defined(Q_OS_LINUX)
111109
filename += QString("_x86_64_%1.appimage").arg(version);
112-
#elif defined(Q_OS_MAC)
113-
filename += QString("_macos-x86_64_%1.dmg").arg(version);
114110
#endif
115111
return DOWNLOAD_BASE_URL + filename;
116112
}
117113

118-
119114
void VersionChecker::downloadAndInstallUpdate(const QString& url, const QString& /*version*/) // Mark version as unused
120115
{
116+
logDebug("Starting download from: " + url);
121117
m_downloadPath = getDownloadDirectory() + QDir::separator() +
122118
QFileInfo(url).fileName();
123119

@@ -181,6 +177,12 @@ void VersionChecker::handleDownloadFinished()
181177
file.close();
182178
m_downloadReply->deleteLater();
183179

180+
// Verify package integrity
181+
if (!verifyDownloadedPackage(m_downloadPath)) {
182+
emit installationFailed("Package verification failed");
183+
return;
184+
}
185+
184186
emit installationStarted();
185187

186188
// Try to install the downloaded update
@@ -225,10 +227,6 @@ bool VersionChecker::installUpdate(const QString& filePath)
225227
QFile::remove(backupPath);
226228

227229
return true;
228-
#elif defined(Q_OS_MAC)
229-
// For macOS, mount DMG and run installer
230-
// TODO: Implement macOS update installation
231-
return false;
232230
#else
233231
return false;
234232
#endif
@@ -254,3 +252,15 @@ VersionChecker::ReleaseInfo VersionChecker::findLatestRelease(const QList<Releas
254252

255253
return latest;
256254
}
255+
256+
bool VersionChecker::verifyDownloadedPackage(const QString& filePath) const
257+
{
258+
logDebug("Verifying downloaded package: " + filePath);
259+
QFileInfo fileInfo(filePath);
260+
return fileInfo.exists() && fileInfo.size() > 0;
261+
}
262+
263+
void VersionChecker::logDebug(const QString& message) const
264+
{
265+
qDebug() << "[VersionChecker]" << message;
266+
}

src/versionchecker.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ private slots:
4949
bool installUpdate(const QString &filePath);
5050
QString getPlatformSpecificPattern();
5151
QString getDownloadDirectory();
52+
void logDebug(const QString& message) const;
53+
bool verifyDownloadedPackage(const QString& filePath) const;
54+
QString getTemporaryBackupPath(const QString& originalPath) const;
55+
bool restoreBackup(const QString& backupPath, const QString& originalPath);
56+
void cleanupBackup(const QString& backupPath);
5257
};
5358

5459
#endif // VERSIONCHECKER_H

0 commit comments

Comments
 (0)