Check for updates automatically (but don't install) (#13437)

This PR adds support to the About Dialog for checking the store to see
if there's a new version of the Terminal package available. We'll only
do this once per day, per terminal window.

In dev mode, we'll always fake it and say there's an update to `x.y.z`
available.

This also involved pulling all of the About dialog code out into its own
class. All that is goodness.

We don't currently provide a button for _installing_ the update. We just
check. Incremental progress is better than none.

Co-authored-by: Mike Griese <migrie@microsoft.com>
Co-authored-by: Leonard Hecker <lhecker@microsoft.com>
This commit is contained in:
Dustin L. Howett
2023-04-06 18:31:19 -05:00
committed by GitHub
parent 7fbd3be8c3
commit ea44375f6d
11 changed files with 280 additions and 42 deletions

View File

@@ -2283,6 +2283,8 @@ xunit
xutr
XVIRTUALSCREEN
XWalk
xwwyzz
xxyyzz
yact
YCast
YCENTER

View File

@@ -0,0 +1,128 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "AboutDialog.h"
#include "AboutDialog.g.cpp"
#include <LibraryResources.h>
#include <WtExeUtils.h>
#include "../../types/inc/utils.hpp"
#include "Utils.h"
using namespace winrt;
using namespace winrt::Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal;
using namespace ::TerminalApp;
using namespace std::chrono_literals;
namespace winrt
{
namespace WUX = Windows::UI::Xaml;
using IInspectable = Windows::Foundation::IInspectable;
}
namespace winrt::TerminalApp::implementation
{
AboutDialog::AboutDialog()
{
InitializeComponent();
}
winrt::hstring AboutDialog::ApplicationDisplayName()
{
return CascadiaSettings::ApplicationDisplayName();
}
winrt::hstring AboutDialog::ApplicationVersion()
{
return CascadiaSettings::ApplicationVersion();
}
void AboutDialog::_SendFeedbackOnClick(const IInspectable& /*sender*/, const Windows::UI::Xaml::Controls::ContentDialogButtonClickEventArgs& /*eventArgs*/)
{
#if defined(WT_BRANDING_RELEASE)
ShellExecute(nullptr, nullptr, L"https://go.microsoft.com/fwlink/?linkid=2125419", nullptr, nullptr, SW_SHOW);
#else
ShellExecute(nullptr, nullptr, L"https://go.microsoft.com/fwlink/?linkid=2204904", nullptr, nullptr, SW_SHOW);
#endif
}
void AboutDialog::_ThirdPartyNoticesOnClick(const IInspectable& /*sender*/, const Windows::UI::Xaml::RoutedEventArgs& /*eventArgs*/)
{
std::filesystem::path currentPath{ wil::GetModuleFileNameW<std::wstring>(nullptr) };
currentPath.replace_filename(L"NOTICE.html");
ShellExecute(nullptr, nullptr, currentPath.c_str(), nullptr, nullptr, SW_SHOW);
}
bool AboutDialog::UpdatesAvailable() const
{
return !_pendingUpdateVersion.empty();
}
winrt::hstring AboutDialog::PendingUpdateVersion() const
{
return _pendingUpdateVersion;
}
void AboutDialog::_SetPendingUpdateVersion(const winrt::hstring& version)
{
_pendingUpdateVersion = version;
_PropertyChangedHandlers(*this, WUX::Data::PropertyChangedEventArgs{ L"PendingUpdateVersion" });
_PropertyChangedHandlers(*this, WUX::Data::PropertyChangedEventArgs{ L"UpdatesAvailable" });
}
winrt::fire_and_forget AboutDialog::QueueUpdateCheck()
{
auto strongThis = get_strong();
auto now{ std::chrono::system_clock::now() };
if (now - _lastUpdateCheck < std::chrono::days{ 1 })
{
co_return;
}
_lastUpdateCheck = now;
if (!IsPackaged())
{
co_return;
}
co_await wil::resume_foreground(strongThis->Dispatcher());
_SetPendingUpdateVersion({});
CheckingForUpdates(true);
try
{
#ifdef WT_BRANDING_DEV
// **DEV BRANDING**: Always sleep for three seconds and then report that
// there is an update available. This lets us test the system.
co_await winrt::resume_after(std::chrono::seconds{ 3 });
co_await wil::resume_foreground(strongThis->Dispatcher());
_SetPendingUpdateVersion(L"X.Y.Z");
#else // release build, likely has a store context
if (auto storeContext{ winrt::Windows::Services::Store::StoreContext::GetDefault() })
{
const auto updates = co_await storeContext.GetAppAndOptionalStorePackageUpdatesAsync();
co_await wil::resume_foreground(strongThis->Dispatcher());
const auto numUpdates = updates.Size();
if (numUpdates > 0)
{
const auto update = updates.GetAt(0);
const auto version = update.Package().Id().Version();
const auto str = fmt::format(FMT_COMPILE(L"{}.{}.{}"), version.Major, version.Minor, version.Build);
_SetPendingUpdateVersion(winrt::hstring{ str });
}
}
#endif
}
catch (...)
{
// do nothing on failure
}
co_await wil::resume_foreground(strongThis->Dispatcher());
CheckingForUpdates(false);
}
}

View File

@@ -0,0 +1,40 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "AboutDialog.g.h"
namespace winrt::TerminalApp::implementation
{
struct AboutDialog : AboutDialogT<AboutDialog>
{
public:
AboutDialog();
winrt::hstring ApplicationDisplayName();
winrt::hstring ApplicationVersion();
bool UpdatesAvailable() const;
winrt::hstring PendingUpdateVersion() const;
winrt::fire_and_forget QueueUpdateCheck();
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
WINRT_OBSERVABLE_PROPERTY(bool, CheckingForUpdates, _PropertyChangedHandlers, false);
private:
friend struct AboutDialogT<AboutDialog>; // for Xaml to bind events
void _SetPendingUpdateVersion(const winrt::hstring& pendingUpdateVersion);
std::chrono::system_clock::time_point _lastUpdateCheck{};
winrt::hstring _pendingUpdateVersion;
void _ThirdPartyNoticesOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
void _SendFeedbackOnClick(const IInspectable& sender, const Windows::UI::Xaml::Controls::ContentDialogButtonClickEventArgs& eventArgs);
};
}
namespace winrt::TerminalApp::factory_implementation
{
BASIC_FACTORY(AboutDialog);
}

View File

@@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
namespace TerminalApp
{
[default_interface] runtimeclass AboutDialog : Windows.UI.Xaml.Controls.ContentDialog, Windows.UI.Xaml.Data.INotifyPropertyChanged
{
AboutDialog();
String ApplicationDisplayName { get; };
String ApplicationVersion { get; };
Boolean CheckingForUpdates { get; };
Boolean UpdatesAvailable { get; };
String PendingUpdateVersion { get; };
void QueueUpdateCheck();
}
}

View File

@@ -0,0 +1,63 @@
<!--
Copyright (c) Microsoft Corporation. All rights reserved. Licensed under
the MIT License. See LICENSE in the project root for license information.
-->
<ContentDialog x:Class="TerminalApp.AboutDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:TerminalApp"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mux="using:Microsoft.UI.Xaml.Controls"
x:Uid="AboutDialog"
DefaultButton="Close"
PrimaryButtonClick="_SendFeedbackOnClick"
Style="{StaticResource DefaultContentDialogStyle}"
mc:Ignorable="d">
<StackPanel Orientation="Vertical">
<TextBlock IsTextSelectionEnabled="True">
<Run Text="{x:Bind ApplicationDisplayName}" /> <LineBreak />
<Run x:Uid="AboutDialog_VersionLabel" />
<Run Text="{x:Bind ApplicationVersion}" />
</TextBlock>
<StackPanel Orientation="Vertical">
<StackPanel Padding="0,4,0,4"
VerticalAlignment="Center"
Orientation="Horizontal"
Visibility="{x:Bind CheckingForUpdates, Mode=OneWay}">
<mux:ProgressRing Width="16"
Height="16"
IsActive="True"
IsIndeterminate="True" />
<TextBlock x:Uid="AboutDialog_CheckingForUpdatesLabel"
Padding="4,0,0,0" />
</StackPanel>
<StackPanel Padding="0,4,0,4"
VerticalAlignment="Center"
Orientation="Vertical"
Visibility="{x:Bind UpdatesAvailable, Mode=OneWay}">
<TextBlock IsTextSelectionEnabled="False">
<Run x:Uid="AboutDialog_UpdateAvailableLabel" /> <LineBreak />
<Run x:Uid="AboutDialog_VersionLabel" />
<Run Text="{x:Bind PendingUpdateVersion, Mode=OneWay}" />
</TextBlock>
<!-- <Button x:Uid="AboutDialog_InstallUpdateButton"
Margin="0" />-->
</StackPanel>
</StackPanel>
<HyperlinkButton x:Uid="AboutDialog_SourceCodeLink"
NavigateUri="https://go.microsoft.com/fwlink/?linkid=2203152" />
<HyperlinkButton x:Uid="AboutDialog_DocumentationLink"
NavigateUri="https://go.microsoft.com/fwlink/?linkid=2125416" />
<HyperlinkButton x:Uid="AboutDialog_ReleaseNotesLink"
NavigateUri="https://go.microsoft.com/fwlink/?linkid=2125417" />
<HyperlinkButton x:Uid="AboutDialog_PrivacyPolicyLink"
NavigateUri="https://go.microsoft.com/fwlink/?linkid=2125418" />
<HyperlinkButton x:Uid="AboutDialog_ThirdPartyNoticesLink"
Click="_ThirdPartyNoticesOnClick" />
</StackPanel>
</ContentDialog>

View File

@@ -758,6 +758,18 @@
<value>Suggestions found: {0}</value>
<comment>{0} will be replaced with a number.</comment>
</data>
<data name="AboutDialog_CheckingForUpdatesLabel.Text" xml:space="preserve">
<value>Checking for updates...</value>
<comment></comment>
</data>
<data name="AboutDialog_UpdateAvailableLabel.Text" xml:space="preserve">
<value>An update is available.</value>
<comment></comment>
</data>
<data name="AboutDialog_InstallUpdateButton.Content" xml:space="preserve">
<value>Install now</value>
<comment></comment>
</data>
<data name="DuplicateRemainingProfilesEntry" xml:space="preserve">
<value>The "newTabMenu" field contains more than one entry of type "remainingProfiles". Only the first one will be considered.</value>
<comment>{Locked="newTabMenu"} {Locked="remainingProfiles"}</comment>

View File

@@ -36,6 +36,9 @@
</ItemGroup>
<!-- When we add other user controls, they should go in here as so: -->
<ItemGroup>
<Page Include="AboutDialog.xaml">
<SubType>Designer</SubType>
</Page>
<Page Include="MinMaxCloseControl.xaml">
<SubType>Designer</SubType>
</Page>
@@ -132,6 +135,9 @@
<ClInclude Include="AppKeyBindings.h">
<DependentUpon>AppKeyBindings.idl</DependentUpon>
</ClInclude>
<ClInclude Include="AboutDialog.h">
<DependentUpon>AboutDialog.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="App.h">
<DependentUpon>App.xaml</DependentUpon>
</ClInclude>
@@ -231,6 +237,9 @@
<ClCompile Include="ShortcutActionDispatch.cpp">
<DependentUpon>ShortcutActionDispatch.idl</DependentUpon>
</ClCompile>
<ClCompile Include="AboutDialog.cpp">
<DependentUpon>AboutDialog.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="App.cpp">
<DependentUpon>App.xaml</DependentUpon>
</ClCompile>
@@ -256,6 +265,9 @@
<Midl Include="ActionPaletteItem.idl" />
<Midl Include="CommandLinePaletteItem.idl" />
<Midl Include="IDirectKeyListener.idl" />
<Midl Include="AboutDialog.idl">
<DependentUpon>AboutDialog.xaml</DependentUpon>
</Midl>
<Midl Include="App.idl">
<DependentUpon>App.xaml</DependentUpon>
</Midl>

View File

@@ -685,6 +685,7 @@ namespace winrt::TerminalApp::implementation
// Notes link, send feedback link and privacy policy link.
void TerminalPage::_ShowAboutDialog()
{
AboutDialog().QueueUpdateCheck();
_ShowDialogHelper(L"AboutDialog");
}
@@ -698,22 +699,6 @@ namespace winrt::TerminalApp::implementation
return CascadiaSettings::ApplicationVersion();
}
void TerminalPage::_SendFeedbackOnClick(const IInspectable& /*sender*/, const Windows::UI::Xaml::Controls::ContentDialogButtonClickEventArgs& /*eventArgs*/)
{
#if defined(WT_BRANDING_RELEASE)
ShellExecute(nullptr, nullptr, L"https://go.microsoft.com/fwlink/?linkid=2125419", nullptr, nullptr, SW_SHOW);
#else
ShellExecute(nullptr, nullptr, L"https://go.microsoft.com/fwlink/?linkid=2204904", nullptr, nullptr, SW_SHOW);
#endif
}
void TerminalPage::_ThirdPartyNoticesOnClick(const IInspectable& /*sender*/, const Windows::UI::Xaml::RoutedEventArgs& /*eventArgs*/)
{
std::filesystem::path currentPath{ wil::GetModuleFileNameW<std::wstring>(nullptr) };
currentPath.replace_filename(L"NOTICE.html");
ShellExecute(nullptr, nullptr, currentPath.c_str(), nullptr, nullptr, SW_SHOW);
}
// Method Description:
// - Helper to show a content dialog
// - We only open a content dialog if there isn't one open already

