mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-08-29 01:59:34 +02:00
[Settings] Improve update-notification UX (#49872)
## Summary of the Pull Request Refreshes the Settings update experience with a shared update coordinator, consistent state badges, and a compact floating status surface available across pages. The surface supports checking, update available, downloading, ready to install, network failure, and download failure states; it can be dismissed and reopens when users return to General while attention is still needed. https://github.com/user-attachments/assets/7b1a7266-efc3-4b6f-9bea-1d25a032ead6 ## PR Checklist - [ ] Closes: N/A - [x] **Communication:** I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected - [x] **Tests:** Added/updated and all pass - [x] **Localization:** All end-user-facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: N/A ## Detailed Description of the Pull Request / Additional comments - Centralizes persisted and transient update state in a shared `UpdateViewModel` used by Dashboard, General, navigation, and the floating surface. - Adds reusable status, activity, and badge controls with stable layout, dismissal/reopen behavior, in-app What's New navigation, and retry-safe single-flight update actions. - Keeps the existing General update glyph while adding an adjacent state badge. - Adds Debug-only controls for previewing every updater state and running the complete state flow without affecting production screenshots. - Adds focused coverage for state mapping, activity visibility, IPC/launch failures, transient operation recovery, and duplicate installer-launch prevention. ## Validation Steps Performed - Built `Settings.UI` for x64 Debug. - Built `Settings.UI.UnitTests` for x64 Debug. - Ran focused `ViewModelTests.Update` and `ViewModelTests.General` tests with `vstest.console.exe`: 39 passed. - Exercised the Debug updater flow across all states and verified that a dismissed notification reopens after navigating back to General. --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 120899e5-fc77-425e-918b-644929b65739 Copilot-Session: 7da8be2f-1f73-48e9-8ae9-aa2448f2a5e3
This commit is contained in:
@@ -38,9 +38,9 @@ namespace ViewModelTests
|
||||
bool isAdmin,
|
||||
Func<string, int> ipcMSGCallBackFunc,
|
||||
Func<string, int> ipcMSGRestartAsAdminMSGCallBackFunc,
|
||||
Func<string, int> 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<string, int> sendMockIPCConfigMSG = msg => 0;
|
||||
Func<string, int> sendRestartAdminIPCMessage = msg => 0;
|
||||
Func<string, int> 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<string, int> sendMockIPCConfigMSG = msg =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(msg))
|
||||
@@ -118,24 +118,7 @@ namespace ViewModelTests
|
||||
};
|
||||
|
||||
Func<string, int> sendRestartAdminIPCMessage = msg => { return 0; };
|
||||
Func<string, int> sendCheckForUpdatesIPCMessage = msg =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(msg))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
GeneralSettingsCustomAction action = JsonSerializer.Deserialize<GeneralSettingsCustomAction>(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<GeneralSettings>.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<string, int> sendMockIPCConfigMSG = msg => { return 0; };
|
||||
Func<string, int> sendRestartAdminIPCMessage = msg => { return 0; };
|
||||
Func<string, int> sendCheckForUpdatesIPCMessage = msg => { return 0; };
|
||||
Action sendCheckForUpdatesIPCMessage = () => { };
|
||||
GeneralViewModel viewModel = new TestGeneralViewModel(
|
||||
settingsRepository: SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object),
|
||||
"GeneralSettings_RunningAsAdminText",
|
||||
@@ -209,7 +192,7 @@ namespace ViewModelTests
|
||||
|
||||
// Arrange
|
||||
Func<string, int> sendRestartAdminIPCMessage = msg => { return 0; };
|
||||
Func<string, int> sendCheckForUpdatesIPCMessage = msg => { return 0; };
|
||||
Action sendCheckForUpdatesIPCMessage = () => { };
|
||||
GeneralViewModel viewModel = new TestGeneralViewModel(
|
||||
settingsRepository: SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object),
|
||||
"GeneralSettings_RunningAsAdminText",
|
||||
@@ -251,7 +234,7 @@ namespace ViewModelTests
|
||||
};
|
||||
|
||||
Func<string, int> sendRestartAdminIPCMessage = msg => { return 0; };
|
||||
Func<string, int> sendCheckForUpdatesIPCMessage = msg => { return 0; };
|
||||
Action sendCheckForUpdatesIPCMessage = () => { };
|
||||
|
||||
// Arrange
|
||||
GeneralViewModel viewModel = new TestGeneralViewModel(
|
||||
@@ -299,7 +282,7 @@ namespace ViewModelTests
|
||||
};
|
||||
|
||||
Func<string, int> sendRestartAdminIPCMessage = msg => { return 0; };
|
||||
Func<string, int> sendCheckForUpdatesIPCMessage = msg => { return 0; };
|
||||
Action sendCheckForUpdatesIPCMessage = () => { };
|
||||
viewModel = new TestGeneralViewModel(
|
||||
settingsRepository: SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object),
|
||||
"GeneralSettings_RunningAsAdminText",
|
||||
@@ -341,7 +324,7 @@ namespace ViewModelTests
|
||||
};
|
||||
|
||||
Func<string, int> sendRestartAdminIPCMessage = msg => { return 0; };
|
||||
Func<string, int> sendCheckForUpdatesIPCMessage = msg => { return 0; };
|
||||
Action sendCheckForUpdatesIPCMessage = () => { };
|
||||
GeneralViewModel viewModel = new TestGeneralViewModel(
|
||||
settingsRepository: SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object),
|
||||
"GeneralSettings_RunningAsAdminText",
|
||||
@@ -383,7 +366,7 @@ namespace ViewModelTests
|
||||
};
|
||||
|
||||
Func<string, int> sendRestartAdminIPCMessage = msg => { return 0; };
|
||||
Func<string, int> sendCheckForUpdatesIPCMessage = msg => { return 0; };
|
||||
Action sendCheckForUpdatesIPCMessage = () => { };
|
||||
GeneralViewModel viewModel = new TestGeneralViewModel(
|
||||
settingsRepository: SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object),
|
||||
"GeneralSettings_RunningAsAdminText",
|
||||
|
||||
363
src/settings-ui/Settings.UI.UnitTests/ViewModelTests/Update.cs
Normal file
363
src/settings-ui/Settings.UI.UnitTests/ViewModelTests/Update.cs
Normal file
@@ -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<GeneralSettings>
|
||||
{
|
||||
public TestSettingsRepository(GeneralSettings settings)
|
||||
{
|
||||
SettingsConfig = settings;
|
||||
}
|
||||
|
||||
public GeneralSettings SettingsConfig { get; set; }
|
||||
|
||||
public event Action<GeneralSettings> 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<GeneralSettingsCustomAction>(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<GeneralSettings> settingsRepository,
|
||||
UpdatingSettings initialSettings,
|
||||
Func<string, int> sendMessage,
|
||||
Func<UpdatingSettings> loadSettings = null,
|
||||
Action startUpdate = null)
|
||||
{
|
||||
loadSettings ??= () => initialSettings;
|
||||
startUpdate ??= () => { };
|
||||
|
||||
return new UpdateViewModel(
|
||||
settingsRepository,
|
||||
sendMessage,
|
||||
loadSettings,
|
||||
startUpdate,
|
||||
false,
|
||||
false,
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,7 +58,6 @@
|
||||
x:Key="EmptyObjectToObjectConverter"
|
||||
EmptyValue="Collapsed"
|
||||
NotEmptyValue="Visible" />
|
||||
<converters:UpdateStateToBoolConverter x:Key="UpdateStateToBoolConverter" />
|
||||
<tkconverters:StringVisibilityConverter x:Key="StringVisibilityConverter" />
|
||||
<x:Double x:Key="SettingsCardSpacing">2</x:Double>
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -8,73 +8,39 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Button
|
||||
Click="SWVersionButtonClicked"
|
||||
Style="{StaticResource SubtleButtonStyle}"
|
||||
Visibility="{x:Bind UpdateAvailable, Mode=OneTime}">
|
||||
<Grid ColumnSpacing="16">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border
|
||||
Width="20"
|
||||
Height="20"
|
||||
CornerRadius="10">
|
||||
<Border.Background>
|
||||
<LinearGradientBrush StartPoint="0,0" EndPoint="0.5,1">
|
||||
<GradientStop Offset="0.0" Color="#FFC328" />
|
||||
<GradientStop Offset="1.0" Color="#FC9A03" />
|
||||
</LinearGradientBrush>
|
||||
</Border.Background>
|
||||
<FontIcon
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
FontSize="11"
|
||||
Foreground="Black"
|
||||
Glyph="" />
|
||||
</Border>
|
||||
<StackPanel Grid.Column="1" Orientation="Vertical">
|
||||
<TextBlock x:Uid="UpdateAvailableTextBlock" FontWeight="SemiBold" />
|
||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Style="{StaticResource CaptionTextBlockStyle}">
|
||||
<Run x:Uid="GeneralVersion" />
|
||||
<Run Text="{x:Bind UpdateSettingsConfig.NewVersion, Mode=OneTime}" />
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Button>
|
||||
<Grid
|
||||
Padding="0,0,4,0"
|
||||
VerticalAlignment="Center"
|
||||
ColumnSpacing="16"
|
||||
Visibility="{x:Bind UpdateAvailable, Converter={StaticResource ReverseBoolToVisibilityConverter}, Mode=OneTime}">
|
||||
<Button
|
||||
Padding="8,4"
|
||||
AutomationProperties.AutomationId="DashboardUpdateButton"
|
||||
AutomationProperties.Name="{x:Bind ViewModel.StatusTitle, Mode=OneWay}"
|
||||
Click="UpdateButton_Click"
|
||||
Style="{StaticResource SubtleButtonStyle}">
|
||||
<Grid ColumnSpacing="16">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border
|
||||
<Grid
|
||||
Width="20"
|
||||
Height="20"
|
||||
CornerRadius="10">
|
||||
<Border.Background>
|
||||
<LinearGradientBrush StartPoint="0,0" EndPoint="0.5,1">
|
||||
<GradientStop Offset="0.0" Color="#6FB538" />
|
||||
<GradientStop Offset="1.0" Color="#397A24" />
|
||||
</LinearGradientBrush>
|
||||
</Border.Background>
|
||||
<FontIcon
|
||||
VerticalAlignment="Center">
|
||||
<local:UpdateStateBadgeControl State="{x:Bind ViewModel.CurrentUpdateUIState, Mode=OneWay}" Visibility="{x:Bind ViewModel.IsProgressActive, Converter={StaticResource ReverseBoolToVisibilityConverter}, Mode=OneWay}" />
|
||||
<ProgressRing
|
||||
Width="18"
|
||||
Height="18"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
FontSize="11"
|
||||
Foreground="White"
|
||||
Glyph="" />
|
||||
</Border>
|
||||
<StackPanel Grid.Column="1" Orientation="Vertical">
|
||||
<TextBlock x:Uid="YoureUpToDate" FontWeight="SemiBold" />
|
||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Style="{StaticResource CaptionTextBlockStyle}">
|
||||
<Run x:Uid="General_VersionLastChecked" />
|
||||
<Run Text="{x:Bind LastCheckedDateFriendly, Mode=OneTime}" />
|
||||
</TextBlock>
|
||||
IsActive="{x:Bind ViewModel.IsProgressActive, Mode=OneWay}"
|
||||
Visibility="{x:Bind ViewModel.IsProgressActive, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" />
|
||||
</Grid>
|
||||
<StackPanel
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Vertical">
|
||||
<TextBlock FontWeight="SemiBold" Text="{x:Bind ViewModel.StatusTitle, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind ViewModel.StatusDescription, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</UserControl>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="Microsoft.PowerToys.Settings.UI.Controls.UpdateActivityControl"
|
||||
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:Microsoft.PowerToys.Settings.UI.Controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
x:Name="Root"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid x:Name="ShadowHost" Padding="12">
|
||||
<Border
|
||||
x:Name="ActivitySurface"
|
||||
Width="440"
|
||||
MaxHeight="600"
|
||||
Padding="20"
|
||||
AutomationProperties.AutomationId="UpdateActivitySurface"
|
||||
AutomationProperties.Name="{x:Bind ViewModel.StatusTitle, Mode=OneWay}"
|
||||
Background="{ThemeResource AcrylicInAppFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource SurfaceStrokeColorFlyoutBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Translation="0,0,12"
|
||||
Visibility="{x:Bind ViewModel.IsActivityVisible, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}">
|
||||
<Border.Shadow>
|
||||
<ThemeShadow />
|
||||
</Border.Shadow>
|
||||
<local:UpdateStatusControl x:Name="UpdateStatus" ViewModel="{Binding ViewModel, ElementName=Root}" />
|
||||
</Border>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="Microsoft.PowerToys.Settings.UI.Controls.UpdateStateBadgeControl"
|
||||
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<LinearGradientBrush x:Key="UpdateBadgeSuccessBrush" StartPoint="0,0" EndPoint="0.5,1">
|
||||
<GradientStop Offset="0.0" Color="#6FB538" />
|
||||
<GradientStop Offset="1.0" Color="#397A24" />
|
||||
</LinearGradientBrush>
|
||||
<LinearGradientBrush x:Key="UpdateBadgeAttentionBrush" StartPoint="0,0" EndPoint="0.5,1">
|
||||
<GradientStop Offset="0.0" Color="#FFC328" />
|
||||
<GradientStop Offset="1.0" Color="#FC9A03" />
|
||||
</LinearGradientBrush>
|
||||
<SolidColorBrush x:Key="UpdateBadgeForegroundBrush" Color="White" />
|
||||
<SolidColorBrush x:Key="UpdateBadgeAttentionForegroundBrush" Color="Black" />
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<LinearGradientBrush x:Key="UpdateBadgeSuccessBrush" StartPoint="0,0" EndPoint="0.5,1">
|
||||
<GradientStop Offset="0.0" Color="#6FB538" />
|
||||
<GradientStop Offset="1.0" Color="#397A24" />
|
||||
</LinearGradientBrush>
|
||||
<LinearGradientBrush x:Key="UpdateBadgeAttentionBrush" StartPoint="0,0" EndPoint="0.5,1">
|
||||
<GradientStop Offset="0.0" Color="#FFC328" />
|
||||
<GradientStop Offset="1.0" Color="#FC9A03" />
|
||||
</LinearGradientBrush>
|
||||
<SolidColorBrush x:Key="UpdateBadgeForegroundBrush" Color="White" />
|
||||
<SolidColorBrush x:Key="UpdateBadgeAttentionForegroundBrush" Color="Black" />
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="HighContrast">
|
||||
<SolidColorBrush x:Key="UpdateBadgeSuccessBrush" Color="{ThemeResource SystemColorHighlightColor}" />
|
||||
<SolidColorBrush x:Key="UpdateBadgeAttentionBrush" Color="{ThemeResource SystemColorHighlightColor}" />
|
||||
<SolidColorBrush x:Key="UpdateBadgeForegroundBrush" Color="{ThemeResource SystemColorHighlightTextColor}" />
|
||||
<SolidColorBrush x:Key="UpdateBadgeAttentionForegroundBrush" Color="{ThemeResource SystemColorHighlightTextColor}" />
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid>
|
||||
<Border
|
||||
x:Name="Badge"
|
||||
Width="20"
|
||||
Height="20"
|
||||
Background="{ThemeResource UpdateBadgeSuccessBrush}"
|
||||
CornerRadius="10">
|
||||
<FontIcon
|
||||
x:Name="BadgeIcon"
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
FontSize="11"
|
||||
Foreground="{ThemeResource UpdateBadgeForegroundBrush}"
|
||||
Glyph="" />
|
||||
</Border>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="UpdateBadgeStates">
|
||||
<VisualState x:Name="SuccessState" />
|
||||
<VisualState x:Name="CheckingState">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="Badge.Background" Value="{ThemeResource AccentFillColorDefaultBrush}" />
|
||||
<Setter Target="BadgeIcon.FontSize" Value="10" />
|
||||
<Setter Target="BadgeIcon.Foreground" Value="{ThemeResource TextOnAccentFillColorPrimaryBrush}" />
|
||||
<Setter Target="BadgeIcon.Glyph" Value="" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="AttentionState">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="Badge.Background" Value="{ThemeResource UpdateBadgeAttentionBrush}" />
|
||||
<Setter Target="BadgeIcon.Foreground" Value="{ThemeResource UpdateBadgeAttentionForegroundBrush}" />
|
||||
<Setter Target="BadgeIcon.Glyph" Value="" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="DownloadingState">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="Badge.Background" Value="{ThemeResource AccentFillColorDefaultBrush}" />
|
||||
<Setter Target="BadgeIcon.FontSize" Value="10" />
|
||||
<Setter Target="BadgeIcon.Foreground" Value="{ThemeResource TextOnAccentFillColorPrimaryBrush}" />
|
||||
<Setter Target="BadgeIcon.Glyph" Value="" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="ErrorState">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="Badge.Background" Value="{ThemeResource SystemFillColorCriticalBrush}" />
|
||||
<Setter Target="BadgeIcon.FontSize" Value="9" />
|
||||
<Setter Target="BadgeIcon.Foreground" Value="{ThemeResource TextOnAccentFillColorPrimaryBrush}" />
|
||||
<Setter Target="BadgeIcon.Glyph" Value="" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="Microsoft.PowerToys.Settings.UI.Controls.UpdateStatusControl"
|
||||
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:Microsoft.PowerToys.Settings.UI.Controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
x:Name="Root"
|
||||
AutomationProperties.AutomationId="UpdateStatusControl"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<StackPanel DataContext="{Binding ViewModel, ElementName=Root}" Spacing="16">
|
||||
<Grid ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<local:UpdateStateBadgeControl
|
||||
Margin="0,2,0,0"
|
||||
VerticalAlignment="Top"
|
||||
State="{Binding CurrentUpdateUIState, Mode=OneWay}" />
|
||||
|
||||
<StackPanel
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Top"
|
||||
Spacing="4">
|
||||
<TextBlock
|
||||
AutomationProperties.AutomationId="UpdateStatusTitle"
|
||||
AutomationProperties.LiveSetting="Polite"
|
||||
Style="{StaticResource BodyStrongTextBlockStyle}"
|
||||
Text="{Binding StatusTitle, Mode=OneWay}"
|
||||
TextWrapping="WrapWholeWords" />
|
||||
<StackPanel
|
||||
HorizontalAlignment="Left"
|
||||
Orientation="Horizontal"
|
||||
Spacing="6">
|
||||
<TextBlock
|
||||
AutomationProperties.AutomationId="UpdateStatusDescription"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{Binding StatusDescription, Mode=OneWay}"
|
||||
TextWrapping="WrapWholeWords" />
|
||||
<Border
|
||||
Padding="5,1"
|
||||
VerticalAlignment="Center"
|
||||
Background="{ThemeResource ControlAltFillColorQuarternaryBrush}"
|
||||
CornerRadius="4"
|
||||
Visibility="{Binding ShowPrereleaseBadge, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}">
|
||||
<TextBlock
|
||||
x:Uid="General_PreviewBadge"
|
||||
FontSize="10"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
Width="32"
|
||||
Height="32"
|
||||
Margin="0,-6,-8,0"
|
||||
Padding="0"
|
||||
VerticalAlignment="Top"
|
||||
AutomationProperties.AutomationId="DismissUpdateActivityButton"
|
||||
AutomationProperties.Name="{x:Bind CloseButtonText, Mode=OneTime}"
|
||||
Click="DismissButton_Click"
|
||||
Style="{StaticResource SubtleButtonStyle}"
|
||||
ToolTipService.ToolTip="{x:Bind CloseButtonText, Mode=OneTime}">
|
||||
<FontIcon
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
FontSize="12"
|
||||
Glyph="" />
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<Grid Height="4">
|
||||
<ProgressBar
|
||||
Height="4"
|
||||
AutomationProperties.AutomationId="UpdateStatusProgress"
|
||||
IsIndeterminate="True"
|
||||
Visibility="{Binding IsProgressActive, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" />
|
||||
</Grid>
|
||||
|
||||
<StackPanel Spacing="8">
|
||||
<Button
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Center"
|
||||
AutomationProperties.AutomationId="UpdatePrimaryActionButton"
|
||||
Command="{Binding PrimaryActionCommand}"
|
||||
Content="{Binding PrimaryActionText, Mode=OneWay}"
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
<HyperlinkButton
|
||||
x:Uid="SeeWhatsNew"
|
||||
HorizontalAlignment="Center"
|
||||
AutomationProperties.AutomationId="UpdateReleaseNotesButton"
|
||||
Click="SeeWhatsNewButton_Click"
|
||||
Style="{StaticResource TextButtonStyle}"
|
||||
Visibility="{Binding ShowReleaseLink, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,44 @@
|
||||
// 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.Helpers;
|
||||
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 UpdateStatusControl : UserControl
|
||||
{
|
||||
public static readonly DependencyProperty ViewModelProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(ViewModel),
|
||||
typeof(UpdateViewModel),
|
||||
typeof(UpdateStatusControl),
|
||||
new PropertyMetadata(null));
|
||||
|
||||
public string CloseButtonText { get; } = ResourceLoaderInstance.ResourceLoader.GetString("ColorPicker_Close/Content");
|
||||
|
||||
public UpdateViewModel ViewModel
|
||||
{
|
||||
get => (UpdateViewModel)GetValue(ViewModelProperty);
|
||||
set => SetValue(ViewModelProperty, value);
|
||||
}
|
||||
|
||||
public UpdateStatusControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void DismissButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel?.DismissActivity();
|
||||
}
|
||||
|
||||
private void SeeWhatsNewButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
((App)App.Current)!.OpenScoobe();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -148,6 +148,11 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
ShellPage.Navigate(type);
|
||||
}
|
||||
|
||||
public void BeginWindowSession()
|
||||
{
|
||||
shellPage.BeginWindowSession();
|
||||
}
|
||||
|
||||
public void CloseHiddenWindow()
|
||||
{
|
||||
var hWnd = WindowNative.GetWindowHandle(this);
|
||||
@@ -164,6 +169,7 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
|
||||
if (!App.IsSecondaryWindowOpen())
|
||||
{
|
||||
shellPage.Dispose();
|
||||
App.ClearSettingsWindow();
|
||||
}
|
||||
else
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
mc:Ignorable="d">
|
||||
|
||||
<local:NavigablePage.Resources>
|
||||
<converters:UpdateStateToBoolConverter x:Key="UpdateStateToBoolConverter" />
|
||||
<converters:StringToInfoBarSeverityConverter x:Key="StringToInfoBarSeverityConverter" />
|
||||
</local:NavigablePage.Resources>
|
||||
|
||||
@@ -39,38 +38,24 @@
|
||||
</StackPanel>
|
||||
</tkcontrols:SettingsExpander.Header>
|
||||
<tkcontrols:SettingsExpander.Description>
|
||||
<StackPanel Orientation="Vertical">
|
||||
<TextBlock Style="{StaticResource SecondaryTextStyle}">
|
||||
<Run x:Uid="General_VersionLastChecked" />
|
||||
<Run Text="{x:Bind ViewModel.UpdateCheckedDate, Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
<HyperlinkButton
|
||||
x:Uid="ReleaseNotes"
|
||||
Margin="0,2,0,0"
|
||||
Click="ReleaseNotesButton_Click"
|
||||
FontWeight="SemiBold" />
|
||||
</StackPanel>
|
||||
<HyperlinkButton
|
||||
x:Uid="ReleaseNotes"
|
||||
Click="ReleaseNotesButton_Click"
|
||||
FontWeight="SemiBold" />
|
||||
</tkcontrols:SettingsExpander.Description>
|
||||
<Grid Visibility="{x:Bind ViewModel.IsUpdatePanelVisible, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<StackPanel
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<controls:UpdateStateBadgeControl
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal"
|
||||
Spacing="18"
|
||||
Visibility="{x:Bind ViewModel.IsNewVersionDownloading, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}">
|
||||
<ProgressRing Width="24" Height="24" />
|
||||
<TextBlock
|
||||
x:Uid="General_CheckingForUpdates"
|
||||
VerticalAlignment="Center"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
</StackPanel>
|
||||
State="{x:Bind SharedUpdateViewModel.CurrentUpdateUIState, Mode=OneWay}"
|
||||
ToolTipService.ToolTip="{x:Bind SharedUpdateViewModel.StatusTitle, Mode=OneWay}"
|
||||
Visibility="{x:Bind SharedUpdateViewModel.ShowUpdateBadge, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" />
|
||||
<Button
|
||||
x:Name="GeneralPageCheckForUpdatesButton"
|
||||
x:Uid="GeneralPage_CheckForUpdates"
|
||||
HorizontalAlignment="Right"
|
||||
Command="{Binding CheckForUpdatesEventHandler}"
|
||||
IsEnabled="{Binding IsDownloadAllowed}"
|
||||
Visibility="{Binding Mode=OneWay, Path=IsNewVersionDownloading, Converter={StaticResource BoolNegationConverter}}" />
|
||||
</Grid>
|
||||
AutomationProperties.AutomationId="GeneralOpenUpdateSurfaceButton"
|
||||
Click="UpdateStatusCard_Click"
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
</StackPanel>
|
||||
<tkcontrols:SettingsExpander.ItemsHeader>
|
||||
<InfoBar
|
||||
x:Uid="GPO_SomeSettingsAreManaged"
|
||||
@@ -156,152 +141,6 @@
|
||||
</tkcontrols:SettingsExpander.Items>
|
||||
</tkcontrols:SettingsExpander>
|
||||
</controls:GPOInfoControl>
|
||||
|
||||
<StackPanel Orientation="Vertical">
|
||||
<InfoBar
|
||||
x:Uid="General_UpToDate"
|
||||
IsClosable="False"
|
||||
IsOpen="{x:Bind ViewModel.IsNewVersionCheckedAndUpToDate, Mode=OneWay}"
|
||||
IsTabStop="{x:Bind ViewModel.IsNewVersionCheckedAndUpToDate, Mode=OneWay}"
|
||||
Severity="Success" />
|
||||
|
||||
<!-- Network error while checking for new version -->
|
||||
<InfoBar
|
||||
x:Uid="General_CantCheck"
|
||||
IsClosable="False"
|
||||
IsOpen="{x:Bind ViewModel.IsNoNetwork, Mode=OneWay}"
|
||||
IsTabStop="{x:Bind ViewModel.IsNoNetwork, Mode=OneWay}"
|
||||
Severity="Error" />
|
||||
|
||||
<!-- New version available -->
|
||||
<InfoBar
|
||||
Title="{x:Bind ViewModel.NewVersionAvailableTitle, Mode=OneWay}"
|
||||
IsClosable="False"
|
||||
IsOpen="{x:Bind ViewModel.PowerToysUpdatingState, Mode=OneWay, Converter={StaticResource UpdateStateToBoolConverter}, ConverterParameter=ReadyToDownload}"
|
||||
IsTabStop="{x:Bind ViewModel.PowerToysUpdatingState, Mode=OneWay, Converter={StaticResource UpdateStateToBoolConverter}, ConverterParameter=ReadyToDownload}"
|
||||
Message="{x:Bind ViewModel.PowerToysNewAvailableVersion, Mode=OneWay}"
|
||||
Severity="Warning">
|
||||
|
||||
<InfoBar.Content>
|
||||
<StackPanel Spacing="16">
|
||||
<Border
|
||||
Padding="6,2"
|
||||
HorizontalAlignment="Left"
|
||||
Background="{ThemeResource AccentFillColorDefaultBrush}"
|
||||
CornerRadius="4"
|
||||
Visibility="{x:Bind ViewModel.IsPrereleaseUpdate, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<TextBlock
|
||||
x:Uid="General_PreviewBadge"
|
||||
Foreground="{ThemeResource TextOnAccentFillColorPrimaryBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}" />
|
||||
</Border>
|
||||
<Button
|
||||
x:Uid="General_DownloadAndInstall"
|
||||
Margin="0,0,0,16"
|
||||
Command="{Binding UpdateNowButtonEventHandler}"
|
||||
IsEnabled="{Binding IsDownloadAllowed}"
|
||||
Visibility="{Binding Mode=OneWay, Path=IsNewVersionDownloading, Converter={StaticResource BoolNegationConverter}}" />
|
||||
|
||||
<!-- In progress panel -->
|
||||
<StackPanel
|
||||
Margin="0,0,0,16"
|
||||
Orientation="Horizontal"
|
||||
Spacing="18"
|
||||
Visibility="{x:Bind ViewModel.IsNewVersionDownloading, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}">
|
||||
<ProgressRing Width="24" Height="24" />
|
||||
<TextBlock
|
||||
x:Uid="General_Downloading"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</InfoBar.Content>
|
||||
<InfoBar.ActionButton>
|
||||
<HyperlinkButton
|
||||
x:Uid="SeeWhatsNew"
|
||||
HorizontalAlignment="Right"
|
||||
NavigateUri="{Binding PowerToysNewAvailableVersionLink, Mode=OneWay}"
|
||||
Style="{StaticResource TextButtonStyle}" />
|
||||
</InfoBar.ActionButton>
|
||||
</InfoBar>
|
||||
|
||||
<!-- Ready to install -->
|
||||
<InfoBar
|
||||
Title="{x:Bind ViewModel.NewVersionReadyToInstallTitle, Mode=OneWay}"
|
||||
IsClosable="False"
|
||||
IsOpen="{x:Bind ViewModel.PowerToysUpdatingState, Mode=OneWay, Converter={StaticResource UpdateStateToBoolConverter}, ConverterParameter=ReadyToInstall}"
|
||||
IsTabStop="{x:Bind ViewModel.PowerToysUpdatingState, Mode=OneWay, Converter={StaticResource UpdateStateToBoolConverter}, ConverterParameter=ReadyToInstall}"
|
||||
Message="{x:Bind ViewModel.PowerToysNewAvailableVersion, Mode=OneWay}"
|
||||
Severity="Warning">
|
||||
<InfoBar.Content>
|
||||
<StackPanel Spacing="16">
|
||||
<Border
|
||||
Padding="6,2"
|
||||
HorizontalAlignment="Left"
|
||||
Background="{ThemeResource AccentFillColorDefaultBrush}"
|
||||
CornerRadius="4"
|
||||
Visibility="{x:Bind ViewModel.IsPrereleaseUpdate, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<TextBlock
|
||||
x:Uid="General_PreviewBadge"
|
||||
Foreground="{ThemeResource TextOnAccentFillColorPrimaryBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}" />
|
||||
</Border>
|
||||
<Button
|
||||
x:Uid="General_InstallNow"
|
||||
Margin="0,0,0,16"
|
||||
Command="{Binding UpdateNowButtonEventHandler}"
|
||||
IsEnabled="{Binding IsDownloadAllowed}"
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
</StackPanel>
|
||||
</InfoBar.Content>
|
||||
<InfoBar.ActionButton>
|
||||
<HyperlinkButton
|
||||
x:Uid="SeeWhatsNew"
|
||||
HorizontalAlignment="Right"
|
||||
NavigateUri="{Binding PowerToysNewAvailableVersionLink, Mode=OneWay}"
|
||||
Style="{StaticResource TextButtonStyle}" />
|
||||
</InfoBar.ActionButton>
|
||||
</InfoBar>
|
||||
|
||||
<!-- Install failed -->
|
||||
<InfoBar
|
||||
x:Uid="General_FailedToDownloadTheNewVersion"
|
||||
IsClosable="False"
|
||||
IsOpen="{x:Bind ViewModel.PowerToysUpdatingState, Mode=OneWay, Converter={StaticResource UpdateStateToBoolConverter}, ConverterParameter=ErrorDownloading}"
|
||||
IsTabStop="{x:Bind ViewModel.PowerToysUpdatingState, Mode=OneWay, Converter={StaticResource UpdateStateToBoolConverter}, ConverterParameter=ErrorDownloading}"
|
||||
Message="{x:Bind ViewModel.PowerToysNewAvailableVersion, Mode=OneWay}"
|
||||
Severity="Error">
|
||||
<InfoBar.Content>
|
||||
<StackPanel Spacing="16">
|
||||
<Button
|
||||
x:Uid="General_TryAgainToDownloadAndInstall"
|
||||
Command="{Binding UpdateNowButtonEventHandler}"
|
||||
IsEnabled="{Binding IsDownloadAllowed}"
|
||||
Visibility="{Binding Mode=OneWay, Path=IsNewVersionDownloading, Converter={StaticResource BoolNegationConverter}}" />
|
||||
|
||||
<!-- In progress panel -->
|
||||
<StackPanel
|
||||
Margin="0,0,0,16"
|
||||
Orientation="Horizontal"
|
||||
Spacing="18"
|
||||
Visibility="{x:Bind ViewModel.IsNewVersionDownloading, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<ProgressRing Width="24" Height="24" />
|
||||
<TextBlock
|
||||
x:Uid="General_Downloading"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</InfoBar.Content>
|
||||
<InfoBar.ActionButton>
|
||||
<HyperlinkButton
|
||||
x:Uid="SeeWhatsNew"
|
||||
HorizontalAlignment="Right"
|
||||
NavigateUri="{Binding PowerToysNewAvailableVersionLink, Mode=OneWay}"
|
||||
Style="{StaticResource TextButtonStyle}" />
|
||||
</InfoBar.ActionButton>
|
||||
</InfoBar>
|
||||
</StackPanel>
|
||||
</controls:SettingsGroup>
|
||||
|
||||
<controls:SettingsGroup x:Uid="StartupAndPermissions">
|
||||
|
||||
@@ -27,6 +27,8 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
/// </summary>
|
||||
public GeneralViewModel ViewModel { get; set; }
|
||||
|
||||
public UpdateViewModel SharedUpdateViewModel => ShellPage.ShellHandler.UpdateViewModel;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GeneralPage"/> class.
|
||||
/// General Settings page constructor.
|
||||
@@ -39,14 +41,6 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
var loader = Helpers.ResourceLoaderInstance.ResourceLoader;
|
||||
var settingsUtils = SettingsUtils.Default;
|
||||
|
||||
Action stateUpdatingAction = () =>
|
||||
{
|
||||
this.DispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
ViewModel.RefreshUpdatingState();
|
||||
});
|
||||
};
|
||||
|
||||
Action hideBackupAndRestoreMessageArea = () =>
|
||||
{
|
||||
this.DispatcherQueue.TryEnqueue(async () =>
|
||||
@@ -74,9 +68,8 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
ShellPage.IsUserAnAdmin,
|
||||
ShellPage.SendDefaultIPCMessage,
|
||||
ShellPage.SendRestartAdminIPCMessage,
|
||||
ShellPage.SendCheckForUpdatesIPCMessage,
|
||||
ShellPage.ShellHandler.UpdateViewModel.CheckForUpdates,
|
||||
string.Empty,
|
||||
stateUpdatingAction,
|
||||
hideBackupAndRestoreMessageArea,
|
||||
doRefreshBackupRestoreStatus,
|
||||
PickSingleFolderDialog,
|
||||
@@ -94,7 +87,21 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
|
||||
doRefreshBackupRestoreStatus(100);
|
||||
|
||||
this.Loaded += (s, e) => ViewModel.OnPageLoaded();
|
||||
this.Loaded += GeneralPage_Loaded;
|
||||
}
|
||||
|
||||
private void GeneralPage_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.OnPageLoaded();
|
||||
if (SharedUpdateViewModel.CurrentUpdateUIState != UpdateViewModel.UpdateUIState.UpToDate)
|
||||
{
|
||||
SharedUpdateViewModel.RequestActivity();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateStatusCard_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ShellPage.ShellHandler?.OpenUpdateActivity();
|
||||
}
|
||||
|
||||
private void OpenColorsSettings_Click(object sender, RoutedEventArgs e)
|
||||
|
||||
@@ -172,8 +172,11 @@
|
||||
<InfoBadge
|
||||
x:Name="UpdateInfoBadge"
|
||||
Margin="0,0,2,0"
|
||||
AutomationProperties.AccessibilityView="Content"
|
||||
AutomationProperties.AutomationId="UpdateNavigationBadge"
|
||||
AutomationProperties.Name="{x:Bind UpdateViewModel.StatusTitle, Mode=OneWay}"
|
||||
Style="{StaticResource UpdateInfoBadgeStyle}"
|
||||
Visibility="Collapsed" />
|
||||
Visibility="{x:Bind UpdateViewModel.ShowUpdateBadge, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" />
|
||||
</NavigationViewItem.InfoBadge>
|
||||
</NavigationViewItem>
|
||||
<NavigationViewItemSeparator />
|
||||
@@ -463,6 +466,14 @@
|
||||
</i:Interaction.Behaviors>
|
||||
<Frame x:Name="shellFrame" />
|
||||
</NavigationView>
|
||||
<controls:UpdateActivityControl
|
||||
x:Name="UpdateActivity"
|
||||
Grid.Row="1"
|
||||
Margin="12"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom"
|
||||
Canvas.ZIndex="2"
|
||||
ViewModel="{x:Bind UpdateViewModel, Mode=OneWay}" />
|
||||
<ContentDialog
|
||||
x:Name="CloseDialog"
|
||||
x:Uid="CloseDialog"
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Abstractions;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -85,6 +84,8 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
/// </summary>
|
||||
public ShellViewModel ViewModel { get; }
|
||||
|
||||
public UpdateViewModel UpdateViewModel { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of functions that handle IPC responses.
|
||||
/// </summary>
|
||||
@@ -102,7 +103,6 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
private CancellationTokenSource _searchDebounceCts;
|
||||
private const int SearchDebounceMs = 500;
|
||||
private bool _disposed;
|
||||
private IFileSystemWatcher _updateStateWatcher;
|
||||
|
||||
// Removed trace id counter per cleanup
|
||||
|
||||
@@ -115,7 +115,9 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
InitializeComponent();
|
||||
SetWindowTitle();
|
||||
var settingsUtils = SettingsUtils.Default;
|
||||
ViewModel = new ShellViewModel(SettingsRepository<GeneralSettings>.GetInstance(settingsUtils));
|
||||
var generalSettingsRepository = SettingsRepository<GeneralSettings>.GetInstance(settingsUtils);
|
||||
ViewModel = new ShellViewModel(generalSettingsRepository);
|
||||
UpdateViewModel = new UpdateViewModel(generalSettingsRepository, SendCheckForUpdatesIPCMessage);
|
||||
DataContext = ViewModel;
|
||||
ShellHandler = this;
|
||||
ViewModel.Initialize(shellFrame, navigationView, KeyboardAccelerators);
|
||||
@@ -140,12 +142,6 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
_searchSuggestions.Add(child.Content?.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
UpdateGeneralInfoBadge();
|
||||
_updateStateWatcher = Helper.GetFileWatcher(string.Empty, UpdatingSettings.SettingsFile, () =>
|
||||
{
|
||||
DispatcherQueue.TryEnqueue(UpdateGeneralInfoBadge);
|
||||
});
|
||||
}
|
||||
|
||||
public static int SendDefaultIPCMessage(string msg)
|
||||
@@ -156,8 +152,12 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
|
||||
public static int SendCheckForUpdatesIPCMessage(string msg)
|
||||
{
|
||||
CheckForUpdatesMsgCallback?.Invoke(msg);
|
||||
if (CheckForUpdatesMsgCallback is null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
CheckForUpdatesMsgCallback(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -232,6 +232,16 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
shellFrame.Navigate(typeof(DashboardPage));
|
||||
}
|
||||
|
||||
public void OpenUpdateActivity()
|
||||
{
|
||||
UpdateActivity.Open();
|
||||
}
|
||||
|
||||
public void BeginWindowSession()
|
||||
{
|
||||
UpdateViewModel.BeginWindowSession();
|
||||
}
|
||||
|
||||
// Tell the current page view model to update
|
||||
public void SignalGeneralDataUpdate()
|
||||
{
|
||||
@@ -645,28 +655,12 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
return;
|
||||
}
|
||||
|
||||
_updateStateWatcher?.Dispose();
|
||||
UpdateViewModel.Dispose();
|
||||
_searchDebounceCts?.Cancel();
|
||||
_searchDebounceCts?.Dispose();
|
||||
_searchDebounceCts = null;
|
||||
_disposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void UpdateGeneralInfoBadge()
|
||||
{
|
||||
try
|
||||
{
|
||||
var config = UpdatingSettings.LoadSettings();
|
||||
bool updateAvailable = config != null &&
|
||||
(config.State == UpdatingSettings.UpdatingState.ReadyToDownload ||
|
||||
config.State == UpdatingSettings.UpdatingState.ReadyToInstall);
|
||||
UpdateInfoBadge.Visibility = updateAvailable ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
UpdateInfoBadge.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2579,7 +2579,7 @@ From there, simply click on one of the supported files in the File Explorer and
|
||||
<value>An update is available:</value>
|
||||
</data>
|
||||
<data name="General_UpdateAvailableTitle" xml:space="preserve">
|
||||
<value>An update is available:</value>
|
||||
<value>An update is available</value>
|
||||
</data>
|
||||
<data name="General_PreviewUpdateAvailableTitle" xml:space="preserve">
|
||||
<value>Preview update available:</value>
|
||||
@@ -2612,7 +2612,7 @@ From there, simply click on one of the supported files in the File Explorer and
|
||||
<value>An update is ready to install:</value>
|
||||
</data>
|
||||
<data name="General_UpToDate.Title" xml:space="preserve">
|
||||
<value>PowerToys is up to date</value>
|
||||
<value>You're up to date</value>
|
||||
</data>
|
||||
<data name="General_CantCheck.Title" xml:space="preserve">
|
||||
<value>Network error. Please try again later</value>
|
||||
|
||||
@@ -8,7 +8,6 @@ using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.IO.Abstractions;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
@@ -51,10 +50,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
private GeneralSettings GeneralSettingsConfig { get; set; }
|
||||
|
||||
private UpdatingSettings UpdatingSettingsConfig { get; set; }
|
||||
|
||||
public ButtonClickCommand CheckForUpdatesEventHandler { get; set; }
|
||||
|
||||
public Windows.ApplicationModel.Resources.ResourceLoader ResourceLoader { get; set; }
|
||||
|
||||
private Action HideBackupAndRestoreMessageAreaAction { get; set; }
|
||||
@@ -71,36 +66,29 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
public ButtonClickCommand RestartElevatedButtonEventHandler { get; set; }
|
||||
|
||||
public ButtonClickCommand UpdateNowButtonEventHandler { get; set; }
|
||||
|
||||
public Func<string, int> SendConfigMSG { get; }
|
||||
|
||||
public Func<string, int> SendRestartAsAdminConfigMSG { get; }
|
||||
|
||||
public Func<string, int> SendCheckForUpdatesConfigMSG { get; }
|
||||
|
||||
public string RunningAsUserDefaultText { get; set; }
|
||||
|
||||
public string RunningAsAdminDefaultText { get; set; }
|
||||
|
||||
private string _settingsConfigFileFolder = string.Empty;
|
||||
private readonly Action _checkForUpdatesAction;
|
||||
|
||||
private string _settingsConfigFileFolder = string.Empty;
|
||||
private ISettingsRepository<GeneralSettings> _settingsRepository;
|
||||
private Microsoft.UI.Dispatching.DispatcherQueue _dispatcherQueue;
|
||||
|
||||
private IFileSystemWatcher _fileWatcher;
|
||||
|
||||
private Func<Task<string>> PickSingleFolderDialog { get; }
|
||||
|
||||
private SettingsBackupAndRestoreUtils settingsBackupAndRestoreUtils = SettingsBackupAndRestoreUtils.Instance;
|
||||
|
||||
private const string InstallScopeRegKey = @"Software\Classes\powertoys\";
|
||||
|
||||
public GeneralViewModel(ISettingsRepository<GeneralSettings> settingsRepository, string runAsAdminText, string runAsUserText, bool isElevated, bool isAdmin, Func<string, int> ipcMSGCallBackFunc, Func<string, int> ipcMSGRestartAsAdminMSGCallBackFunc, Func<string, int> ipcMSGCheckForUpdatesCallBackFunc, string configFileSubfolder = "", Action dispatcherAction = null, Action hideBackupAndRestoreMessageAreaAction = null, Action<int> doBackupAndRestoreDryRun = null, Func<Task<string>> pickSingleFolderDialog = null, Windows.ApplicationModel.Resources.ResourceLoader resourceLoader = null)
|
||||
public GeneralViewModel(ISettingsRepository<GeneralSettings> settingsRepository, string runAsAdminText, string runAsUserText, bool isElevated, bool isAdmin, Func<string, int> ipcMSGCallBackFunc, Func<string, int> ipcMSGRestartAsAdminMSGCallBackFunc, Action checkForUpdatesAction, string configFileSubfolder = "", Action hideBackupAndRestoreMessageAreaAction = null, Action<int> doBackupAndRestoreDryRun = null, Func<Task<string>> pickSingleFolderDialog = null, Windows.ApplicationModel.Resources.ResourceLoader resourceLoader = null)
|
||||
{
|
||||
CheckForUpdatesEventHandler = new ButtonClickCommand(CheckForUpdatesClick);
|
||||
RestartElevatedButtonEventHandler = new ButtonClickCommand(RestartElevated);
|
||||
UpdateNowButtonEventHandler = new ButtonClickCommand(UpdateNowClick);
|
||||
BackupConfigsEventHandler = new ButtonClickCommand(BackupConfigsClick);
|
||||
SelectSettingBackupDirEventHandler = new ButtonClickCommand(SelectSettingBackupDir);
|
||||
RestoreConfigsEventHandler = new ButtonClickCommand(RestoreConfigsClick);
|
||||
@@ -112,21 +100,17 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
// To obtain the general settings configuration of PowerToys if it exists, else to create a new file and return the default configurations.
|
||||
ArgumentNullException.ThrowIfNull(settingsRepository);
|
||||
ArgumentNullException.ThrowIfNull(checkForUpdatesAction);
|
||||
|
||||
_settingsRepository = settingsRepository;
|
||||
_settingsRepository.SettingsChanged += OnSettingsChanged;
|
||||
_dispatcherQueue = GetDispatcherQueue();
|
||||
_checkForUpdatesAction = checkForUpdatesAction;
|
||||
|
||||
GeneralSettingsConfig = settingsRepository.SettingsConfig;
|
||||
UpdatingSettingsConfig = UpdatingSettings.LoadSettings();
|
||||
if (UpdatingSettingsConfig == null)
|
||||
{
|
||||
UpdatingSettingsConfig = new UpdatingSettings();
|
||||
}
|
||||
|
||||
// set the callback functions value to handle outgoing IPC message.
|
||||
SendConfigMSG = ipcMSGCallBackFunc;
|
||||
SendCheckForUpdatesConfigMSG = ipcMSGCheckForUpdatesCallBackFunc;
|
||||
SendRestartAsAdminConfigMSG = ipcMSGRestartAsAdminMSGCallBackFunc;
|
||||
|
||||
// Update Settings file folder:
|
||||
@@ -186,12 +170,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
_isAdmin = isAdmin;
|
||||
|
||||
_updatingState = UpdatingSettingsConfig.State;
|
||||
_newAvailableVersion = UpdatingSettingsConfig.NewVersion;
|
||||
_newAvailableVersionLink = UpdatingSettingsConfig.ReleasePageLink;
|
||||
_isPrereleaseUpdate = UpdatingSettingsConfig.IsPrerelease;
|
||||
_updateCheckedDate = FriendlyDateHelper.Format(UpdatingSettingsConfig.LastCheckedDateTime);
|
||||
|
||||
_newUpdatesToastIsGpoDisabled = GPOWrapper.GetDisableNewUpdateToastValue() == GpoRuleConfigured.Enabled;
|
||||
_autoDownloadUpdatesIsGpoDisabled = GPOWrapper.GetDisableAutomaticUpdateDownloadValue() == GpoRuleConfigured.Enabled;
|
||||
_includePrereleaseUpdatesIsGpoDisabled = GPOWrapper.GetDisablePreviewUpdatesValue() == GpoRuleConfigured.Enabled;
|
||||
@@ -211,11 +189,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
_enableViewDataDiagnostics = DataDiagnosticsSettings.GetViewEnabledValue();
|
||||
_enableViewDataDiagnosticsOnLoad = _enableViewDataDiagnostics;
|
||||
|
||||
if (dispatcherAction != null)
|
||||
{
|
||||
_fileWatcher = Helper.GetFileWatcher(string.Empty, UpdatingSettings.SettingsFile, dispatcherAction);
|
||||
}
|
||||
|
||||
// Diagnostic data retention policy
|
||||
string etwDirPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft\\PowerToys\\etw");
|
||||
DeleteDiagnosticDataOlderThan28Days(etwDirPath);
|
||||
@@ -284,15 +257,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
private bool _enableViewDataDiagnosticsOnLoad;
|
||||
private bool _viewDiagnosticDataViewerChanged;
|
||||
|
||||
private UpdatingSettings.UpdatingState _updatingState = UpdatingSettings.UpdatingState.UpToDate;
|
||||
private string _newAvailableVersion = string.Empty;
|
||||
private string _newAvailableVersionLink = string.Empty;
|
||||
private bool _isPrereleaseUpdate;
|
||||
private string _updateCheckedDate = string.Empty;
|
||||
|
||||
private bool _isNewVersionDownloading;
|
||||
private bool _isNewVersionChecked;
|
||||
private bool _isNoNetwork;
|
||||
private bool _isBugReportRunning;
|
||||
|
||||
private bool _settingsBackupRestoreMessageVisible;
|
||||
@@ -659,7 +623,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
_includePrereleaseUpdates = value;
|
||||
GeneralSettingsConfig.IncludePrereleaseUpdates = value;
|
||||
NotifyPropertyChanged();
|
||||
CheckForUpdatesClick();
|
||||
_checkForUpdatesAction();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -842,28 +806,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
public bool IsCurrentVersionPreview => string.Equals(GetPowerToysVersionChannel(), "preview", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
public string NewVersionAvailableTitle => GetResourceString(IsPrereleaseUpdate ? "General_PreviewUpdateAvailableTitle" : "General_UpdateAvailableTitle");
|
||||
|
||||
public string NewVersionReadyToInstallTitle => GetResourceString(IsPrereleaseUpdate ? "General_PreviewUpdateReadyToInstallTitle" : "General_UpdateReadyToInstallTitle");
|
||||
|
||||
public string UpdateCheckedDate
|
||||
{
|
||||
get
|
||||
{
|
||||
RequestUpdateCheckedDate();
|
||||
return _updateCheckedDate;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (_updateCheckedDate != value)
|
||||
{
|
||||
_updateCheckedDate = value;
|
||||
NotifyPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string LastSettingsBackupDate
|
||||
{
|
||||
get
|
||||
@@ -1013,109 +955,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public UpdatingSettings.UpdatingState PowerToysUpdatingState
|
||||
{
|
||||
get
|
||||
{
|
||||
return _updatingState;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
if (value != _updatingState)
|
||||
{
|
||||
_updatingState = value;
|
||||
NotifyPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string PowerToysNewAvailableVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
return _newAvailableVersion;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
if (value != _newAvailableVersion)
|
||||
{
|
||||
_newAvailableVersion = value;
|
||||
NotifyPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string PowerToysNewAvailableVersionLink
|
||||
{
|
||||
get
|
||||
{
|
||||
return _newAvailableVersionLink;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
if (value != _newAvailableVersionLink)
|
||||
{
|
||||
_newAvailableVersionLink = value;
|
||||
NotifyPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsPrereleaseUpdate
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isPrereleaseUpdate;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
if (value != _isPrereleaseUpdate)
|
||||
{
|
||||
_isPrereleaseUpdate = value;
|
||||
NotifyPropertyChanged();
|
||||
NotifyPropertyChanged(nameof(NewVersionAvailableTitle));
|
||||
NotifyPropertyChanged(nameof(NewVersionReadyToInstallTitle));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsNewVersionDownloading
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isNewVersionDownloading;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value != _isNewVersionDownloading)
|
||||
{
|
||||
_isNewVersionDownloading = value;
|
||||
NotifyPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsNewVersionCheckedAndUpToDate
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isNewVersionChecked;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsNoNetwork
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isNoNetwork;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsBugReportRunning
|
||||
{
|
||||
get
|
||||
@@ -1157,22 +996,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsDownloadAllowed
|
||||
{
|
||||
get
|
||||
{
|
||||
return !_isDevBuild && !IsNewVersionDownloading;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsUpdatePanelVisible
|
||||
{
|
||||
get
|
||||
{
|
||||
return PowerToysUpdatingState == UpdatingSettings.UpdatingState.UpToDate || PowerToysUpdatingState == UpdatingSettings.UpdatingState.NetworkError;
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<LanguageModel> Languages { get; } = new ObservableCollection<LanguageModel>();
|
||||
|
||||
public int LanguagesIndex
|
||||
@@ -1328,25 +1151,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
NotifyPropertyChanged(nameof(SettingsBackupRestoreMessageVisible), false);
|
||||
}
|
||||
|
||||
// callback function to launch the URL to check for updates.
|
||||
private void CheckForUpdatesClick()
|
||||
{
|
||||
GeneralSettingsConfig.CustomActionName = "check_for_updates";
|
||||
|
||||
OutGoingGeneralSettings outsettings = new OutGoingGeneralSettings(GeneralSettingsConfig);
|
||||
GeneralSettingsCustomAction customaction = new GeneralSettingsCustomAction(outsettings);
|
||||
|
||||
SendCheckForUpdatesConfigMSG(customaction.ToString());
|
||||
}
|
||||
|
||||
private void UpdateNowClick()
|
||||
{
|
||||
IsNewVersionDownloading = string.IsNullOrEmpty(UpdatingSettingsConfig.DownloadedInstallerFilename);
|
||||
NotifyPropertyChanged(nameof(IsDownloadAllowed));
|
||||
|
||||
Process.Start(new ProcessStartInfo(Helper.GetPowerToysInstallationFolder() + "\\PowerToys.exe") { Arguments = "powertoys://update_now/" });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class <c>GetResourceString</c> gets a localized text.
|
||||
/// </summary>
|
||||
@@ -1373,16 +1177,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public void RequestUpdateCheckedDate()
|
||||
{
|
||||
GeneralSettingsConfig.CustomActionName = "request_update_state_date";
|
||||
|
||||
OutGoingGeneralSettings outsettings = new OutGoingGeneralSettings(GeneralSettingsConfig);
|
||||
GeneralSettingsCustomAction customaction = new GeneralSettingsCustomAction(outsettings);
|
||||
|
||||
SendCheckForUpdatesConfigMSG(customaction.ToString());
|
||||
}
|
||||
|
||||
public void RestartElevated()
|
||||
{
|
||||
GeneralSettingsConfig.CustomActionName = "restart_elevation";
|
||||
@@ -1423,55 +1217,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
NotifyAllBackupAndRestoreProperties();
|
||||
}
|
||||
|
||||
public void RefreshUpdatingState()
|
||||
{
|
||||
object oLock = new object();
|
||||
lock (oLock)
|
||||
{
|
||||
var config = UpdatingSettings.LoadSettings();
|
||||
|
||||
// Retry loading if failed
|
||||
for (int i = 0; i < 3 && config == null; i++)
|
||||
{
|
||||
System.Threading.Thread.Sleep(100);
|
||||
config = UpdatingSettings.LoadSettings();
|
||||
}
|
||||
|
||||
if (config == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UpdatingSettingsConfig = config;
|
||||
|
||||
if (PowerToysUpdatingState != config.State)
|
||||
{
|
||||
IsNewVersionDownloading = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool dateChanged = UpdateCheckedDate == FriendlyDateHelper.Format(UpdatingSettingsConfig.LastCheckedDateTime);
|
||||
bool fileDownloaded = string.IsNullOrEmpty(UpdatingSettingsConfig.DownloadedInstallerFilename);
|
||||
IsNewVersionDownloading = !(dateChanged || fileDownloaded);
|
||||
}
|
||||
|
||||
PowerToysUpdatingState = UpdatingSettingsConfig.State;
|
||||
PowerToysNewAvailableVersion = UpdatingSettingsConfig.NewVersion;
|
||||
PowerToysNewAvailableVersionLink = UpdatingSettingsConfig.ReleasePageLink;
|
||||
IsPrereleaseUpdate = UpdatingSettingsConfig.IsPrerelease;
|
||||
UpdateCheckedDate = FriendlyDateHelper.Format(UpdatingSettingsConfig.LastCheckedDateTime);
|
||||
|
||||
_isNoNetwork = PowerToysUpdatingState == UpdatingSettings.UpdatingState.NetworkError;
|
||||
NotifyPropertyChanged(nameof(IsNoNetwork));
|
||||
NotifyPropertyChanged(nameof(IsNewVersionDownloading));
|
||||
NotifyPropertyChanged(nameof(IsUpdatePanelVisible));
|
||||
_isNewVersionChecked = PowerToysUpdatingState == UpdatingSettings.UpdatingState.UpToDate && !IsNewVersionDownloading;
|
||||
NotifyPropertyChanged(nameof(IsNewVersionCheckedAndUpToDate));
|
||||
|
||||
NotifyPropertyChanged(nameof(IsDownloadAllowed));
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeLanguages()
|
||||
{
|
||||
var lang = LanguageModel.LoadSetting();
|
||||
|
||||
578
src/settings-ui/Settings.UI/ViewModels/UpdateViewModel.cs
Normal file
578
src/settings-ui/Settings.UI/ViewModels/UpdateViewModel.cs
Normal file
@@ -0,0 +1,578 @@
|
||||
// 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.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Abstractions;
|
||||
using System.Threading;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
|
||||
using Microsoft.UI.Dispatching;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
{
|
||||
public sealed class UpdateViewModel : Observable, IDisposable
|
||||
{
|
||||
public enum UpdateUIState
|
||||
{
|
||||
UpToDate = 0,
|
||||
Checking,
|
||||
NetworkError,
|
||||
ReadyToDownload,
|
||||
Downloading,
|
||||
ReadyToInstall,
|
||||
ErrorDownloading,
|
||||
}
|
||||
|
||||
internal enum TransientUpdateOperation
|
||||
{
|
||||
None,
|
||||
Checking,
|
||||
Downloading,
|
||||
Installing,
|
||||
}
|
||||
|
||||
private readonly ISettingsRepository<GeneralSettings> _settingsRepository;
|
||||
private readonly Func<string, int> _sendCheckForUpdatesConfigMessage;
|
||||
private readonly Func<UpdatingSettings> _loadSettings;
|
||||
private readonly Action _startUpdate;
|
||||
private readonly DispatcherQueue _dispatcherQueue;
|
||||
private readonly DispatcherQueueTimer _updateCheckTimeoutTimer;
|
||||
private readonly bool _isDevBuild;
|
||||
private IFileSystemWatcher _fileWatcher;
|
||||
private UpdatingSettings _updatingSettings;
|
||||
private TransientUpdateOperation _activeUpdateOperation;
|
||||
private UpdateUIState? _transientFailureState;
|
||||
private bool _isActivityRequested;
|
||||
private bool _isActivityDismissed;
|
||||
private bool _disposed;
|
||||
|
||||
#if DEBUG
|
||||
private UpdateUIState? _debugPreviewState;
|
||||
#endif
|
||||
|
||||
public UpdateViewModel(
|
||||
ISettingsRepository<GeneralSettings> settingsRepository,
|
||||
Func<string, int> sendCheckForUpdatesConfigMessage)
|
||||
: this(
|
||||
settingsRepository,
|
||||
sendCheckForUpdatesConfigMessage,
|
||||
UpdatingSettings.LoadSettings,
|
||||
StartUpdate,
|
||||
Helper.GetProductVersion() == "v0.0.1",
|
||||
true,
|
||||
DispatcherQueue.GetForCurrentThread())
|
||||
{
|
||||
}
|
||||
|
||||
internal UpdateViewModel(
|
||||
ISettingsRepository<GeneralSettings> settingsRepository,
|
||||
Func<string, int> sendCheckForUpdatesConfigMessage,
|
||||
Func<UpdatingSettings> loadSettings,
|
||||
Action startUpdate,
|
||||
bool isDevBuild,
|
||||
bool watchForChanges,
|
||||
DispatcherQueue dispatcherQueue)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(settingsRepository);
|
||||
ArgumentNullException.ThrowIfNull(sendCheckForUpdatesConfigMessage);
|
||||
ArgumentNullException.ThrowIfNull(loadSettings);
|
||||
ArgumentNullException.ThrowIfNull(startUpdate);
|
||||
|
||||
_settingsRepository = settingsRepository;
|
||||
_sendCheckForUpdatesConfigMessage = sendCheckForUpdatesConfigMessage;
|
||||
_loadSettings = loadSettings;
|
||||
_startUpdate = startUpdate;
|
||||
_dispatcherQueue = dispatcherQueue;
|
||||
_isDevBuild = isDevBuild;
|
||||
_updatingSettings = _loadSettings() ?? new UpdatingSettings();
|
||||
|
||||
if (_dispatcherQueue is not null)
|
||||
{
|
||||
_updateCheckTimeoutTimer = _dispatcherQueue.CreateTimer();
|
||||
_updateCheckTimeoutTimer.Interval = TimeSpan.FromMinutes(2);
|
||||
_updateCheckTimeoutTimer.IsRepeating = false;
|
||||
_updateCheckTimeoutTimer.Tick += UpdateCheckTimeoutTimer_Tick;
|
||||
}
|
||||
|
||||
CheckForUpdatesCommand = new RelayCommand(CheckForUpdates, () => CanStartAction);
|
||||
UpdateNowCommand = new RelayCommand(UpdateNow, () => CanStartAction);
|
||||
PrimaryActionCommand = new RelayCommand(ExecutePrimaryAction, () => CanStartAction);
|
||||
|
||||
if (watchForChanges)
|
||||
{
|
||||
_fileWatcher = Helper.GetFileWatcher(string.Empty, UpdatingSettings.SettingsFile, OnUpdateStateFileChanged);
|
||||
}
|
||||
}
|
||||
|
||||
public RelayCommand CheckForUpdatesCommand { get; }
|
||||
|
||||
public RelayCommand UpdateNowCommand { get; }
|
||||
|
||||
public RelayCommand PrimaryActionCommand { get; }
|
||||
|
||||
public UpdateUIState CurrentUpdateUIState
|
||||
{
|
||||
get
|
||||
{
|
||||
#if DEBUG
|
||||
if (_debugPreviewState.HasValue)
|
||||
{
|
||||
return _debugPreviewState.Value;
|
||||
}
|
||||
#endif
|
||||
return _transientFailureState ?? GetUpdateUIState(_updatingSettings.State, _activeUpdateOperation);
|
||||
}
|
||||
}
|
||||
|
||||
public string StatusTitle
|
||||
{
|
||||
get
|
||||
{
|
||||
var resourceLoader = ResourceLoaderInstance.ResourceLoader;
|
||||
return CurrentUpdateUIState switch
|
||||
{
|
||||
UpdateUIState.Checking => resourceLoader.GetString("General_CheckingForUpdates/Text"),
|
||||
UpdateUIState.NetworkError => resourceLoader.GetString("General_CantCheck/Title"),
|
||||
UpdateUIState.ReadyToDownload or
|
||||
UpdateUIState.ReadyToInstall => resourceLoader.GetString("General_UpdateAvailableTitle"),
|
||||
UpdateUIState.Downloading => resourceLoader.GetString("General_Downloading/Text"),
|
||||
UpdateUIState.ErrorDownloading => resourceLoader.GetString("General_FailedToDownloadTheNewVersion/Title"),
|
||||
_ => resourceLoader.GetString("General_UpToDate/Title"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public string StatusDescription
|
||||
{
|
||||
get
|
||||
{
|
||||
if (CurrentUpdateUIState is UpdateUIState.ReadyToDownload or
|
||||
UpdateUIState.Downloading or
|
||||
UpdateUIState.ReadyToInstall or
|
||||
UpdateUIState.ErrorDownloading)
|
||||
{
|
||||
return DisplayVersion;
|
||||
}
|
||||
|
||||
var lastCheckedDate = FriendlyDateHelper.Format(_updatingSettings.LastCheckedDateTime);
|
||||
if (string.IsNullOrEmpty(lastCheckedDate))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return ResourceLoaderInstance.ResourceLoader.GetString("General_VersionLastChecked/Text") + lastCheckedDate;
|
||||
}
|
||||
}
|
||||
|
||||
public string DisplayVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
#if DEBUG
|
||||
if (_debugPreviewState.HasValue)
|
||||
{
|
||||
return "v0.99.0";
|
||||
}
|
||||
#endif
|
||||
return _updatingSettings.NewVersion;
|
||||
}
|
||||
}
|
||||
|
||||
public string ReleasePageLink => _updatingSettings.ReleasePageLink;
|
||||
|
||||
public bool IsPrereleaseUpdate => _updatingSettings.IsPrerelease;
|
||||
|
||||
public bool IsProgressActive => CurrentUpdateUIState is UpdateUIState.Checking or UpdateUIState.Downloading;
|
||||
|
||||
public string PrimaryActionText
|
||||
{
|
||||
get
|
||||
{
|
||||
var resourceLoader = ResourceLoaderInstance.ResourceLoader;
|
||||
return CurrentUpdateUIState switch
|
||||
{
|
||||
UpdateUIState.ReadyToDownload or UpdateUIState.Downloading => resourceLoader.GetString("General_DownloadAndInstall/Content"),
|
||||
UpdateUIState.ReadyToInstall => resourceLoader.GetString("General_InstallNow/Content"),
|
||||
UpdateUIState.ErrorDownloading => resourceLoader.GetString("General_TryAgainToDownloadAndInstall/Content"),
|
||||
_ => resourceLoader.GetString("GeneralPage_CheckForUpdates/Content"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShowReleaseLink => CurrentUpdateUIState is
|
||||
UpdateUIState.ReadyToDownload or
|
||||
UpdateUIState.Downloading or
|
||||
UpdateUIState.ReadyToInstall or
|
||||
UpdateUIState.ErrorDownloading;
|
||||
|
||||
public bool ShowPrereleaseBadge => ShowReleaseLink && IsPrereleaseUpdate;
|
||||
|
||||
public bool CanStartAction
|
||||
{
|
||||
get
|
||||
{
|
||||
#if DEBUG
|
||||
if (_debugPreviewState.HasValue)
|
||||
{
|
||||
return !IsProgressActive;
|
||||
}
|
||||
#endif
|
||||
return !_isDevBuild && _activeUpdateOperation == TransientUpdateOperation.None;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsActivityVisible =>
|
||||
_isActivityRequested ||
|
||||
(!_isActivityDismissed && CurrentUpdateUIState != UpdateUIState.UpToDate);
|
||||
|
||||
public bool ShowUpdateBadge => CurrentUpdateUIState is
|
||||
UpdateUIState.ReadyToDownload or
|
||||
UpdateUIState.Downloading or
|
||||
UpdateUIState.ReadyToInstall or
|
||||
UpdateUIState.ErrorDownloading;
|
||||
|
||||
internal static UpdateUIState GetUpdateUIState(
|
||||
UpdatingSettings.UpdatingState updatingState,
|
||||
TransientUpdateOperation activeUpdateOperation)
|
||||
{
|
||||
if (activeUpdateOperation == TransientUpdateOperation.Checking)
|
||||
{
|
||||
return UpdateUIState.Checking;
|
||||
}
|
||||
|
||||
if (activeUpdateOperation == TransientUpdateOperation.Downloading)
|
||||
{
|
||||
return UpdateUIState.Downloading;
|
||||
}
|
||||
|
||||
if (activeUpdateOperation == TransientUpdateOperation.Installing)
|
||||
{
|
||||
return UpdateUIState.ReadyToInstall;
|
||||
}
|
||||
|
||||
return updatingState switch
|
||||
{
|
||||
UpdatingSettings.UpdatingState.NetworkError => UpdateUIState.NetworkError,
|
||||
UpdatingSettings.UpdatingState.ReadyToDownload => UpdateUIState.ReadyToDownload,
|
||||
UpdatingSettings.UpdatingState.ReadyToInstall => UpdateUIState.ReadyToInstall,
|
||||
UpdatingSettings.UpdatingState.ErrorDownloading => UpdateUIState.ErrorDownloading,
|
||||
_ => UpdateUIState.UpToDate,
|
||||
};
|
||||
}
|
||||
|
||||
public void RequestActivity()
|
||||
{
|
||||
if (!_isActivityRequested || _isActivityDismissed)
|
||||
{
|
||||
_isActivityRequested = true;
|
||||
_isActivityDismissed = false;
|
||||
OnPropertyChanged(nameof(IsActivityVisible));
|
||||
}
|
||||
}
|
||||
|
||||
public void DismissActivity()
|
||||
{
|
||||
if (_isActivityRequested || !_isActivityDismissed)
|
||||
{
|
||||
_isActivityRequested = false;
|
||||
_isActivityDismissed = true;
|
||||
OnPropertyChanged(nameof(IsActivityVisible));
|
||||
}
|
||||
}
|
||||
|
||||
public void BeginWindowSession()
|
||||
{
|
||||
bool wasVisible = IsActivityVisible;
|
||||
_isActivityRequested = false;
|
||||
_isActivityDismissed = false;
|
||||
|
||||
if (IsActivityVisible != wasVisible)
|
||||
{
|
||||
OnPropertyChanged(nameof(IsActivityVisible));
|
||||
}
|
||||
}
|
||||
|
||||
public void CheckForUpdates()
|
||||
{
|
||||
#if DEBUG
|
||||
if (_debugPreviewState.HasValue)
|
||||
{
|
||||
SetDebugPreviewState(UpdateUIState.Checking);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (!CanStartAction)
|
||||
{
|
||||
Logger.LogWarning("An update operation is already in progress.");
|
||||
return;
|
||||
}
|
||||
|
||||
var generalSettings = _settingsRepository.SettingsConfig;
|
||||
generalSettings.CustomActionName = "check_for_updates";
|
||||
var customAction = new GeneralSettingsCustomAction(new OutGoingGeneralSettings(generalSettings));
|
||||
|
||||
RequestActivity();
|
||||
StartTransientOperation(TransientUpdateOperation.Checking);
|
||||
try
|
||||
{
|
||||
if (_sendCheckForUpdatesConfigMessage(customAction.ToString()) != 0)
|
||||
{
|
||||
FailTransientOperation(UpdateUIState.NetworkError, "Failed to send the update check request.");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
FailTransientOperation(UpdateUIState.NetworkError, "Failed to send the update check request.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateNow()
|
||||
{
|
||||
#if DEBUG
|
||||
if (_debugPreviewState.HasValue)
|
||||
{
|
||||
SetDebugPreviewState(CurrentUpdateUIState == UpdateUIState.ReadyToInstall
|
||||
? UpdateUIState.UpToDate
|
||||
: UpdateUIState.Downloading);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (!CanStartAction)
|
||||
{
|
||||
Logger.LogWarning("An update operation is already in progress.");
|
||||
return;
|
||||
}
|
||||
|
||||
RequestActivity();
|
||||
StartTransientOperation(string.IsNullOrEmpty(_updatingSettings.DownloadedInstallerFilename)
|
||||
? TransientUpdateOperation.Downloading
|
||||
: TransientUpdateOperation.Installing);
|
||||
|
||||
try
|
||||
{
|
||||
_startUpdate();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
FailTransientOperation(UpdateUIState.ErrorDownloading, "Failed to start the PowerToys update.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExecutePrimaryAction()
|
||||
{
|
||||
if (CurrentUpdateUIState is UpdateUIState.UpToDate or UpdateUIState.Checking or UpdateUIState.NetworkError)
|
||||
{
|
||||
CheckForUpdates();
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateNow();
|
||||
}
|
||||
}
|
||||
|
||||
internal void RefreshUpdatingState()
|
||||
{
|
||||
var updatingSettings = LoadSettingsWithRetry();
|
||||
if (updatingSettings == null)
|
||||
{
|
||||
Logger.LogWarning("Failed to load the PowerToys update state.");
|
||||
HandleUpdateStateRefreshFailure();
|
||||
return;
|
||||
}
|
||||
|
||||
ApplyUpdatingSettings(updatingSettings);
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
internal bool IsPreviewing => _debugPreviewState.HasValue;
|
||||
|
||||
internal void SetDebugPreviewState(UpdateUIState? state)
|
||||
{
|
||||
if (_debugPreviewState != state)
|
||||
{
|
||||
_debugPreviewState = state;
|
||||
CompleteTransientOperation();
|
||||
_transientFailureState = null;
|
||||
NotifyStateChanged();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
if (_updateCheckTimeoutTimer is not null)
|
||||
{
|
||||
_updateCheckTimeoutTimer.Stop();
|
||||
_updateCheckTimeoutTimer.Tick -= UpdateCheckTimeoutTimer_Tick;
|
||||
}
|
||||
|
||||
_fileWatcher?.Dispose();
|
||||
_fileWatcher = null;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private static void StartUpdate()
|
||||
{
|
||||
Process.Start(new ProcessStartInfo(Path.Combine(Helper.GetPowerToysInstallationFolder(), "PowerToys.exe"))
|
||||
{
|
||||
Arguments = "powertoys://update_now/",
|
||||
});
|
||||
}
|
||||
|
||||
private void OnUpdateStateFileChanged()
|
||||
{
|
||||
var updatingSettings = LoadSettingsWithRetry();
|
||||
if (updatingSettings == null)
|
||||
{
|
||||
Logger.LogWarning("Failed to load the PowerToys update state after it changed.");
|
||||
QueueUpdateStateRefreshFailure();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_dispatcherQueue == null || _dispatcherQueue.HasThreadAccess)
|
||||
{
|
||||
ApplyUpdatingSettings(updatingSettings);
|
||||
}
|
||||
else if (!_dispatcherQueue.TryEnqueue(() => ApplyUpdatingSettings(updatingSettings)))
|
||||
{
|
||||
Logger.LogWarning("Failed to queue a PowerToys update state refresh.");
|
||||
}
|
||||
}
|
||||
|
||||
private UpdatingSettings LoadSettingsWithRetry()
|
||||
{
|
||||
for (var attempt = 0; attempt < 4; attempt++)
|
||||
{
|
||||
var updatingSettings = _loadSettings();
|
||||
if (updatingSettings != null)
|
||||
{
|
||||
return updatingSettings;
|
||||
}
|
||||
|
||||
if (attempt < 3)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void ApplyUpdatingSettings(UpdatingSettings updatingSettings)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_updatingSettings = updatingSettings;
|
||||
CompleteTransientOperation();
|
||||
_transientFailureState = null;
|
||||
NotifyStateChanged();
|
||||
}
|
||||
|
||||
private void StartTransientOperation(TransientUpdateOperation operation)
|
||||
{
|
||||
CompleteTransientOperation();
|
||||
_activeUpdateOperation = operation;
|
||||
_transientFailureState = null;
|
||||
|
||||
// The runner persists update state only after an automatic download finishes,
|
||||
// so a check timeout is safe only when automatic downloads are disabled.
|
||||
if (operation == TransientUpdateOperation.Checking && !_settingsRepository.SettingsConfig.AutoDownloadUpdates)
|
||||
{
|
||||
_updateCheckTimeoutTimer?.Start();
|
||||
}
|
||||
|
||||
NotifyStateChanged();
|
||||
}
|
||||
|
||||
private void CompleteTransientOperation()
|
||||
{
|
||||
_updateCheckTimeoutTimer?.Stop();
|
||||
_activeUpdateOperation = TransientUpdateOperation.None;
|
||||
}
|
||||
|
||||
private void FailTransientOperation(UpdateUIState failureState, string message, Exception exception = null)
|
||||
{
|
||||
if (exception is null)
|
||||
{
|
||||
Logger.LogError(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogError(message, exception);
|
||||
}
|
||||
|
||||
CompleteTransientOperation();
|
||||
_transientFailureState = failureState;
|
||||
NotifyStateChanged();
|
||||
}
|
||||
|
||||
private void HandleUpdateStateRefreshFailure()
|
||||
{
|
||||
if (_activeUpdateOperation == TransientUpdateOperation.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var failureState = _activeUpdateOperation == TransientUpdateOperation.Checking
|
||||
? UpdateUIState.NetworkError
|
||||
: UpdateUIState.ErrorDownloading;
|
||||
FailTransientOperation(failureState, "The active PowerToys update operation could not refresh its state.");
|
||||
}
|
||||
|
||||
private void QueueUpdateStateRefreshFailure()
|
||||
{
|
||||
if (_dispatcherQueue is null || _dispatcherQueue.HasThreadAccess)
|
||||
{
|
||||
HandleUpdateStateRefreshFailure();
|
||||
}
|
||||
else if (!_dispatcherQueue.TryEnqueue(HandleUpdateStateRefreshFailure))
|
||||
{
|
||||
Logger.LogWarning("Failed to queue PowerToys update state error handling.");
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateCheckTimeoutTimer_Tick(DispatcherQueueTimer sender, object args)
|
||||
{
|
||||
if (_activeUpdateOperation == TransientUpdateOperation.Checking)
|
||||
{
|
||||
FailTransientOperation(UpdateUIState.NetworkError, "The PowerToys update check timed out.");
|
||||
}
|
||||
}
|
||||
|
||||
private void NotifyStateChanged()
|
||||
{
|
||||
OnPropertyChanged(nameof(CurrentUpdateUIState));
|
||||
OnPropertyChanged(nameof(StatusTitle));
|
||||
OnPropertyChanged(nameof(StatusDescription));
|
||||
OnPropertyChanged(nameof(DisplayVersion));
|
||||
OnPropertyChanged(nameof(ReleasePageLink));
|
||||
OnPropertyChanged(nameof(IsPrereleaseUpdate));
|
||||
OnPropertyChanged(nameof(IsProgressActive));
|
||||
OnPropertyChanged(nameof(PrimaryActionText));
|
||||
OnPropertyChanged(nameof(ShowReleaseLink));
|
||||
OnPropertyChanged(nameof(ShowPrereleaseBadge));
|
||||
OnPropertyChanged(nameof(CanStartAction));
|
||||
OnPropertyChanged(nameof(IsActivityVisible));
|
||||
OnPropertyChanged(nameof(ShowUpdateBadge));
|
||||
CheckForUpdatesCommand.OnCanExecuteChanged();
|
||||
UpdateNowCommand.OnCanExecuteChanged();
|
||||
PrimaryActionCommand.OnCanExecuteChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user