From 6412c77a9d57d7f5b41733fff836d344201697dc Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Sat, 22 Apr 2023 20:37:01 -0500 Subject: [PATCH 1/7] Add GitHub types for Release API --- scwx-qt/scwx-qt.cmake | 4 +- scwx-qt/source/scwx/qt/types/github_types.cpp | 39 +++++++++++++++++++ scwx-qt/source/scwx/qt/types/github_types.hpp | 36 +++++++++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 scwx-qt/source/scwx/qt/types/github_types.cpp create mode 100644 scwx-qt/source/scwx/qt/types/github_types.hpp diff --git a/scwx-qt/scwx-qt.cmake b/scwx-qt/scwx-qt.cmake index 641f9abb..aa491d40 100644 --- a/scwx-qt/scwx-qt.cmake +++ b/scwx-qt/scwx-qt.cmake @@ -126,10 +126,12 @@ set(SRC_SETTINGS source/scwx/qt/settings/general_settings.cpp source/scwx/qt/settings/settings_variable.cpp source/scwx/qt/settings/settings_variable_base.cpp) set(HDR_TYPES source/scwx/qt/types/font_types.hpp + source/scwx/qt/types/github_types.hpp source/scwx/qt/types/qt_types.hpp source/scwx/qt/types/radar_product_record.hpp source/scwx/qt/types/text_event_key.hpp) -set(SRC_TYPES source/scwx/qt/types/radar_product_record.cpp +set(SRC_TYPES source/scwx/qt/types/github_types.cpp + source/scwx/qt/types/radar_product_record.cpp source/scwx/qt/types/text_event_key.cpp) set(HDR_UI source/scwx/qt/ui/about_dialog.hpp source/scwx/qt/ui/alert_dialog.hpp diff --git a/scwx-qt/source/scwx/qt/types/github_types.cpp b/scwx-qt/source/scwx/qt/types/github_types.cpp new file mode 100644 index 00000000..e741c23b --- /dev/null +++ b/scwx-qt/source/scwx/qt/types/github_types.cpp @@ -0,0 +1,39 @@ +#include + +#include + +namespace scwx +{ +namespace qt +{ +namespace types +{ +namespace gh +{ + +Release tag_invoke(boost::json::value_to_tag, + const boost::json::value& jv) +{ + auto jo = jv.as_object(); + + Release release {}; + + // Required parameters + release.name_ = jo.at("name").as_string(); + release.htmlUrl_ = jo.at("html_url").as_string(); + release.draft_ = jo.at("draft").as_bool(); + release.prerelease_ = jo.at("prerelease").as_bool(); + + // Optional parameters + if (jo.contains("body")) + { + release.body_ = jo.at("body").as_string(); + } + + return release; +} + +} // namespace gh +} // namespace types +} // namespace qt +} // namespace scwx diff --git a/scwx-qt/source/scwx/qt/types/github_types.hpp b/scwx-qt/source/scwx/qt/types/github_types.hpp new file mode 100644 index 00000000..829cfd28 --- /dev/null +++ b/scwx-qt/source/scwx/qt/types/github_types.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include + +#include + +namespace scwx +{ +namespace qt +{ +namespace types +{ +namespace gh +{ + +/** + * @brief GitHub Release object + * + * + */ +struct Release +{ + std::string name_ {}; + std::string htmlUrl_ {}; + std::string body_ {}; + bool draft_ {}; + bool prerelease_ {}; +}; + +Release tag_invoke(boost::json::value_to_tag, + const boost::json::value& jv); + +} // namespace gh +} // namespace types +} // namespace qt +} // namespace scwx From 08654bb7b032254e7aa9d9cdd6527187c59f5374 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Sat, 22 Apr 2023 22:42:09 -0500 Subject: [PATCH 2/7] Update manager and test --- scwx-qt/scwx-qt.cmake | 6 +- .../source/scwx/qt/manager/update_manager.cpp | 256 ++++++++++++++++++ .../source/scwx/qt/manager/update_manager.hpp | 43 +++ .../scwx/qt/manager/update_manager.test.cpp | 40 +++ test/test.cmake | 3 +- 5 files changed, 345 insertions(+), 3 deletions(-) create mode 100644 scwx-qt/source/scwx/qt/manager/update_manager.cpp create mode 100644 scwx-qt/source/scwx/qt/manager/update_manager.hpp create mode 100644 test/source/scwx/qt/manager/update_manager.test.cpp diff --git a/scwx-qt/scwx-qt.cmake b/scwx-qt/scwx-qt.cmake index aa491d40..d5143fc6 100644 --- a/scwx-qt/scwx-qt.cmake +++ b/scwx-qt/scwx-qt.cmake @@ -64,12 +64,14 @@ set(HDR_MANAGER source/scwx/qt/manager/radar_product_manager.hpp source/scwx/qt/manager/radar_product_manager_notifier.hpp source/scwx/qt/manager/resource_manager.hpp source/scwx/qt/manager/settings_manager.hpp - source/scwx/qt/manager/text_event_manager.hpp) + source/scwx/qt/manager/text_event_manager.hpp + source/scwx/qt/manager/update_manager.hpp) set(SRC_MANAGER source/scwx/qt/manager/radar_product_manager.cpp source/scwx/qt/manager/radar_product_manager_notifier.cpp source/scwx/qt/manager/resource_manager.cpp source/scwx/qt/manager/settings_manager.cpp - source/scwx/qt/manager/text_event_manager.cpp) + source/scwx/qt/manager/text_event_manager.cpp + source/scwx/qt/manager/update_manager.cpp) set(HDR_MAP source/scwx/qt/map/alert_layer.hpp source/scwx/qt/map/color_table_layer.hpp source/scwx/qt/map/draw_layer.hpp diff --git a/scwx-qt/source/scwx/qt/manager/update_manager.cpp b/scwx-qt/source/scwx/qt/manager/update_manager.cpp new file mode 100644 index 00000000..fe96936f --- /dev/null +++ b/scwx-qt/source/scwx/qt/manager/update_manager.cpp @@ -0,0 +1,256 @@ +#include +#include + +#include +#include + +#include +#include + +namespace scwx +{ +namespace qt +{ +namespace manager +{ + +static const std::string logPrefix_ = "scwx::qt::manager::update_manager"; +static const auto logger_ = scwx::util::Logger::Create(logPrefix_); + +static const std::string kGithubApiBase {"https://api.github.com"}; +static const std::string kScwxReleaseEndpoint { + kGithubApiBase + "/repos/dpaulat/supercell-wx/releases"}; + +class UpdateManager::Impl +{ +public: + explicit Impl(UpdateManager* self) : self_ {self}, releases_ {} {} + + ~Impl() {} + + static std::string GetVersionString(const std::string& releaseName); + static boost::json::value ParseResponseText(const std::string& s); + + size_t PopulateReleases(); + size_t AddReleases(const boost::json::value& json); + std::pair::iterator, std::string> + FindLatestRelease(); + + UpdateManager* self_; + + std::vector releases_; + types::gh::Release latestRelease_; + std::string latestVersion_; +}; + +UpdateManager::UpdateManager() : p(std::make_unique(this)) {} +UpdateManager::~UpdateManager() = default; + +types::gh::Release UpdateManager::latest_release() const +{ + return p->latestRelease_; +} + +std::string UpdateManager::latest_version() const +{ + return p->latestVersion_; +} + +std::string +UpdateManager::Impl::GetVersionString(const std::string& releaseName) +{ + static const std::regex re {"\\d+\\.\\d+\\.\\d+"}; + std::string versionString {}; + std::smatch m; + + std::regex_search(releaseName, m, re); + + if (!m.empty()) + { + versionString = m[0].str(); + } + + return versionString; +} + +boost::json::value UpdateManager::Impl::ParseResponseText(const std::string& s) +{ + boost::json::stream_parser p; + boost::json::error_code ec; + + p.write(s, ec); + if (ec) + { + logger_->warn("{}", ec.message()); + return nullptr; + } + + p.finish(ec); + if (ec) + { + logger_->warn("{}", ec.message()); + return nullptr; + } + + return p.release(); +} + +bool UpdateManager::CheckForUpdates(const std::string& currentVersion) +{ + logger_->info("Checking for updates"); + + // Query GitHub for releases + size_t numReleases = p->PopulateReleases(); + bool newRelease = false; + + // If GitHub returned valid releases + if (numReleases > 0) + { + // Get the latest release + auto [latestRelease, latestVersion] = p->FindLatestRelease(); + + // Validate the latest release, and compare to the current version + if (latestRelease != p->releases_.end() && latestVersion > currentVersion) + { + logger_->info("An update is available: {}", latestVersion); + + p->latestRelease_ = *latestRelease; + p->latestVersion_ = latestVersion; + newRelease = true; + emit UpdateAvailable(latestVersion, *latestRelease); + } + } + + return newRelease; +} + +size_t UpdateManager::Impl::PopulateReleases() +{ + static constexpr size_t perPage = + 100u; // The number of results per page (max 100) + size_t page = 1u; // Page number of the results to fetch + size_t numResults = 0u; + + static const std::string perPageString {fmt::format("{}", perPage)}; + + // Clear any existing releases + releases_.clear(); + + do + { + const std::string pageString {fmt::format("{}", page)}; + + cpr::Response r = cpr::Get( + cpr::Url {kScwxReleaseEndpoint}, + cpr::Parameters {{"per_page", perPageString}, {"page", pageString}}, + cpr::Header {{"accept", "application/vnd.github+json"}, + {"X-GitHub-Api-Version", "2022-11-28"}}); + + // Successful REST API query + if (r.status_code == 200) + { + boost::json::value json = Impl::ParseResponseText(r.text); + if (json == nullptr) + { + logger_->warn("Response not JSON: {}", r.header["content-type"]); + break; + } + + // Add results from response + size_t newResults = AddReleases(json); + numResults += newResults; + + if (newResults < perPage) + { + // We have reached the last page of results + break; + } + } + else + { + logger_->warn( + "Invalid API response: [{}] {}", r.status_code, r.error.message); + break; + } + + // Check page is less than 100, this is to prevent an infinite loop + } while (++page < 100); + + return numResults; +} + +size_t UpdateManager::Impl::AddReleases(const boost::json::value& json) +{ + // Parse releases + std::vector newReleases {}; + try + { + newReleases = + boost::json::value_to>(json); + } + catch (const std::exception& ex) + { + logger_->warn("Error parsing JSON: {}", ex.what()); + } + + size_t newReleaseCount = newReleases.size(); + + // Add releases to the current list + releases_.insert(releases_.end(), newReleases.begin(), newReleases.end()); + + return newReleaseCount; +} + +std::pair::iterator, std::string> +UpdateManager::Impl::FindLatestRelease() +{ + // Initialize the latest release to the end iterator + std::vector::iterator latestRelease = releases_.end(); + std::string latestReleaseVersion {}; + + for (auto it = releases_.begin(); it != releases_.end(); ++it) + { + if (it->draft_ || it->prerelease_) + { + // Skip drafts and prereleases + continue; + } + + // Get the version string of the current release + std::string currentVersion {GetVersionString(it->name_)}; + + // If not set, or current version is lexographically newer + if (latestRelease == releases_.end() || + currentVersion > latestReleaseVersion) + { + // Update the latest release + latestRelease = it; + latestReleaseVersion = currentVersion; + } + } + + return {latestRelease, latestReleaseVersion}; +} + +std::shared_ptr UpdateManager::Instance() +{ + static std::weak_ptr updateManagerReference_ {}; + static std::mutex instanceMutex_ {}; + + std::unique_lock lock(instanceMutex_); + + std::shared_ptr updateManager = + updateManagerReference_.lock(); + + if (updateManager == nullptr) + { + updateManager = std::make_shared(); + updateManagerReference_ = updateManager; + } + + return updateManager; +} + +} // namespace manager +} // namespace qt +} // namespace scwx diff --git a/scwx-qt/source/scwx/qt/manager/update_manager.hpp b/scwx-qt/source/scwx/qt/manager/update_manager.hpp new file mode 100644 index 00000000..10a97350 --- /dev/null +++ b/scwx-qt/source/scwx/qt/manager/update_manager.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include + +#include +#include + +#include + +namespace scwx +{ +namespace qt +{ +namespace manager +{ + +class UpdateManager : public QObject +{ + Q_OBJECT + +public: + explicit UpdateManager(); + ~UpdateManager(); + + types::gh::Release latest_release() const; + std::string latest_version() const; + + bool CheckForUpdates(const std::string& currentVersion = {}); + + static std::shared_ptr Instance(); + +signals: + void UpdateAvailable(const std::string& latestVersion, + const types::gh::Release& latestRelease); + +private: + class Impl; + std::unique_ptr p; +}; + +} // namespace manager +} // namespace qt +} // namespace scwx diff --git a/test/source/scwx/qt/manager/update_manager.test.cpp b/test/source/scwx/qt/manager/update_manager.test.cpp new file mode 100644 index 00000000..6558eb3b --- /dev/null +++ b/test/source/scwx/qt/manager/update_manager.test.cpp @@ -0,0 +1,40 @@ +#include + +#include + +namespace scwx +{ +namespace qt +{ +namespace manager +{ + +TEST(UpdateManagerTest, CheckForUpdates) +{ + auto updateManager = UpdateManager::Instance(); + bool updateFound; + types::gh::Release latestRelease; + std::string latestVersion; + + // Check for updates, and expect an update to be found + updateFound = updateManager->CheckForUpdates("0.0.0"); + latestRelease = updateManager->latest_release(); + latestVersion = updateManager->latest_version(); + + EXPECT_EQ(updateFound, true); + EXPECT_GT(latestRelease.name_.size(), 0); + EXPECT_GT(latestRelease.htmlUrl_.size(), 0); + EXPECT_GT(latestRelease.body_.size(), 0); + EXPECT_EQ(latestRelease.draft_, false); + EXPECT_EQ(latestRelease.prerelease_, false); + EXPECT_GT(latestVersion, "0.0.0"); + + // Check for updates, and expect no updates to be found + updateFound = updateManager->CheckForUpdates("9999.99.99"); + + EXPECT_EQ(updateFound, false); +} + +} // namespace manager +} // namespace qt +} // namespace scwx diff --git a/test/test.cmake b/test/test.cmake index a0bc5d20..0e67647e 100644 --- a/test/test.cmake +++ b/test/test.cmake @@ -21,7 +21,8 @@ set(SRC_PROVIDER_TESTS source/scwx/provider/aws_level2_data_provider.test.cpp source/scwx/provider/warnings_provider.test.cpp) set(SRC_QT_CONFIG_TESTS source/scwx/qt/config/county_database.test.cpp source/scwx/qt/config/radar_site.test.cpp) -set(SRC_QT_MANAGER_TESTS source/scwx/qt/manager/settings_manager.test.cpp) +set(SRC_QT_MANAGER_TESTS source/scwx/qt/manager/settings_manager.test.cpp + source/scwx/qt/manager/update_manager.test.cpp) set(SRC_QT_MODEL_TESTS source/scwx/qt/model/imgui_context_model.test.cpp) set(SRC_QT_SETTINGS_TESTS source/scwx/qt/settings/settings_container.test.cpp source/scwx/qt/settings/settings_variable.test.cpp) From 26ea4f47716d0ad7d71658e3e20ca5c49bab8806 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Sat, 22 Apr 2023 23:28:30 -0500 Subject: [PATCH 3/7] Use mutex to allow for concurrent update requests --- scwx-qt/source/scwx/qt/manager/update_manager.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/scwx-qt/source/scwx/qt/manager/update_manager.cpp b/scwx-qt/source/scwx/qt/manager/update_manager.cpp index fe96936f..f6eba5ef 100644 --- a/scwx-qt/source/scwx/qt/manager/update_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/update_manager.cpp @@ -24,7 +24,7 @@ static const std::string kScwxReleaseEndpoint { class UpdateManager::Impl { public: - explicit Impl(UpdateManager* self) : self_ {self}, releases_ {} {} + explicit Impl(UpdateManager* self) : self_ {self} {} ~Impl() {} @@ -38,9 +38,11 @@ public: UpdateManager* self_; - std::vector releases_; - types::gh::Release latestRelease_; - std::string latestVersion_; + std::mutex updateMutex_ {}; + + std::vector releases_ {}; + types::gh::Release latestRelease_ {}; + std::string latestVersion_ {}; }; UpdateManager::UpdateManager() : p(std::make_unique(this)) {} @@ -97,6 +99,8 @@ boost::json::value UpdateManager::Impl::ParseResponseText(const std::string& s) bool UpdateManager::CheckForUpdates(const std::string& currentVersion) { + std::unique_lock lock(p->updateMutex_); + logger_->info("Checking for updates"); // Query GitHub for releases From c1aa34607f4f783378ef6177c3b4eca9bcb14a23 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Sat, 22 Apr 2023 23:31:23 -0500 Subject: [PATCH 4/7] Add Update Dialog --- scwx-qt/scwx-qt.cmake | 9 +- scwx-qt/source/scwx/qt/ui/update_dialog.cpp | 71 +++++++ scwx-qt/source/scwx/qt/ui/update_dialog.hpp | 43 ++++ scwx-qt/source/scwx/qt/ui/update_dialog.ui | 214 ++++++++++++++++++++ 4 files changed, 334 insertions(+), 3 deletions(-) create mode 100644 scwx-qt/source/scwx/qt/ui/update_dialog.cpp create mode 100644 scwx-qt/source/scwx/qt/ui/update_dialog.hpp create mode 100644 scwx-qt/source/scwx/qt/ui/update_dialog.ui diff --git a/scwx-qt/scwx-qt.cmake b/scwx-qt/scwx-qt.cmake index d5143fc6..15d09a62 100644 --- a/scwx-qt/scwx-qt.cmake +++ b/scwx-qt/scwx-qt.cmake @@ -145,7 +145,8 @@ set(HDR_UI source/scwx/qt/ui/about_dialog.hpp source/scwx/qt/ui/level2_settings_widget.hpp source/scwx/qt/ui/level3_products_widget.hpp source/scwx/qt/ui/radar_site_dialog.hpp - source/scwx/qt/ui/settings_dialog.hpp) + source/scwx/qt/ui/settings_dialog.hpp + source/scwx/qt/ui/update_dialog.hpp) set(SRC_UI source/scwx/qt/ui/about_dialog.cpp source/scwx/qt/ui/alert_dialog.cpp source/scwx/qt/ui/alert_dock_widget.cpp @@ -156,13 +157,15 @@ set(SRC_UI source/scwx/qt/ui/about_dialog.cpp source/scwx/qt/ui/level2_settings_widget.cpp source/scwx/qt/ui/level3_products_widget.cpp source/scwx/qt/ui/radar_site_dialog.cpp - source/scwx/qt/ui/settings_dialog.cpp) + source/scwx/qt/ui/settings_dialog.cpp + source/scwx/qt/ui/update_dialog.cpp) set(UI_UI source/scwx/qt/ui/about_dialog.ui source/scwx/qt/ui/alert_dialog.ui source/scwx/qt/ui/alert_dock_widget.ui source/scwx/qt/ui/imgui_debug_dialog.ui source/scwx/qt/ui/radar_site_dialog.ui - source/scwx/qt/ui/settings_dialog.ui) + source/scwx/qt/ui/settings_dialog.ui + source/scwx/qt/ui/update_dialog.ui) set(HDR_UTIL source/scwx/qt/util/color.hpp source/scwx/qt/util/file.hpp source/scwx/qt/util/font.hpp diff --git a/scwx-qt/source/scwx/qt/ui/update_dialog.cpp b/scwx-qt/source/scwx/qt/ui/update_dialog.cpp new file mode 100644 index 00000000..93b3ac0a --- /dev/null +++ b/scwx-qt/source/scwx/qt/ui/update_dialog.cpp @@ -0,0 +1,71 @@ +#include "update_dialog.hpp" +#include "ui_update_dialog.h" +#include +#include + +#include +#include + +namespace scwx +{ +namespace qt +{ +namespace ui +{ + +class UpdateDialogImpl +{ +public: + explicit UpdateDialogImpl() = default; + ~UpdateDialogImpl() = default; + + std::string downloadUrl_ {}; +}; + +UpdateDialog::UpdateDialog(QWidget* parent) : + QDialog(parent), + p {std::make_unique()}, + ui(new Ui::UpdateDialog) +{ + ui->setupUi(this); + + int titleFontId = + manager::ResourceManager::FontId(types::Font::din1451alt_g); + QString titleFontFamily = + QFontDatabase::applicationFontFamilies(titleFontId).at(0); + QFont titleFont(titleFontFamily, 12); + ui->bannerLabel->setFont(titleFont); + + ui->releaseNotesText->setOpenExternalLinks(true); +} + +UpdateDialog::~UpdateDialog() +{ + delete ui; +} + +void UpdateDialog::UpdateReleaseInfo(const std::string& latestVersion, + const types::gh::Release& latestRelease) +{ + ui->versionLabel->setText(tr("Supercell Wx v%1 is now available. You are " + "currently running version %2.") + .arg(latestVersion.c_str()) + .arg(main::kVersionString_.c_str())); + + ui->releaseNotesText->setMarkdown( + QString::fromStdString(latestRelease.body_)); + + p->downloadUrl_ = latestRelease.htmlUrl_; +} + +void UpdateDialog::on_downloadButton_clicked() +{ + if (!p->downloadUrl_.empty()) + { + QDesktopServices::openUrl(QUrl {QString::fromStdString(p->downloadUrl_)}); + } +} + +} // namespace ui +} // namespace qt +} // namespace scwx diff --git a/scwx-qt/source/scwx/qt/ui/update_dialog.hpp b/scwx-qt/source/scwx/qt/ui/update_dialog.hpp new file mode 100644 index 00000000..0df39648 --- /dev/null +++ b/scwx-qt/source/scwx/qt/ui/update_dialog.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include + +#include + +namespace Ui +{ +class UpdateDialog; +} + +namespace scwx +{ +namespace qt +{ +namespace ui +{ + +class UpdateDialogImpl; + +class UpdateDialog : public QDialog +{ + Q_OBJECT + +public: + explicit UpdateDialog(QWidget* parent = nullptr); + ~UpdateDialog(); + + void UpdateReleaseInfo(const std::string& latestVersion, + const types::gh::Release& latestRelease); + +private slots: + void on_downloadButton_clicked(); + +private: + friend UpdateDialogImpl; + std::unique_ptr p; + Ui::UpdateDialog* ui; +}; + +} // namespace ui +} // namespace qt +} // namespace scwx diff --git a/scwx-qt/source/scwx/qt/ui/update_dialog.ui b/scwx-qt/source/scwx/qt/ui/update_dialog.ui new file mode 100644 index 00000000..3ecb2ef2 --- /dev/null +++ b/scwx-qt/source/scwx/qt/ui/update_dialog.ui @@ -0,0 +1,214 @@ + + + UpdateDialog + + + + 0 + 0 + 400 + 300 + + + + Update Available + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 64 + 64 + + + + + + + :/res/icons/scwx-256.png + + + true + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + A new version of Supercell Wx is available! + + + true + + + + + + + Supercell Wx X.Y.Z is now available. You are running version X.Y.Z. + + + true + + + + + + + + + + + + + Release Notes: + + + + + + + + + + &Enable Update Notifications + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Download + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + + + + + + buttonBox + accepted() + UpdateDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + UpdateDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + From 19f0ab98982f4336d918c4dce31f7dd30934a931 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Sat, 22 Apr 2023 23:33:25 -0500 Subject: [PATCH 5/7] Connect update manager to update dialog --- scwx-qt/source/scwx/qt/main/main_window.cpp | 52 +++++++++++++++++++++ scwx-qt/source/scwx/qt/main/main_window.hpp | 1 + scwx-qt/source/scwx/qt/main/main_window.ui | 6 +++ 3 files changed, 59 insertions(+) diff --git a/scwx-qt/source/scwx/qt/main/main_window.cpp b/scwx-qt/source/scwx/qt/main/main_window.cpp index 5bb9eeb7..e164210f 100644 --- a/scwx-qt/source/scwx/qt/main/main_window.cpp +++ b/scwx-qt/source/scwx/qt/main/main_window.cpp @@ -4,9 +4,11 @@ #include "./ui_main_window.h" #include +#include #include #include #include +#include #include #include #include @@ -18,10 +20,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -61,8 +65,10 @@ public: imGuiDebugDialog_ {nullptr}, radarSiteDialog_ {nullptr}, settingsDialog_ {nullptr}, + updateDialog_ {nullptr}, radarProductModel_ {nullptr}, textEventManager_ {manager::TextEventManager::Instance()}, + updateManager_ {manager::UpdateManager::Instance()}, maps_ {}, elevationCuts_ {}, elevationButtonsChanged_ {false}, @@ -94,6 +100,7 @@ public: } ~MainWindowImpl() = default; + void AsyncSetup(); void ConfigureMapLayout(); void ConnectMapSignals(); void ConnectOtherSignals(); @@ -126,9 +133,11 @@ public: ui::ImGuiDebugDialog* imGuiDebugDialog_; ui::RadarSiteDialog* radarSiteDialog_; ui::SettingsDialog* settingsDialog_; + ui::UpdateDialog* updateDialog_; std::unique_ptr radarProductModel_; std::shared_ptr textEventManager_; + std::shared_ptr updateManager_; std::vector maps_; std::vector elevationCuts_; @@ -223,6 +232,9 @@ MainWindow::MainWindow(QWidget* parent) : // About Dialog p->aboutDialog_ = new ui::AboutDialog(this); + // Update Dialog + p->updateDialog_ = new ui::UpdateDialog(this); + auto& mapSettings = manager::SettingsManager::map_settings(); for (size_t i = 0; i < p->maps_.size(); i++) { @@ -236,6 +248,7 @@ MainWindow::MainWindow(QWidget* parent) : p->ConnectMapSignals(); p->ConnectOtherSignals(); p->HandleFocusChange(p->activeMap_); + p->AsyncSetup(); Application::FinishInitialization(); } @@ -381,6 +394,29 @@ void MainWindow::on_actionGitHubRepository_triggered() QDesktopServices::openUrl(QUrl {"https://github.com/dpaulat/supercell-wx"}); } +void MainWindow::on_actionCheckForUpdates_triggered() +{ + scwx::util::async( + [this]() + { + if (!p->updateManager_->CheckForUpdates(main::kVersionString_)) + { + QMetaObject::invokeMethod( + this, + [this]() + { + QMessageBox* messageBox = new QMessageBox(this); + messageBox->setIcon(QMessageBox::Icon::Information); + messageBox->setWindowTitle(tr("Check for Updates")); + messageBox->setText(tr("Supercell Wx is up to date.")); + messageBox->setStandardButtons( + QMessageBox::StandardButton::Ok); + messageBox->show(); + }); + } + }); +} + void MainWindow::on_actionAboutSupercellWx_triggered() { p->aboutDialog_->show(); @@ -468,6 +504,13 @@ void MainWindow::on_resourceTreeView_doubleClicked(const QModelIndex& index) p->activeMap_->SelectRadarProduct(group, product, 0, time); } +void MainWindowImpl::AsyncSetup() +{ + // Check for updates + scwx::util::async( + [this]() { updateManager_->CheckForUpdates(main::kVersionString_); }); +} + void MainWindowImpl::ConfigureMapLayout() { auto& generalSettings = manager::SettingsManager::general_settings(); @@ -629,6 +672,15 @@ void MainWindowImpl::ConnectOtherSignals() map->SelectRadarSite(selectedRadarSite); } }); + connect(updateManager_.get(), + &manager::UpdateManager::UpdateAvailable, + this, + [this](const std::string& latestVersion, + const types::gh::Release& latestRelease) + { + updateDialog_->UpdateReleaseInfo(latestVersion, latestRelease); + updateDialog_->show(); + }); } void MainWindowImpl::HandleFocusChange(QWidget* focused) diff --git a/scwx-qt/source/scwx/qt/main/main_window.hpp b/scwx-qt/source/scwx/qt/main/main_window.hpp index f67374fe..6ed96aba 100644 --- a/scwx-qt/source/scwx/qt/main/main_window.hpp +++ b/scwx-qt/source/scwx/qt/main/main_window.hpp @@ -41,6 +41,7 @@ private slots: void on_actionUserManual_triggered(); void on_actionDiscord_triggered(); void on_actionGitHubRepository_triggered(); + void on_actionCheckForUpdates_triggered(); void on_actionAboutSupercellWx_triggered(); void on_radarSiteSelectButton_clicked(); void on_resourceTreeCollapseAllButton_clicked(); diff --git a/scwx-qt/source/scwx/qt/main/main_window.ui b/scwx-qt/source/scwx/qt/main/main_window.ui index 77d45c5e..9a66af52 100644 --- a/scwx-qt/source/scwx/qt/main/main_window.ui +++ b/scwx-qt/source/scwx/qt/main/main_window.ui @@ -68,6 +68,7 @@ + @@ -404,6 +405,11 @@ Dump Radar &Product Records + + + &Check for Updates + + From a609f14f1fa56e54a6039b8c5d815a8204d65773 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Sun, 23 Apr 2023 00:30:18 -0500 Subject: [PATCH 6/7] Adding update notifications setting option --- scwx-qt/source/scwx/qt/main/main_window.cpp | 9 +++++++-- .../source/scwx/qt/settings/general_settings.cpp | 14 ++++++++++++-- .../source/scwx/qt/settings/general_settings.hpp | 1 + scwx-qt/source/scwx/qt/ui/settings_dialog.cpp | 9 ++++++++- scwx-qt/source/scwx/qt/ui/settings_dialog.ui | 11 +++++++++-- test/data | 2 +- 6 files changed, 38 insertions(+), 8 deletions(-) diff --git a/scwx-qt/source/scwx/qt/main/main_window.cpp b/scwx-qt/source/scwx/qt/main/main_window.cpp index e164210f..4b87d60b 100644 --- a/scwx-qt/source/scwx/qt/main/main_window.cpp +++ b/scwx-qt/source/scwx/qt/main/main_window.cpp @@ -506,9 +506,14 @@ void MainWindow::on_resourceTreeView_doubleClicked(const QModelIndex& index) void MainWindowImpl::AsyncSetup() { + auto& generalSettings = manager::SettingsManager::general_settings(); + // Check for updates - scwx::util::async( - [this]() { updateManager_->CheckForUpdates(main::kVersionString_); }); + if (generalSettings.update_notifications_enabled().GetValue()) + { + scwx::util::async( + [this]() { updateManager_->CheckForUpdates(main::kVersionString_); }); + } } void MainWindowImpl::ConfigureMapLayout() diff --git a/scwx-qt/source/scwx/qt/settings/general_settings.cpp b/scwx-qt/source/scwx/qt/settings/general_settings.cpp index ddc2098a..55f74a00 100644 --- a/scwx-qt/source/scwx/qt/settings/general_settings.cpp +++ b/scwx-qt/source/scwx/qt/settings/general_settings.cpp @@ -21,6 +21,7 @@ public: gridWidth_.SetDefault(1); gridHeight_.SetDefault(1); mapboxApiKey_.SetDefault("?"); + updateNotificationsEnabled_.SetDefault(true); fontSizes_.SetElementMinimum(1); fontSizes_.SetElementMaximum(72); @@ -42,6 +43,7 @@ public: SettingsVariable gridWidth_ {"grid_width"}; SettingsVariable gridHeight_ {"grid_height"}; SettingsVariable mapboxApiKey_ {"mapbox_api_key"}; + SettingsVariable updateNotificationsEnabled_ {"update_notifications"}; }; GeneralSettings::GeneralSettings() : @@ -52,7 +54,8 @@ GeneralSettings::GeneralSettings() : &p->fontSizes_, &p->gridWidth_, &p->gridHeight_, - &p->mapboxApiKey_}); + &p->mapboxApiKey_, + &p->updateNotificationsEnabled_}); SetDefaults(); } GeneralSettings::~GeneralSettings() = default; @@ -92,6 +95,11 @@ SettingsVariable& GeneralSettings::mapbox_api_key() const return p->mapboxApiKey_; } +SettingsVariable& GeneralSettings::update_notifications_enabled() const +{ + return p->updateNotificationsEnabled_; +} + bool operator==(const GeneralSettings& lhs, const GeneralSettings& rhs) { return (lhs.p->debugEnabled_ == rhs.p->debugEnabled_ && @@ -99,7 +107,9 @@ bool operator==(const GeneralSettings& lhs, const GeneralSettings& rhs) lhs.p->fontSizes_ == rhs.p->fontSizes_ && lhs.p->gridWidth_ == rhs.p->gridWidth_ && lhs.p->gridHeight_ == rhs.p->gridHeight_ && - lhs.p->mapboxApiKey_ == rhs.p->mapboxApiKey_); + lhs.p->mapboxApiKey_ == rhs.p->mapboxApiKey_ && + lhs.p->updateNotificationsEnabled_ == + rhs.p->updateNotificationsEnabled_); } } // namespace settings diff --git a/scwx-qt/source/scwx/qt/settings/general_settings.hpp b/scwx-qt/source/scwx/qt/settings/general_settings.hpp index 1770150f..48757e28 100644 --- a/scwx-qt/source/scwx/qt/settings/general_settings.hpp +++ b/scwx-qt/source/scwx/qt/settings/general_settings.hpp @@ -33,6 +33,7 @@ public: SettingsVariable& grid_height() const; SettingsVariable& grid_width() const; SettingsVariable& mapbox_api_key() const; + SettingsVariable& update_notifications_enabled() const; friend bool operator==(const GeneralSettings& lhs, const GeneralSettings& rhs); diff --git a/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp b/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp index 02c82b44..b8250d7f 100644 --- a/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp +++ b/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp @@ -84,6 +84,7 @@ public: &gridWidth_, &gridHeight_, &mapboxApiKey_, + &updateNotificationsEnabled_, &debugEnabled_}} { // Configure default alert phenomena colors @@ -137,7 +138,8 @@ public: settings::SettingsInterface gridWidth_ {}; settings::SettingsInterface gridHeight_ {}; settings::SettingsInterface mapboxApiKey_ {}; - settings::SettingsInterface debugEnabled_ {}; + settings::SettingsInterface updateNotificationsEnabled_ {}; + settings::SettingsInterface debugEnabled_ {}; std::unordered_map> colorTables_ {}; @@ -315,6 +317,11 @@ void SettingsDialogImpl::SetupGeneralTab() mapboxApiKey_.SetEditWidget(self_->ui->mapboxApiKeyLineEdit); mapboxApiKey_.SetResetButton(self_->ui->resetMapboxApiKeyButton); + updateNotificationsEnabled_.SetSettingsVariable( + generalSettings.update_notifications_enabled()); + updateNotificationsEnabled_.SetEditWidget( + self_->ui->enableUpdateNotificationsCheckBox); + debugEnabled_.SetSettingsVariable(generalSettings.debug_enabled()); debugEnabled_.SetEditWidget(self_->ui->debugEnabledCheckBox); } diff --git a/scwx-qt/source/scwx/qt/ui/settings_dialog.ui b/scwx-qt/source/scwx/qt/ui/settings_dialog.ui index 28bb1733..f021c230 100644 --- a/scwx-qt/source/scwx/qt/ui/settings_dialog.ui +++ b/scwx-qt/source/scwx/qt/ui/settings_dialog.ui @@ -224,6 +224,13 @@ + + + + Update Notifications Enabled + + + @@ -268,8 +275,8 @@ 0 0 - 489 - 382 + 66 + 18 diff --git a/test/data b/test/data index 16017d0b..5b507378 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit 16017d0b6446228ecfd8ef4cd733e8b1595ab2fb +Subproject commit 5b5073780fe44e55eb4c33799036683b28ffd2bd From d005b02ec524b752e4051700f1278c70acb55fea Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Sun, 23 Apr 2023 00:31:37 -0500 Subject: [PATCH 7/7] Remove "Enable Update Notifications" setting from Update Dialog This is a desirable feature, but the SettingsInterface class needs work to support multiple edit widgets. --- scwx-qt/source/scwx/qt/ui/update_dialog.ui | 7 ------- 1 file changed, 7 deletions(-) diff --git a/scwx-qt/source/scwx/qt/ui/update_dialog.ui b/scwx-qt/source/scwx/qt/ui/update_dialog.ui index 3ecb2ef2..0facca2a 100644 --- a/scwx-qt/source/scwx/qt/ui/update_dialog.ui +++ b/scwx-qt/source/scwx/qt/ui/update_dialog.ui @@ -111,13 +111,6 @@ - - - - &Enable Update Notifications - - -