View File

@@ -301,8 +301,6 @@ namespace winrt::TerminalApp::implementation
void _SettingsButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
void _CommandPaletteButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
void _AboutButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
void _ThirdPartyNoticesOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
void _SendFeedbackOnClick(const IInspectable& sender, const Windows::UI::Xaml::Controls::ContentDialogButtonClickEventArgs& eventArgs);
void _KeyDownHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e);
static ::Microsoft::Terminal::Core::ControlKeyStates _GetPressedModifierKeys() noexcept;

View File

@@ -96,30 +96,8 @@
just this weird hole that appeared in Row 0.
-->
<ContentDialog x:Name="AboutDialog"
x:Uid="AboutDialog"
Grid.Row="2"
x:Load="False"
DefaultButton="Close"
PrimaryButtonClick="_SendFeedbackOnClick">
<StackPanel Orientation="Vertical">
<TextBlock IsTextSelectionEnabled="True">
<Run Text="{x:Bind ApplicationDisplayName}" /> <LineBreak />
<Run x:Uid="AboutDialog_VersionLabel" />
<Run Text="{x:Bind ApplicationVersion}" />
</TextBlock>
<HyperlinkButton x:Uid="AboutDialog_SourceCodeLink"
NavigateUri="https://go.microsoft.com/fwlink/?linkid=2203152" />
<HyperlinkButton x:Uid="AboutDialog_DocumentationLink"
NavigateUri="https://go.microsoft.com/fwlink/?linkid=2125416" />
<HyperlinkButton x:Uid="AboutDialog_ReleaseNotesLink"
NavigateUri="https://go.microsoft.com/fwlink/?linkid=2125417" />
<HyperlinkButton x:Uid="AboutDialog_PrivacyPolicyLink"
NavigateUri="https://go.microsoft.com/fwlink/?linkid=2125418" />
<HyperlinkButton x:Uid="AboutDialog_ThirdPartyNoticesLink"
Click="_ThirdPartyNoticesOnClick" />
</StackPanel>
</ContentDialog>
<local:AboutDialog x:Name="AboutDialog"
Grid.Row="2" />
<ContentDialog x:Name="QuitDialog"
x:Uid="QuitDialog"

View File

@@ -59,6 +59,7 @@
#include <winrt/Microsoft.Terminal.TerminalConnection.h>
#include <winrt/Microsoft.Terminal.Settings.Editor.h>
#include <winrt/Microsoft.Terminal.Settings.Model.h>
#include <winrt/Windows.Services.Store.h>
#include <winrt/Windows.Storage.h>
#include <winrt/Windows.Storage.Provider.h>
#include <winrt/Windows.Storage.Pickers.h>