mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-22 09:14:25 +08:00
changed.将更新系统全面纳入到统一的设置接口
This commit is contained in:
355
LanMountainDesktop.Tests/UpdateSettingsInterfaceTests.cs
Normal file
355
LanMountainDesktop.Tests/UpdateSettingsInterfaceTests.cs
Normal file
@@ -0,0 +1,355 @@
|
|||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using LanMountainDesktop.Models;
|
||||||
|
using LanMountainDesktop.PluginSdk;
|
||||||
|
using LanMountainDesktop.Services;
|
||||||
|
using LanMountainDesktop.Services.Settings;
|
||||||
|
using LanMountainDesktop.Services.Update;
|
||||||
|
using LanMountainDesktop.Shared.Contracts.Update;
|
||||||
|
using LanMountainDesktop.ViewModels;
|
||||||
|
using Xunit;
|
||||||
|
using UpdateDownloadResult = LanMountainDesktop.Services.Update.DownloadResult;
|
||||||
|
using SettingsUpdateState = LanMountainDesktop.Services.Settings.UpdateSettingsState;
|
||||||
|
|
||||||
|
namespace LanMountainDesktop.Tests;
|
||||||
|
|
||||||
|
public sealed class UpdateSettingsInterfaceTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task UpdateSettingsViewModel_RoutesActionsThroughUpdateSettingsService()
|
||||||
|
{
|
||||||
|
var update = new FakeUpdateSettingsService();
|
||||||
|
var viewModel = new UpdateSettingsViewModel(new FakeSettingsFacade(update));
|
||||||
|
|
||||||
|
Assert.Equal(0, update.SaveCalls);
|
||||||
|
|
||||||
|
update.CheckReport = new UpdateCheckReport(
|
||||||
|
true,
|
||||||
|
"1.2.3",
|
||||||
|
"1.0.0",
|
||||||
|
UpdatePayloadKind.DeltaPlonds,
|
||||||
|
"dist-1",
|
||||||
|
UpdateSettingsValues.ChannelStable,
|
||||||
|
DateTimeOffset.Parse("2026-05-06T00:00:00Z"),
|
||||||
|
42,
|
||||||
|
null,
|
||||||
|
null);
|
||||||
|
|
||||||
|
await ((IAsyncRelayCommand)viewModel.CheckCommand).ExecuteAsync(null);
|
||||||
|
|
||||||
|
Assert.Equal(1, update.CheckCalls);
|
||||||
|
Assert.Equal("1.2.3", viewModel.LatestVersionText);
|
||||||
|
Assert.True(viewModel.IsDeltaUpdate);
|
||||||
|
|
||||||
|
update.SetPhase(UpdatePhase.Checked);
|
||||||
|
await ((IAsyncRelayCommand)viewModel.DownloadCommand).ExecuteAsync(null);
|
||||||
|
Assert.Equal(1, update.DownloadCalls);
|
||||||
|
|
||||||
|
update.SetPhase(UpdatePhase.Downloaded);
|
||||||
|
await ((IAsyncRelayCommand)viewModel.InstallCommand).ExecuteAsync(null);
|
||||||
|
Assert.Equal(1, update.InstallCalls);
|
||||||
|
|
||||||
|
update.SetPhase(UpdatePhase.Downloading);
|
||||||
|
await ((IAsyncRelayCommand)viewModel.PauseCommand).ExecuteAsync(null);
|
||||||
|
Assert.Equal(1, update.PauseCalls);
|
||||||
|
|
||||||
|
update.SetPhase(UpdatePhase.PausedDownloading);
|
||||||
|
await ((IAsyncRelayCommand)viewModel.ResumeCommand).ExecuteAsync(null);
|
||||||
|
Assert.Equal(1, update.ResumeCalls);
|
||||||
|
|
||||||
|
update.SetPhase(UpdatePhase.Downloading);
|
||||||
|
await ((IAsyncRelayCommand)viewModel.CancelCommand).ExecuteAsync(null);
|
||||||
|
Assert.Equal(1, update.CancelCalls);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void UpdateSettingsViewModel_SavesPreferencesThroughUpdateSettingsService()
|
||||||
|
{
|
||||||
|
var update = new FakeUpdateSettingsService();
|
||||||
|
var viewModel = new UpdateSettingsViewModel(new FakeSettingsFacade(update));
|
||||||
|
|
||||||
|
viewModel.SelectedUpdateChannelValue = UpdateSettingsValues.ChannelPreview;
|
||||||
|
viewModel.SelectedUpdateSourceValue = UpdateSettingsValues.DownloadSourceGitHub;
|
||||||
|
viewModel.SelectedUpdateModeValue = UpdateSettingsValues.ModeManual;
|
||||||
|
viewModel.DownloadThreadsSliderValue = 12;
|
||||||
|
viewModel.ForceReinstall = true;
|
||||||
|
|
||||||
|
Assert.True(update.SaveCalls >= 5);
|
||||||
|
Assert.Equal(UpdateSettingsValues.ChannelPreview, update.State.UpdateChannel);
|
||||||
|
Assert.Equal(UpdateSettingsValues.DownloadSourceGitHub, update.State.UpdateDownloadSource);
|
||||||
|
Assert.Equal(UpdateSettingsValues.ModeManual, update.State.UpdateMode);
|
||||||
|
Assert.Equal(12, update.State.UpdateDownloadThreads);
|
||||||
|
Assert.True(update.State.ForceUpdateReinstall);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void UpdateSettingsViewModel_RestoresPersistedPendingAndLastCheckedState()
|
||||||
|
{
|
||||||
|
var update = new FakeUpdateSettingsService
|
||||||
|
{
|
||||||
|
State = DefaultUpdateState() with
|
||||||
|
{
|
||||||
|
PendingUpdateVersion = "2.0.0",
|
||||||
|
PendingUpdatePublishedAtUtcMs = DateTimeOffset.Parse("2026-05-06T00:00:00Z").ToUnixTimeMilliseconds(),
|
||||||
|
LastUpdateCheckUtcMs = DateTimeOffset.Parse("2026-05-07T00:00:00Z").ToUnixTimeMilliseconds()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var viewModel = new UpdateSettingsViewModel(new FakeSettingsFacade(update));
|
||||||
|
|
||||||
|
Assert.True(viewModel.IsUpdateAvailable);
|
||||||
|
Assert.Equal("2.0.0", viewModel.LatestVersionText);
|
||||||
|
Assert.NotEmpty(viewModel.PublishedAtText);
|
||||||
|
Assert.Contains("Last checked", viewModel.LastCheckedText, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SettingsUpdateManifestProvider_UsesSelectedUpdateSource()
|
||||||
|
{
|
||||||
|
var update = new FakeUpdateSettingsService
|
||||||
|
{
|
||||||
|
State = DefaultUpdateState() with { UpdateDownloadSource = UpdateSettingsValues.DownloadSourceGitHub }
|
||||||
|
};
|
||||||
|
var plonds = new FakeManifestProvider("plonds");
|
||||||
|
var github = new FakeManifestProvider("github");
|
||||||
|
var provider = new SettingsUpdateManifestProvider(new FakeSettingsFacade(update), plonds, github);
|
||||||
|
|
||||||
|
var manifest = await provider.GetLatestAsync(
|
||||||
|
UpdateSettingsValues.ChannelStable,
|
||||||
|
"windows-x64",
|
||||||
|
new Version(1, 0, 0),
|
||||||
|
CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.Equal("github", manifest?.DistributionId);
|
||||||
|
Assert.Equal(0, plonds.GetLatestCalls);
|
||||||
|
Assert.Equal(1, github.GetLatestCalls);
|
||||||
|
|
||||||
|
update.State = update.State with { UpdateDownloadSource = UpdateSettingsValues.DownloadSourcePlonds };
|
||||||
|
manifest = await provider.GetLatestAsync(
|
||||||
|
UpdateSettingsValues.ChannelStable,
|
||||||
|
"windows-x64",
|
||||||
|
new Version(1, 0, 0),
|
||||||
|
CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.Equal("plonds", manifest?.DistributionId);
|
||||||
|
Assert.Equal(1, plonds.GetLatestCalls);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FromFullInstaller_IncludesPreferredInstallerInMirrors()
|
||||||
|
{
|
||||||
|
var release = new GitHubReleaseInfo(
|
||||||
|
"v1.2.3",
|
||||||
|
"Release",
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
DateTimeOffset.Parse("2026-05-06T00:00:00Z"),
|
||||||
|
[new GitHubReleaseAsset("LanMountainDesktop-setup-x64.exe", "https://example.test/setup.exe", 123, "abc")]);
|
||||||
|
|
||||||
|
var manifest = UpdateManifestMapper.FromFullInstaller(release, UpdateSettingsValues.ChannelStable, "windows-x64");
|
||||||
|
|
||||||
|
Assert.NotNull(manifest.InstallerMirrors);
|
||||||
|
var mirror = Assert.Single(manifest.InstallerMirrors!);
|
||||||
|
Assert.Equal("https://example.test/setup.exe", mirror.Url);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ApplyDownloadSource_UsesGhProxyForGithubProxySource()
|
||||||
|
{
|
||||||
|
var url = "https://github.com/owner/repo/releases/download/v1/app.exe";
|
||||||
|
|
||||||
|
Assert.Equal(url, UpdateDownloadEngine.ApplyDownloadSource(url, UpdateSettingsValues.DownloadSourceGitHub));
|
||||||
|
Assert.Equal(
|
||||||
|
$"{UpdateSettingsValues.DefaultGhProxyBaseUrl}{url}",
|
||||||
|
UpdateDownloadEngine.ApplyDownloadSource(url, UpdateSettingsValues.DownloadSourceGhProxy));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SettingsUpdateState DefaultUpdateState() => new(
|
||||||
|
IncludePrereleaseUpdates: false,
|
||||||
|
UpdateChannel: UpdateSettingsValues.ChannelStable,
|
||||||
|
UpdateMode: UpdateSettingsValues.ModeSilentDownload,
|
||||||
|
UpdateDownloadSource: UpdateSettingsValues.DownloadSourcePlonds,
|
||||||
|
UpdateDownloadThreads: UpdateSettingsValues.DefaultDownloadThreads,
|
||||||
|
ForceUpdateReinstall: false,
|
||||||
|
UseGhProxyMirror: false,
|
||||||
|
PendingUpdateInstallerPath: null,
|
||||||
|
PendingUpdateVersion: null,
|
||||||
|
PendingUpdatePublishedAtUtcMs: null,
|
||||||
|
LastUpdateCheckUtcMs: null,
|
||||||
|
PendingUpdateSha256: null);
|
||||||
|
|
||||||
|
private sealed class FakeUpdateSettingsService : IUpdateSettingsService
|
||||||
|
{
|
||||||
|
public SettingsUpdateState State { get; set; } = DefaultUpdateState();
|
||||||
|
public UpdatePhase CurrentPhase { get; private set; } = UpdatePhase.Idle;
|
||||||
|
public UpdateCheckReport CheckReport { get; set; } = new(false, null, null, null, null, null, null, null, null, null);
|
||||||
|
public UpdateDownloadResult DownloadResult { get; set; } = new(true, "downloaded", null, true);
|
||||||
|
public InstallResult InstallResult { get; set; } = new(true, null, false);
|
||||||
|
public int SaveCalls { get; private set; }
|
||||||
|
public int CheckCalls { get; private set; }
|
||||||
|
public int DownloadCalls { get; private set; }
|
||||||
|
public int InstallCalls { get; private set; }
|
||||||
|
public int PauseCalls { get; private set; }
|
||||||
|
public int ResumeCalls { get; private set; }
|
||||||
|
public int CancelCalls { get; private set; }
|
||||||
|
|
||||||
|
public event Action<UpdatePhase>? PhaseChanged;
|
||||||
|
public event Action<UpdateProgressReport>? ProgressChanged;
|
||||||
|
|
||||||
|
public void SetPhase(UpdatePhase phase)
|
||||||
|
{
|
||||||
|
CurrentPhase = phase;
|
||||||
|
PhaseChanged?.Invoke(phase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SettingsUpdateState Get() => State;
|
||||||
|
|
||||||
|
public void Save(SettingsUpdateState state)
|
||||||
|
{
|
||||||
|
SaveCalls++;
|
||||||
|
State = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<UpdateCheckReport> CheckAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
CheckCalls++;
|
||||||
|
SetPhase(UpdatePhase.Checked);
|
||||||
|
return Task.FromResult(CheckReport);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<UpdateDownloadResult> DownloadAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
DownloadCalls++;
|
||||||
|
SetPhase(UpdatePhase.Downloaded);
|
||||||
|
return Task.FromResult(DownloadResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<InstallResult> InstallAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
InstallCalls++;
|
||||||
|
SetPhase(UpdatePhase.Installed);
|
||||||
|
return Task.FromResult(InstallResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task RollbackAsync(CancellationToken cancellationToken = default) => Task.CompletedTask;
|
||||||
|
|
||||||
|
public Task PauseAsync()
|
||||||
|
{
|
||||||
|
PauseCalls++;
|
||||||
|
SetPhase(UpdatePhase.PausedDownloading);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<UpdateDownloadResult> ResumeAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
ResumeCalls++;
|
||||||
|
SetPhase(UpdatePhase.Downloaded);
|
||||||
|
return Task.FromResult(DownloadResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task CancelAsync()
|
||||||
|
{
|
||||||
|
CancelCalls++;
|
||||||
|
SetPhase(UpdatePhase.Idle);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task AutoCheckIfEnabledAsync(CancellationToken cancellationToken = default) => Task.CompletedTask;
|
||||||
|
|
||||||
|
public bool TryApplyOnExit() => false;
|
||||||
|
|
||||||
|
public Task<UpdateCheckResult> CheckForUpdatesAsync(Version currentVersion, bool includePrerelease, CancellationToken cancellationToken = default)
|
||||||
|
=> Task.FromResult(new UpdateCheckResult(true, false, currentVersion.ToString(), string.Empty, null, null, null));
|
||||||
|
|
||||||
|
public Task<UpdateCheckResult> ForceCheckForUpdatesAsync(Version currentVersion, bool includePrerelease, CancellationToken cancellationToken = default)
|
||||||
|
=> CheckForUpdatesAsync(currentVersion, includePrerelease, cancellationToken);
|
||||||
|
|
||||||
|
public Task<PlondsUpdatePayload?> GetPlondsUpdatePayloadAsync(Version currentVersion, bool includePrerelease, bool isForce = false, CancellationToken cancellationToken = default)
|
||||||
|
=> Task.FromResult<PlondsUpdatePayload?>(null);
|
||||||
|
|
||||||
|
public Task<LanMountainDesktop.Services.UpdateDownloadResult> DownloadAssetAsync(
|
||||||
|
GitHubReleaseAsset asset,
|
||||||
|
string destinationFilePath,
|
||||||
|
string downloadSource,
|
||||||
|
int maxParallelSegments,
|
||||||
|
IProgress<double>? progress = null,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
=> Task.FromResult(new LanMountainDesktop.Services.UpdateDownloadResult(false, null, "not used", false));
|
||||||
|
|
||||||
|
public Task<LanMountainDesktop.Services.UpdateDownloadResult> RedownloadAssetAsync(
|
||||||
|
GitHubReleaseAsset asset,
|
||||||
|
string destinationFilePath,
|
||||||
|
string downloadSource,
|
||||||
|
int maxParallelSegments,
|
||||||
|
IProgress<double>? progress = null,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
=> Task.FromResult(new LanMountainDesktop.Services.UpdateDownloadResult(false, null, "not used", false));
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class FakeManifestProvider(string providerName) : IUpdateManifestProvider
|
||||||
|
{
|
||||||
|
public string ProviderName { get; } = providerName;
|
||||||
|
public int GetLatestCalls { get; private set; }
|
||||||
|
|
||||||
|
public Task<UpdateManifest?> GetLatestAsync(string channel, string platform, Version currentVersion, CancellationToken ct)
|
||||||
|
{
|
||||||
|
GetLatestCalls++;
|
||||||
|
return Task.FromResult<UpdateManifest?>(CreateManifest(ProviderName, channel, platform));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<UpdateManifest?> GetByVersionAsync(string version, string channel, string platform, CancellationToken ct)
|
||||||
|
=> Task.FromResult<UpdateManifest?>(CreateManifest(ProviderName, channel, platform));
|
||||||
|
|
||||||
|
public Task<IReadOnlyList<UpdateManifest>> GetIncrementalChainAsync(string channel, string platform, Version fromVersion, Version toVersion, CancellationToken ct)
|
||||||
|
=> Task.FromResult<IReadOnlyList<UpdateManifest>>([CreateManifest(ProviderName, channel, platform)]);
|
||||||
|
|
||||||
|
private static UpdateManifest CreateManifest(string id, string channel, string platform) => new(
|
||||||
|
id,
|
||||||
|
"1.0.0",
|
||||||
|
"1.1.0",
|
||||||
|
platform,
|
||||||
|
channel,
|
||||||
|
DateTimeOffset.Parse("2026-05-06T00:00:00Z"),
|
||||||
|
UpdatePayloadKind.DeltaPlonds,
|
||||||
|
"https://example.test/filemap.json",
|
||||||
|
"https://example.test/filemap.json.sig",
|
||||||
|
null,
|
||||||
|
[],
|
||||||
|
null,
|
||||||
|
new Dictionary<string, string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class FakeSettingsFacade(IUpdateSettingsService update) : ISettingsFacadeService
|
||||||
|
{
|
||||||
|
public ISettingsService Settings => throw new NotSupportedException();
|
||||||
|
public ISettingsCatalog Catalog => throw new NotSupportedException();
|
||||||
|
public IGridSettingsService Grid => throw new NotSupportedException();
|
||||||
|
public IWallpaperSettingsService Wallpaper => throw new NotSupportedException();
|
||||||
|
public IWallpaperMediaService WallpaperMedia => throw new NotSupportedException();
|
||||||
|
public IThemeAppearanceService Theme => throw new NotSupportedException();
|
||||||
|
public IStatusBarSettingsService StatusBar => throw new NotSupportedException();
|
||||||
|
public ITextCapsuleSettingsService TextCapsule => throw new NotSupportedException();
|
||||||
|
public IWeatherSettingsService Weather => throw new NotSupportedException();
|
||||||
|
public IRegionSettingsService Region { get; } = new FakeRegionSettingsService();
|
||||||
|
public IPrivacySettingsService Privacy => throw new NotSupportedException();
|
||||||
|
public IUpdateSettingsService Update { get; } = update;
|
||||||
|
public ILauncherCatalogService LauncherCatalog => throw new NotSupportedException();
|
||||||
|
public ILauncherPolicyService LauncherPolicy => throw new NotSupportedException();
|
||||||
|
public IPluginManagementSettingsService PluginManagement => throw new NotSupportedException();
|
||||||
|
public IPluginCatalogSettingsService PluginCatalog => throw new NotSupportedException();
|
||||||
|
public IApplicationInfoService ApplicationInfo { get; } = new FakeApplicationInfoService();
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class FakeRegionSettingsService : IRegionSettingsService
|
||||||
|
{
|
||||||
|
public RegionSettingsState Get() => new("en-US", null);
|
||||||
|
public void Save(RegionSettingsState state) { }
|
||||||
|
public TimeZoneService GetTimeZoneService() => throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class FakeApplicationInfoService : IApplicationInfoService
|
||||||
|
{
|
||||||
|
public string GetAppVersionText() => "1.0.0";
|
||||||
|
public string GetAppCodenameText() => "Test";
|
||||||
|
public AppRenderBackendInfo GetRenderBackendInfo() => throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1210,7 +1210,7 @@ public partial class App : Application
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
HostUpdateOrchestratorProvider.GetOrCreate().TryApplyOnExit();
|
_settingsFacade.Update.TryApplyOnExit();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
|||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using LanMountainDesktop.Services;
|
using LanMountainDesktop.Services;
|
||||||
using LanMountainDesktop.Services.Settings;
|
using LanMountainDesktop.Services.Settings;
|
||||||
using LanMountainDesktop.Services.Update;
|
|
||||||
using LanMountainDesktop.Shared.Contracts.Update;
|
using LanMountainDesktop.Shared.Contracts.Update;
|
||||||
using UpdateSettingsValues = LanMountainDesktop.Services.UpdateSettingsValues;
|
using UpdateSettingsValues = LanMountainDesktop.Services.UpdateSettingsValues;
|
||||||
|
|
||||||
@@ -14,27 +13,28 @@ namespace LanMountainDesktop.ViewModels;
|
|||||||
|
|
||||||
public sealed partial class UpdateSettingsViewModel : ViewModelBase, IDisposable
|
public sealed partial class UpdateSettingsViewModel : ViewModelBase, IDisposable
|
||||||
{
|
{
|
||||||
private readonly UpdateOrchestrator _orchestrator;
|
|
||||||
private readonly ISettingsFacadeService _settingsFacade;
|
private readonly ISettingsFacadeService _settingsFacade;
|
||||||
|
private readonly IUpdateSettingsService _updateSettingsService;
|
||||||
private readonly LocalizationService _localizationService;
|
private readonly LocalizationService _localizationService;
|
||||||
private readonly string _languageCode;
|
private readonly string _languageCode;
|
||||||
|
private bool _suppressPreferenceSave;
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
public UpdateSettingsViewModel(UpdateOrchestrator orchestrator, ISettingsFacadeService settingsFacade)
|
public UpdateSettingsViewModel(ISettingsFacadeService settingsFacade)
|
||||||
{
|
{
|
||||||
_orchestrator = orchestrator ?? throw new ArgumentNullException(nameof(orchestrator));
|
|
||||||
_settingsFacade = settingsFacade ?? throw new ArgumentNullException(nameof(settingsFacade));
|
_settingsFacade = settingsFacade ?? throw new ArgumentNullException(nameof(settingsFacade));
|
||||||
|
_updateSettingsService = _settingsFacade.Update;
|
||||||
_localizationService = new LocalizationService();
|
_localizationService = new LocalizationService();
|
||||||
_languageCode = _localizationService.NormalizeLanguageCode(_settingsFacade.Region.Get().LanguageCode);
|
_languageCode = _localizationService.NormalizeLanguageCode(_settingsFacade.Region.Get().LanguageCode);
|
||||||
|
|
||||||
CurrentPhase = _orchestrator.CurrentPhase;
|
CurrentPhase = _updateSettingsService.CurrentPhase;
|
||||||
CurrentVersionText = _settingsFacade.ApplicationInfo.GetAppVersionText();
|
CurrentVersionText = _settingsFacade.ApplicationInfo.GetAppVersionText();
|
||||||
RefreshLocalizedText();
|
RefreshLocalizedText();
|
||||||
LoadPreferenceState();
|
LoadPreferenceState();
|
||||||
StatusMessage = GetPhaseStatusText(CurrentPhase);
|
StatusMessage = GetPhaseStatusText(CurrentPhase);
|
||||||
|
|
||||||
_orchestrator.PhaseChanged += OnOrchestratorPhaseChanged;
|
_updateSettingsService.PhaseChanged += OnUpdatePhaseChanged;
|
||||||
_orchestrator.ProgressChanged += OnOrchestratorProgressChanged;
|
_updateSettingsService.ProgressChanged += OnUpdateProgressChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
[ObservableProperty] private UpdatePhase _currentPhase = UpdatePhase.Idle;
|
[ObservableProperty] private UpdatePhase _currentPhase = UpdatePhase.Idle;
|
||||||
@@ -208,7 +208,7 @@ public sealed partial class UpdateSettingsViewModel : ViewModelBase, IDisposable
|
|||||||
private async Task CheckAsync()
|
private async Task CheckAsync()
|
||||||
{
|
{
|
||||||
StatusMessage = GetCheckingStatusText();
|
StatusMessage = GetCheckingStatusText();
|
||||||
var report = await _orchestrator.CheckAsync(CancellationToken.None);
|
var report = await _updateSettingsService.CheckAsync(CancellationToken.None);
|
||||||
LastCheckedText = string.Format(
|
LastCheckedText = string.Format(
|
||||||
CultureInfo.CurrentCulture,
|
CultureInfo.CurrentCulture,
|
||||||
L("settings.update.last_checked_format", "Last checked: {0}"),
|
L("settings.update.last_checked_format", "Last checked: {0}"),
|
||||||
@@ -244,7 +244,7 @@ public sealed partial class UpdateSettingsViewModel : ViewModelBase, IDisposable
|
|||||||
private async Task DownloadAsync()
|
private async Task DownloadAsync()
|
||||||
{
|
{
|
||||||
StatusMessage = GetDownloadingStatusText();
|
StatusMessage = GetDownloadingStatusText();
|
||||||
var result = await _orchestrator.DownloadAsync(CancellationToken.None);
|
var result = await _updateSettingsService.DownloadAsync(CancellationToken.None);
|
||||||
if (result.Success)
|
if (result.Success)
|
||||||
{
|
{
|
||||||
StatusMessage = GetDownloadCompleteStatusText();
|
StatusMessage = GetDownloadCompleteStatusText();
|
||||||
@@ -263,7 +263,7 @@ public sealed partial class UpdateSettingsViewModel : ViewModelBase, IDisposable
|
|||||||
private async Task InstallAsync()
|
private async Task InstallAsync()
|
||||||
{
|
{
|
||||||
StatusMessage = GetInstallingStatusText();
|
StatusMessage = GetInstallingStatusText();
|
||||||
var result = await _orchestrator.InstallAsync(CancellationToken.None);
|
var result = await _updateSettingsService.InstallAsync(CancellationToken.None);
|
||||||
if (result.Success)
|
if (result.Success)
|
||||||
{
|
{
|
||||||
StatusMessage = GetInstallSuccessStatusText();
|
StatusMessage = GetInstallSuccessStatusText();
|
||||||
@@ -278,14 +278,14 @@ public sealed partial class UpdateSettingsViewModel : ViewModelBase, IDisposable
|
|||||||
private async Task RollbackAsync()
|
private async Task RollbackAsync()
|
||||||
{
|
{
|
||||||
StatusMessage = GetRollingBackStatusText();
|
StatusMessage = GetRollingBackStatusText();
|
||||||
await _orchestrator.RollbackAsync(CancellationToken.None);
|
await _updateSettingsService.RollbackAsync(CancellationToken.None);
|
||||||
StatusMessage = GetRollbackCompleteStatusText();
|
StatusMessage = GetRollbackCompleteStatusText();
|
||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand(CanExecute = nameof(CanPause))]
|
[RelayCommand(CanExecute = nameof(CanPause))]
|
||||||
private async Task PauseAsync()
|
private async Task PauseAsync()
|
||||||
{
|
{
|
||||||
await _orchestrator.PauseAsync();
|
await _updateSettingsService.PauseAsync();
|
||||||
StatusMessage = GetPausedStatusText();
|
StatusMessage = GetPausedStatusText();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,7 +293,7 @@ public sealed partial class UpdateSettingsViewModel : ViewModelBase, IDisposable
|
|||||||
private async Task ResumeAsync()
|
private async Task ResumeAsync()
|
||||||
{
|
{
|
||||||
StatusMessage = GetResumingStatusText();
|
StatusMessage = GetResumingStatusText();
|
||||||
var result = await _orchestrator.ResumeAsync(CancellationToken.None);
|
var result = await _updateSettingsService.ResumeAsync(CancellationToken.None);
|
||||||
if (result.Success)
|
if (result.Success)
|
||||||
{
|
{
|
||||||
StatusMessage = GetResumeCompleteStatusText();
|
StatusMessage = GetResumeCompleteStatusText();
|
||||||
@@ -307,18 +307,18 @@ public sealed partial class UpdateSettingsViewModel : ViewModelBase, IDisposable
|
|||||||
[RelayCommand(CanExecute = nameof(CanCancel))]
|
[RelayCommand(CanExecute = nameof(CanCancel))]
|
||||||
private async Task CancelAsync()
|
private async Task CancelAsync()
|
||||||
{
|
{
|
||||||
await _orchestrator.CancelAsync();
|
await _updateSettingsService.CancelAsync();
|
||||||
StatusMessage = GetCancelStatusText();
|
StatusMessage = GetCancelStatusText();
|
||||||
ProgressDetail = string.Empty;
|
ProgressDetail = string.Empty;
|
||||||
ProgressFraction = 0;
|
ProgressFraction = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnOrchestratorPhaseChanged(UpdatePhase phase)
|
private void OnUpdatePhaseChanged(UpdatePhase phase)
|
||||||
{
|
{
|
||||||
CurrentPhase = phase;
|
CurrentPhase = phase;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnOrchestratorProgressChanged(UpdateProgressReport report)
|
private void OnUpdateProgressChanged(UpdateProgressReport report)
|
||||||
{
|
{
|
||||||
ProgressFraction = report.ProgressFraction;
|
ProgressFraction = report.ProgressFraction;
|
||||||
|
|
||||||
@@ -348,16 +348,56 @@ public sealed partial class UpdateSettingsViewModel : ViewModelBase, IDisposable
|
|||||||
|
|
||||||
private void LoadPreferenceState()
|
private void LoadPreferenceState()
|
||||||
{
|
{
|
||||||
var state = _settingsFacade.Update.Get();
|
var state = _updateSettingsService.Get();
|
||||||
|
_suppressPreferenceSave = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
SelectedUpdateChannelValue = state.UpdateChannel;
|
SelectedUpdateChannelValue = state.UpdateChannel;
|
||||||
SelectedUpdateSourceValue = state.UpdateDownloadSource;
|
SelectedUpdateSourceValue = state.UpdateDownloadSource;
|
||||||
SelectedUpdateModeValue = state.UpdateMode;
|
SelectedUpdateModeValue = state.UpdateMode;
|
||||||
DownloadThreadsSliderValue = UpdateSettingsValues.NormalizeDownloadThreads(state.UpdateDownloadThreads);
|
DownloadThreadsSliderValue = UpdateSettingsValues.NormalizeDownloadThreads(state.UpdateDownloadThreads);
|
||||||
ForceReinstall = state.ForceUpdateReinstall;
|
ForceReinstall = state.ForceUpdateReinstall;
|
||||||
|
ApplyPersistedUpdateState(state);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_suppressPreferenceSave = false;
|
||||||
|
}
|
||||||
|
|
||||||
SyncComboBoxSelections();
|
SyncComboBoxSelections();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ApplyPersistedUpdateState(LanMountainDesktop.Services.Settings.UpdateSettingsState state)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(state.PendingUpdateVersion))
|
||||||
|
{
|
||||||
|
IsUpdateAvailable = true;
|
||||||
|
LatestVersionText = state.PendingUpdateVersion;
|
||||||
|
PublishedAtText = state.PendingUpdatePublishedAtUtcMs is > 0
|
||||||
|
? DateTimeOffset
|
||||||
|
.FromUnixTimeMilliseconds(state.PendingUpdatePublishedAtUtcMs.Value)
|
||||||
|
.ToLocalTime()
|
||||||
|
.ToString("g", CultureInfo.CurrentCulture)
|
||||||
|
: string.Empty;
|
||||||
|
UpdateTypeText = ForceReinstall
|
||||||
|
? L("settings.update.type_reinstall", "Reinstall")
|
||||||
|
: UpdateTypeText;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.LastUpdateCheckUtcMs is > 0)
|
||||||
|
{
|
||||||
|
LastCheckedText = string.Format(
|
||||||
|
CultureInfo.CurrentCulture,
|
||||||
|
L("settings.update.last_checked_format", "Last checked: {0}"),
|
||||||
|
DateTimeOffset
|
||||||
|
.FromUnixTimeMilliseconds(state.LastUpdateCheckUtcMs.Value)
|
||||||
|
.ToLocalTime()
|
||||||
|
.ToString("g", CultureInfo.CurrentCulture));
|
||||||
|
}
|
||||||
|
|
||||||
|
OnPropertyChanged(nameof(LatestVersionDisplayText));
|
||||||
|
}
|
||||||
|
|
||||||
private void SyncComboBoxSelections()
|
private void SyncComboBoxSelections()
|
||||||
{
|
{
|
||||||
SelectedChannel = ChannelOptions.FirstOrDefault(o => o.Value == SelectedUpdateChannelValue)
|
SelectedChannel = ChannelOptions.FirstOrDefault(o => o.Value == SelectedUpdateChannelValue)
|
||||||
@@ -463,8 +503,13 @@ public sealed partial class UpdateSettingsViewModel : ViewModelBase, IDisposable
|
|||||||
|
|
||||||
private void SavePreferenceState()
|
private void SavePreferenceState()
|
||||||
{
|
{
|
||||||
var current = _settingsFacade.Update.Get();
|
if (_suppressPreferenceSave)
|
||||||
_settingsFacade.Update.Save(current with
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var current = _updateSettingsService.Get();
|
||||||
|
_updateSettingsService.Save(current with
|
||||||
{
|
{
|
||||||
UpdateChannel = SelectedUpdateChannelValue,
|
UpdateChannel = SelectedUpdateChannelValue,
|
||||||
UpdateDownloadSource = SelectedUpdateSourceValue,
|
UpdateDownloadSource = SelectedUpdateSourceValue,
|
||||||
@@ -599,7 +644,7 @@ public sealed partial class UpdateSettingsViewModel : ViewModelBase, IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
_orchestrator.PhaseChanged -= OnOrchestratorPhaseChanged;
|
_updateSettingsService.PhaseChanged -= OnUpdatePhaseChanged;
|
||||||
_orchestrator.ProgressChanged -= OnOrchestratorProgressChanged;
|
_updateSettingsService.ProgressChanged -= OnUpdateProgressChanged;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -511,9 +511,7 @@ public partial class MainWindow : Window
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await HostUpdateOrchestratorProvider
|
await _updateSettingsService.AutoCheckIfEnabledAsync(default);
|
||||||
.GetOrCreate()
|
|
||||||
.AutoCheckIfEnabledAsync(default);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using LanMountainDesktop.PluginSdk;
|
using LanMountainDesktop.PluginSdk;
|
||||||
using LanMountainDesktop.Services.Settings;
|
using LanMountainDesktop.Services.Settings;
|
||||||
using LanMountainDesktop.Services.Update;
|
|
||||||
using LanMountainDesktop.ViewModels;
|
using LanMountainDesktop.ViewModels;
|
||||||
|
|
||||||
namespace LanMountainDesktop.Views.SettingsPages;
|
namespace LanMountainDesktop.Views.SettingsPages;
|
||||||
@@ -16,9 +15,7 @@ namespace LanMountainDesktop.Views.SettingsPages;
|
|||||||
public partial class UpdateSettingsPage : SettingsPageBase
|
public partial class UpdateSettingsPage : SettingsPageBase
|
||||||
{
|
{
|
||||||
public UpdateSettingsPage()
|
public UpdateSettingsPage()
|
||||||
: this(new UpdateSettingsViewModel(
|
: this(new UpdateSettingsViewModel(HostSettingsFacadeProvider.GetOrCreate()))
|
||||||
HostUpdateOrchestratorProvider.GetOrCreate(),
|
|
||||||
HostSettingsFacadeProvider.GetOrCreate()))
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user