Files
community/Ink Canvas/Windows/SettingsViews/Pages/UpdatePage.xaml.cs
T
2026-05-01 20:37:39 +08:00

941 lines
37 KiB
C#

using Ink_Canvas.Helpers;
using Ink_Canvas.Windows.SettingsViews.Helpers;
using iNKORE.UI.WPF.Modern.Common.IconKeys;
using iNKORE.UI.WPF.Modern.Controls;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using MessageBox = iNKORE.UI.WPF.Modern.Controls.MessageBox;
namespace Ink_Canvas.Windows.SettingsViews.Pages
{
public partial class UpdatePage : iNKORE.UI.WPF.Modern.Controls.Page
{
private enum UpdateUiState
{
Idle,
Checking,
UpdateAvailable,
Downloading,
Downloaded,
NetworkError
}
private class VersionItem
{
public string Version { get; set; }
public string DownloadUrl { get; set; }
public string ReleaseNotes { get; set; }
}
private bool _isLoaded;
private bool _isChangingUpdateChannelInternally;
private bool _isChangingUpdatePackageArchInternally;
private UpdateUiState _state = UpdateUiState.Idle;
private string _remoteVersion;
private AutoUpdateHelper.UpdateLineGroup _remoteLineGroup;
private string _remoteReleaseNotes;
private List<VersionItem> _versionList = new List<VersionItem>();
private VersionItem _selectedHistoricalItem;
private bool _isHistoryLoaded;
public UpdatePage()
{
InitializeComponent();
Loaded += UpdatePage_Loaded;
}
private async void UpdatePage_Loaded(object sender, RoutedEventArgs e)
{
LoadSettings();
_isLoaded = true;
// 复用启动时自动检查的结果,避免二次检查
var mainWindow = Application.Current.MainWindow as MainWindow;
if (mainWindow != null && !string.IsNullOrEmpty(mainWindow.AvailableLatestVersion))
{
_remoteVersion = mainWindow.AvailableLatestVersion;
_remoteLineGroup = mainWindow.AvailableLatestLineGroup;
_remoteReleaseNotes = mainWindow.AvailableLatestReleaseNotes;
try
{
var statusFile = AutoUpdateHelper.GetUpdateDownloadStatusFilePath(_remoteVersion);
if (System.IO.File.Exists(statusFile) &&
System.IO.File.ReadAllText(statusFile).Trim().ToLower() == "true")
{
ApplyState(UpdateUiState.Downloaded);
}
else
{
ApplyState(UpdateUiState.UpdateAvailable);
}
}
catch
{
ApplyState(UpdateUiState.UpdateAvailable);
}
if (!string.IsNullOrEmpty(_remoteReleaseNotes))
ChangelogViewer.Markdown = _remoteReleaseNotes;
else
ChangelogViewer.Markdown = "切换到 *历史版本* 或点击 *检查更新* 查看具体更新日志。";
return;
}
// 没有缓存的检查结果时,仅展示空白;不主动联网,避免打开页面就触发请求
ApplyState(UpdateUiState.Idle);
ChangelogViewer.Markdown = "点击 *检查更新* 来获取最新版本及更新日志。";
await System.Threading.Tasks.Task.CompletedTask;
}
#region
private void LoadSettings()
{
_isLoaded = false;
try
{
var settings = SettingsManager.Settings;
if (settings.Startup != null)
{
CardAutoUpdate.IsOn = settings.Startup.IsAutoUpdate;
CardSilentUpdate.IsOn = settings.Startup.IsAutoUpdateWithSilence;
AutoUpdateWithSilenceTimeComboBox.InitializeAutoUpdateWithSilenceTimeComboBoxOptions(
AutoUpdateWithSilenceStartTimeComboBox, AutoUpdateWithSilenceEndTimeComboBox);
AutoUpdateWithSilenceStartTimeComboBox.SelectedItem = settings.Startup.AutoUpdateWithSilenceStartTime;
AutoUpdateWithSilenceEndTimeComboBox.SelectedItem = settings.Startup.AutoUpdateWithSilenceEndTime;
foreach (var item in UpdateChannelSelector.Items)
{
if (item is ComboBoxItem cbi && cbi.Tag != null &&
string.Equals(cbi.Tag.ToString(), settings.Startup.UpdateChannel.ToString(), StringComparison.OrdinalIgnoreCase))
{
UpdateChannelSelector.SelectedItem = cbi;
break;
}
}
_isChangingUpdatePackageArchInternally = true;
try
{
string wantTag = settings.Startup.UpdatePackageArchitecture == UpdatePackageArchitecture.X64 ? "X64" : "X86";
foreach (var item in UpdatePackageArchitectureSelector.Items)
{
if (item is ComboBoxItem cbi && cbi.Tag != null &&
string.Equals(cbi.Tag.ToString(), wantTag, StringComparison.OrdinalIgnoreCase))
{
UpdatePackageArchitectureSelector.SelectedItem = cbi;
break;
}
}
}
finally
{
_isChangingUpdatePackageArchInternally = false;
}
}
}
catch (Exception ex)
{
Debug.WriteLine($"加载更新设置时出错: {ex.Message}");
}
_isLoaded = true;
}
#endregion
#region
private void ToggleSwitchIsAutoUpdate_Toggled(object sender, RoutedEventArgs e)
{
if (!_isLoaded) return;
try
{
bool newState = CardAutoUpdate.IsOn;
SettingsManager.Settings.Startup.IsAutoUpdate = newState;
if (!newState)
{
SettingsManager.Settings.Startup.IsAutoUpdateWithSilence = false;
CardSilentUpdate.IsOn = false;
}
SettingsManager.SaveSettingsToFile();
}
catch (Exception ex)
{
Debug.WriteLine($"设置自动更新时出错: {ex.Message}");
}
}
private void ToggleSwitchIsAutoUpdateWithSilence_Toggled(object sender, RoutedEventArgs e)
{
if (!_isLoaded) return;
try
{
SettingsManager.Settings.Startup.IsAutoUpdateWithSilence = CardSilentUpdate.IsOn;
SettingsManager.SaveSettingsToFile();
}
catch (Exception ex)
{
Debug.WriteLine($"设置静默更新时出错: {ex.Message}");
}
}
private void AutoUpdateWithSilenceStartTimeComboBox_SelectionChanged(object sender, RoutedEventArgs e)
{
if (!_isLoaded) return;
try
{
SettingsManager.Settings.Startup.AutoUpdateWithSilenceStartTime =
(string)AutoUpdateWithSilenceStartTimeComboBox.SelectedItem;
SettingsManager.SaveSettingsToFile();
}
catch (Exception ex)
{
Debug.WriteLine($"设置静默更新开始时间时出错: {ex.Message}");
}
}
private void AutoUpdateWithSilenceEndTimeComboBox_SelectionChanged(object sender, RoutedEventArgs e)
{
if (!_isLoaded) return;
try
{
SettingsManager.Settings.Startup.AutoUpdateWithSilenceEndTime =
(string)AutoUpdateWithSilenceEndTimeComboBox.SelectedItem;
SettingsManager.SaveSettingsToFile();
}
catch (Exception ex)
{
Debug.WriteLine($"设置静默更新结束时间时出错: {ex.Message}");
}
}
#endregion
#region
private void UpdatePackageArchitectureSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (!_isLoaded) return;
if (_isChangingUpdatePackageArchInternally) return;
if (!(UpdatePackageArchitectureSelector.SelectedItem is ComboBoxItem cbi) || cbi.Tag == null) return;
var newArch = string.Equals(cbi.Tag.ToString(), "X64", StringComparison.OrdinalIgnoreCase)
? UpdatePackageArchitecture.X64
: UpdatePackageArchitecture.X86;
if (SettingsManager.Settings.Startup.UpdatePackageArchitecture == newArch)
return;
SettingsManager.Settings.Startup.UpdatePackageArchitecture = newArch;
SettingsManager.SaveSettingsToFile();
LogHelper.WriteLogToFile($"Settings | Update package architecture: {newArch}");
}
private async void UpdateChannelSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (!_isLoaded) return;
if (_isChangingUpdateChannelInternally) return;
if (!(UpdateChannelSelector.SelectedItem is ComboBoxItem cbi) || cbi.Tag == null) return;
var oldChannel = SettingsManager.Settings.Startup.UpdateChannel;
string channel = cbi.Tag.ToString();
UpdateChannel newChannel = channel == "Beta" ? UpdateChannel.Beta
: channel == "Preview" ? UpdateChannel.Preview
: UpdateChannel.Release;
if (SettingsManager.Settings.Startup.UpdateChannel == newChannel)
return;
bool isTestChannel = newChannel == UpdateChannel.Preview || newChannel == UpdateChannel.Beta;
if (isTestChannel && !SettingsManager.Settings.Startup.HasAcceptedTelemetryPrivacy)
{
MessageBox.Show(
"加入预览 / 测试通道前,请先在关于页面勾选“我已阅读并同意 privacy 中的隐私说明”。",
"需要同意隐私说明",
MessageBoxButton.OK,
MessageBoxImage.Warning);
SettingsManager.Settings.Startup.UpdateChannel = oldChannel;
RevertChannelSelection(oldChannel);
SettingsManager.SaveSettingsToFile();
LogHelper.WriteLogToFile("Settings | User not accepted privacy, reverted update channel");
return;
}
if (isTestChannel && SettingsManager.Settings.Startup.TelemetryUploadLevel == TelemetryUploadLevel.None)
{
var result = MessageBox.Show(
"加入预览 / 测试通道需要开启匿名基础数据上传。\n\n是否立即开启匿名基础数据上传?",
"需要开启匿名使用数据上传",
MessageBoxButton.YesNo,
MessageBoxImage.Warning);
if (result == MessageBoxResult.Yes)
{
SettingsManager.Settings.Startup.TelemetryUploadLevel = TelemetryUploadLevel.Basic;
SettingsManager.SaveSettingsToFile();
LogHelper.WriteLogToFile("Settings | Telemetry enabled (Basic) for preview/beta update channel");
}
else
{
SettingsManager.Settings.Startup.UpdateChannel = oldChannel;
RevertChannelSelection(oldChannel);
SettingsManager.SaveSettingsToFile();
LogHelper.WriteLogToFile("Settings | User declined telemetry, reverted update channel");
return;
}
}
SettingsManager.Settings.Startup.UpdateChannel = newChannel;
DeviceIdentifier.UpdateUsageChannel(newChannel);
LogHelper.WriteLogToFile($"Settings | Update channel changed to {SettingsManager.Settings.Startup.UpdateChannel}");
SettingsManager.SaveSettingsToFile();
// 通道切换后强制刷新更新日志和历史版本缓存
_isHistoryLoaded = false;
_versionList.Clear();
VersionComboBox.ItemsSource = null;
ReleaseNotesViewer.Markdown = "";
await LoadChangelogAsync();
if (SettingsManager.Settings.Startup.IsAutoUpdate)
{
LogHelper.WriteLogToFile($"AutoUpdate | Channel changed to {newChannel}, performing immediate update check");
var mainWindow = Application.Current.MainWindow as MainWindow;
if (mainWindow != null)
{
mainWindow.ResetUpdateCheckRetry();
await System.Threading.Tasks.Task.Run(() =>
{
try
{
Dispatcher.Invoke(() =>
{
mainWindow.AutoUpdate();
});
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"AutoUpdate | Error during channel switch update check: {ex.Message}", LogHelper.LogType.Error);
}
});
}
}
}
private void RevertChannelSelection(UpdateChannel targetChannel)
{
Dispatcher.BeginInvoke(new Action(() =>
{
_isChangingUpdateChannelInternally = true;
try
{
string targetTag = targetChannel.ToString();
foreach (var item in UpdateChannelSelector.Items)
{
if (item is ComboBoxItem cbi && cbi.Tag != null &&
string.Equals(cbi.Tag.ToString(), targetTag, StringComparison.OrdinalIgnoreCase))
{
UpdateChannelSelector.SelectedItem = cbi;
break;
}
}
}
finally
{
_isChangingUpdateChannelInternally = false;
}
}), DispatcherPriority.Normal);
}
#endregion
#region
private void ApplyState(UpdateUiState state, string customSubtitle = null)
{
_state = state;
CheckUpdateButton.Visibility = Visibility.Collapsed;
UpdateNowButton.Visibility = Visibility.Collapsed;
UpdateLaterButton.Visibility = Visibility.Collapsed;
SkipVersionButton.Visibility = Visibility.Collapsed;
CancelDownloadButton.Visibility = Visibility.Collapsed;
ProgressPanel.Visibility = Visibility.Collapsed;
CheckUpdateButton.IsEnabled = true;
switch (state)
{
case UpdateUiState.Idle:
StatusIcon.Icon = SegoeFluentIcons.Completed;
StatusTitle.Text = "已是最新版本";
StatusSubtitle.Text = customSubtitle ?? BuildLastCheckSubtitle();
CheckUpdateButton.Visibility = Visibility.Visible;
break;
case UpdateUiState.Checking:
StatusIcon.Icon = SegoeFluentIcons.Sync;
StatusTitle.Text = "正在检查更新...";
StatusSubtitle.Text = "";
CheckUpdateButton.Visibility = Visibility.Visible;
CheckUpdateButton.IsEnabled = false;
ProgressPanel.Visibility = Visibility.Visible;
ProgressText.Text = "正在连接更新服务器...";
ProgressBar.IsIndeterminate = true;
break;
case UpdateUiState.UpdateAvailable:
StatusIcon.Icon = SegoeFluentIcons.Upload;
StatusTitle.Text = $"检测到新版本 {_remoteVersion}";
StatusSubtitle.Text = customSubtitle ?? $"当前版本 {GetCurrentVersion()} → {_remoteVersion}";
UpdateNowButton.Visibility = Visibility.Visible;
UpdateLaterButton.Visibility = Visibility.Visible;
SkipVersionButton.Visibility = Visibility.Visible;
break;
case UpdateUiState.Downloading:
StatusIcon.Icon = SegoeFluentIcons.Download;
StatusTitle.Text = "正在下载更新...";
StatusSubtitle.Text = customSubtitle ?? $"目标版本 {_remoteVersion}";
ProgressPanel.Visibility = Visibility.Visible;
ProgressBar.IsIndeterminate = false;
break;
case UpdateUiState.Downloaded:
StatusIcon.Icon = SegoeFluentIcons.Download;
StatusTitle.Text = "更新已下载完成";
StatusSubtitle.Text = customSubtitle ?? $"将在软件关闭时自动安装 {_remoteVersion}";
CheckUpdateButton.Visibility = Visibility.Visible;
break;
case UpdateUiState.NetworkError:
StatusIcon.Icon = SegoeFluentIcons.Error;
StatusTitle.Text = "网络错误";
StatusSubtitle.Text = customSubtitle ?? "请检查网络连接后重试。";
CheckUpdateButton.Visibility = Visibility.Visible;
break;
}
}
private string BuildLastCheckSubtitle()
{
string current = GetCurrentVersion();
return $"当前版本 {current}";
}
private static string GetCurrentVersion()
{
try
{
return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
}
catch
{
return "未知";
}
}
#endregion
#region
private async System.Threading.Tasks.Task LoadChangelogAsync()
{
try
{
ChangelogViewer.Markdown = "正在加载更新日志...";
// 优先尝试从 GitHub API 获取最新 Release 的 body(带超时,失败则回退到镜像)
try
{
var apiTask = AutoUpdateHelper.GetAllGithubReleases(SettingsManager.Settings.Startup.UpdateChannel);
var completed = await System.Threading.Tasks.Task.WhenAny(apiTask, System.Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(8)));
if (completed == apiTask)
{
var releases = await apiTask;
var latest = releases?
.OrderByDescending(r => ParseVersionForSort(r.version))
.Select(r => (Tuple<string, string, string>)Tuple.Create(r.version, r.downloadUrl, r.releaseNotes))
.FirstOrDefault();
if (latest != null && !string.IsNullOrWhiteSpace(latest.Item3))
{
ChangelogViewer.Markdown = latest.Item3;
return;
}
LogHelper.WriteLogToFile("UpdatePage | GitHub API 未返回可用的更新日志,回退到镜像", LogHelper.LogType.Warning);
}
else
{
LogHelper.WriteLogToFile("UpdatePage | GitHub API 获取更新日志超时,回退到镜像", LogHelper.LogType.Warning);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"UpdatePage | GitHub API 获取更新日志失败,回退到镜像: {ex.Message}", LogHelper.LogType.Warning);
}
// 回退到镜像源 UpdateLog.md
string md = await AutoUpdateHelper.GetUpdateLog(SettingsManager.Settings.Startup.UpdateChannel);
ChangelogViewer.Markdown = string.IsNullOrEmpty(md) ? "暂无更新日志。" : md;
}
catch (Exception ex)
{
ChangelogViewer.Markdown = $"加载更新日志失败:{ex.Message}";
}
}
#endregion
#region / /
private async void CheckUpdateButton_Click(object sender, RoutedEventArgs e)
{
ApplyState(UpdateUiState.Checking);
try
{
LogHelper.WriteLogToFile("ManualUpdate | Manual update button clicked");
string remoteVersion = null;
string apiReleaseNotes = null;
AutoUpdateHelper.UpdateLineGroup lineGroup = null;
// 优先通过 GitHub Releases API 获取最新版本
try
{
var releases = await AutoUpdateHelper.GetAllGithubReleases(SettingsManager.Settings.Startup.UpdateChannel);
var latest = releases?
.OrderByDescending(r => ParseVersionForSort(r.version))
.Select(r => Tuple.Create(r.version, r.downloadUrl, r.releaseNotes))
.FirstOrDefault();
if (latest != null && !string.IsNullOrEmpty(latest.Item1))
{
var localVersion = new Version(GetCurrentVersion());
var remote = ParseVersionForSort(latest.Item1);
if (remote > localVersion)
{
remoteVersion = latest.Item1.TrimStart('v', 'V');
apiReleaseNotes = latest.Item3;
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"UpdatePage | GitHub API 检查更新失败,回退到 CheckForUpdates: {ex.Message}", LogHelper.LogType.Warning);
}
// 回退:调用统一的 CheckForUpdates(包含镜像源 txt 方案)
if (string.IsNullOrEmpty(remoteVersion))
{
var (rv, lg, notes) = await AutoUpdateHelper.CheckForUpdates(
SettingsManager.Settings.Startup.UpdateChannel, true, false);
remoteVersion = rv;
lineGroup = lg;
if (string.IsNullOrEmpty(apiReleaseNotes)) apiReleaseNotes = notes;
}
if (!string.IsNullOrEmpty(remoteVersion))
{
_remoteVersion = remoteVersion;
_remoteLineGroup = lineGroup;
_remoteReleaseNotes = apiReleaseNotes;
if (!string.IsNullOrEmpty(apiReleaseNotes))
ChangelogViewer.Markdown = apiReleaseNotes;
ApplyState(UpdateUiState.UpdateAvailable);
}
else
{
ApplyState(UpdateUiState.Idle);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"Error in CheckUpdateButton_Click: {ex.Message}", LogHelper.LogType.Error);
ApplyState(UpdateUiState.NetworkError, ex.Message);
}
}
private bool _downloadCancelled;
private static List<AutoUpdateHelper.UpdateLineGroup> _cachedOrderedGroups;
private static UpdateChannel _cachedGroupsChannel;
private static async System.Threading.Tasks.Task<List<AutoUpdateHelper.UpdateLineGroup>> GetOrderedGroupsCachedAsync(UpdateChannel channel)
{
if (_cachedOrderedGroups != null && _cachedOrderedGroups.Count > 0 && _cachedGroupsChannel == channel)
return _cachedOrderedGroups;
var groups = await AutoUpdateHelper.GetAvailableLineGroupsOrdered(channel);
_cachedOrderedGroups = groups;
_cachedGroupsChannel = channel;
return groups;
}
private async System.Threading.Tasks.Task<bool> DownloadWithProgressAsync()
{
_downloadCancelled = false;
var groups = await GetOrderedGroupsCachedAsync(SettingsManager.Settings.Startup.UpdateChannel);
if (groups == null || groups.Count == 0)
{
LogHelper.WriteLogToFile("UpdatePage | 没有可用的下载线路组", LogHelper.LogType.Error);
return false;
}
return await AutoUpdateHelper.DownloadSetupFileWithFallback(_remoteVersion, groups, (percent, text) =>
{
if (_downloadCancelled) return;
Dispatcher.Invoke(() =>
{
if (_state != UpdateUiState.Downloading) return;
ProgressBar.IsIndeterminate = false;
ProgressBar.Value = percent;
ProgressText.Text = text;
});
});
}
private async void UpdateNowButton_Click(object sender, RoutedEventArgs e)
{
if (string.IsNullOrEmpty(_remoteVersion)) return;
ApplyState(UpdateUiState.Downloading);
CancelDownloadButton.Visibility = Visibility.Visible;
ProgressBar.Value = 0;
ProgressText.Text = "正在准备下载...";
try
{
bool ok = await DownloadWithProgressAsync();
if (!ok)
{
ApplyState(UpdateUiState.NetworkError, "更新下载失败,请检查网络连接后重试。");
return;
}
MessageBoxResult result = MessageBox.Show(
"更新已下载完成,点击确定后将关闭软件并安装新版本!",
"安装更新",
MessageBoxButton.OKCancel,
MessageBoxImage.Information);
if (result == MessageBoxResult.OK)
{
App.IsAppExitByUser = true;
AutoUpdateHelper.InstallNewVersionApp(_remoteVersion, true);
Application.Current.Shutdown();
}
else
{
ApplyState(UpdateUiState.Downloaded);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"Error in UpdateNowButton_Click: {ex.Message}", LogHelper.LogType.Error);
ApplyState(UpdateUiState.NetworkError, ex.Message);
}
}
private async void UpdateLaterButton_Click(object sender, RoutedEventArgs e)
{
if (string.IsNullOrEmpty(_remoteVersion)) return;
var mainWindow = Application.Current.MainWindow as MainWindow;
if (mainWindow == null) return;
ApplyState(UpdateUiState.Downloading);
CancelDownloadButton.Visibility = Visibility.Visible;
ProgressBar.Value = 0;
ProgressText.Text = "正在后台下载...";
try
{
bool ok = await DownloadWithProgressAsync();
if (!ok)
{
ApplyState(UpdateUiState.NetworkError, "更新下载失败,请检查网络连接后重试。");
return;
}
SettingsManager.Settings.Startup.IsAutoUpdate = true;
SettingsManager.Settings.Startup.IsAutoUpdateWithSilence = true;
SettingsManager.SaveSettingsToFile();
CardAutoUpdate.IsOn = true;
CardSilentUpdate.IsOn = true;
mainWindow.StartSilentUpdateTimer();
ApplyState(UpdateUiState.Downloaded);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"Error in UpdateLaterButton_Click: {ex.Message}", LogHelper.LogType.Error);
ApplyState(UpdateUiState.NetworkError, ex.Message);
}
}
private void SkipVersionButton_Click(object sender, RoutedEventArgs e)
{
if (string.IsNullOrEmpty(_remoteVersion)) return;
SettingsManager.Settings.Startup.SkippedVersion = _remoteVersion;
SettingsManager.SaveSettingsToFile();
LogHelper.WriteLogToFile($"ManualUpdate | User chose to skip version {_remoteVersion}");
ApplyState(UpdateUiState.Idle, $"已跳过版本 {_remoteVersion}");
}
private void CancelDownloadButton_Click(object sender, RoutedEventArgs e)
{
_downloadCancelled = true;
AutoUpdateHelper.RequestCancelDownload();
ApplyState(UpdateUiState.UpdateAvailable);
}
#endregion
#region
private async void UpdateTabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.OriginalSource != UpdateTabControl) return;
if (UpdateTabControl.SelectedItem == HistoryTabItem && !_isHistoryLoaded)
{
await LoadHistoryAsync();
}
}
private async void HistoryTabItem_GotFocus(object sender, RoutedEventArgs e)
{
// 兼容用户首次切到历史版本 Tab 时再加载
if (_isHistoryLoaded) return;
await LoadHistoryAsync();
}
private async System.Threading.Tasks.Task LoadHistoryAsync()
{
try
{
_isHistoryLoaded = true;
ReleaseNotesViewer.Markdown = "正在获取历史版本...";
RollbackButton.IsEnabled = false;
var releases = await AutoUpdateHelper.GetAllGithubReleases(SettingsManager.Settings.Startup.UpdateChannel);
_versionList = releases
.Select(r => new VersionItem { Version = r.version, DownloadUrl = r.downloadUrl, ReleaseNotes = r.releaseNotes })
.OrderByDescending(v => ParseVersionForSort(v.Version))
.ToList();
VersionComboBox.ItemsSource = _versionList;
if (_versionList.Count > 0)
{
VersionComboBox.SelectedIndex = 0;
RollbackButton.IsEnabled = true;
}
else
{
ReleaseNotesViewer.Markdown = "未获取到历史版本信息。";
}
}
catch (Exception ex)
{
ReleaseNotesViewer.Markdown = $"加载历史版本失败:{ex.Message}";
}
}
private static Version ParseVersionForSort(string version)
{
var v = (version ?? "").TrimStart('v', 'V');
return Version.TryParse(v, out var result) ? result : new Version(0, 0, 0, 0);
}
private void VersionComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
_selectedHistoricalItem = VersionComboBox.SelectedItem as VersionItem;
if (_selectedHistoricalItem != null)
{
ReleaseNotesViewer.Markdown = _selectedHistoricalItem.ReleaseNotes ?? "无更新日志";
LogHelper.WriteLogToFile($"HistoryRollback | 用户选择版本: {_selectedHistoricalItem.Version}");
}
Keyboard.ClearFocus();
}
private async void RollbackButton_Click(object sender, RoutedEventArgs e)
{
if (_selectedHistoricalItem == null) return;
int days = await AskPauseDaysAsync();
if (days < 0)
{
LogHelper.WriteLogToFile("HistoryRollback | 用户取消了回滚操作");
return;
}
if (days == 0)
{
MainWindow.Settings.Startup.AutoUpdatePauseUntilDate = "";
}
else
{
DateTime pauseUntilDate = DateTime.Now.AddDays(days);
MainWindow.Settings.Startup.AutoUpdatePauseUntilDate = pauseUntilDate.ToString("yyyy-MM-dd");
LogHelper.WriteLogToFile($"HistoryRollback | 用户选择暂停自动更新 {days} 天,截止日期: {pauseUntilDate:yyyy-MM-dd}");
}
MainWindow.SaveSettingsToFile();
LogHelper.WriteLogToFile($"HistoryRollback | 用户确认回滚,目标版本: {_selectedHistoricalItem.Version}");
RollbackButton.IsEnabled = false;
VersionComboBox.IsEnabled = false;
RollbackProgressPanel.Visibility = Visibility.Visible;
RollbackProgressBar.Value = 0;
RollbackProgressText.Text = "正在准备下载...";
bool downloadSuccess = false;
try
{
downloadSuccess = await AutoUpdateHelper.StartManualDownloadAndInstall(
_selectedHistoricalItem.Version,
SettingsManager.Settings.Startup.UpdateChannel,
(percent, text) =>
{
Dispatcher.Invoke(() =>
{
RollbackProgressBar.Value = percent;
RollbackProgressText.Text = text;
});
});
}
catch (Exception ex)
{
RollbackProgressText.Text = $"下载失败: {ex.Message}";
LogHelper.WriteLogToFile($"HistoryRollback | 下载异常: {ex.Message}", LogHelper.LogType.Error);
}
if (downloadSuccess)
{
RollbackProgressBar.Value = 100;
RollbackProgressText.Text = "下载完成,准备安装...";
}
else
{
RollbackProgressText.Text = "下载失败,请检查网络后重试。";
RollbackButton.IsEnabled = true;
VersionComboBox.IsEnabled = true;
}
}
private async System.Threading.Tasks.Task<int> AskPauseDaysAsync()
{
var dialog = new ContentDialog
{
Title = "暂停自动更新",
PrimaryButtonText = "确定",
SecondaryButtonText = "取消"
};
var panel = new iNKORE.UI.WPF.Controls.SimpleStackPanel
{
Spacing = 16,
Margin = new Thickness(0, 10, 0, 0)
};
var textBlock = new TextBlock
{
Text = "请选择在回滚后多久不再接收自动更新:",
FontSize = 14
};
var daysComboBox = new ComboBox
{
Width = 200,
Height = 36,
HorizontalAlignment = HorizontalAlignment.Left
};
for (int i = 0; i <= 7; i++)
{
daysComboBox.Items.Add(new ComboBoxItem { Content = $"{i} 天", Tag = i });
}
daysComboBox.SelectedIndex = 0;
panel.Children.Add(textBlock);
panel.Children.Add(daysComboBox);
dialog.Content = panel;
var result = await dialog.ShowAsync();
if (result != ContentDialogResult.Primary) return -1;
if (daysComboBox.SelectedItem is ComboBoxItem cbi && cbi.Tag is int days)
return days;
return 1;
}
#endregion
#region
private async void FixVersionButton_Click(object sender, RoutedEventArgs e)
{
var confirm = MessageBox.Show(
"此操作将下载当前选择通道的最新版本并安装,软件将自动关闭并更新。\n\n确定要执行版本修复吗?",
"版本修复确认",
MessageBoxButton.YesNo,
MessageBoxImage.Question);
if (confirm != MessageBoxResult.Yes) return;
FixVersionButton.IsEnabled = false;
FixVersionButton.Content = "正在修复...";
try
{
bool result = await AutoUpdateHelper.FixVersion(SettingsManager.Settings.Startup.UpdateChannel);
if (!result)
{
MessageBox.Show(
"版本修复失败,可能是网络问题或当前已是最新版本。",
"修复失败",
MessageBoxButton.OK,
MessageBoxImage.Error);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"Error in FixVersionButton_Click: {ex.Message}", LogHelper.LogType.Error);
MessageBox.Show(
$"版本修复过程中发生错误: {ex.Message}",
"修复错误",
MessageBoxButton.OK,
MessageBoxImage.Error);
}
finally
{
FixVersionButton.IsEnabled = true;
FixVersionButton.Content = "版本修复";
}
}
#endregion
}
}