mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 11:48:06 +01:00
updating: do not update update_check date when we couldn't do it (#9038)
* updating: do not update update_check date when we couldn't do it - improve general settings page "Last Checked" feature
This commit is contained in:
@@ -61,7 +61,7 @@ namespace updating
|
|||||||
throw std::runtime_error("Release object doesn't have the required asset");
|
throw std::runtime_error("Release object doesn't have the required asset");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::future<nonstd::expected<new_version_download_info, std::wstring>> get_new_github_version_info_async(const notifications::strings& strings, const bool prerelease)
|
std::future<nonstd::expected<github_version_info, std::wstring>> 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 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)
|
if (VERSION_MAJOR == 0 && VERSION_MINOR == 0)
|
||||||
@@ -104,7 +104,7 @@ namespace updating
|
|||||||
|
|
||||||
if (github_version <= current_version)
|
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") };
|
Uri release_page_url{ release_object.GetNamedString(L"html_url") };
|
||||||
@@ -142,24 +142,29 @@ namespace updating
|
|||||||
return installer_download_dst;
|
return installer_download_dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::future<void> try_autoupdate(const bool download_updates_automatically, const notifications::strings& strings)
|
std::future<bool> try_autoupdate(const bool download_updates_automatically, const notifications::strings& strings)
|
||||||
{
|
{
|
||||||
const auto new_version = co_await get_new_github_version_info_async(strings);
|
const auto version_check_result = co_await get_github_version_info_async(strings);
|
||||||
if (!new_version)
|
if (!version_check_result)
|
||||||
{
|
{
|
||||||
co_return;
|
co_return false;
|
||||||
}
|
}
|
||||||
|
if (std::holds_alternative<version_up_to_date>(*version_check_result))
|
||||||
|
{
|
||||||
|
co_return true;
|
||||||
|
}
|
||||||
|
const auto new_version = std::get<new_version_download_info>(*version_check_result);
|
||||||
|
|
||||||
if (download_updates_automatically && !could_be_costly_connection())
|
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;
|
bool download_success = false;
|
||||||
for (size_t i = 0; i < MAX_DOWNLOAD_ATTEMPTS; ++i)
|
for (size_t i = 0; i < MAX_DOWNLOAD_ATTEMPTS; ++i)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
http::HttpClient client;
|
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;
|
download_success = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -170,44 +175,45 @@ namespace updating
|
|||||||
}
|
}
|
||||||
if (!download_success)
|
if (!download_success)
|
||||||
{
|
{
|
||||||
updating::notifications::show_install_error(new_version.value(), strings);
|
updating::notifications::show_install_error(new_version, strings);
|
||||||
co_return;
|
co_return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
updating::notifications::show_version_ready(new_version.value(), strings);
|
updating::notifications::show_version_ready(new_version, strings);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
updating::notifications::show_visit_github(new_version.value(), strings);
|
updating::notifications::show_visit_github(new_version, strings);
|
||||||
}
|
}
|
||||||
|
co_return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::future<std::wstring> download_update(const notifications::strings& strings)
|
std::future<std::wstring> download_update(const notifications::strings& strings)
|
||||||
{
|
{
|
||||||
const auto new_version = co_await get_new_github_version_info_async(strings);
|
const auto version_check_result = co_await get_github_version_info_async(strings);
|
||||||
if (!new_version)
|
if (!version_check_result || std::holds_alternative<version_up_to_date>(*version_check_result))
|
||||||
{
|
{
|
||||||
co_return L"";
|
co_return L"";
|
||||||
}
|
}
|
||||||
|
const auto new_version = std::get<new_version_download_info>(*version_check_result);
|
||||||
auto installer_download_dst = create_download_path() / new_version->installer_filename;
|
auto installer_download_dst = create_download_path() / new_version.installer_filename;
|
||||||
updating::notifications::show_download_start(new_version.value(), strings);
|
updating::notifications::show_download_start(new_version, strings);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto progressUpdateHandle = [&](float progress) {
|
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;
|
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 (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
updating::notifications::show_install_error(new_version.value(), strings);
|
updating::notifications::show_install_error(new_version, strings);
|
||||||
co_return L"";
|
co_return L"";
|
||||||
}
|
}
|
||||||
|
|
||||||
co_return new_version->installer_filename;
|
co_return new_version.installer_filename;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <future>
|
#include <future>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <variant>
|
||||||
#include <winrt/Windows.Foundation.h>
|
#include <winrt/Windows.Foundation.h>
|
||||||
#include <expected.hpp>
|
#include <expected.hpp>
|
||||||
|
|
||||||
@@ -13,6 +14,9 @@
|
|||||||
namespace updating
|
namespace updating
|
||||||
{
|
{
|
||||||
using winrt::Windows::Foundation::Uri;
|
using winrt::Windows::Foundation::Uri;
|
||||||
|
struct version_up_to_date {};
|
||||||
|
using github_version_info = std::variant<new_version_download_info, version_up_to_date>;
|
||||||
|
|
||||||
struct new_version_download_info
|
struct new_version_download_info
|
||||||
{
|
{
|
||||||
Uri release_page_uri = nullptr;
|
Uri release_page_uri = nullptr;
|
||||||
@@ -21,10 +25,11 @@ namespace updating
|
|||||||
std::wstring installer_filename;
|
std::wstring installer_filename;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::future<void> try_autoupdate(const bool download_updates_automatically, const notifications::strings&);
|
// Returns whether the update check has succeeded
|
||||||
|
std::future<bool> try_autoupdate(const bool download_updates_automatically, const notifications::strings&);
|
||||||
std::filesystem::path get_pending_updates_path();
|
std::filesystem::path get_pending_updates_path();
|
||||||
std::future<std::wstring> download_update(const notifications::strings&);
|
std::future<std::wstring> download_update(const notifications::strings&);
|
||||||
std::future<nonstd::expected<new_version_download_info, std::wstring>> get_new_github_version_info_async(const notifications::strings& strings, const bool prerelease = false);
|
std::future<nonstd::expected<github_version_info, std::wstring>> get_github_version_info_async(const notifications::strings& strings, const bool prerelease = false);
|
||||||
|
|
||||||
// non-localized
|
// non-localized
|
||||||
constexpr inline std::wstring_view INSTALLER_FILENAME_PATTERN = L"powertoyssetup";
|
constexpr inline std::wstring_view INSTALLER_FILENAME_PATTERN = L"powertoyssetup";
|
||||||
|
|||||||
@@ -85,19 +85,25 @@ std::optional<std::wstring> dispatch_json_action_to_module(const json::JsonObjec
|
|||||||
}
|
}
|
||||||
else if (action == L"check_for_updates")
|
else if (action == L"check_for_updates")
|
||||||
{
|
{
|
||||||
auto new_version_info = check_for_updates();
|
if (auto update_check_result = check_for_updates())
|
||||||
const VersionHelper latestVersion =
|
{
|
||||||
new_version_info ? new_version_info->version :
|
VersionHelper latestVersion{ VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION };
|
||||||
VersionHelper{ VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION };
|
bool isVersionLatest = true;
|
||||||
|
if (auto new_version = std::get_if<updating::new_version_download_info>(&*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;
|
result.emplace(json.Stringify());
|
||||||
json.SetNamedValue(L"version", json::JsonValue::CreateStringValue(latestVersion.toWstring()));
|
|
||||||
json.SetNamedValue(L"isVersionLatest", json::JsonValue::CreateBooleanValue(!new_version_info));
|
|
||||||
|
|
||||||
result.emplace(json.Stringify());
|
UpdateState::store([](UpdateState& state) {
|
||||||
UpdateState::store([](UpdateState& state) {
|
state.github_update_last_checked_date.emplace(timeutil::now());
|
||||||
state.github_update_last_checked_date.emplace(timeutil::now());
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
else if (action == L"request_update_state_date")
|
else if (action == L"request_update_state_date")
|
||||||
{
|
{
|
||||||
@@ -107,7 +113,7 @@ std::optional<std::wstring> dispatch_json_action_to_module(const json::JsonObjec
|
|||||||
if (update_state.github_update_last_checked_date)
|
if (update_state.github_update_last_checked_date)
|
||||||
{
|
{
|
||||||
const time_t date = *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());
|
result.emplace(json.Stringify());
|
||||||
|
|||||||
@@ -14,6 +14,12 @@
|
|||||||
|
|
||||||
auto Strings = create_notifications_strings();
|
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()
|
bool start_msi_uninstallation_sequence()
|
||||||
{
|
{
|
||||||
const auto package_path = updating::get_msi_package_path();
|
const auto package_path = updating::get_msi_package_path();
|
||||||
@@ -40,8 +46,6 @@ bool start_msi_uninstallation_sequence()
|
|||||||
|
|
||||||
void github_update_worker()
|
void github_update_worker()
|
||||||
{
|
{
|
||||||
const int64_t update_check_period_minutes = 60 * 24;
|
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
auto state = UpdateState::read();
|
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);
|
int64_t last_checked_minutes_ago = timeutil::diff::in_minutes(timeutil::now(), *state.github_update_last_checked_date);
|
||||||
if (last_checked_minutes_ago < 0)
|
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;
|
const bool download_updates_automatically = get_general_settings().downloadUpdatesAutomatically;
|
||||||
|
bool update_check_ok = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
updating::try_autoupdate(download_updates_automatically, Strings).get();
|
update_check_ok = updating::try_autoupdate(download_updates_automatically, Strings).get();
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
// Couldn't autoupdate
|
// 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<updating::new_version_download_info> check_for_updates()
|
std::optional<updating::github_version_info> check_for_updates()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
const auto new_version = updating::get_new_github_version_info_async(Strings).get();
|
auto version_check_result = updating::get_github_version_info_async(Strings).get();
|
||||||
if (!new_version)
|
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;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
updating::notifications::show_available(new_version.value(), Strings);
|
if (std::holds_alternative<updating::version_up_to_date>(*version_check_result))
|
||||||
return std::move(new_version.value());
|
{
|
||||||
|
updating::notifications::show_unavailable(Strings, Strings.GITHUB_NEW_VERSION_UP_TO_DATE);
|
||||||
|
return std::move(*version_check_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto new_version = std::get<updating::new_version_download_info>(*version_check_result);
|
||||||
|
updating::notifications::show_available(new_version, Strings);
|
||||||
|
return std::move(new_version);
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,5 +4,5 @@
|
|||||||
|
|
||||||
bool start_msi_uninstallation_sequence();
|
bool start_msi_uninstallation_sequence();
|
||||||
void github_update_worker();
|
void github_update_worker();
|
||||||
std::optional<updating::new_version_download_info> check_for_updates();
|
std::optional<updating::github_version_info> check_for_updates();
|
||||||
bool launch_pending_update();
|
bool launch_pending_update();
|
||||||
@@ -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)")]
|
[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
|
public bool IsDarkThemeRadioButtonChecked
|
||||||
{
|
{
|
||||||
@@ -388,10 +396,9 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
|
|||||||
GeneralSettingsCustomAction customaction = new GeneralSettingsCustomAction(outsettings);
|
GeneralSettingsCustomAction customaction = new GeneralSettingsCustomAction(outsettings);
|
||||||
|
|
||||||
SendCheckForUpdatesConfigMSG(customaction.ToString());
|
SendCheckForUpdatesConfigMSG(customaction.ToString());
|
||||||
RequestUpdateCheckedDate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RequestUpdateCheckedDate()
|
public void RequestUpdateCheckedDate()
|
||||||
{
|
{
|
||||||
GeneralSettingsConfig.CustomActionName = "request_update_state_date";
|
GeneralSettingsConfig.CustomActionName = "request_update_state_date";
|
||||||
|
|
||||||
|
|||||||
@@ -705,7 +705,7 @@
|
|||||||
<value>Version:</value>
|
<value>Version:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="General_VersionLastChecked.Text" xml:space="preserve">
|
<data name="General_VersionLastChecked.Text" xml:space="preserve">
|
||||||
<value>Last checked: </value>
|
<value>Last successfully checked: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="General_Version.AutomationProperties.Name" xml:space="preserve">
|
<data name="General_Version.AutomationProperties.Name" xml:space="preserve">
|
||||||
<value>Version</value>
|
<value>Version</value>
|
||||||
|
|||||||
@@ -133,7 +133,9 @@
|
|||||||
|
|
||||||
<StackPanel Orientation="Horizontal"
|
<StackPanel Orientation="Horizontal"
|
||||||
Margin="{StaticResource SmallBottomMargin}"
|
Margin="{StaticResource SmallBottomMargin}"
|
||||||
AutomationProperties.LabeledBy="{Binding ElementName=General_VersionLastChecked}">
|
AutomationProperties.LabeledBy="{Binding ElementName=General_VersionLastChecked}"
|
||||||
|
Visibility="{Binding AutoUpdatesEnabled,
|
||||||
|
Converter={StaticResource VisibleIfTrueConverter}}">
|
||||||
<TextBlock x:Name="General_VersionLastChecked" x:Uid="General_VersionLastChecked" />
|
<TextBlock x:Name="General_VersionLastChecked" x:Uid="General_VersionLastChecked" />
|
||||||
<TextBlock Text="{x:Bind ViewModel.UpdateCheckedDate, Mode=OneWay}"
|
<TextBlock Text="{x:Bind ViewModel.UpdateCheckedDate, Mode=OneWay}"
|
||||||
Foreground="{ThemeResource ListViewItemForegroundPointerOver}"
|
Foreground="{ThemeResource ListViewItemForegroundPointerOver}"
|
||||||
@@ -144,12 +146,14 @@
|
|||||||
Style="{StaticResource AccentButtonStyle}"
|
Style="{StaticResource AccentButtonStyle}"
|
||||||
Foreground="White"
|
Foreground="White"
|
||||||
Command="{Binding CheckForUpdatesEventHandler}"
|
Command="{Binding CheckForUpdatesEventHandler}"
|
||||||
|
IsEnabled="{Binding AutoUpdatesEnabled}"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ToggleSwitch x:Uid="GeneralPage_ToggleSwitch_AutoDownloadUpdates"
|
<ToggleSwitch x:Uid="GeneralPage_ToggleSwitch_AutoDownloadUpdates"
|
||||||
Margin="{StaticResource MediumTopMargin}"
|
Margin="{StaticResource MediumTopMargin}"
|
||||||
Visibility="{Binding Mode=TwoWay, Path=IsAdmin, Converter={StaticResource VisibleIfTrueConverter}}"
|
Visibility="{Binding Mode=TwoWay, Path=IsAdmin, Converter={StaticResource VisibleIfTrueConverter}}"
|
||||||
IsOn="{Binding Mode=TwoWay, Path=AutoDownloadUpdates}"/>
|
IsOn="{Binding Mode=TwoWay, Path=AutoDownloadUpdates}"
|
||||||
|
IsEnabled="{Binding AutoUpdatesEnabled}" />
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,11 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
|||||||
string version = json.GetNamedString("version", string.Empty);
|
string version = json.GetNamedString("version", string.Empty);
|
||||||
bool isLatest = json.GetNamedBoolean("isVersionLatest", false);
|
bool isLatest = json.GetNamedBoolean("isVersionLatest", false);
|
||||||
|
|
||||||
|
if (json.ContainsKey("version"))
|
||||||
|
{
|
||||||
|
ViewModel.RequestUpdateCheckedDate();
|
||||||
|
}
|
||||||
|
|
||||||
var str = string.Empty;
|
var str = string.Empty;
|
||||||
if (isLatest)
|
if (isLatest)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user