diff --git a/src/common/VersionHelper.cpp b/src/common/VersionHelper.cpp
index 92f014d65c..fefdb82432 100644
--- a/src/common/VersionHelper.cpp
+++ b/src/common/VersionHelper.cpp
@@ -27,3 +27,14 @@ VersionHelper::VersionHelper(int major, int minor, int revision) :
revision(revision)
{
}
+
+std::wstring VersionHelper::toWstring() const
+{
+ std::wstring result{ L"v" };
+ result += std::to_wstring(major);
+ result += L'.';
+ result += std::to_wstring(minor);
+ result += L'.';
+ result += std::to_wstring(revision);
+ return result;
+}
diff --git a/src/common/VersionHelper.h b/src/common/VersionHelper.h
index 1f575dd507..6f1f22e59c 100644
--- a/src/common/VersionHelper.h
+++ b/src/common/VersionHelper.h
@@ -13,4 +13,6 @@ struct VersionHelper
int major;
int minor;
int revision;
+
+ std::wstring toWstring() const;
};
diff --git a/src/common/notifications.cpp b/src/common/notifications.cpp
index e7051e423c..a15c9f672e 100644
--- a/src/common/notifications.cpp
+++ b/src/common/notifications.cpp
@@ -33,6 +33,11 @@ namespace
constexpr std::wstring_view WIN32_AUMID = L"Microsoft.PowerToysWin32";
}
+namespace localized_strings
+{
+ constexpr std::wstring_view SNOOZE_BUTTON = L"Snooze";
+}
+
static DWORD loop_thread_id()
{
static const DWORD thread_id = GetCurrentThreadId();
@@ -213,7 +218,14 @@ void notifications::show_toast_with_activations(std::wstring message, std::wstri
toast_xml += selection_id;
toast_xml += LR"(" type="selection" defaultInput=")";
toast_xml += std::to_wstring(b.durations[0].minutes);
- toast_xml += LR"(">)";
+ toast_xml += L'"';
+ if (!b.snooze_title.empty())
+ {
+ toast_xml += LR"( title=")";
+ toast_xml += b.snooze_title;
+ toast_xml += L'"';
+ }
+ toast_xml += L'>';
for (const auto& duration : b.durations)
{
toast_xml += LR"()";
+ toast_xml += LR"( content=")";
+ toast_xml += localized_strings::SNOOZE_BUTTON;
+ toast_xml += LR"(" />)";
} },
actions[i]);
}
diff --git a/src/common/notifications.h b/src/common/notifications.h
index 65be031ed3..b4f57e5398 100644
--- a/src/common/notifications.h
+++ b/src/common/notifications.h
@@ -22,6 +22,7 @@ namespace notifications
struct snooze_button
{
+ std::wstring snooze_title;
std::vector durations;
};
diff --git a/src/common/updating/updating.cpp b/src/common/updating/updating.cpp
index e463525e92..c102f193a3 100644
--- a/src/common/updating/updating.cpp
+++ b/src/common/updating/updating.cpp
@@ -28,6 +28,10 @@ namespace
const wchar_t LATEST_RELEASE_ENDPOINT[] = L"https://api.github.com/repos/microsoft/PowerToys/releases/latest";
const wchar_t MSIX_PACKAGE_NAME[] = L"Microsoft.PowerToys";
const wchar_t MSIX_PACKAGE_PUBLISHER[] = L"CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US";
+
+ const wchar_t UPDATE_NOTIFY_TOAST_TAG[] = L"PTUpdateNotifyTag";
+ const wchar_t UPDATE_READY_TOAST_TAG[] = L"PTUpdateReadyTag";
+ const size_t MAX_DOWNLOAD_ATTEMPTS = 3;
}
namespace localized_strings
@@ -37,12 +41,14 @@ namespace localized_strings
const wchar_t UNINSTALLATION_SUCCESS[] = L"Previous version of PowerToys was uninstalled successfully.";
const wchar_t UNINSTALLATION_UNKNOWN_ERROR[] = L"Error: please uninstall the previous version of PowerToys manually.";
- const wchar_t GITHUB_NEW_VERSION_READY_TO_INSTALL[] = L"An update to PowerToys is ready to install.";
+ const wchar_t GITHUB_NEW_VERSION_READY_TO_INSTALL[] = L"An update to PowerToys is ready to install.\n";
+ const wchar_t GITHUB_NEW_VERSION_DOWNLOAD_INSTALL_ERROR[] = L"Error: couldn't download PowerToys installer. Visit our GitHub page to update.\n";
const wchar_t GITHUB_NEW_VERSION_UPDATE_NOW[] = L"Update now";
const wchar_t GITHUB_NEW_VERSION_UPDATE_AFTER_RESTART[] = L"At next launch";
- const wchar_t GITHUB_NEW_VERSION_AVAILABLE_OFFER_VISIT[] = L"An update to PowerToys is available. Visit our GitHub page to get ";
+ const wchar_t GITHUB_NEW_VERSION_AVAILABLE_OFFER_VISIT[] = L"An update to PowerToys is available. Visit our GitHub page to update.\n";
const wchar_t GITHUB_NEW_VERSION_AGREE[] = L"Visit";
+ const wchar_t GITHUB_NEW_VERSION_SNOOZE_TITLE[] = L"Click Snooze to be reminded in:";
const wchar_t GITHUB_NEW_VERSION_UPDATE_SNOOZE_1D[] = L"1 day";
const wchar_t GITHUB_NEW_VERSION_UPDATE_SNOOZE_5D[] = L"5 days";
}
@@ -204,6 +210,17 @@ namespace updating
return { std::move(path_str) };
}
+ std::future attempt_to_download_installer(const std::filesystem::path& destination, const winrt::Windows::Foundation::Uri& url)
+ {
+ namespace storage = winrt::Windows::Storage;
+
+ auto client = create_http_client();
+ auto response = co_await client.GetAsync(url);
+ (void)response.EnsureSuccessStatusCode();
+ auto msi_installer_file_stream = co_await storage::Streams::FileRandomAccessStream::OpenAsync(destination.c_str(), storage::FileAccessMode::ReadWrite, storage::StorageOpenOptions::AllowReadersAndWriters, storage::Streams::FileOpenDisposition::CreateAlways);
+ co_await response.Content().WriteToStreamAsync(msi_installer_file_stream);
+ }
+
std::future try_autoupdate(const bool download_updates_automatically)
{
const auto new_version = co_await get_new_github_version_info_async();
@@ -212,32 +229,58 @@ namespace updating
co_return;
}
using namespace localized_strings;
- namespace storage = winrt::Windows::Storage;
+ auto current_version_to_next_version = VersionHelper{ VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION }.toWstring();
+ current_version_to_next_version += L" -> ";
+ current_version_to_next_version += new_version->version_string;
if (download_updates_automatically && !could_be_costly_connection())
{
- auto client = create_http_client();
- auto response = co_await client.GetAsync(new_version->msi_download_url);
- (void)response.EnsureSuccessStatusCode();
-
- auto download_dst = get_pending_updates_path();
+ auto installer_download_dst = get_pending_updates_path();
std::error_code _;
- std::filesystem::create_directories(download_dst, _);
- download_dst /= new_version->msi_filename;
- auto msi_installer_file_stream = co_await storage::Streams::FileRandomAccessStream::OpenAsync(download_dst.c_str(), storage::FileAccessMode::ReadWrite, storage::StorageOpenOptions::AllowReadersAndWriters, storage::Streams::FileOpenDisposition::CreateAlways);
- co_await response.Content().WriteToStreamAsync(msi_installer_file_stream);
- notifications::toast_params toast_params{ L"PTUpdateReadyTag", false };
+ std::filesystem::create_directories(installer_download_dst, _);
+ installer_download_dst /= new_version->msi_filename;
+
+ bool download_success = false;
+ for (size_t i = 0; i < MAX_DOWNLOAD_ATTEMPTS; ++i)
+ {
+ try
+ {
+ co_await attempt_to_download_installer(installer_download_dst, new_version->msi_download_url);
+ download_success = true;
+ break;
+ }
+ catch (...)
+ {
+ // reattempt to download or do nothing
+ }
+ }
+ if (!download_success)
+ {
+ notifications::toast_params toast_params{ UPDATE_NOTIFY_TOAST_TAG, false };
+ std::wstring contents = GITHUB_NEW_VERSION_DOWNLOAD_INSTALL_ERROR;
+ contents += current_version_to_next_version;
+
+ notifications::show_toast_with_activations(std::move(contents), {}, { notifications::link_button{ GITHUB_NEW_VERSION_AGREE, new_version->release_page_uri.ToString().c_str() } }, std::move(toast_params));
+
+ co_return;
+ }
+
+ notifications::toast_params toast_params{ UPDATE_READY_TOAST_TAG, false };
std::wstring new_version_ready{ GITHUB_NEW_VERSION_READY_TO_INSTALL };
- new_version_ready += L" ";
- new_version_ready += new_version->version_string;
- notifications::show_toast_with_activations(std::move(new_version_ready), {}, { notifications::link_button{ GITHUB_NEW_VERSION_UPDATE_NOW, L"powertoys://update_now/" }, notifications::link_button{ GITHUB_NEW_VERSION_UPDATE_AFTER_RESTART, L"powertoys://schedule_update/" }, notifications::snooze_button{ { { GITHUB_NEW_VERSION_UPDATE_SNOOZE_1D, 24 * 60 }, { GITHUB_NEW_VERSION_UPDATE_SNOOZE_5D, 120 * 60 } } } }, std::move(toast_params));
+ new_version_ready += current_version_to_next_version;
+
+ notifications::show_toast_with_activations(std::move(new_version_ready),
+ {},
+ { notifications::link_button{ GITHUB_NEW_VERSION_UPDATE_NOW, L"powertoys://update_now/" },
+ notifications::link_button{ GITHUB_NEW_VERSION_UPDATE_AFTER_RESTART, L"powertoys://schedule_update/" },
+ notifications::snooze_button{ GITHUB_NEW_VERSION_SNOOZE_TITLE, { { GITHUB_NEW_VERSION_UPDATE_SNOOZE_1D, 24 * 60 }, { GITHUB_NEW_VERSION_UPDATE_SNOOZE_5D, 120 * 60 } } } },
+ std::move(toast_params));
}
else
{
- notifications::toast_params toast_params{ L"PTUpdateNotifyTag", false };
+ notifications::toast_params toast_params{ UPDATE_NOTIFY_TOAST_TAG, false };
std::wstring contents = GITHUB_NEW_VERSION_AVAILABLE_OFFER_VISIT;
- contents += new_version->version_string;
- contents += L'.';
+ contents += current_version_to_next_version;
notifications::show_toast_with_activations(std::move(contents), {}, { notifications::link_button{ GITHUB_NEW_VERSION_AGREE, new_version->release_page_uri.ToString().c_str() } }, std::move(toast_params));
}
}