diff --git a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/General.cs b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/General.cs index d71d9e4610..a0d4b75b0f 100644 --- a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/General.cs +++ b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/General.cs @@ -38,9 +38,9 @@ namespace ViewModelTests bool isAdmin, Func ipcMSGCallBackFunc, Func ipcMSGRestartAsAdminMSGCallBackFunc, - Func ipcMSGCheckForUpdatesCallBackFunc, + Action checkForUpdatesAction, string configFileSubfolder = "") - : base(settingsRepository, runAsAdminText, runAsUserText, isElevated, isAdmin, ipcMSGCallBackFunc, ipcMSGRestartAsAdminMSGCallBackFunc, ipcMSGCheckForUpdatesCallBackFunc, configFileSubfolder) + : base(settingsRepository, runAsAdminText, runAsUserText, isElevated, isAdmin, ipcMSGCallBackFunc, ipcMSGRestartAsAdminMSGCallBackFunc, checkForUpdatesAction, configFileSubfolder) { } @@ -70,7 +70,7 @@ namespace ViewModelTests // Arrange Func sendMockIPCConfigMSG = msg => 0; Func sendRestartAdminIPCMessage = msg => 0; - Func sendCheckForUpdatesIPCMessage = msg => 0; + Action checkForUpdates = () => { }; var viewModel = new TestGeneralViewModel( settingsRepository: generalSettingsRepository, runAsAdminText: "GeneralSettings_RunningAsAdminText", @@ -79,7 +79,7 @@ namespace ViewModelTests isAdmin: false, ipcMSGCallBackFunc: sendMockIPCConfigMSG, ipcMSGRestartAsAdminMSGCallBackFunc: sendRestartAdminIPCMessage, - ipcMSGCheckForUpdatesCallBackFunc: sendCheckForUpdatesIPCMessage, + checkForUpdatesAction: checkForUpdates, configFileSubfolder: string.Empty); // Verify that the old settings persisted @@ -98,7 +98,7 @@ namespace ViewModelTests public void IncludePrereleaseUpdatesShouldSendUpdatedSettingWhenSuccessful() { bool sawExpectedIpcPayload = false; - bool sawExpectedUpdateCheckPayload = false; + bool updateCheckRequested = false; Func sendMockIPCConfigMSG = msg => { if (string.IsNullOrWhiteSpace(msg)) @@ -118,24 +118,7 @@ namespace ViewModelTests }; Func sendRestartAdminIPCMessage = msg => { return 0; }; - Func sendCheckForUpdatesIPCMessage = msg => - { - if (string.IsNullOrWhiteSpace(msg)) - { - return 0; - } - - GeneralSettingsCustomAction action = JsonSerializer.Deserialize(msg); - if (action?.GeneralSettingsAction?.GeneralSettings is null) - { - return 0; - } - - Assert.IsTrue(action.GeneralSettingsAction.GeneralSettings.IncludePrereleaseUpdates); - Assert.AreEqual("check_for_updates", action.GeneralSettingsAction.GeneralSettings.CustomActionName); - sawExpectedUpdateCheckPayload = true; - return 0; - }; + Action checkForUpdates = () => updateCheckRequested = true; GeneralViewModel viewModel = new TestGeneralViewModel( settingsRepository: SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), "GeneralSettings_RunningAsAdminText", @@ -144,7 +127,7 @@ namespace ViewModelTests false, sendMockIPCConfigMSG, sendRestartAdminIPCMessage, - sendCheckForUpdatesIPCMessage, + checkForUpdates, GeneralSettingsFileName); Assert.IsFalse(viewModel.IncludePrereleaseUpdates); @@ -152,7 +135,7 @@ namespace ViewModelTests viewModel.IncludePrereleaseUpdates = true; Assert.IsTrue(sawExpectedIpcPayload); - Assert.IsTrue(sawExpectedUpdateCheckPayload); + Assert.IsTrue(updateCheckRequested); } [TestMethod] @@ -161,7 +144,7 @@ namespace ViewModelTests // Arrange Func sendMockIPCConfigMSG = msg => { return 0; }; Func sendRestartAdminIPCMessage = msg => { return 0; }; - Func sendCheckForUpdatesIPCMessage = msg => { return 0; }; + Action sendCheckForUpdatesIPCMessage = () => { }; GeneralViewModel viewModel = new TestGeneralViewModel( settingsRepository: SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), "GeneralSettings_RunningAsAdminText", @@ -209,7 +192,7 @@ namespace ViewModelTests // Arrange Func sendRestartAdminIPCMessage = msg => { return 0; }; - Func sendCheckForUpdatesIPCMessage = msg => { return 0; }; + Action sendCheckForUpdatesIPCMessage = () => { }; GeneralViewModel viewModel = new TestGeneralViewModel( settingsRepository: SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), "GeneralSettings_RunningAsAdminText", @@ -251,7 +234,7 @@ namespace ViewModelTests }; Func sendRestartAdminIPCMessage = msg => { return 0; }; - Func sendCheckForUpdatesIPCMessage = msg => { return 0; }; + Action sendCheckForUpdatesIPCMessage = () => { }; // Arrange GeneralViewModel viewModel = new TestGeneralViewModel( @@ -299,7 +282,7 @@ namespace ViewModelTests }; Func sendRestartAdminIPCMessage = msg => { return 0; }; - Func sendCheckForUpdatesIPCMessage = msg => { return 0; }; + Action sendCheckForUpdatesIPCMessage = () => { }; viewModel = new TestGeneralViewModel( settingsRepository: SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), "GeneralSettings_RunningAsAdminText", @@ -341,7 +324,7 @@ namespace ViewModelTests }; Func sendRestartAdminIPCMessage = msg => { return 0; }; - Func sendCheckForUpdatesIPCMessage = msg => { return 0; }; + Action sendCheckForUpdatesIPCMessage = () => { }; GeneralViewModel viewModel = new TestGeneralViewModel( settingsRepository: SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), "GeneralSettings_RunningAsAdminText", @@ -383,7 +366,7 @@ namespace ViewModelTests }; Func sendRestartAdminIPCMessage = msg => { return 0; }; - Func sendCheckForUpdatesIPCMessage = msg => { return 0; }; + Action sendCheckForUpdatesIPCMessage = () => { }; GeneralViewModel viewModel = new TestGeneralViewModel( settingsRepository: SettingsRepository.GetInstance(mockGeneralSettingsUtils.Object), "GeneralSettings_RunningAsAdminText", diff --git a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/Update.cs b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/Update.cs new file mode 100644 index 0000000000..1f34dce8f3 --- /dev/null +++ b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/Update.cs @@ -0,0 +1,363 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Text.Json; +using Microsoft.PowerToys.Settings.UI.Library; +using Microsoft.PowerToys.Settings.UI.Library.Interfaces; +using Microsoft.PowerToys.Settings.UI.ViewModels; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace ViewModelTests +{ + [TestClass] + public class Update + { + private sealed class TestSettingsRepository : ISettingsRepository + { + public TestSettingsRepository(GeneralSettings settings) + { + SettingsConfig = settings; + } + + public GeneralSettings SettingsConfig { get; set; } + + public event Action SettingsChanged; + + public bool ReloadSettings() + { + SettingsChanged?.Invoke(SettingsConfig); + return true; + } + } + + [DataTestMethod] + [DataRow(UpdatingSettings.UpdatingState.UpToDate, (int)UpdateViewModel.TransientUpdateOperation.None, UpdateViewModel.UpdateUIState.UpToDate)] + [DataRow(UpdatingSettings.UpdatingState.NetworkError, (int)UpdateViewModel.TransientUpdateOperation.None, UpdateViewModel.UpdateUIState.NetworkError)] + [DataRow(UpdatingSettings.UpdatingState.ReadyToDownload, (int)UpdateViewModel.TransientUpdateOperation.None, UpdateViewModel.UpdateUIState.ReadyToDownload)] + [DataRow(UpdatingSettings.UpdatingState.ReadyToInstall, (int)UpdateViewModel.TransientUpdateOperation.None, UpdateViewModel.UpdateUIState.ReadyToInstall)] + [DataRow(UpdatingSettings.UpdatingState.ErrorDownloading, (int)UpdateViewModel.TransientUpdateOperation.None, UpdateViewModel.UpdateUIState.ErrorDownloading)] + [DataRow(UpdatingSettings.UpdatingState.UpToDate, (int)UpdateViewModel.TransientUpdateOperation.Checking, UpdateViewModel.UpdateUIState.Checking)] + [DataRow(UpdatingSettings.UpdatingState.ReadyToDownload, (int)UpdateViewModel.TransientUpdateOperation.Checking, UpdateViewModel.UpdateUIState.Checking)] + [DataRow(UpdatingSettings.UpdatingState.ReadyToInstall, (int)UpdateViewModel.TransientUpdateOperation.Checking, UpdateViewModel.UpdateUIState.Checking)] + [DataRow(UpdatingSettings.UpdatingState.UpToDate, (int)UpdateViewModel.TransientUpdateOperation.Downloading, UpdateViewModel.UpdateUIState.Downloading)] + [DataRow(UpdatingSettings.UpdatingState.ReadyToDownload, (int)UpdateViewModel.TransientUpdateOperation.Downloading, UpdateViewModel.UpdateUIState.Downloading)] + [DataRow(UpdatingSettings.UpdatingState.ReadyToInstall, (int)UpdateViewModel.TransientUpdateOperation.Downloading, UpdateViewModel.UpdateUIState.Downloading)] + [DataRow(UpdatingSettings.UpdatingState.ReadyToInstall, (int)UpdateViewModel.TransientUpdateOperation.Installing, UpdateViewModel.UpdateUIState.ReadyToInstall)] + public void GetUpdateUIStateShouldMapPersistentAndTransientStates( + UpdatingSettings.UpdatingState updatingState, + int activeUpdateOperation, + UpdateViewModel.UpdateUIState expected) + { + Assert.AreEqual( + expected, + UpdateViewModel.GetUpdateUIState( + updatingState, + (UpdateViewModel.TransientUpdateOperation)activeUpdateOperation)); + } + + [TestMethod] + public void CheckForUpdatesShouldShowProgressAndSendCurrentSettings() + { + string sentMessage = null; + var generalSettings = new GeneralSettings + { + IncludePrereleaseUpdates = true, + }; + var viewModel = CreateViewModel( + new TestSettingsRepository(generalSettings), + new UpdatingSettings(), + message => + { + sentMessage = message; + return 0; + }); + + viewModel.CheckForUpdates(); + + Assert.AreEqual(UpdateViewModel.UpdateUIState.Checking, viewModel.CurrentUpdateUIState); + Assert.IsTrue(viewModel.IsActivityVisible); + Assert.IsFalse(viewModel.CanStartAction); + + var action = JsonSerializer.Deserialize(sentMessage); + Assert.IsTrue(action.GeneralSettingsAction.GeneralSettings.IncludePrereleaseUpdates); + Assert.AreEqual("check_for_updates", action.GeneralSettingsAction.GeneralSettings.CustomActionName); + } + + [TestMethod] + public void CheckForUpdatesShouldUseCheckingStateForAnExistingUpdate() + { + var updatingSettings = new UpdatingSettings + { + State = UpdatingSettings.UpdatingState.ReadyToInstall, + DownloadedInstallerFilename = "PowerToysSetup.exe", + }; + var viewModel = CreateViewModel( + new TestSettingsRepository(new GeneralSettings()), + updatingSettings, + message => 0); + + viewModel.CheckForUpdates(); + + Assert.AreEqual(UpdateViewModel.UpdateUIState.Checking, viewModel.CurrentUpdateUIState); + Assert.IsFalse(viewModel.CanStartAction); + } + + [TestMethod] + public void CheckForUpdatesShouldRecoverWhenIpcDeliveryFails() + { + var viewModel = CreateViewModel( + new TestSettingsRepository(new GeneralSettings()), + new UpdatingSettings(), + message => 1); + + viewModel.CheckForUpdates(); + + Assert.AreEqual(UpdateViewModel.UpdateUIState.NetworkError, viewModel.CurrentUpdateUIState); + Assert.IsTrue(viewModel.CanStartAction); + Assert.IsTrue(viewModel.IsActivityVisible); + } + + [TestMethod] + public void CheckForUpdatesShouldRecoverWhenIpcDeliveryThrows() + { + var viewModel = CreateViewModel( + new TestSettingsRepository(new GeneralSettings()), + new UpdatingSettings(), + message => throw new InvalidOperationException()); + + viewModel.CheckForUpdates(); + + Assert.AreEqual(UpdateViewModel.UpdateUIState.NetworkError, viewModel.CurrentUpdateUIState); + Assert.IsTrue(viewModel.CanStartAction); + } + + [TestMethod] + public void RefreshUpdatingStateShouldCompleteTransientOperation() + { + var currentSettings = new UpdatingSettings(); + var viewModel = CreateViewModel( + new TestSettingsRepository(new GeneralSettings()), + currentSettings, + message => 0, + () => currentSettings); + + viewModel.CheckForUpdates(); + currentSettings = new UpdatingSettings + { + State = UpdatingSettings.UpdatingState.NetworkError, + }; + + viewModel.RefreshUpdatingState(); + + Assert.AreEqual(UpdateViewModel.UpdateUIState.NetworkError, viewModel.CurrentUpdateUIState); + Assert.IsTrue(viewModel.CanStartAction); + } + + [TestMethod] + public void RefreshUpdatingStateShouldRecoverWhenStateCannotBeLoaded() + { + var viewModel = CreateViewModel( + new TestSettingsRepository(new GeneralSettings()), + new UpdatingSettings(), + message => 0, + () => null); + + viewModel.CheckForUpdates(); + viewModel.RefreshUpdatingState(); + + Assert.AreEqual(UpdateViewModel.UpdateUIState.NetworkError, viewModel.CurrentUpdateUIState); + Assert.IsTrue(viewModel.CanStartAction); + } + + [TestMethod] + public void UpdateNowShouldShowDownloadingUntilUpdaterStateChanges() + { + bool updateStarted = false; + var viewModel = CreateViewModel( + new TestSettingsRepository(new GeneralSettings()), + new UpdatingSettings + { + State = UpdatingSettings.UpdatingState.ReadyToDownload, + }, + message => 0, + startUpdate: () => updateStarted = true); + + viewModel.UpdateNow(); + + Assert.IsTrue(updateStarted); + Assert.AreEqual(UpdateViewModel.UpdateUIState.Downloading, viewModel.CurrentUpdateUIState); + Assert.IsTrue(viewModel.IsActivityVisible); + } + + [TestMethod] + public void UpdateNowShouldPreventStartingDownloadedInstallerTwice() + { + int updateStartCount = 0; + var viewModel = CreateViewModel( + new TestSettingsRepository(new GeneralSettings()), + new UpdatingSettings + { + State = UpdatingSettings.UpdatingState.ReadyToInstall, + DownloadedInstallerFilename = "PowerToysSetup.exe", + }, + message => 0, + startUpdate: () => updateStartCount++); + + viewModel.UpdateNow(); + viewModel.UpdateNow(); + + Assert.AreEqual(1, updateStartCount); + Assert.AreEqual(UpdateViewModel.UpdateUIState.ReadyToInstall, viewModel.CurrentUpdateUIState); + Assert.IsFalse(viewModel.CanStartAction); + } + + [TestMethod] + public void UpdateNowShouldRecoverWhenStartingUpdaterFails() + { + var viewModel = CreateViewModel( + new TestSettingsRepository(new GeneralSettings()), + new UpdatingSettings + { + State = UpdatingSettings.UpdatingState.ReadyToDownload, + }, + message => 0, + startUpdate: () => throw new InvalidOperationException()); + + viewModel.UpdateNow(); + + Assert.AreEqual(UpdateViewModel.UpdateUIState.ErrorDownloading, viewModel.CurrentUpdateUIState); + Assert.IsTrue(viewModel.CanStartAction); + Assert.IsTrue(viewModel.IsActivityVisible); + } + + [TestMethod] + public void UpdateNowShouldClearFailureWhenRetryingDownloadedInstaller() + { + int updateStartCount = 0; + var viewModel = CreateViewModel( + new TestSettingsRepository(new GeneralSettings()), + new UpdatingSettings + { + State = UpdatingSettings.UpdatingState.ReadyToInstall, + DownloadedInstallerFilename = "PowerToysSetup.exe", + }, + message => 0, + startUpdate: () => + { + updateStartCount++; + if (updateStartCount == 1) + { + throw new InvalidOperationException(); + } + }); + + viewModel.UpdateNow(); + Assert.AreEqual(UpdateViewModel.UpdateUIState.ErrorDownloading, viewModel.CurrentUpdateUIState); + + viewModel.UpdateNow(); + + Assert.AreEqual(2, updateStartCount); + Assert.AreEqual(UpdateViewModel.UpdateUIState.ReadyToInstall, viewModel.CurrentUpdateUIState); + Assert.IsFalse(viewModel.CanStartAction); + } + +#if DEBUG + [TestMethod] + public void UpdateNowShouldNotStartUpdaterWhilePreviewing() + { + bool updateStarted = false; + var viewModel = CreateViewModel( + new TestSettingsRepository(new GeneralSettings()), + new UpdatingSettings(), + message => 0, + startUpdate: () => updateStarted = true); + viewModel.SetDebugPreviewState(UpdateViewModel.UpdateUIState.ReadyToDownload); + + viewModel.UpdateNow(); + + Assert.IsFalse(updateStarted); + Assert.AreEqual(UpdateViewModel.UpdateUIState.Downloading, viewModel.CurrentUpdateUIState); + } +#endif + + [TestMethod] + public void DismissingActivityShouldHideSurfaceButKeepUpdateBadge() + { + var viewModel = CreateViewModel( + new TestSettingsRepository(new GeneralSettings()), + new UpdatingSettings + { + State = UpdatingSettings.UpdatingState.ReadyToDownload, + }, + message => 0); + + viewModel.RequestActivity(); + viewModel.DismissActivity(); + + Assert.IsFalse(viewModel.IsActivityVisible); + Assert.IsTrue(viewModel.ShowUpdateBadge); + } + + [TestMethod] + public void UpToDateActivityShouldRemainHiddenAtStartOfWindowSession() + { + var viewModel = CreateViewModel( + new TestSettingsRepository(new GeneralSettings()), + new UpdatingSettings(), + message => 0); + + viewModel.RequestActivity(); + Assert.IsTrue(viewModel.IsActivityVisible); + + viewModel.DismissActivity(); + + Assert.IsFalse(viewModel.IsActivityVisible); + + viewModel.BeginWindowSession(); + + Assert.IsFalse(viewModel.IsActivityVisible); + } + + [TestMethod] + public void AvailableUpdateShouldShowActivityAtStartOfWindowSession() + { + var viewModel = CreateViewModel( + new TestSettingsRepository(new GeneralSettings()), + new UpdatingSettings + { + State = UpdatingSettings.UpdatingState.ReadyToDownload, + }, + message => 0); + + Assert.IsTrue(viewModel.IsActivityVisible); + + viewModel.DismissActivity(); + Assert.IsFalse(viewModel.IsActivityVisible); + + viewModel.BeginWindowSession(); + Assert.IsTrue(viewModel.IsActivityVisible); + } + + private static UpdateViewModel CreateViewModel( + ISettingsRepository settingsRepository, + UpdatingSettings initialSettings, + Func sendMessage, + Func loadSettings = null, + Action startUpdate = null) + { + loadSettings ??= () => initialSettings; + startUpdate ??= () => { }; + + return new UpdateViewModel( + settingsRepository, + sendMessage, + loadSettings, + startUpdate, + false, + false, + null); + } + } +} diff --git a/src/settings-ui/Settings.UI/Converters/UpdateStateToBoolConverter.cs b/src/settings-ui/Settings.UI/Converters/UpdateStateToBoolConverter.cs deleted file mode 100644 index 15d874a430..0000000000 --- a/src/settings-ui/Settings.UI/Converters/UpdateStateToBoolConverter.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -using Microsoft.UI.Xaml.Data; - -namespace Microsoft.PowerToys.Settings.UI.Converters -{ - public sealed partial class UpdateStateToBoolConverter : IValueConverter - { - public object Convert(object value, Type targetType, object parameter, string language) - { - if (value == null || parameter == null) - { - return false; - } - else - { - if (value.ToString() == (string)parameter) - { - return true; - } - else - { - return false; - } - } - } - - public object ConvertBack(object value, Type targetType, object parameter, string language) - { - return value; - } - } -} diff --git a/src/settings-ui/Settings.UI/SettingsXAML/App.xaml b/src/settings-ui/Settings.UI/SettingsXAML/App.xaml index 00797056a2..864e5a644d 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/App.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/App.xaml @@ -58,7 +58,6 @@ x:Key="EmptyObjectToObjectConverter" EmptyValue="Collapsed" NotEmptyValue="Visible" /> - 2 diff --git a/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs index e23eb1dbde..0666906a1f 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs @@ -110,11 +110,19 @@ namespace Microsoft.PowerToys.Settings.UI public static void OpenSettingsWindow(Type type = null, bool ensurePageIsSelected = false) { + bool isNewWindowSession = settingsWindow == null || + !NativeMethods.IsWindowVisible(settingsWindow.GetWindowHandle()); + if (settingsWindow == null) { settingsWindow = new MainWindow(); } + if (isNewWindowSession) + { + settingsWindow.BeginWindowSession(); + } + settingsWindow.Activate(); if (type != null) diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Controls/Dashboard/CheckUpdateControl.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Controls/Dashboard/CheckUpdateControl.xaml index df8a477612..1dd8680922 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Controls/Dashboard/CheckUpdateControl.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Controls/Dashboard/CheckUpdateControl.xaml @@ -8,73 +8,39 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> - - - + diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Controls/Dashboard/CheckUpdateControl.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Controls/Dashboard/CheckUpdateControl.xaml.cs index 8ad9646215..2e85e49755 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Controls/Dashboard/CheckUpdateControl.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Controls/Dashboard/CheckUpdateControl.xaml.cs @@ -2,33 +2,25 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.PowerToys.Settings.UI.Helpers; -using Microsoft.PowerToys.Settings.UI.Library; -using Microsoft.PowerToys.Settings.UI.Services; +using Microsoft.PowerToys.Settings.UI.ViewModels; using Microsoft.PowerToys.Settings.UI.Views; +using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; namespace Microsoft.PowerToys.Settings.UI.Controls { public sealed partial class CheckUpdateControl : UserControl { - public bool UpdateAvailable { get; set; } - - public UpdatingSettings UpdateSettingsConfig { get; set; } - - public string LastCheckedDateFriendly { get; set; } + public UpdateViewModel ViewModel => ShellPage.ShellHandler?.UpdateViewModel; public CheckUpdateControl() { InitializeComponent(); - UpdateSettingsConfig = UpdatingSettings.LoadSettings(); - UpdateAvailable = UpdateSettingsConfig != null && (UpdateSettingsConfig.State == UpdatingSettings.UpdatingState.ReadyToInstall || UpdateSettingsConfig.State == UpdatingSettings.UpdatingState.ReadyToDownload); - LastCheckedDateFriendly = FriendlyDateHelper.Format(UpdateSettingsConfig?.LastCheckedDateTime); } - private void SWVersionButtonClicked(object sender, Microsoft.UI.Xaml.RoutedEventArgs e) + private void UpdateButton_Click(object sender, RoutedEventArgs e) { - NavigationService.Navigate(typeof(GeneralPage)); + ShellPage.ShellHandler?.OpenUpdateActivity(); } } } diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Controls/UpdateActivityControl.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Controls/UpdateActivityControl.xaml new file mode 100644 index 0000000000..1907156cc9 --- /dev/null +++ b/src/settings-ui/Settings.UI/SettingsXAML/Controls/UpdateActivityControl.xaml @@ -0,0 +1,32 @@ + + + + + + + + + + + + diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Controls/UpdateActivityControl.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Controls/UpdateActivityControl.xaml.cs new file mode 100644 index 0000000000..45d8b76d4a --- /dev/null +++ b/src/settings-ui/Settings.UI/SettingsXAML/Controls/UpdateActivityControl.xaml.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.PowerToys.Settings.UI.ViewModels; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + +namespace Microsoft.PowerToys.Settings.UI.Controls +{ + public sealed partial class UpdateActivityControl : UserControl + { + public static readonly DependencyProperty ViewModelProperty = + DependencyProperty.Register( + nameof(ViewModel), + typeof(UpdateViewModel), + typeof(UpdateActivityControl), + new PropertyMetadata(null)); + + public UpdateViewModel ViewModel + { + get => (UpdateViewModel)GetValue(ViewModelProperty); + set => SetValue(ViewModelProperty, value); + } + + public UpdateActivityControl() + { + InitializeComponent(); + } + + public void Open() + { + ViewModel?.RequestActivity(); + } + } +} diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Controls/UpdateStateBadgeControl.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Controls/UpdateStateBadgeControl.xaml new file mode 100644 index 0000000000..7ffb81a057 --- /dev/null +++ b/src/settings-ui/Settings.UI/SettingsXAML/Controls/UpdateStateBadgeControl.xaml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Controls/UpdateStateBadgeControl.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Controls/UpdateStateBadgeControl.xaml.cs new file mode 100644 index 0000000000..dc81bf428e --- /dev/null +++ b/src/settings-ui/Settings.UI/SettingsXAML/Controls/UpdateStateBadgeControl.xaml.cs @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.PowerToys.Settings.UI.ViewModels; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + +namespace Microsoft.PowerToys.Settings.UI.Controls +{ + public sealed partial class UpdateStateBadgeControl : UserControl + { + public static readonly DependencyProperty StateProperty = + DependencyProperty.Register( + nameof(State), + typeof(UpdateViewModel.UpdateUIState), + typeof(UpdateStateBadgeControl), + new PropertyMetadata(UpdateViewModel.UpdateUIState.UpToDate, OnStateChanged)); + + private bool _isLoaded; + + public UpdateViewModel.UpdateUIState State + { + get => (UpdateViewModel.UpdateUIState)GetValue(StateProperty); + set => SetValue(StateProperty, value); + } + + public UpdateStateBadgeControl() + { + InitializeComponent(); + Loaded += UpdateStateBadgeControl_Loaded; + } + + private static void OnStateChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args) + { + var control = (UpdateStateBadgeControl)dependencyObject; + if (control._isLoaded) + { + control.UpdateVisualState(); + } + } + + private void UpdateStateBadgeControl_Loaded(object sender, RoutedEventArgs e) + { + _isLoaded = true; + UpdateVisualState(); + } + + private void UpdateVisualState() + { + string stateName = State switch + { + UpdateViewModel.UpdateUIState.Checking => "CheckingState", + UpdateViewModel.UpdateUIState.ReadyToDownload or + UpdateViewModel.UpdateUIState.ReadyToInstall => "AttentionState", + UpdateViewModel.UpdateUIState.Downloading => "DownloadingState", + UpdateViewModel.UpdateUIState.NetworkError or + UpdateViewModel.UpdateUIState.ErrorDownloading => "ErrorState", + _ => "SuccessState", + }; + + VisualStateManager.GoToState(this, stateName, false); + } + } +} diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Controls/UpdateStatusControl.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Controls/UpdateStatusControl.xaml new file mode 100644 index 0000000000..090f4ca5d4 --- /dev/null +++ b/src/settings-ui/Settings.UI/SettingsXAML/Controls/UpdateStatusControl.xaml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +