diff --git a/src/common/updating/updating.cpp b/src/common/updating/updating.cpp index c2a0e5cfa8..601ea0d0e9 100644 --- a/src/common/updating/updating.cpp +++ b/src/common/updating/updating.cpp @@ -61,7 +61,7 @@ namespace updating throw std::runtime_error("Release object doesn't have the required asset"); } - std::future> get_new_github_version_info_async(const notifications::strings& strings, const bool prerelease) + std::future> get_github_version_info_async(const notifications::strings& strings, const bool prerelease) { // If the current version starts with 0.0.*, it means we're on a local build from a farm and shouldn't check for updates. if (VERSION_MAJOR == 0 && VERSION_MINOR == 0) @@ -104,7 +104,7 @@ namespace updating if (github_version <= current_version) { - co_return nonstd::make_unexpected(strings.GITHUB_NEW_VERSION_UP_TO_DATE); + co_return version_up_to_date{}; } Uri release_page_url{ release_object.GetNamedString(L"html_url") }; @@ -142,24 +142,29 @@ namespace updating return installer_download_dst; } - std::future try_autoupdate(const bool download_updates_automatically, const notifications::strings& strings) + std::future try_autoupdate(const bool download_updates_automatically, const notifications::strings& strings) { - const auto new_version = co_await get_new_github_version_info_async(strings); - if (!new_version) + const auto version_check_result = co_await get_github_version_info_async(strings); + if (!version_check_result) { - co_return; + co_return false; } + if (std::holds_alternative(*version_check_result)) + { + co_return true; + } + const auto new_version = std::get(*version_check_result); if (download_updates_automatically && !could_be_costly_connection()) { - auto installer_download_dst = create_download_path() / new_version->installer_filename; + auto installer_download_dst = create_download_path() / new_version.installer_filename; bool download_success = false; for (size_t i = 0; i < MAX_DOWNLOAD_ATTEMPTS; ++i) { try { http::HttpClient client; - co_await client.download(new_version->installer_download_url, installer_download_dst); + co_await client.download(new_version.installer_download_url, installer_download_dst); download_success = true; break; } @@ -170,44 +175,45 @@ namespace updating } if (!download_success) { - updating::notifications::show_install_error(new_version.value(), strings); - co_return; + updating::notifications::show_install_error(new_version, strings); + co_return false; } - updating::notifications::show_version_ready(new_version.value(), strings); + updating::notifications::show_version_ready(new_version, strings); } else { - updating::notifications::show_visit_github(new_version.value(), strings); + updating::notifications::show_visit_github(new_version, strings); } + co_return true; } std::future download_update(const notifications::strings& strings) { - const auto new_version = co_await get_new_github_version_info_async(strings); - if (!new_version) + const auto version_check_result = co_await get_github_version_info_async(strings); + if (!version_check_result || std::holds_alternative(*version_check_result)) { co_return L""; } - - auto installer_download_dst = create_download_path() / new_version->installer_filename; - updating::notifications::show_download_start(new_version.value(), strings); + const auto new_version = std::get(*version_check_result); + auto installer_download_dst = create_download_path() / new_version.installer_filename; + updating::notifications::show_download_start(new_version, strings); try { auto progressUpdateHandle = [&](float progress) { - updating::notifications::update_download_progress(new_version.value(), progress, strings); + updating::notifications::update_download_progress(new_version, progress, strings); }; http::HttpClient client; - co_await client.download(new_version->installer_download_url, installer_download_dst, progressUpdateHandle); + co_await client.download(new_version.installer_download_url, installer_download_dst, progressUpdateHandle); } catch (...) { - updating::notifications::show_install_error(new_version.value(), strings); + updating::notifications::show_install_error(new_version, strings); co_return L""; } - co_return new_version->installer_filename; + co_return new_version.installer_filename; } } diff --git a/src/common/updating/updating.h b/src/common/updating/updating.h index 58d0ff6f6b..dcde36eb7c 100644 --- a/src/common/updating/updating.h +++ b/src/common/updating/updating.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -13,6 +14,9 @@ namespace updating { using winrt::Windows::Foundation::Uri; + struct version_up_to_date {}; + using github_version_info = std::variant; + struct new_version_download_info { Uri release_page_uri = nullptr; @@ -21,10 +25,11 @@ namespace updating std::wstring installer_filename; }; - std::future try_autoupdate(const bool download_updates_automatically, const notifications::strings&); + // Returns whether the update check has succeeded + std::future try_autoupdate(const bool download_updates_automatically, const notifications::strings&); std::filesystem::path get_pending_updates_path(); std::future download_update(const notifications::strings&); - std::future> get_new_github_version_info_async(const notifications::strings& strings, const bool prerelease = false); + std::future> get_github_version_info_async(const notifications::strings& strings, const bool prerelease = false); // non-localized constexpr inline std::wstring_view INSTALLER_FILENAME_PATTERN = L"powertoyssetup"; diff --git a/src/runner/settings_window.cpp b/src/runner/settings_window.cpp index f3cb599fbc..46c0dc120c 100644 --- a/src/runner/settings_window.cpp +++ b/src/runner/settings_window.cpp @@ -85,19 +85,25 @@ std::optional dispatch_json_action_to_module(const json::JsonObjec } else if (action == L"check_for_updates") { - auto new_version_info = check_for_updates(); - const VersionHelper latestVersion = - new_version_info ? new_version_info->version : - VersionHelper{ VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION }; + if (auto update_check_result = check_for_updates()) + { + VersionHelper latestVersion{ VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION }; + bool isVersionLatest = true; + if (auto new_version = std::get_if(&*update_check_result)) + { + latestVersion = new_version->version; + isVersionLatest = false; + } + json::JsonObject json; + json.SetNamedValue(L"version", json::value(latestVersion.toWstring())); + json.SetNamedValue(L"isVersionLatest", json::value(isVersionLatest)); - json::JsonObject json; - json.SetNamedValue(L"version", json::JsonValue::CreateStringValue(latestVersion.toWstring())); - json.SetNamedValue(L"isVersionLatest", json::JsonValue::CreateBooleanValue(!new_version_info)); + result.emplace(json.Stringify()); - result.emplace(json.Stringify()); - UpdateState::store([](UpdateState& state) { - state.github_update_last_checked_date.emplace(timeutil::now()); - }); + UpdateState::store([](UpdateState& state) { + state.github_update_last_checked_date.emplace(timeutil::now()); + }); + } } else if (action == L"request_update_state_date") { @@ -107,7 +113,7 @@ std::optional dispatch_json_action_to_module(const json::JsonObjec if (update_state.github_update_last_checked_date) { const time_t date = *update_state.github_update_last_checked_date; - json.SetNamedValue(L"updateStateDate", json::JsonValue::CreateStringValue(std::to_wstring(date))); + json.SetNamedValue(L"updateStateDate", json::value(std::to_wstring(date))); } result.emplace(json.Stringify()); diff --git a/src/runner/update_utils.cpp b/src/runner/update_utils.cpp index 85827f76f6..5a5cec9c54 100644 --- a/src/runner/update_utils.cpp +++ b/src/runner/update_utils.cpp @@ -14,6 +14,12 @@ auto Strings = create_notifications_strings(); +namespace +{ + constexpr int64_t UPDATE_CHECK_INTERVAL_MINUTES = 60 * 24; + constexpr int64_t UPDATE_CHECK_AFTER_FAILED_INTERVAL_MINUTES = 60 * 2; +} + bool start_msi_uninstallation_sequence() { const auto package_path = updating::get_msi_package_path(); @@ -40,8 +46,6 @@ bool start_msi_uninstallation_sequence() void github_update_worker() { - const int64_t update_check_period_minutes = 60 * 24; - for (;;) { auto state = UpdateState::read(); @@ -51,40 +55,57 @@ void github_update_worker() int64_t last_checked_minutes_ago = timeutil::diff::in_minutes(timeutil::now(), *state.github_update_last_checked_date); if (last_checked_minutes_ago < 0) { - last_checked_minutes_ago = update_check_period_minutes; + last_checked_minutes_ago = UPDATE_CHECK_INTERVAL_MINUTES; } - sleep_minutes_till_next_update = max(0, update_check_period_minutes - last_checked_minutes_ago); + sleep_minutes_till_next_update = max(0, UPDATE_CHECK_INTERVAL_MINUTES - last_checked_minutes_ago); } - std::this_thread::sleep_for(std::chrono::minutes(sleep_minutes_till_next_update)); + std::this_thread::sleep_for(std::chrono::minutes{ sleep_minutes_till_next_update }); const bool download_updates_automatically = get_general_settings().downloadUpdatesAutomatically; + bool update_check_ok = false; try { - updating::try_autoupdate(download_updates_automatically, Strings).get(); + update_check_ok = updating::try_autoupdate(download_updates_automatically, Strings).get(); } catch (...) { // Couldn't autoupdate + update_check_ok = false; + } + + if (update_check_ok) + { + UpdateState::store([](UpdateState& state) { + state.github_update_last_checked_date.emplace(timeutil::now()); + }); + } + else + { + std::this_thread::sleep_for(std::chrono::minutes{ UPDATE_CHECK_AFTER_FAILED_INTERVAL_MINUTES }); } - UpdateState::store([](UpdateState& state) { - state.github_update_last_checked_date.emplace(timeutil::now()); - }); } } -std::optional check_for_updates() +std::optional check_for_updates() { try { - const auto new_version = updating::get_new_github_version_info_async(Strings).get(); - if (!new_version) + auto version_check_result = updating::get_github_version_info_async(Strings).get(); + if (!version_check_result) { - updating::notifications::show_unavailable(Strings, std::move(new_version.error())); + updating::notifications::show_unavailable(Strings, std::move(version_check_result.error())); return std::nullopt; } - updating::notifications::show_available(new_version.value(), Strings); - return std::move(new_version.value()); + if (std::holds_alternative(*version_check_result)) + { + updating::notifications::show_unavailable(Strings, Strings.GITHUB_NEW_VERSION_UP_TO_DATE); + return std::move(*version_check_result); + } + + auto new_version = std::get(*version_check_result); + updating::notifications::show_available(new_version, Strings); + return std::move(new_version); } catch (...) { diff --git a/src/runner/update_utils.h b/src/runner/update_utils.h index dd6da24f8b..72e4119853 100644 --- a/src/runner/update_utils.h +++ b/src/runner/update_utils.h @@ -4,5 +4,5 @@ bool start_msi_uninstallation_sequence(); void github_update_worker(); -std::optional check_for_updates(); +std::optional check_for_updates(); bool launch_pending_update(); \ No newline at end of file diff --git a/src/settings-ui/Microsoft.PowerToys.Settings.UI.Library/ViewModels/GeneralViewModel.cs b/src/settings-ui/Microsoft.PowerToys.Settings.UI.Library/ViewModels/GeneralViewModel.cs index a7bf581ee1..62a6b6133c 100644 --- a/src/settings-ui/Microsoft.PowerToys.Settings.UI.Library/ViewModels/GeneralViewModel.cs +++ b/src/settings-ui/Microsoft.PowerToys.Settings.UI.Library/ViewModels/GeneralViewModel.cs @@ -237,6 +237,14 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels } } + public static bool AutoUpdatesEnabled + { + get + { + return Helper.GetProductVersion() != "v0.0.1"; + } + } + [SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "This may throw if the XAML page is not initialized in tests (https://github.com/microsoft/PowerToys/pull/2676)")] public bool IsDarkThemeRadioButtonChecked { @@ -388,10 +396,9 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels GeneralSettingsCustomAction customaction = new GeneralSettingsCustomAction(outsettings); SendCheckForUpdatesConfigMSG(customaction.ToString()); - RequestUpdateCheckedDate(); } - private void RequestUpdateCheckedDate() + public void RequestUpdateCheckedDate() { GeneralSettingsConfig.CustomActionName = "request_update_state_date"; diff --git a/src/settings-ui/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw index bde52751fd..5e9d9e32ee 100644 --- a/src/settings-ui/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw +++ b/src/settings-ui/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw @@ -705,7 +705,7 @@ Version: - Last checked: + Last successfully checked: Version diff --git a/src/settings-ui/Microsoft.PowerToys.Settings.UI/Views/GeneralPage.xaml b/src/settings-ui/Microsoft.PowerToys.Settings.UI/Views/GeneralPage.xaml index 516bdb0a77..51a598a348 100644 --- a/src/settings-ui/Microsoft.PowerToys.Settings.UI/Views/GeneralPage.xaml +++ b/src/settings-ui/Microsoft.PowerToys.Settings.UI/Views/GeneralPage.xaml @@ -133,7 +133,9 @@ + AutomationProperties.LabeledBy="{Binding ElementName=General_VersionLastChecked}" + Visibility="{Binding AutoUpdatesEnabled, + Converter={StaticResource VisibleIfTrueConverter}}"> + IsOn="{Binding Mode=TwoWay, Path=AutoDownloadUpdates}" + IsEnabled="{Binding AutoUpdatesEnabled}" /> diff --git a/src/settings-ui/Microsoft.PowerToys.Settings.UI/Views/GeneralPage.xaml.cs b/src/settings-ui/Microsoft.PowerToys.Settings.UI/Views/GeneralPage.xaml.cs index 1b0ca226e5..42788cb58e 100644 --- a/src/settings-ui/Microsoft.PowerToys.Settings.UI/Views/GeneralPage.xaml.cs +++ b/src/settings-ui/Microsoft.PowerToys.Settings.UI/Views/GeneralPage.xaml.cs @@ -56,6 +56,11 @@ namespace Microsoft.PowerToys.Settings.UI.Views string version = json.GetNamedString("version", string.Empty); bool isLatest = json.GetNamedBoolean("isVersionLatest", false); + if (json.ContainsKey("version")) + { + ViewModel.RequestUpdateCheckedDate(); + } + var str = string.Empty; if (isLatest) {