add:更新面板
This commit is contained in:
@@ -27,6 +27,37 @@ namespace Ink_Canvas.Helpers
|
|||||||
private static readonly string updatesFolderPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "AutoUpdate");
|
private static readonly string updatesFolderPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "AutoUpdate");
|
||||||
private static string statusFilePath;
|
private static string statusFilePath;
|
||||||
|
|
||||||
|
// 全局下载取消令牌;UI 通过 RequestCancelDownload 取消当前下载
|
||||||
|
private static CancellationTokenSource _activeDownloadCts;
|
||||||
|
private static readonly object _activeDownloadLock = new object();
|
||||||
|
|
||||||
|
public static void RequestCancelDownload()
|
||||||
|
{
|
||||||
|
lock (_activeDownloadLock)
|
||||||
|
{
|
||||||
|
try { _activeDownloadCts?.Cancel(); } catch { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CancellationTokenSource BeginDownloadSession()
|
||||||
|
{
|
||||||
|
lock (_activeDownloadLock)
|
||||||
|
{
|
||||||
|
try { _activeDownloadCts?.Cancel(); } catch { }
|
||||||
|
_activeDownloadCts = new CancellationTokenSource();
|
||||||
|
return _activeDownloadCts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EndDownloadSession(CancellationTokenSource cts)
|
||||||
|
{
|
||||||
|
lock (_activeDownloadLock)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(_activeDownloadCts, cts)) _activeDownloadCts = null;
|
||||||
|
}
|
||||||
|
try { cts?.Dispose(); } catch { }
|
||||||
|
}
|
||||||
|
|
||||||
public static bool IsX64UpdatePackageSelected()
|
public static bool IsX64UpdatePackageSelected()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -383,6 +414,8 @@ namespace Ink_Canvas.Helpers
|
|||||||
// 获取所有可用线路组,按延迟排序
|
// 获取所有可用线路组,按延迟排序
|
||||||
public static async Task<List<UpdateLineGroup>> GetAvailableLineGroupsOrdered(UpdateChannel channel)
|
public static async Task<List<UpdateLineGroup>> GetAvailableLineGroupsOrdered(UpdateChannel channel)
|
||||||
{
|
{
|
||||||
|
var cached = TryGetCachedOrderedGroups(channel);
|
||||||
|
if (cached != null) return cached;
|
||||||
var groups = ChannelLineGroups[channel];
|
var groups = ChannelLineGroups[channel];
|
||||||
var availableGroups = new List<(UpdateLineGroup group, long delay)>();
|
var availableGroups = new List<(UpdateLineGroup group, long delay)>();
|
||||||
|
|
||||||
@@ -468,9 +501,46 @@ namespace Ink_Canvas.Helpers
|
|||||||
LogHelper.WriteLogToFile("AutoUpdate | 所有线路组均不可用", LogHelper.LogType.Error);
|
LogHelper.WriteLogToFile("AutoUpdate | 所有线路组均不可用", LogHelper.LogType.Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CacheOrderedGroups(channel, orderedGroups);
|
||||||
return orderedGroups;
|
return orderedGroups;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 缓存按延迟排序后的线路组,避免短时间内重复测速
|
||||||
|
private static readonly Dictionary<UpdateChannel, (List<UpdateLineGroup> groups, DateTime cachedAt)> _orderedGroupsCache
|
||||||
|
= new Dictionary<UpdateChannel, (List<UpdateLineGroup>, DateTime)>();
|
||||||
|
private static readonly TimeSpan _orderedGroupsCacheTtl = TimeSpan.FromMinutes(15);
|
||||||
|
|
||||||
|
private static List<UpdateLineGroup> TryGetCachedOrderedGroups(UpdateChannel channel)
|
||||||
|
{
|
||||||
|
lock (_orderedGroupsCache)
|
||||||
|
{
|
||||||
|
if (_orderedGroupsCache.TryGetValue(channel, out var entry) &&
|
||||||
|
entry.groups != null && entry.groups.Count > 0 &&
|
||||||
|
DateTime.UtcNow - entry.cachedAt < _orderedGroupsCacheTtl)
|
||||||
|
{
|
||||||
|
LogHelper.WriteLogToFile($"AutoUpdate | 复用线路组延迟检测缓存({entry.groups.Count} 个)");
|
||||||
|
return new List<UpdateLineGroup>(entry.groups);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CacheOrderedGroups(UpdateChannel channel, List<UpdateLineGroup> groups)
|
||||||
|
{
|
||||||
|
lock (_orderedGroupsCache)
|
||||||
|
{
|
||||||
|
_orderedGroupsCache[channel] = (new List<UpdateLineGroup>(groups), DateTime.UtcNow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void InvalidateOrderedGroupsCache()
|
||||||
|
{
|
||||||
|
lock (_orderedGroupsCache)
|
||||||
|
{
|
||||||
|
_orderedGroupsCache.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static async Task<long> GetDownloadUrlDelay(string url)
|
private static async Task<long> GetDownloadUrlDelay(string url)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -945,6 +1015,7 @@ namespace Ink_Canvas.Helpers
|
|||||||
// 使用多线路组下载新版(支持自动切换)
|
// 使用多线路组下载新版(支持自动切换)
|
||||||
public static async Task<bool> DownloadSetupFileWithFallback(string version, List<UpdateLineGroup> groups, Action<double, string> progressCallback = null)
|
public static async Task<bool> DownloadSetupFileWithFallback(string version, List<UpdateLineGroup> groups, Action<double, string> progressCallback = null)
|
||||||
{
|
{
|
||||||
|
var session = BeginDownloadSession();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
version = NormalizeVersionForUpdate(version);
|
version = NormalizeVersionForUpdate(version);
|
||||||
@@ -1021,6 +1092,13 @@ namespace Ink_Canvas.Helpers
|
|||||||
progressCallback?.Invoke(0, "所有线路组下载均失败");
|
progressCallback?.Invoke(0, "所有线路组下载均失败");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
LogHelper.WriteLogToFile("AutoUpdate | 下载已被用户取消", LogHelper.LogType.Warning);
|
||||||
|
SaveDownloadStatus(false);
|
||||||
|
progressCallback?.Invoke(0, "下载已取消");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
LogHelper.WriteLogToFile($"AutoUpdate | 下载更新时出错: {ex.Message}", LogHelper.LogType.Error);
|
LogHelper.WriteLogToFile($"AutoUpdate | 下载更新时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||||
@@ -1033,6 +1111,10 @@ namespace Ink_Canvas.Helpers
|
|||||||
progressCallback?.Invoke(0, $"下载异常: {ex.Message}");
|
progressCallback?.Invoke(0, $"下载异常: {ex.Message}");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
EndDownloadSession(session);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 下载文件的具体实现
|
// 下载文件的具体实现
|
||||||
@@ -1043,6 +1125,12 @@ namespace Ink_Canvas.Helpers
|
|||||||
// 降低并发数,减少网络压力
|
// 降低并发数,减少网络压力
|
||||||
int[] threadOptions = { 32, 16, 8, 4, 1 };
|
int[] threadOptions = { 32, 16, 8, 4, 1 };
|
||||||
|
|
||||||
|
CancellationToken externalToken;
|
||||||
|
lock (_activeDownloadLock)
|
||||||
|
{
|
||||||
|
externalToken = _activeDownloadCts?.Token ?? CancellationToken.None;
|
||||||
|
}
|
||||||
|
|
||||||
// 检查服务器是否支持Range分块下载
|
// 检查服务器是否支持Range分块下载
|
||||||
bool supportRange = false;
|
bool supportRange = false;
|
||||||
long totalSize = -1;
|
long totalSize = -1;
|
||||||
@@ -1146,7 +1234,7 @@ namespace Ink_Canvas.Helpers
|
|||||||
// 增加连接超时设置
|
// 增加连接超时设置
|
||||||
client.Timeout = TimeSpan.FromSeconds(30);
|
client.Timeout = TimeSpan.FromSeconds(30);
|
||||||
|
|
||||||
var downloadCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token);
|
var downloadCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, externalToken);
|
||||||
var lastReadTime = DateTime.UtcNow;
|
var lastReadTime = DateTime.UtcNow;
|
||||||
bool dataReceived = false;
|
bool dataReceived = false;
|
||||||
|
|
||||||
@@ -1339,12 +1427,18 @@ namespace Ink_Canvas.Helpers
|
|||||||
LogHelper.WriteLogToFile($"AutoUpdate | 开始单线程下载: {fileUrl}");
|
LogHelper.WriteLogToFile($"AutoUpdate | 开始单线程下载: {fileUrl}");
|
||||||
progressCallback?.Invoke(0, "开始单线程下载");
|
progressCallback?.Invoke(0, "开始单线程下载");
|
||||||
|
|
||||||
|
CancellationToken token;
|
||||||
|
lock (_activeDownloadLock)
|
||||||
|
{
|
||||||
|
token = _activeDownloadCts?.Token ?? CancellationToken.None;
|
||||||
|
}
|
||||||
|
|
||||||
using (var client = new HttpClient())
|
using (var client = new HttpClient())
|
||||||
{
|
{
|
||||||
client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36");
|
client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36");
|
||||||
client.Timeout = TimeSpan.FromMinutes(10); // 单线程下载设置更长的超时时间
|
client.Timeout = TimeSpan.FromMinutes(10); // 单线程下载设置更长的超时时间
|
||||||
|
|
||||||
using (var resp = await client.GetAsync(fileUrl, HttpCompletionOption.ResponseHeadersRead))
|
using (var resp = await client.GetAsync(fileUrl, HttpCompletionOption.ResponseHeadersRead, token))
|
||||||
{
|
{
|
||||||
resp.EnsureSuccessStatusCode();
|
resp.EnsureSuccessStatusCode();
|
||||||
using (var fs = new FileStream(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None))
|
using (var fs = new FileStream(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None))
|
||||||
@@ -1355,9 +1449,9 @@ namespace Ink_Canvas.Helpers
|
|||||||
long downloaded = 0;
|
long downloaded = 0;
|
||||||
var lastProgressUpdate = DateTime.UtcNow;
|
var lastProgressUpdate = DateTime.UtcNow;
|
||||||
|
|
||||||
while ((read = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0)
|
while ((read = await stream.ReadAsync(buffer, 0, buffer.Length, token)) > 0)
|
||||||
{
|
{
|
||||||
await fs.WriteAsync(buffer, 0, read);
|
await fs.WriteAsync(buffer, 0, read, token);
|
||||||
downloaded += read;
|
downloaded += read;
|
||||||
|
|
||||||
// 限制进度更新频率,避免UI卡顿
|
// 限制进度更新频率,避免UI卡顿
|
||||||
@@ -1379,6 +1473,13 @@ namespace Ink_Canvas.Helpers
|
|||||||
LogHelper.WriteLogToFile("AutoUpdate | 单线程下载完成");
|
LogHelper.WriteLogToFile("AutoUpdate | 单线程下载完成");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
LogHelper.WriteLogToFile("AutoUpdate | 单线程下载已被取消", LogHelper.LogType.Warning);
|
||||||
|
progressCallback?.Invoke(0, "下载已取消");
|
||||||
|
try { if (File.Exists(destinationPath)) File.Delete(destinationPath); } catch { }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
LogHelper.WriteLogToFile($"AutoUpdate | 单线程下载失败: {ex.Message}", LogHelper.LogType.Error);
|
LogHelper.WriteLogToFile($"AutoUpdate | 单线程下载失败: {ex.Message}", LogHelper.LogType.Error);
|
||||||
|
|||||||
@@ -2268,11 +2268,15 @@
|
|||||||
<!-- 通知弹窗 -->
|
<!-- 通知弹窗 -->
|
||||||
<Grid Name="GridNotifications" Margin="0,110" Visibility="Collapsed" HorizontalAlignment="Center"
|
<Grid Name="GridNotifications" Margin="0,110" Visibility="Collapsed" HorizontalAlignment="Center"
|
||||||
VerticalAlignment="Bottom">
|
VerticalAlignment="Bottom">
|
||||||
<Border CornerRadius="6" MaxHeight="200" Padding="20 10" Background="{DynamicResource SettingsPageBackground}" Opacity="0.95"
|
<ui:InfoBar x:Name="NotificationInfoBar"
|
||||||
BorderBrush="#b91c1c" BorderThickness="1.5">
|
IsOpen="True"
|
||||||
<TextBlock Name="TextBlockNotice" Text="{i18n:I18n Key=Notification_TestText}" VerticalAlignment="Center" HorizontalAlignment="Center"
|
IsClosable="False"
|
||||||
FontSize="16" Foreground="{DynamicResource SettingsPageForeground}" />
|
Severity="Warning"
|
||||||
</Border>
|
Title="提醒"
|
||||||
|
MaxWidth="900"
|
||||||
|
Message="{Binding Text, ElementName=TextBlockNotice}" />
|
||||||
|
<TextBlock x:Name="TextBlockNotice" Visibility="Collapsed"
|
||||||
|
Text="{i18n:I18n Key=Notification_TestText}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
<!--// Old UI //-->
|
<!--// Old UI //-->
|
||||||
<Viewbox Name="ViewBoxStackPanelMain" Visibility="Collapsed" Margin="10,10,10,55" HorizontalAlignment="Right"
|
<Viewbox Name="ViewBoxStackPanelMain" Visibility="Collapsed" Margin="10,10,10,55" HorizontalAlignment="Right"
|
||||||
|
|||||||
+10
-123
@@ -45,7 +45,7 @@ namespace Ink_Canvas
|
|||||||
private List<System.Windows.Controls.Canvas> whiteboardPages = new List<System.Windows.Controls.Canvas>();
|
private List<System.Windows.Controls.Canvas> whiteboardPages = new List<System.Windows.Controls.Canvas>();
|
||||||
private int currentPageIndex;
|
private int currentPageIndex;
|
||||||
private System.Windows.Controls.Canvas currentCanvas;
|
private System.Windows.Controls.Canvas currentCanvas;
|
||||||
private AutoUpdateHelper.UpdateLineGroup AvailableLatestLineGroup;
|
internal AutoUpdateHelper.UpdateLineGroup AvailableLatestLineGroup;
|
||||||
|
|
||||||
// 全局快捷键管理器
|
// 全局快捷键管理器
|
||||||
private GlobalHotkeyManager _globalHotkeyManager;
|
private GlobalHotkeyManager _globalHotkeyManager;
|
||||||
@@ -1203,7 +1203,6 @@ namespace Ink_Canvas
|
|||||||
LoadCustomBackgroundColor();
|
LoadCustomBackgroundColor();
|
||||||
SetWindowMode();
|
SetWindowMode();
|
||||||
|
|
||||||
// HasNewUpdateWindow hasNewUpdateWindow = new HasNewUpdateWindow();
|
|
||||||
// 根据设置应用主题
|
// 根据设置应用主题
|
||||||
switch (Settings.Appearance.Theme)
|
switch (Settings.Appearance.Theme)
|
||||||
{
|
{
|
||||||
@@ -1846,6 +1845,7 @@ namespace Ink_Canvas
|
|||||||
var (remoteVersion, lineGroup, apiReleaseNotes) = await AutoUpdateHelper.CheckForUpdates(Settings.Startup.UpdateChannel);
|
var (remoteVersion, lineGroup, apiReleaseNotes) = await AutoUpdateHelper.CheckForUpdates(Settings.Startup.UpdateChannel);
|
||||||
AvailableLatestVersion = remoteVersion;
|
AvailableLatestVersion = remoteVersion;
|
||||||
AvailableLatestLineGroup = lineGroup;
|
AvailableLatestLineGroup = lineGroup;
|
||||||
|
AvailableLatestReleaseNotes = apiReleaseNotes;
|
||||||
|
|
||||||
// 声明下载状态变量,用于整个方法
|
// 声明下载状态变量,用于整个方法
|
||||||
bool isDownloadSuccessful = false;
|
bool isDownloadSuccessful = false;
|
||||||
@@ -1882,9 +1882,6 @@ namespace Ink_Canvas
|
|||||||
SaveSettingsToFile();
|
SaveSettingsToFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取当前版本
|
|
||||||
string currentVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
|
||||||
|
|
||||||
// 如果启用了静默更新,则自动下载更新而不显示提示
|
// 如果启用了静默更新,则自动下载更新而不显示提示
|
||||||
if (Settings.Startup.IsAutoUpdateWithSilence)
|
if (Settings.Startup.IsAutoUpdateWithSilence)
|
||||||
{
|
{
|
||||||
@@ -1908,121 +1905,10 @@ namespace Ink_Canvas
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果没有启用静默更新,则显示常规更新窗口
|
// 如果没有启用静默更新,则记录日志并依赖 Toast 通知用户。
|
||||||
string releaseDate = DateTime.Now.ToString("yyyy年MM月dd日");
|
// 用户可在 设置 → 更新 中查看版本说明并选择更新方式。
|
||||||
|
LogHelper.WriteLogToFile(
|
||||||
// 从服务器获取更新日志
|
$"AutoUpdate | New version {AvailableLatestVersion} available; user notified via toast, will act from settings page.");
|
||||||
string releaseNotes = await AutoUpdateHelper.GetUpdateLog(Settings.Startup.UpdateChannel);
|
|
||||||
|
|
||||||
// 如果获取失败,使用默认文本
|
|
||||||
if (string.IsNullOrEmpty(releaseNotes))
|
|
||||||
{
|
|
||||||
releaseNotes = $@"# InkCanvasForClass v{AvailableLatestVersion}更新
|
|
||||||
|
|
||||||
无法获取更新日志,但新版本已准备就绪。";
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建并显示更新窗口
|
|
||||||
HasNewUpdateWindow updateWindow = new HasNewUpdateWindow(currentVersion, AvailableLatestVersion, releaseDate, releaseNotes);
|
|
||||||
updateWindow.Owner = this;
|
|
||||||
bool? dialogResult = updateWindow.ShowDialog();
|
|
||||||
|
|
||||||
// 如果窗口被关闭但没有点击按钮,则不执行任何操作
|
|
||||||
if (dialogResult != true)
|
|
||||||
{
|
|
||||||
LogHelper.WriteLogToFile("AutoUpdate | Update dialog closed without selection");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 不再从更新窗口获取自动更新设置
|
|
||||||
|
|
||||||
// 根据用户选择处理更新
|
|
||||||
switch (updateWindow.Result)
|
|
||||||
{
|
|
||||||
case HasNewUpdateWindow.UpdateResult.UpdateNow:
|
|
||||||
// 立即更新:显示下载进度,下载完成后立即安装
|
|
||||||
LogHelper.WriteLogToFile("AutoUpdate | User chose to update now");
|
|
||||||
|
|
||||||
// 显示下载进度提示
|
|
||||||
MessageBox.Show("开始下载更新,请稍候...", "正在更新", MessageBoxButton.OK, MessageBoxImage.Information);
|
|
||||||
|
|
||||||
// 下载更新文件,使用多线路组下载功能
|
|
||||||
isDownloadSuccessful = await DownloadUpdateWithFallback(AvailableLatestVersion, AvailableLatestLineGroup, Settings.Startup.UpdateChannel);
|
|
||||||
|
|
||||||
if (isDownloadSuccessful)
|
|
||||||
{
|
|
||||||
// 下载成功,提示用户准备安装
|
|
||||||
MessageBoxResult result = MessageBox.Show("更新已下载完成,点击确定后将关闭软件并安装新版本!", "安装更新", MessageBoxButton.OKCancel, MessageBoxImage.Information);
|
|
||||||
|
|
||||||
// 只有当用户点击确定按钮后才关闭软件
|
|
||||||
if (result == MessageBoxResult.OK)
|
|
||||||
{
|
|
||||||
// 设置为用户主动退出,避免被看门狗判定为崩溃
|
|
||||||
App.IsAppExitByUser = true;
|
|
||||||
|
|
||||||
// 准备批处理脚本
|
|
||||||
AutoUpdateHelper.InstallNewVersionApp(AvailableLatestVersion, true); // 修改为静默模式,避免重复启动进程
|
|
||||||
|
|
||||||
// 关闭软件,让安装程序接管
|
|
||||||
Application.Current.Shutdown();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogHelper.WriteLogToFile("AutoUpdate | User cancelled update installation");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 下载失败
|
|
||||||
MessageBox.Show("更新下载失败,请检查网络连接后重试。", "下载失败", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case HasNewUpdateWindow.UpdateResult.UpdateLater:
|
|
||||||
// 稍后更新:静默下载,在软件关闭时自动安装
|
|
||||||
LogHelper.WriteLogToFile("AutoUpdate | User chose to update later");
|
|
||||||
|
|
||||||
// 不管设置如何,都进行下载,使用多线路组下载功能
|
|
||||||
isDownloadSuccessful = await DownloadUpdateWithFallback(AvailableLatestVersion, AvailableLatestLineGroup, Settings.Startup.UpdateChannel);
|
|
||||||
|
|
||||||
if (isDownloadSuccessful)
|
|
||||||
{
|
|
||||||
LogHelper.WriteLogToFile("AutoUpdate | Update downloaded successfully, will install when application closes");
|
|
||||||
|
|
||||||
// 设置标志,在应用程序关闭时安装
|
|
||||||
Settings.Startup.IsAutoUpdate = true;
|
|
||||||
Settings.Startup.IsAutoUpdateWithSilence = true;
|
|
||||||
|
|
||||||
// 启动检查定时器
|
|
||||||
timerCheckAutoUpdateWithSilence.Start();
|
|
||||||
|
|
||||||
// 通知用户
|
|
||||||
MessageBox.Show("更新已下载完成,将在软件关闭时自动安装。", "更新已准备就绪", MessageBoxButton.OK, MessageBoxImage.Information);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogHelper.WriteLogToFile("AutoUpdate | Update download failed", LogHelper.LogType.Error);
|
|
||||||
MessageBox.Show("更新下载失败,请检查网络连接后重试。", "下载失败", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case HasNewUpdateWindow.UpdateResult.SkipVersion:
|
|
||||||
// 跳过该版本:记录到设置中
|
|
||||||
LogHelper.WriteLogToFile($"AutoUpdate | User chose to skip version {AvailableLatestVersion}");
|
|
||||||
|
|
||||||
// 记录要跳过的版本号
|
|
||||||
Settings.Startup.SkippedVersion = AvailableLatestVersion;
|
|
||||||
|
|
||||||
// 保存设置到文件
|
|
||||||
SaveSettingsToFile();
|
|
||||||
|
|
||||||
// 通知用户
|
|
||||||
MessageBox.Show($"已设置跳过版本 {AvailableLatestVersion},在下次发布新版本之前不会再提示更新。",
|
|
||||||
"已跳过此版本",
|
|
||||||
MessageBoxButton.OK,
|
|
||||||
MessageBoxImage.Information);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (hasValidLineGroup)
|
else if (hasValidLineGroup)
|
||||||
{
|
{
|
||||||
@@ -2271,9 +2157,10 @@ namespace Ink_Canvas
|
|||||||
|
|
||||||
private void HistoryRollbackButton_Click(object sender, RoutedEventArgs e)
|
private void HistoryRollbackButton_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var win = new HistoryRollbackWindow(Settings.Startup.UpdateChannel);
|
var settingsWindow = new Windows.SettingsViews.SettingsWindow();
|
||||||
win.Owner = this;
|
settingsWindow.Owner = this;
|
||||||
win.ShowDialog();
|
settingsWindow.Show();
|
||||||
|
settingsWindow.NavigateToPage("UpdatePage");
|
||||||
}
|
}
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
[DllImport("user32.dll")]
|
||||||
|
|||||||
@@ -95,7 +95,11 @@ namespace Ink_Canvas
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 可用的最新版本号
|
/// 可用的最新版本号
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private string AvailableLatestVersion;
|
internal string AvailableLatestVersion;
|
||||||
|
/// <summary>
|
||||||
|
/// 最近一次自动检查得到的更新说明(Markdown)
|
||||||
|
/// </summary>
|
||||||
|
internal string AvailableLatestReleaseNotes;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 静默更新检查定时器
|
/// 静默更新检查定时器
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,346 +0,0 @@
|
|||||||
<Window x:Class="Ink_Canvas.HasNewUpdateWindow"
|
|
||||||
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"
|
|
||||||
xmlns:local="clr-namespace:Ink_Canvas"
|
|
||||||
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
|
|
||||||
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
|
|
||||||
xmlns:mdxam="clr-namespace:MdXaml;assembly=MdXaml"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
ui:WindowHelper.UseModernWindowStyle="False"
|
|
||||||
ui:WindowHelper.SystemBackdropType="Mica"
|
|
||||||
Title="InkCanvasForClass CE有新版本可用" Height="650" Width="900" ResizeMode="NoResize"
|
|
||||||
WindowStartupLocation="CenterScreen" WindowStyle="None"
|
|
||||||
Background="Transparent">
|
|
||||||
<Window.Template>
|
|
||||||
<ControlTemplate TargetType="Window">
|
|
||||||
<Border Background="{DynamicResource SettingsPageBackground}"
|
|
||||||
CornerRadius="8"
|
|
||||||
BorderBrush="{DynamicResource SettingsPageBorderBrush}"
|
|
||||||
BorderThickness="1">
|
|
||||||
<Border.Effect>
|
|
||||||
<DropShadowEffect Color="#000000" BlurRadius="20" ShadowDepth="0" Opacity="0.1"/>
|
|
||||||
</Border.Effect>
|
|
||||||
<ContentPresenter/>
|
|
||||||
</Border>
|
|
||||||
</ControlTemplate>
|
|
||||||
</Window.Template>
|
|
||||||
<Window.Resources>
|
|
||||||
<!-- 主题相关颜色资源 -->
|
|
||||||
<SolidColorBrush x:Key="UpdateWindowPrimaryBrush" Color="#2563eb"/>
|
|
||||||
<SolidColorBrush x:Key="UpdateWindowPrimaryHoverBrush" Color="#1d4ed8"/>
|
|
||||||
<SolidColorBrush x:Key="UpdateWindowPrimaryPressedBrush" Color="#1e40af"/>
|
|
||||||
<SolidColorBrush x:Key="UpdateWindowCardBackgroundBrush" Color="#f8fafc"/>
|
|
||||||
<SolidColorBrush x:Key="UpdateWindowCardBorderBrush" Color="#e5e7eb"/>
|
|
||||||
<SolidColorBrush x:Key="UpdateWindowTextPrimaryBrush" Color="#1f2937"/>
|
|
||||||
<SolidColorBrush x:Key="UpdateWindowTextSecondaryBrush" Color="#6b7280"/>
|
|
||||||
<SolidColorBrush x:Key="UpdateWindowCloseButtonBrush" Color="#666666"/>
|
|
||||||
|
|
||||||
<!-- 渐变背景定义 -->
|
|
||||||
<LinearGradientBrush x:Key="HeaderGradient" StartPoint="0,0" EndPoint="1,1">
|
|
||||||
<GradientStop Color="#2563eb" Offset="0"/>
|
|
||||||
<GradientStop Color="White" Offset="1"/>
|
|
||||||
</LinearGradientBrush>
|
|
||||||
|
|
||||||
<!-- 现代按钮样式 -->
|
|
||||||
<Style x:Key="ModernPrimaryButton" TargetType="Button">
|
|
||||||
<Setter Property="Background" Value="{DynamicResource UpdateWindowPrimaryBrush}"/>
|
|
||||||
<Setter Property="Foreground" Value="White"/>
|
|
||||||
<Setter Property="BorderThickness" Value="0"/>
|
|
||||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
|
||||||
<Setter Property="FontSize" Value="14"/>
|
|
||||||
<Setter Property="Padding" Value="20,10"/>
|
|
||||||
<Setter Property="Height" Value="40"/>
|
|
||||||
<Setter Property="MinWidth" Value="200"/>
|
|
||||||
<Setter Property="HorizontalAlignment" Value="Center"/>
|
|
||||||
<Setter Property="Cursor" Value="Hand"/>
|
|
||||||
<Setter Property="Template">
|
|
||||||
<Setter.Value>
|
|
||||||
<ControlTemplate TargetType="Button">
|
|
||||||
<Border Background="{TemplateBinding Background}"
|
|
||||||
CornerRadius="6"
|
|
||||||
BorderThickness="{TemplateBinding BorderThickness}"
|
|
||||||
BorderBrush="{TemplateBinding BorderBrush}">
|
|
||||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
|
||||||
</Border>
|
|
||||||
<ControlTemplate.Triggers>
|
|
||||||
<Trigger Property="IsMouseOver" Value="True">
|
|
||||||
<Setter Property="Background" Value="{DynamicResource UpdateWindowPrimaryHoverBrush}"/>
|
|
||||||
</Trigger>
|
|
||||||
<Trigger Property="IsPressed" Value="True">
|
|
||||||
<Setter Property="Background" Value="{DynamicResource UpdateWindowPrimaryPressedBrush}"/>
|
|
||||||
</Trigger>
|
|
||||||
</ControlTemplate.Triggers>
|
|
||||||
</ControlTemplate>
|
|
||||||
</Setter.Value>
|
|
||||||
</Setter>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style x:Key="ModernSecondaryButton" TargetType="Button">
|
|
||||||
<Setter Property="Background" Value="{DynamicResource SettingsPageBackground}"/>
|
|
||||||
<Setter Property="Foreground" Value="{DynamicResource UpdateWindowTextPrimaryBrush}"/>
|
|
||||||
<Setter Property="BorderThickness" Value="1"/>
|
|
||||||
<Setter Property="BorderBrush" Value="{DynamicResource SettingsPageBorderBrush}"/>
|
|
||||||
<Setter Property="FontWeight" Value="Medium"/>
|
|
||||||
<Setter Property="FontSize" Value="14"/>
|
|
||||||
<Setter Property="Padding" Value="20,10"/>
|
|
||||||
<Setter Property="Height" Value="40"/>
|
|
||||||
<Setter Property="MinWidth" Value="200"/>
|
|
||||||
<Setter Property="HorizontalAlignment" Value="Center"/>
|
|
||||||
<Setter Property="Cursor" Value="Hand"/>
|
|
||||||
<Setter Property="Template">
|
|
||||||
<Setter.Value>
|
|
||||||
<ControlTemplate TargetType="Button">
|
|
||||||
<Border Background="{TemplateBinding Background}"
|
|
||||||
CornerRadius="6"
|
|
||||||
BorderThickness="{TemplateBinding BorderThickness}"
|
|
||||||
BorderBrush="{TemplateBinding BorderBrush}">
|
|
||||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
|
||||||
</Border>
|
|
||||||
<ControlTemplate.Triggers>
|
|
||||||
<Trigger Property="IsMouseOver" Value="True">
|
|
||||||
<Setter Property="Background">
|
|
||||||
<Setter.Value>
|
|
||||||
<SolidColorBrush Color="#FF2563eb" Opacity="0.1"/>
|
|
||||||
</Setter.Value>
|
|
||||||
</Setter>
|
|
||||||
</Trigger>
|
|
||||||
<Trigger Property="IsPressed" Value="True">
|
|
||||||
<Setter Property="Background">
|
|
||||||
<Setter.Value>
|
|
||||||
<SolidColorBrush Color="#FF2563eb" Opacity="0.2"/>
|
|
||||||
</Setter.Value>
|
|
||||||
</Setter>
|
|
||||||
</Trigger>
|
|
||||||
</ControlTemplate.Triggers>
|
|
||||||
</ControlTemplate>
|
|
||||||
</Setter.Value>
|
|
||||||
</Setter>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style x:Key="ModernTertiaryButton" TargetType="Button">
|
|
||||||
<Setter Property="Background" Value="Transparent"/>
|
|
||||||
<Setter Property="Foreground" Value="{DynamicResource UpdateWindowTextSecondaryBrush}"/>
|
|
||||||
<Setter Property="BorderThickness" Value="0"/>
|
|
||||||
<Setter Property="FontWeight" Value="Medium"/>
|
|
||||||
<Setter Property="FontSize" Value="14"/>
|
|
||||||
<Setter Property="Padding" Value="20,10"/>
|
|
||||||
<Setter Property="Height" Value="40"/>
|
|
||||||
<Setter Property="MinWidth" Value="200"/>
|
|
||||||
<Setter Property="HorizontalAlignment" Value="Center"/>
|
|
||||||
<Setter Property="Cursor" Value="Hand"/>
|
|
||||||
<Setter Property="Template">
|
|
||||||
<Setter.Value>
|
|
||||||
<ControlTemplate TargetType="Button">
|
|
||||||
<Border Background="{TemplateBinding Background}"
|
|
||||||
CornerRadius="6"
|
|
||||||
BorderThickness="{TemplateBinding BorderThickness}"
|
|
||||||
BorderBrush="{TemplateBinding BorderBrush}">
|
|
||||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
|
||||||
</Border>
|
|
||||||
<ControlTemplate.Triggers>
|
|
||||||
<Trigger Property="IsMouseOver" Value="True">
|
|
||||||
<Setter Property="Background">
|
|
||||||
<Setter.Value>
|
|
||||||
<SolidColorBrush Color="#FF2563eb" Opacity="0.1"/>
|
|
||||||
</Setter.Value>
|
|
||||||
</Setter>
|
|
||||||
<Setter Property="Foreground" Value="{DynamicResource UpdateWindowTextPrimaryBrush}"/>
|
|
||||||
</Trigger>
|
|
||||||
<Trigger Property="IsPressed" Value="True">
|
|
||||||
<Setter Property="Background">
|
|
||||||
<Setter.Value>
|
|
||||||
<SolidColorBrush Color="#FF2563eb" Opacity="0.2"/>
|
|
||||||
</Setter.Value>
|
|
||||||
</Setter>
|
|
||||||
</Trigger>
|
|
||||||
</ControlTemplate.Triggers>
|
|
||||||
</ControlTemplate>
|
|
||||||
</Setter.Value>
|
|
||||||
</Setter>
|
|
||||||
</Style>
|
|
||||||
</Window.Resources>
|
|
||||||
|
|
||||||
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
|
|
||||||
<Grid Margin="0,0,0,0" Background="Transparent">
|
|
||||||
<ikw:SimpleStackPanel VerticalAlignment="Stretch">
|
|
||||||
<!-- 统一的主容器 -->
|
|
||||||
<Border Background="{DynamicResource SettingsPageBackground}" CornerRadius="8" Margin="0,0,0,0"
|
|
||||||
BorderThickness="1" BorderBrush="{DynamicResource SettingsPageBorderBrush}" Padding="0">
|
|
||||||
|
|
||||||
<ikw:SimpleStackPanel>
|
|
||||||
<!-- 现代化标题栏 -->
|
|
||||||
<Border Background="{StaticResource HeaderGradient}" CornerRadius="8,8,0,0" Margin="0,0,0,0"
|
|
||||||
MouseLeftButtonDown="TitleBar_MouseLeftButtonDown">
|
|
||||||
<Grid>
|
|
||||||
<!-- 标题栏内容 -->
|
|
||||||
<ikw:SimpleStackPanel Orientation="Horizontal" Margin="32,24,80,24" Spacing="16">
|
|
||||||
<Border Background="White" CornerRadius="16" Padding="16" Margin="0,0,0,0">
|
|
||||||
<Image Source="/Resources/Icons-fluent/party.png" Width="24" Height="24"/>
|
|
||||||
</Border>
|
|
||||||
<ikw:SimpleStackPanel Orientation="Vertical" Spacing="4">
|
|
||||||
<TextBlock Text="新版本发布" FontSize="24" FontWeight="Bold" Foreground="{DynamicResource UpdateWindowTextPrimaryBrush}" TextAlignment="Left"/>
|
|
||||||
<TextBlock Text="InkCanvasForClass CE 为您带来全新体验" FontSize="14" TextAlignment="Left" Foreground="{DynamicResource UpdateWindowTextSecondaryBrush}"/>
|
|
||||||
</ikw:SimpleStackPanel>
|
|
||||||
</ikw:SimpleStackPanel>
|
|
||||||
|
|
||||||
<!-- 自定义关闭按钮 -->
|
|
||||||
<Button x:Name="CloseButton"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
VerticalAlignment="Top"
|
|
||||||
Margin="0,12,12,0"
|
|
||||||
Width="32" Height="32"
|
|
||||||
Background="Transparent"
|
|
||||||
BorderThickness="0"
|
|
||||||
Click="CloseButton_Click"
|
|
||||||
Cursor="Hand"
|
|
||||||
ToolTip="关闭">
|
|
||||||
<Button.Template>
|
|
||||||
<ControlTemplate TargetType="Button">
|
|
||||||
<Border Background="{TemplateBinding Background}"
|
|
||||||
CornerRadius="16"
|
|
||||||
BorderThickness="{TemplateBinding BorderThickness}"
|
|
||||||
BorderBrush="{TemplateBinding BorderBrush}">
|
|
||||||
<Path Data="M8,8 L16,16 M16,8 L8,16"
|
|
||||||
Stroke="{DynamicResource UpdateWindowCloseButtonBrush}"
|
|
||||||
StrokeThickness="2"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
VerticalAlignment="Center"/>
|
|
||||||
</Border>
|
|
||||||
<ControlTemplate.Triggers>
|
|
||||||
<Trigger Property="IsMouseOver" Value="True">
|
|
||||||
<Setter Property="Background">
|
|
||||||
<Setter.Value>
|
|
||||||
<SolidColorBrush Color="#FF2563eb" Opacity="0.1"/>
|
|
||||||
</Setter.Value>
|
|
||||||
</Setter>
|
|
||||||
</Trigger>
|
|
||||||
<Trigger Property="IsPressed" Value="True">
|
|
||||||
<Setter Property="Background">
|
|
||||||
<Setter.Value>
|
|
||||||
<SolidColorBrush Color="#FF2563eb" Opacity="0.2"/>
|
|
||||||
</Setter.Value>
|
|
||||||
</Setter>
|
|
||||||
</Trigger>
|
|
||||||
</ControlTemplate.Triggers>
|
|
||||||
</ControlTemplate>
|
|
||||||
</Button.Template>
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- 内容标题 -->
|
|
||||||
<Border Background="{DynamicResource UpdateWindowCardBackgroundBrush}" Padding="24,20,24,16">
|
|
||||||
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="12">
|
|
||||||
<Border Background="{DynamicResource UpdateWindowPrimaryBrush}" CornerRadius="8" Padding="8">
|
|
||||||
<TextBlock Text="📝" FontSize="16" Foreground="White"
|
|
||||||
HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
|
||||||
</Border>
|
|
||||||
<ikw:SimpleStackPanel Orientation="Vertical" Spacing="2">
|
|
||||||
<TextBlock Text="更新内容" FontSize="18" FontWeight="SemiBold" Foreground="{DynamicResource UpdateWindowTextPrimaryBrush}"/>
|
|
||||||
<TextBlock Text="查看本次更新的详细内容" FontSize="14" Foreground="{DynamicResource UpdateWindowTextSecondaryBrush}"/>
|
|
||||||
</ikw:SimpleStackPanel>
|
|
||||||
</ikw:SimpleStackPanel>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- 内容区域 -->
|
|
||||||
<ui:ScrollViewerEx Margin="0" VerticalScrollBarVisibility="Auto" Height="200" PanningMode="VerticalOnly">
|
|
||||||
<mdxam:MarkdownScrollViewer x:Name="markdownContent" xml:space="preserve"
|
|
||||||
Foreground="{DynamicResource UpdateWindowTextPrimaryBrush}" MarkdownStyleName="GithubLike"
|
|
||||||
Margin="24,16,24,16">
|
|
||||||
# InkCanvasForClass v5.0.2更新
|
|
||||||
|
|
||||||
你好,旅行者们,本次InkCanvasForClass Community Edition更新带来了如下新功能供您探索:
|
|
||||||
|
|
||||||
1. 全新设计的UI界面,包括浮动工具栏和白板页面均经过重新设计,更加现代化的UI让您在使用的过程中更加舒适。
|
|
||||||
2. 带来了实时修改橡皮大小和橡皮形状的菜单。您可以选择使用圆形橡皮,方形橡皮,和类似希沃白板的真实黑板擦(矩形)橡皮。
|
|
||||||
3. 白板页面支持显示当前时间和日期
|
|
||||||
4. 自动收纳新增对希沃轻白板、智绘教、鸿合屏幕书写等软件的支持,自动查杀新增对鸿合屏幕书写、希沃轻白板等软件的支持。
|
|
||||||
5. 为设置界面重写了全新的UI。
|
|
||||||
6. 重写了随机抽选模块,现在支持更丰富的抽选机制和自定义选项。
|
|
||||||
7. 修复了部分小Bug,提升了整体的用户体验。
|
|
||||||
8. 带来了基于FitToCurve的笔迹平滑,基于贝塞尔曲线平滑,让墨迹线条更加优美好看。
|
|
||||||
</mdxam:MarkdownScrollViewer>
|
|
||||||
</ui:ScrollViewerEx>
|
|
||||||
</ikw:SimpleStackPanel>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- 现代化版本信息 -->
|
|
||||||
<Border Background="{DynamicResource UpdateWindowCardBackgroundBrush}" Margin="0,0,0,0" Padding="24,20">
|
|
||||||
<ikw:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Center" Spacing="24">
|
|
||||||
<ikw:SimpleStackPanel Orientation="Vertical" Spacing="4" HorizontalAlignment="Center">
|
|
||||||
<TextBlock Text="版本信息" FontSize="14" FontWeight="Medium" Foreground="{DynamicResource UpdateWindowTextSecondaryBrush}"/>
|
|
||||||
<TextBlock x:Name="updateVersionInfo" Text="本次更新: 4.9.1 -> 5.9.1"
|
|
||||||
FontWeight="SemiBold" FontSize="16" Foreground="{DynamicResource UpdateWindowTextPrimaryBrush}" TextAlignment="Center"/>
|
|
||||||
</ikw:SimpleStackPanel>
|
|
||||||
<Border Width="1" Background="{DynamicResource SettingsPageBorderBrush}" Margin="0,8,0,8"/>
|
|
||||||
<ikw:SimpleStackPanel Orientation="Vertical" Spacing="4" HorizontalAlignment="Center">
|
|
||||||
<TextBlock Text="发布日期" FontSize="14" FontWeight="Medium" Foreground="{DynamicResource UpdateWindowTextSecondaryBrush}"/>
|
|
||||||
<TextBlock x:Name="updateDateInfo" Text="2024年8月4日发布更新"
|
|
||||||
FontSize="16" Foreground="{DynamicResource UpdateWindowTextPrimaryBrush}" TextAlignment="Center"/>
|
|
||||||
</ikw:SimpleStackPanel>
|
|
||||||
</ikw:SimpleStackPanel>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- 现代化按钮区域 -->
|
|
||||||
<Border Background="{DynamicResource UpdateWindowCardBackgroundBrush}" Margin="0,0,0,0" Padding="32,24" CornerRadius="0,0,8,8">
|
|
||||||
<ikw:SimpleStackPanel Orientation="Vertical" HorizontalAlignment="Center" Spacing="16">
|
|
||||||
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="12" HorizontalAlignment="Center">
|
|
||||||
<Border Background="{DynamicResource UpdateWindowPrimaryBrush}" CornerRadius="8" Padding="8">
|
|
||||||
<TextBlock Text="⚡" FontSize="16" Foreground="White"
|
|
||||||
HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
|
||||||
</Border>
|
|
||||||
<TextBlock Text="选择更新方式" FontWeight="SemiBold" FontSize="18"
|
|
||||||
Foreground="{DynamicResource UpdateWindowTextPrimaryBrush}" VerticalAlignment="Center"/>
|
|
||||||
</ikw:SimpleStackPanel>
|
|
||||||
|
|
||||||
<!-- 立即更新按钮 -->
|
|
||||||
<Button x:Name="UpdateNowButton" Content="立即下载并安装"
|
|
||||||
Style="{StaticResource ModernPrimaryButton}"
|
|
||||||
Click="UpdateNowButton_Click" ToolTip="立即下载更新并在完成后安装"
|
|
||||||
Visibility="Visible" IsEnabled="True"/>
|
|
||||||
|
|
||||||
<!-- 稍后更新按钮 -->
|
|
||||||
<Button x:Name="UpdateLaterButton" Content="下载并在软件关闭时安装"
|
|
||||||
Style="{StaticResource ModernSecondaryButton}"
|
|
||||||
Click="UpdateLaterButton_Click"
|
|
||||||
ToolTip="后台下载更新,在软件关闭时自动安装"
|
|
||||||
Visibility="Visible" IsEnabled="True"/>
|
|
||||||
|
|
||||||
<!-- 跳过版本按钮 -->
|
|
||||||
<Button x:Name="SkipVersionButton" Content="跳过该版本"
|
|
||||||
Style="{StaticResource ModernTertiaryButton}"
|
|
||||||
Click="SkipVersionButton_Click"
|
|
||||||
ToolTip="跳过此版本更新" Visibility="Visible" IsEnabled="True"/>
|
|
||||||
</ikw:SimpleStackPanel>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- 现代化下载进度指示器 -->
|
|
||||||
<Border x:Name="DownloadProgressPanel" Background="{DynamicResource UpdateWindowCardBackgroundBrush}"
|
|
||||||
Margin="0,0,0,0" Padding="32,24" Visibility="Collapsed">
|
|
||||||
<ikw:SimpleStackPanel Orientation="Vertical" HorizontalAlignment="Center" Spacing="16">
|
|
||||||
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="12" HorizontalAlignment="Center">
|
|
||||||
<Border Background="#10b981" CornerRadius="8" Padding="8">
|
|
||||||
<TextBlock Text="📥" FontSize="16" Foreground="White"
|
|
||||||
HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
|
||||||
</Border>
|
|
||||||
<TextBlock Text="正在下载更新" FontWeight="SemiBold" FontSize="18"
|
|
||||||
Foreground="{DynamicResource UpdateWindowTextPrimaryBrush}" VerticalAlignment="Center"/>
|
|
||||||
</ikw:SimpleStackPanel>
|
|
||||||
|
|
||||||
<!-- 现代化进度条 -->
|
|
||||||
<Border Background="{DynamicResource SettingsPageBackground}" CornerRadius="6" Height="6" Width="400">
|
|
||||||
<Border x:Name="ProgressFill" Background="{DynamicResource UpdateWindowPrimaryBrush}" CornerRadius="6"
|
|
||||||
Width="0" HorizontalAlignment="Left">
|
|
||||||
</Border>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<TextBlock x:Name="DownloadProgressText" Text="正在准备下载..."
|
|
||||||
FontSize="14" Foreground="{DynamicResource UpdateWindowTextSecondaryBrush}" HorizontalAlignment="Center"/>
|
|
||||||
</ikw:SimpleStackPanel>
|
|
||||||
</Border>
|
|
||||||
</ikw:SimpleStackPanel>
|
|
||||||
</Grid>
|
|
||||||
</ScrollViewer>
|
|
||||||
</Window>
|
|
||||||
@@ -1,513 +0,0 @@
|
|||||||
using Ink_Canvas.Helpers;
|
|
||||||
using iNKORE.UI.WPF.Modern;
|
|
||||||
using iNKORE.UI.WPF.Modern.Controls;
|
|
||||||
using MdXaml;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows;
|
|
||||||
using System.Windows.Controls;
|
|
||||||
using System.Windows.Input;
|
|
||||||
using System.Windows.Interop;
|
|
||||||
using System.Windows.Media;
|
|
||||||
|
|
||||||
namespace Ink_Canvas
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// HasNewUpdateWindow.xaml 的交互逻辑
|
|
||||||
/// </summary>
|
|
||||||
///
|
|
||||||
|
|
||||||
|
|
||||||
public partial class HasNewUpdateWindow : Window
|
|
||||||
{
|
|
||||||
|
|
||||||
[DllImport("dwmapi.dll")]
|
|
||||||
private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize);
|
|
||||||
|
|
||||||
private const int DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19;
|
|
||||||
private const int DWMWA_USE_IMMERSIVE_DARK_MODE = 20;
|
|
||||||
|
|
||||||
private static bool UseImmersiveDarkMode(IntPtr handle, bool enabled)
|
|
||||||
{
|
|
||||||
if (IsWindows10OrGreater(17763))
|
|
||||||
{
|
|
||||||
var attribute = DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1;
|
|
||||||
if (IsWindows10OrGreater(18985))
|
|
||||||
{
|
|
||||||
attribute = DWMWA_USE_IMMERSIVE_DARK_MODE;
|
|
||||||
}
|
|
||||||
|
|
||||||
int useImmersiveDarkMode = enabled ? 1 : 0;
|
|
||||||
return DwmSetWindowAttribute(handle, attribute, ref useImmersiveDarkMode, sizeof(int)) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsWindows10OrGreater(int build = -1)
|
|
||||||
{
|
|
||||||
return Environment.OSVersion.Version.Major >= 10 && Environment.OSVersion.Version.Build >= build;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 应用当前主题设置
|
|
||||||
/// </summary>
|
|
||||||
private void ApplyCurrentTheme()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// 根据主窗口的主题设置应用主题
|
|
||||||
switch (MainWindow.Settings.Appearance.Theme)
|
|
||||||
{
|
|
||||||
case 0: // 浅色主题
|
|
||||||
ThemeManager.SetRequestedTheme(this, ElementTheme.Light);
|
|
||||||
UpdateColorsForLightTheme();
|
|
||||||
break;
|
|
||||||
case 1: // 深色主题
|
|
||||||
ThemeManager.SetRequestedTheme(this, ElementTheme.Dark);
|
|
||||||
UpdateColorsForDarkTheme();
|
|
||||||
break;
|
|
||||||
case 2: // 跟随系统
|
|
||||||
if (IsSystemThemeLight())
|
|
||||||
{
|
|
||||||
ThemeManager.SetRequestedTheme(this, ElementTheme.Light);
|
|
||||||
UpdateColorsForLightTheme();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ThemeManager.SetRequestedTheme(this, ElementTheme.Dark);
|
|
||||||
UpdateColorsForDarkTheme();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LogHelper.WriteLogToFile($"应用主题时出错: {ex.Message}", LogHelper.LogType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 更新为浅色主题颜色
|
|
||||||
/// </summary>
|
|
||||||
private void UpdateColorsForLightTheme()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// 更新主要颜色资源
|
|
||||||
Resources["UpdateWindowPrimaryBrush"] = new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb));
|
|
||||||
Resources["UpdateWindowPrimaryHoverBrush"] = new SolidColorBrush(Color.FromRgb(0x1d, 0x4e, 0xd8));
|
|
||||||
Resources["UpdateWindowPrimaryPressedBrush"] = new SolidColorBrush(Color.FromRgb(0x1e, 0x40, 0xaf));
|
|
||||||
Resources["UpdateWindowCardBackgroundBrush"] = new SolidColorBrush(Color.FromRgb(0xf8, 0xfa, 0xfc));
|
|
||||||
Resources["UpdateWindowCardBorderBrush"] = new SolidColorBrush(Color.FromRgb(0xe5, 0xe7, 0xeb));
|
|
||||||
Resources["UpdateWindowTextPrimaryBrush"] = new SolidColorBrush(Color.FromRgb(0x1f, 0x29, 0x37));
|
|
||||||
Resources["UpdateWindowTextSecondaryBrush"] = new SolidColorBrush(Color.FromRgb(0x6b, 0x72, 0x80));
|
|
||||||
Resources["UpdateWindowCloseButtonBrush"] = new SolidColorBrush(Color.FromRgb(0x66, 0x66, 0x66));
|
|
||||||
|
|
||||||
// 更新渐变背景
|
|
||||||
var gradient = new LinearGradientBrush();
|
|
||||||
gradient.StartPoint = new Point(0, 0);
|
|
||||||
gradient.EndPoint = new Point(1, 1);
|
|
||||||
gradient.GradientStops.Add(new GradientStop(Color.FromRgb(0x25, 0x63, 0xeb), 0));
|
|
||||||
gradient.GradientStops.Add(new GradientStop(Colors.White, 1));
|
|
||||||
Resources["HeaderGradient"] = gradient;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LogHelper.WriteLogToFile($"更新浅色主题颜色时出错: {ex.Message}", LogHelper.LogType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 更新为深色主题颜色
|
|
||||||
/// </summary>
|
|
||||||
private void UpdateColorsForDarkTheme()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// 更新主要颜色资源
|
|
||||||
Resources["UpdateWindowPrimaryBrush"] = new SolidColorBrush(Color.FromRgb(0x3b, 0x82, 0xf6));
|
|
||||||
Resources["UpdateWindowPrimaryHoverBrush"] = new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb));
|
|
||||||
Resources["UpdateWindowPrimaryPressedBrush"] = new SolidColorBrush(Color.FromRgb(0x1d, 0x4e, 0xd8));
|
|
||||||
Resources["UpdateWindowCardBackgroundBrush"] = new SolidColorBrush(Color.FromRgb(0x2a, 0x2a, 0x2a));
|
|
||||||
Resources["UpdateWindowCardBorderBrush"] = new SolidColorBrush(Color.FromRgb(0x40, 0x40, 0x40));
|
|
||||||
Resources["UpdateWindowTextPrimaryBrush"] = new SolidColorBrush(Color.FromRgb(0xf9, 0xfa, 0xfb));
|
|
||||||
Resources["UpdateWindowTextSecondaryBrush"] = new SolidColorBrush(Color.FromRgb(0x9c, 0xa3, 0xaf));
|
|
||||||
Resources["UpdateWindowCloseButtonBrush"] = new SolidColorBrush(Color.FromRgb(0x9c, 0xa3, 0xaf));
|
|
||||||
|
|
||||||
// 更新渐变背景
|
|
||||||
var gradient = new LinearGradientBrush();
|
|
||||||
gradient.StartPoint = new Point(0, 0);
|
|
||||||
gradient.EndPoint = new Point(1, 1);
|
|
||||||
gradient.GradientStops.Add(new GradientStop(Color.FromRgb(0x3b, 0x82, 0xf6), 0));
|
|
||||||
gradient.GradientStops.Add(new GradientStop(Color.FromRgb(0x1f, 0x1f, 0x1f), 1));
|
|
||||||
Resources["HeaderGradient"] = gradient;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LogHelper.WriteLogToFile($"更新深色主题颜色时出错: {ex.Message}", LogHelper.LogType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 检查系统是否为浅色主题
|
|
||||||
/// </summary>
|
|
||||||
private bool IsSystemThemeLight()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using (var key = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"))
|
|
||||||
{
|
|
||||||
if (key?.GetValue("AppsUseLightTheme") is int value)
|
|
||||||
{
|
|
||||||
return value == 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// 如果无法读取注册表,默认返回true(浅色主题)
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 存储更新版本信息
|
|
||||||
public string CurrentVersion { get; set; }
|
|
||||||
public string NewVersion { get; set; }
|
|
||||||
public string ReleaseDate { get; set; }
|
|
||||||
public string ReleaseNotes { get; set; }
|
|
||||||
|
|
||||||
// 更新按钮结果
|
|
||||||
public enum UpdateResult
|
|
||||||
{
|
|
||||||
UpdateNow,
|
|
||||||
UpdateLater,
|
|
||||||
SkipVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
public UpdateResult Result { get; private set; } = UpdateResult.UpdateLater;
|
|
||||||
|
|
||||||
public HasNewUpdateWindow(string currentVersion, string newVersion, string releaseDate, string releaseNotes = null)
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
|
|
||||||
// 应用当前主题
|
|
||||||
ApplyCurrentTheme();
|
|
||||||
|
|
||||||
// 设置版本信息
|
|
||||||
CurrentVersion = currentVersion;
|
|
||||||
NewVersion = newVersion;
|
|
||||||
ReleaseDate = releaseDate;
|
|
||||||
ReleaseNotes = releaseNotes;
|
|
||||||
|
|
||||||
// 更新UI
|
|
||||||
updateVersionInfo.Text = $"本次更新: {CurrentVersion} -> {NewVersion}";
|
|
||||||
updateDateInfo.Text = $"{ReleaseDate}发布更新";
|
|
||||||
|
|
||||||
// 如果有发布说明,设置到Markdown内容中
|
|
||||||
if (!string.IsNullOrEmpty(ReleaseNotes))
|
|
||||||
{
|
|
||||||
markdownContent.Markdown = ReleaseNotes;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 自动更新和静默更新设置已移至设置界面,此处不再需要
|
|
||||||
|
|
||||||
// 确保按钮可见且可用
|
|
||||||
EnsureButtonsVisibility();
|
|
||||||
|
|
||||||
// 显示窗口动画
|
|
||||||
AnimationsHelper.ShowWithFadeIn(this, 0.25);
|
|
||||||
|
|
||||||
// 设置深色模式
|
|
||||||
UseImmersiveDarkMode(new WindowInteropHelper(this).Handle, true);
|
|
||||||
|
|
||||||
// 窗口加载完成后再次确保按钮可见
|
|
||||||
Loaded += HasNewUpdateWindow_Loaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void HasNewUpdateWindow_Loaded(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
// 窗口加载完成后再次确保按钮可见
|
|
||||||
EnsureButtonsVisibility();
|
|
||||||
|
|
||||||
// 调整窗口大小以适应屏幕分辨率
|
|
||||||
AdjustWindowSizeForScreenResolution();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 确保按钮可见并启用
|
|
||||||
private void EnsureButtonsVisibility()
|
|
||||||
{
|
|
||||||
// 确保立即更新按钮可见
|
|
||||||
UpdateNowButton.Visibility = Visibility.Visible;
|
|
||||||
UpdateNowButton.IsEnabled = true;
|
|
||||||
|
|
||||||
// 确保稍后更新按钮可见
|
|
||||||
UpdateLaterButton.Visibility = Visibility.Visible;
|
|
||||||
UpdateLaterButton.IsEnabled = true;
|
|
||||||
|
|
||||||
// 确保跳过版本按钮可见
|
|
||||||
SkipVersionButton.Visibility = Visibility.Visible;
|
|
||||||
SkipVersionButton.IsEnabled = true;
|
|
||||||
|
|
||||||
// 强制刷新UI
|
|
||||||
UpdateLayout();
|
|
||||||
|
|
||||||
// 记录日志
|
|
||||||
LogHelper.WriteLogToFile("AutoUpdate | Update dialog buttons visibility ensured");
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void UpdateNowButton_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
LogHelper.WriteLogToFile("AutoUpdate | Update Now button clicked");
|
|
||||||
// 禁用按钮,显示进度条
|
|
||||||
UpdateNowButton.IsEnabled = false;
|
|
||||||
UpdateLaterButton.IsEnabled = false;
|
|
||||||
SkipVersionButton.IsEnabled = false;
|
|
||||||
DownloadProgressPanel.Visibility = Visibility.Visible;
|
|
||||||
|
|
||||||
// 重置进度条
|
|
||||||
var progressFill = FindName("ProgressFill") as Border;
|
|
||||||
if (progressFill != null)
|
|
||||||
{
|
|
||||||
progressFill.Width = 0;
|
|
||||||
}
|
|
||||||
DownloadProgressText.Text = "正在准备下载...";
|
|
||||||
|
|
||||||
// 启动多线路下载
|
|
||||||
bool downloadSuccess = false;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// 获取当前通道的所有线路组
|
|
||||||
var groups = AutoUpdateHelper.ChannelLineGroups[MainWindow.Settings.Startup.UpdateChannel];
|
|
||||||
downloadSuccess = await AutoUpdateHelper.DownloadSetupFileWithFallback(NewVersion, groups, (percent, text) =>
|
|
||||||
{
|
|
||||||
Dispatcher.Invoke(() =>
|
|
||||||
{
|
|
||||||
// 更新自定义进度条
|
|
||||||
progressFill = FindName("ProgressFill") as Border;
|
|
||||||
if (progressFill != null)
|
|
||||||
{
|
|
||||||
progressFill.Width = (percent / 100.0) * 400; // 400是进度条总宽度
|
|
||||||
}
|
|
||||||
DownloadProgressText.Text = text;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
if (downloadSuccess)
|
|
||||||
{
|
|
||||||
// 下载完成后自动安装
|
|
||||||
await DownloadAndInstallVersion(NewVersion, null, CancellationToken.None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
DownloadProgressText.Text = $"下载失败: {ex.Message}";
|
|
||||||
LogHelper.WriteLogToFile($"AutoUpdate | 下载异常: {ex.Message}", LogHelper.LogType.Error);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (downloadSuccess)
|
|
||||||
{
|
|
||||||
// 设置进度条为100%
|
|
||||||
progressFill = FindName("ProgressFill") as Border;
|
|
||||||
if (progressFill != null)
|
|
||||||
{
|
|
||||||
progressFill.Width = 400; // 100%完成
|
|
||||||
}
|
|
||||||
DownloadProgressText.Text = "下载完成,准备安装...";
|
|
||||||
await Task.Delay(800);
|
|
||||||
// 设置结果为立即更新
|
|
||||||
Result = UpdateResult.UpdateNow;
|
|
||||||
DialogResult = true;
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DownloadProgressText.Text = "下载失败,请检查网络后重试。";
|
|
||||||
UpdateNowButton.IsEnabled = true;
|
|
||||||
UpdateLaterButton.IsEnabled = true;
|
|
||||||
SkipVersionButton.IsEnabled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateLaterButton_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
LogHelper.WriteLogToFile("AutoUpdate | Update Later button clicked");
|
|
||||||
|
|
||||||
// 设置结果为稍后更新
|
|
||||||
Result = UpdateResult.UpdateLater;
|
|
||||||
|
|
||||||
// 关闭窗口
|
|
||||||
DialogResult = true;
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SkipVersionButton_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
LogHelper.WriteLogToFile("AutoUpdate | Skip Version button clicked");
|
|
||||||
|
|
||||||
// 设置结果为跳过该版本
|
|
||||||
Result = UpdateResult.SkipVersion;
|
|
||||||
|
|
||||||
// 关闭窗口
|
|
||||||
DialogResult = true;
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CloseButton_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
LogHelper.WriteLogToFile("AutoUpdate | Close button clicked");
|
|
||||||
|
|
||||||
// 设置结果为稍后更新(默认行为)
|
|
||||||
Result = UpdateResult.UpdateLater;
|
|
||||||
|
|
||||||
// 关闭窗口
|
|
||||||
DialogResult = false;
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TitleBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
|
||||||
{
|
|
||||||
// 开始拖动窗口
|
|
||||||
if (e.LeftButton == MouseButtonState.Pressed)
|
|
||||||
{
|
|
||||||
DragMove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 根据屏幕分辨率调整窗口大小
|
|
||||||
private void AdjustWindowSizeForScreenResolution()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// 获取主屏幕分辨率
|
|
||||||
double screenWidth = SystemParameters.PrimaryScreenWidth;
|
|
||||||
double screenHeight = SystemParameters.PrimaryScreenHeight;
|
|
||||||
|
|
||||||
LogHelper.WriteLogToFile($"AutoUpdate | Screen resolution: {screenWidth}x{screenHeight}");
|
|
||||||
|
|
||||||
// 始终确保窗口不超过屏幕大小的85%
|
|
||||||
double maxHeight = screenHeight * 0.85;
|
|
||||||
double maxWidth = screenWidth * 0.85;
|
|
||||||
|
|
||||||
bool needsAdjustment = false;
|
|
||||||
|
|
||||||
// 如果窗口高度超过最大允许高度,调整窗口高度
|
|
||||||
if (Height > maxHeight)
|
|
||||||
{
|
|
||||||
Height = maxHeight;
|
|
||||||
needsAdjustment = true;
|
|
||||||
LogHelper.WriteLogToFile($"AutoUpdate | Adjusted window height to: {Height}");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果窗口宽度超过最大允许宽度,调整窗口宽度
|
|
||||||
if (Width > maxWidth)
|
|
||||||
{
|
|
||||||
Width = maxWidth;
|
|
||||||
needsAdjustment = true;
|
|
||||||
LogHelper.WriteLogToFile($"AutoUpdate | Adjusted window width to: {Width}");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果屏幕分辨率较低,调整更多UI元素
|
|
||||||
if (screenHeight < 768 || screenWidth < 1024 || needsAdjustment)
|
|
||||||
{
|
|
||||||
// 查找相关控件并调整大小
|
|
||||||
var markdownViewer = FindName("markdownContent") as MarkdownScrollViewer;
|
|
||||||
var updateNowButton = FindName("UpdateNowButton") as Button;
|
|
||||||
var updateLaterButton = FindName("UpdateLaterButton") as Button;
|
|
||||||
var skipVersionButton = FindName("SkipVersionButton") as Button;
|
|
||||||
|
|
||||||
// 查找包含ScrollViewer的边框控件,减小其高度
|
|
||||||
var contentBorders = FindVisualChildren<Border>().ToList();
|
|
||||||
foreach (var border in contentBorders)
|
|
||||||
{
|
|
||||||
if (border.Child is ScrollViewer || border.Child is ScrollViewerEx)
|
|
||||||
{
|
|
||||||
// 减小内容显示区域的高度
|
|
||||||
if (border.Height > 180)
|
|
||||||
{
|
|
||||||
border.Height = 160;
|
|
||||||
LogHelper.WriteLogToFile("AutoUpdate | Reduced content area height");
|
|
||||||
}
|
|
||||||
else if (border.Child is ScrollViewerEx scrollViewer && scrollViewer.Height > 160)
|
|
||||||
{
|
|
||||||
scrollViewer.Height = 160;
|
|
||||||
LogHelper.WriteLogToFile("AutoUpdate | Reduced scroll viewer height");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 调整按钮大小
|
|
||||||
if (updateNowButton != null && updateLaterButton != null && skipVersionButton != null)
|
|
||||||
{
|
|
||||||
updateNowButton.Height = 42;
|
|
||||||
updateLaterButton.Height = 42;
|
|
||||||
skipVersionButton.Height = 42;
|
|
||||||
updateNowButton.Padding = new Thickness(15, 8, 15, 8);
|
|
||||||
updateLaterButton.Padding = new Thickness(15, 8, 15, 8);
|
|
||||||
skipVersionButton.Padding = new Thickness(15, 8, 15, 8);
|
|
||||||
LogHelper.WriteLogToFile("AutoUpdate | Reduced button sizes for small screen");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 确保窗口在屏幕范围内
|
|
||||||
if (Left < 0) Left = 0;
|
|
||||||
if (Top < 0) Top = 0;
|
|
||||||
if (Left + Width > screenWidth) Left = screenWidth - Width;
|
|
||||||
if (Top + Height > screenHeight) Top = screenHeight - Height;
|
|
||||||
|
|
||||||
LogHelper.WriteLogToFile($"AutoUpdate | Final window size: {Width}x{Height}");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LogHelper.WriteLogToFile($"AutoUpdate | Error adjusting window size: {ex.Message}", LogHelper.LogType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 递归查找指定类型的所有子控件
|
|
||||||
private IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj = null) where T : DependencyObject
|
|
||||||
{
|
|
||||||
if (depObj == null)
|
|
||||||
depObj = this;
|
|
||||||
|
|
||||||
if (depObj != null)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
|
|
||||||
{
|
|
||||||
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
|
|
||||||
if (child != null && child is T)
|
|
||||||
{
|
|
||||||
yield return (T)child;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (T childOfChild in FindVisualChildren<T>(child))
|
|
||||||
{
|
|
||||||
yield return childOfChild;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 多线程分块下载并自动安装
|
|
||||||
private async Task<bool> DownloadAndInstallVersion(string version, string downloadUrl, CancellationToken token)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(downloadUrl))
|
|
||||||
{
|
|
||||||
// 自动更新场景下,downloadUrl为null,直接用主下载目录
|
|
||||||
downloadUrl = AutoUpdateHelper.GetLocalUpdateZipFilePath(version);
|
|
||||||
}
|
|
||||||
LogHelper.WriteLogToFile($"AutoUpdate | 开始安装版本: {version}");
|
|
||||||
AutoUpdateHelper.InstallNewVersionApp(version, true);
|
|
||||||
App.IsAppExitByUser = true;
|
|
||||||
Application.Current.Dispatcher.Invoke(() =>
|
|
||||||
{
|
|
||||||
Application.Current.Shutdown();
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,182 +0,0 @@
|
|||||||
<Window x:Class="Ink_Canvas.HistoryRollbackWindow"
|
|
||||||
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"
|
|
||||||
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
|
|
||||||
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
|
|
||||||
xmlns:mdxam="clr-namespace:MdXaml;assembly=MdXaml"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
Title="历史版本回滚" Height="650" Width="900" ResizeMode="CanResize"
|
|
||||||
WindowStartupLocation="CenterScreen"
|
|
||||||
Topmost="True"
|
|
||||||
Background="{DynamicResource SettingsPageBackground}" MinHeight="550" MinWidth="800"
|
|
||||||
SnapsToDevicePixels="True"
|
|
||||||
TextOptions.TextRenderingMode="ClearType"
|
|
||||||
TextOptions.TextFormattingMode="Display"
|
|
||||||
ui:ThemeManager.IsThemeAware="True"
|
|
||||||
ui:TitleBar.ExtendViewIntoTitleBar="True"
|
|
||||||
ui:WindowHelper.SystemBackdropType="Mica"
|
|
||||||
ui:WindowHelper.UseModernWindowStyle="True"
|
|
||||||
ui:TitleBar.Height="48">
|
|
||||||
<Window.Resources>
|
|
||||||
<!-- 主题相关颜色资源 -->
|
|
||||||
<SolidColorBrush x:Key="PrimaryBrush" Color="#FF2563eb"/>
|
|
||||||
<SolidColorBrush x:Key="TextPrimaryBrush" Color="#FF1f2937"/>
|
|
||||||
<SolidColorBrush x:Key="TextSecondaryBrush" Color="#FF6b7280"/>
|
|
||||||
</Window.Resources>
|
|
||||||
|
|
||||||
<Grid>
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="Auto"/>
|
|
||||||
<RowDefinition Height="*"/>
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
|
|
||||||
<!-- 自定义标题栏 -->
|
|
||||||
<Border x:Name="Border_TitleBarRoot"
|
|
||||||
Height="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=(ui:TitleBar.Height)}">
|
|
||||||
<Grid>
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition/>
|
|
||||||
<ColumnDefinition Width="Auto"/>
|
|
||||||
<ColumnDefinition Width="Auto"/>
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
|
|
||||||
<TextBlock Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=Title}"
|
|
||||||
VerticalAlignment="Center" Margin="12,0,0,0" FontSize="12" FontWeight="SemiBold"/>
|
|
||||||
|
|
||||||
<!--Right Inset-->
|
|
||||||
<Rectangle Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=(ui:TitleBar.SystemOverlayRightInset)}"
|
|
||||||
Grid.Column="2"/>
|
|
||||||
|
|
||||||
<!--Right Buttons-->
|
|
||||||
<ikw:SimpleStackPanel x:Name="StackPanel_RightButtons"
|
|
||||||
Orientation="Horizontal" Grid.Column="1" Spacing="5">
|
|
||||||
</ikw:SimpleStackPanel>
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- 主内容区 -->
|
|
||||||
<Grid Grid.Row="1" Background="{DynamicResource SettingsPageBackground}" Margin="20,20,20,20">
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="Auto"/>
|
|
||||||
<RowDefinition Height="Auto"/>
|
|
||||||
<RowDefinition Height="*"/>
|
|
||||||
<RowDefinition Height="Auto"/>
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
|
|
||||||
<!-- 标题区域 -->
|
|
||||||
<ikw:SimpleStackPanel Grid.Row="0" Orientation="Horizontal" Spacing="12" Margin="0,0,0,16">
|
|
||||||
<Border Background="{DynamicResource PrimaryBrush}" CornerRadius="16" Padding="12" Margin="0,0,0,0">
|
|
||||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Undo}" FontSize="20" Foreground="White"
|
|
||||||
VerticalAlignment="Center"/>
|
|
||||||
</Border>
|
|
||||||
<ikw:SimpleStackPanel VerticalAlignment="Center" Spacing="4">
|
|
||||||
<TextBlock Text="选择要回滚到的历史版本" FontSize="22" FontWeight="Bold"
|
|
||||||
Foreground="{DynamicResource TextPrimaryBrush}"/>
|
|
||||||
</ikw:SimpleStackPanel>
|
|
||||||
</ikw:SimpleStackPanel>
|
|
||||||
|
|
||||||
<!-- 版本选择卡片 -->
|
|
||||||
<Border Grid.Row="1"
|
|
||||||
Background="{DynamicResource SettingsPageBackground}"
|
|
||||||
BorderBrush="{DynamicResource SettingsPageBorderBrush}"
|
|
||||||
BorderThickness="1"
|
|
||||||
CornerRadius="12"
|
|
||||||
Margin="0,0,0,16"
|
|
||||||
Padding="20">
|
|
||||||
<Border.Effect>
|
|
||||||
<DropShadowEffect Color="#000000" BlurRadius="8" ShadowDepth="0" Opacity="0.05"/>
|
|
||||||
</Border.Effect>
|
|
||||||
<ikw:SimpleStackPanel Spacing="16">
|
|
||||||
<TextBlock Text="选择版本" FontSize="16" FontWeight="SemiBold"
|
|
||||||
Foreground="{DynamicResource TextPrimaryBrush}"/>
|
|
||||||
<ComboBox x:Name="VersionComboBox"
|
|
||||||
Width="300" Height="40"
|
|
||||||
DisplayMemberPath="Version"
|
|
||||||
SelectionChanged="VersionComboBox_SelectionChanged"/>
|
|
||||||
</ikw:SimpleStackPanel>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- 发布说明卡片 -->
|
|
||||||
<Border Grid.Row="2"
|
|
||||||
Background="{DynamicResource SettingsPageBackground}"
|
|
||||||
BorderBrush="{DynamicResource SettingsPageBorderBrush}"
|
|
||||||
BorderThickness="1"
|
|
||||||
CornerRadius="12"
|
|
||||||
Margin="0,0,0,16"
|
|
||||||
Padding="20">
|
|
||||||
<Border.Effect>
|
|
||||||
<DropShadowEffect Color="#000000" BlurRadius="8" ShadowDepth="0" Opacity="0.05"/>
|
|
||||||
</Border.Effect>
|
|
||||||
<Grid>
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="Auto"/>
|
|
||||||
<RowDefinition Height="*"/>
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<TextBlock Grid.Row="0"
|
|
||||||
Text="版本更新说明" FontSize="16" FontWeight="SemiBold"
|
|
||||||
Foreground="{DynamicResource TextPrimaryBrush}"
|
|
||||||
Margin="0,0,0,16"/>
|
|
||||||
<Border Grid.Row="1"
|
|
||||||
Background="{DynamicResource SettingsPageBackground}"
|
|
||||||
BorderBrush="{DynamicResource SettingsPageBorderBrush}"
|
|
||||||
BorderThickness="1"
|
|
||||||
CornerRadius="8">
|
|
||||||
<Border.Effect>
|
|
||||||
<DropShadowEffect Color="#000000" BlurRadius="4" ShadowDepth="0" Opacity="0.03"/>
|
|
||||||
</Border.Effect>
|
|
||||||
<ScrollViewer x:Name="InnerReleaseNotesScrollViewer"
|
|
||||||
VerticalScrollBarVisibility="Auto"
|
|
||||||
HorizontalScrollBarVisibility="Disabled"
|
|
||||||
Padding="16"
|
|
||||||
PanningMode="VerticalOnly"
|
|
||||||
PanningRatio="1.0"
|
|
||||||
IsManipulationEnabled="True">
|
|
||||||
<mdxam:MarkdownScrollViewer x:Name="ReleaseNotesViewer"
|
|
||||||
Foreground="{DynamicResource TextPrimaryBrush}"
|
|
||||||
MarkdownStyleName="GithubLike"
|
|
||||||
IsHitTestVisible="False"
|
|
||||||
IsManipulationEnabled="False"/>
|
|
||||||
</ScrollViewer>
|
|
||||||
</Border>
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- 操作按钮区域 -->
|
|
||||||
<ikw:SimpleStackPanel Grid.Row="3" Spacing="16" Margin="0,0,0,0">
|
|
||||||
<Button x:Name="RollbackButton"
|
|
||||||
Content="回滚到此版本"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
Click="RollbackButton_Click"
|
|
||||||
Style="{DynamicResource AccentButtonStyle}"
|
|
||||||
Width="200" Height="48"
|
|
||||||
FontSize="16" FontWeight="SemiBold"/>
|
|
||||||
|
|
||||||
<!-- 下载进度面板 -->
|
|
||||||
<Border x:Name="DownloadProgressPanel"
|
|
||||||
Background="{DynamicResource SettingsPageBackground}"
|
|
||||||
BorderBrush="{DynamicResource SettingsPageBorderBrush}"
|
|
||||||
BorderThickness="1"
|
|
||||||
CornerRadius="12"
|
|
||||||
Visibility="Collapsed"
|
|
||||||
Margin="0,8,0,0"
|
|
||||||
Padding="24">
|
|
||||||
<Border.Effect>
|
|
||||||
<DropShadowEffect Color="#000000" BlurRadius="8" ShadowDepth="0" Opacity="0.05"/>
|
|
||||||
</Border.Effect>
|
|
||||||
<ikw:SimpleStackPanel Spacing="12">
|
|
||||||
<ProgressBar x:Name="DownloadProgressBar"
|
|
||||||
Width="300" Height="8"
|
|
||||||
Minimum="0" Maximum="100" Value="0"/>
|
|
||||||
<TextBlock x:Name="DownloadProgressText"
|
|
||||||
Text="正在下载..."
|
|
||||||
FontSize="14"
|
|
||||||
Foreground="{DynamicResource TextPrimaryBrush}"
|
|
||||||
HorizontalAlignment="Center"/>
|
|
||||||
</ikw:SimpleStackPanel>
|
|
||||||
</Border>
|
|
||||||
</ikw:SimpleStackPanel>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Window>
|
|
||||||
@@ -1,370 +0,0 @@
|
|||||||
using Ink_Canvas.Helpers;
|
|
||||||
using iNKORE.UI.WPF.Modern;
|
|
||||||
using iNKORE.UI.WPF.Modern.Controls;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows;
|
|
||||||
using System.Windows.Controls;
|
|
||||||
using System.Windows.Input;
|
|
||||||
using System.Windows.Media;
|
|
||||||
|
|
||||||
// Added for OrderByDescending
|
|
||||||
|
|
||||||
namespace Ink_Canvas
|
|
||||||
{
|
|
||||||
public partial class HistoryRollbackWindow : Window
|
|
||||||
{
|
|
||||||
private class VersionItem
|
|
||||||
{
|
|
||||||
public string Version { get; set; }
|
|
||||||
public string DownloadUrl { get; set; }
|
|
||||||
public string ReleaseNotes { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<VersionItem> versionList = new List<VersionItem>();
|
|
||||||
private VersionItem selectedItem;
|
|
||||||
private UpdateChannel channel = UpdateChannel.Release;
|
|
||||||
private CancellationTokenSource downloadCts = null;
|
|
||||||
|
|
||||||
public HistoryRollbackWindow(UpdateChannel channel = UpdateChannel.Release)
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
this.channel = channel;
|
|
||||||
|
|
||||||
// 设置窗口置顶
|
|
||||||
this.Topmost = true;
|
|
||||||
|
|
||||||
// 应用当前主题
|
|
||||||
ApplyCurrentTheme();
|
|
||||||
|
|
||||||
// 隐藏主窗口
|
|
||||||
HideMainWindow();
|
|
||||||
|
|
||||||
LoadVersions();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 隐藏主窗口
|
|
||||||
/// </summary>
|
|
||||||
private void HideMainWindow()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// 获取主窗口实例
|
|
||||||
var mainWindow = Application.Current.MainWindow as MainWindow;
|
|
||||||
if (mainWindow != null)
|
|
||||||
{
|
|
||||||
// 隐藏主窗口
|
|
||||||
mainWindow.Visibility = Visibility.Hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LogHelper.WriteLogToFile($"隐藏主窗口时出错: {ex.Message}", LogHelper.LogType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 应用当前主题设置
|
|
||||||
/// </summary>
|
|
||||||
private void ApplyCurrentTheme()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// 根据主窗口的主题设置应用主题
|
|
||||||
switch (MainWindow.Settings.Appearance.Theme)
|
|
||||||
{
|
|
||||||
case 0: // 浅色主题
|
|
||||||
ThemeManager.SetRequestedTheme(this, ElementTheme.Light);
|
|
||||||
UpdateColorsForLightTheme();
|
|
||||||
break;
|
|
||||||
case 1: // 深色主题
|
|
||||||
ThemeManager.SetRequestedTheme(this, ElementTheme.Dark);
|
|
||||||
UpdateColorsForDarkTheme();
|
|
||||||
break;
|
|
||||||
case 2: // 跟随系统
|
|
||||||
if (IsSystemThemeLight())
|
|
||||||
{
|
|
||||||
ThemeManager.SetRequestedTheme(this, ElementTheme.Light);
|
|
||||||
UpdateColorsForLightTheme();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ThemeManager.SetRequestedTheme(this, ElementTheme.Dark);
|
|
||||||
UpdateColorsForDarkTheme();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LogHelper.WriteLogToFile($"应用主题时出错: {ex.Message}", LogHelper.LogType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 更新为浅色主题颜色
|
|
||||||
/// </summary>
|
|
||||||
private void UpdateColorsForLightTheme()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// 更新主要颜色资源
|
|
||||||
Resources["PrimaryBrush"] = new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb));
|
|
||||||
Resources["TextPrimaryBrush"] = new SolidColorBrush(Color.FromRgb(0x1f, 0x29, 0x37));
|
|
||||||
Resources["TextSecondaryBrush"] = new SolidColorBrush(Color.FromRgb(0x6b, 0x72, 0x80));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LogHelper.WriteLogToFile($"更新浅色主题颜色时出错: {ex.Message}", LogHelper.LogType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 更新为深色主题颜色
|
|
||||||
/// </summary>
|
|
||||||
private void UpdateColorsForDarkTheme()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// 更新主要颜色资源
|
|
||||||
Resources["PrimaryBrush"] = new SolidColorBrush(Color.FromRgb(0x3b, 0x82, 0xf6));
|
|
||||||
Resources["TextPrimaryBrush"] = new SolidColorBrush(Color.FromRgb(0xf9, 0xfa, 0xfb));
|
|
||||||
Resources["TextSecondaryBrush"] = new SolidColorBrush(Color.FromRgb(0x9c, 0xa3, 0xaf));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LogHelper.WriteLogToFile($"更新深色主题颜色时出错: {ex.Message}", LogHelper.LogType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 检查系统是否为浅色主题
|
|
||||||
/// </summary>
|
|
||||||
private bool IsSystemThemeLight()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using (var key = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"))
|
|
||||||
{
|
|
||||||
if (key?.GetValue("AppsUseLightTheme") is int value)
|
|
||||||
{
|
|
||||||
return value == 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// 如果无法读取注册表,默认返回true(浅色主题)
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void LoadVersions()
|
|
||||||
{
|
|
||||||
LogHelper.WriteLogToFile($"HistoryRollback | 开始加载历史版本,通道: {channel}");
|
|
||||||
RollbackButton.IsEnabled = false;
|
|
||||||
VersionComboBox.Items.Clear();
|
|
||||||
DownloadProgressPanel.Visibility = Visibility.Collapsed;
|
|
||||||
DownloadProgressBar.Value = 0;
|
|
||||||
DownloadProgressText.Text = "";
|
|
||||||
ReleaseNotesViewer.Markdown = "正在获取历史版本...";
|
|
||||||
var releases = await AutoUpdateHelper.GetAllGithubReleases(channel);
|
|
||||||
versionList.Clear();
|
|
||||||
foreach (var (version, url, notes) in releases)
|
|
||||||
{
|
|
||||||
versionList.Add(new VersionItem { Version = version, DownloadUrl = url, ReleaseNotes = notes });
|
|
||||||
}
|
|
||||||
// 按版本号数字降序排列
|
|
||||||
versionList = versionList.OrderByDescending(v => ParseVersionForSort(v.Version)).ToList();
|
|
||||||
VersionComboBox.ItemsSource = versionList;
|
|
||||||
if (versionList.Count > 0)
|
|
||||||
{
|
|
||||||
VersionComboBox.SelectedIndex = 0;
|
|
||||||
RollbackButton.IsEnabled = true;
|
|
||||||
LogHelper.WriteLogToFile($"HistoryRollback | 加载到 {versionList.Count} 个历史版本");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ReleaseNotesViewer.Markdown = "未获取到历史版本信息。";
|
|
||||||
LogHelper.WriteLogToFile("HistoryRollback | 未获取到历史版本信息", LogHelper.LogType.Warning);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 辅助方法:解析版本号用于排序
|
|
||||||
private Version ParseVersionForSort(string version)
|
|
||||||
{
|
|
||||||
var v = version.TrimStart('v', 'V');
|
|
||||||
Version result;
|
|
||||||
if (Version.TryParse(v, out result))
|
|
||||||
return result;
|
|
||||||
return new Version(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void VersionComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
|
||||||
{
|
|
||||||
selectedItem = VersionComboBox.SelectedItem as VersionItem;
|
|
||||||
if (selectedItem != null)
|
|
||||||
{
|
|
||||||
ReleaseNotesViewer.Markdown = selectedItem.ReleaseNotes ?? "无更新日志";
|
|
||||||
LogHelper.WriteLogToFile($"HistoryRollback | 用户选择版本: {selectedItem.Version}");
|
|
||||||
}
|
|
||||||
// 取消聚焦,防止父级自动滚动
|
|
||||||
Keyboard.ClearFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void RollbackButton_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
if (selectedItem == null) return;
|
|
||||||
|
|
||||||
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,
|
|
||||||
Foreground = (Brush)Resources["TextPrimaryBrush"]
|
|
||||||
};
|
|
||||||
|
|
||||||
var daysComboBox = new System.Windows.Controls.ComboBox
|
|
||||||
{
|
|
||||||
Width = 200,
|
|
||||||
Height = 36,
|
|
||||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Left
|
|
||||||
};
|
|
||||||
|
|
||||||
for (int i = 0; i <= 7; i++)
|
|
||||||
{
|
|
||||||
daysComboBox.Items.Add(new System.Windows.Controls.ComboBoxItem
|
|
||||||
{
|
|
||||||
Content = $"{i} 天",
|
|
||||||
Tag = i
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
daysComboBox.SelectedIndex = 0;
|
|
||||||
|
|
||||||
panel.Children.Add(textBlock);
|
|
||||||
panel.Children.Add(daysComboBox);
|
|
||||||
dialog.Content = panel;
|
|
||||||
|
|
||||||
var dialogResult = await dialog.ShowAsync();
|
|
||||||
|
|
||||||
if (dialogResult == ContentDialogResult.Primary)
|
|
||||||
{
|
|
||||||
int days = 1;
|
|
||||||
if (daysComboBox.SelectedItem is System.Windows.Controls.ComboBoxItem selectedItemCombo &&
|
|
||||||
selectedItemCombo.Tag != null &&
|
|
||||||
int.TryParse(selectedItemCombo.Tag.ToString(), out int selectedDays))
|
|
||||||
{
|
|
||||||
days = selectedDays;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 | 用户选择暂停自动更新 {days} 天");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogHelper.WriteLogToFile("HistoryRollback | 用户取消了回滚操作");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LogHelper.WriteLogToFile($"HistoryRollback | 用户确认回滚,目标版本: {selectedItem.Version}");
|
|
||||||
RollbackButton.IsEnabled = false;
|
|
||||||
VersionComboBox.IsEnabled = false;
|
|
||||||
DownloadProgressPanel.Visibility = Visibility.Visible;
|
|
||||||
DownloadProgressBar.Value = 0;
|
|
||||||
DownloadProgressText.Text = "正在准备下载...";
|
|
||||||
|
|
||||||
bool downloadSuccess = false;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
downloadSuccess = await AutoUpdateHelper.StartManualDownloadAndInstall(
|
|
||||||
selectedItem.Version,
|
|
||||||
channel,
|
|
||||||
(percent, text) =>
|
|
||||||
{
|
|
||||||
Dispatcher.Invoke(() =>
|
|
||||||
{
|
|
||||||
DownloadProgressBar.Value = percent;
|
|
||||||
DownloadProgressText.Text = text;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
DownloadProgressText.Text = $"下载失败: {ex.Message}";
|
|
||||||
LogHelper.WriteLogToFile($"HistoryRollback | 下载异常: {ex.Message}", LogHelper.LogType.Error);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (downloadSuccess)
|
|
||||||
{
|
|
||||||
DownloadProgressBar.Value = 100;
|
|
||||||
DownloadProgressText.Text = "下载完成,准备安装...";
|
|
||||||
await Task.Delay(800);
|
|
||||||
DialogResult = true;
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DownloadProgressText.Text = "下载失败,请检查网络后重试。";
|
|
||||||
RollbackButton.IsEnabled = true;
|
|
||||||
VersionComboBox.IsEnabled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected override void OnClosing(CancelEventArgs e)
|
|
||||||
{
|
|
||||||
downloadCts?.Cancel();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// 获取主窗口实例
|
|
||||||
var mainWindow = Application.Current.MainWindow as MainWindow;
|
|
||||||
if (mainWindow != null)
|
|
||||||
{
|
|
||||||
// 重新显示主窗口
|
|
||||||
mainWindow.Visibility = Visibility.Visible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LogHelper.WriteLogToFile($"显示主窗口时出错: {ex.Message}", LogHelper.LogType.Error);
|
|
||||||
}
|
|
||||||
|
|
||||||
base.OnClosing(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -10,140 +10,308 @@
|
|||||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||||
xmlns:controls="clr-namespace:Ink_Canvas.Controls;assembly=InkCanvas.Controls"
|
xmlns:controls="clr-namespace:Ink_Canvas.Controls;assembly=InkCanvas.Controls"
|
||||||
xmlns:c="clr-namespace:Ink_Canvas.Converter"
|
xmlns:c="clr-namespace:Ink_Canvas.Converter"
|
||||||
|
xmlns:mdxam="clr-namespace:MdXaml;assembly=MdXaml"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
|
|
||||||
Title="更新">
|
Title="更新">
|
||||||
|
|
||||||
<ScrollViewer PanningMode="VerticalFirst">
|
<ScrollViewer PanningMode="VerticalFirst">
|
||||||
<Grid Margin="59,0,59,0">
|
<Grid Margin="59,0,59,0">
|
||||||
<FrameworkElement.Resources>
|
<FrameworkElement.Resources>
|
||||||
<sys:Double x:Key="SettingsCardSpacing">4</sys:Double>
|
<sys:Double x:Key="SettingsCardSpacing">4</sys:Double>
|
||||||
<c:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
|
<c:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
|
||||||
<Style x:Key="SettingsSectionHeaderTextBlockStyle"
|
<Style x:Key="SettingsSectionHeaderTextBlockStyle"
|
||||||
BasedOn="{StaticResource BodyStrongTextBlockStyle}"
|
BasedOn="{StaticResource BodyStrongTextBlockStyle}"
|
||||||
TargetType="TextBlock">
|
TargetType="TextBlock">
|
||||||
<Style.Setters>
|
|
||||||
<Setter Property="Margin" Value="1,30,0,6" />
|
<Setter Property="Margin" Value="1,30,0,6" />
|
||||||
</Style.Setters>
|
</Style>
|
||||||
</Style>
|
</FrameworkElement.Resources>
|
||||||
</FrameworkElement.Resources>
|
|
||||||
<Grid>
|
|
||||||
<ikw:SimpleStackPanel MaxWidth="1000"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
Spacing="{StaticResource SettingsCardSpacing}">
|
|
||||||
|
|
||||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
|
<ikw:SimpleStackPanel MaxWidth="1000"
|
||||||
Text="自动更新" />
|
HorizontalAlignment="Stretch"
|
||||||
|
Spacing="{StaticResource SettingsCardSpacing}">
|
||||||
|
|
||||||
<controls:LabeledSettingsCard x:Name="CardAutoUpdate"
|
<!-- 状态横幅 -->
|
||||||
Header="{i18n:I18n Key=Header_AutoUpdate}"
|
<Border Margin="0,16,0,0"
|
||||||
Description="{i18n:I18n Key=Startup_AutoUpdateHint}"
|
Padding="20"
|
||||||
Icon="{x:Static ui:SegoeFluentIcons.Sync}"
|
CornerRadius="8"
|
||||||
SwitchName="ToggleSwitchIsAutoUpdate"
|
Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}">
|
||||||
Toggled="ToggleSwitchIsAutoUpdate_Toggled" />
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<controls:LabeledSettingsCard x:Name="CardSilentUpdate"
|
<Border Grid.Column="0"
|
||||||
Header="{i18n:I18n Key=Header_SilentUpdate}"
|
Width="48" Height="48"
|
||||||
Description="{i18n:I18n Key=Startup_SilentUpdateHint}"
|
CornerRadius="24"
|
||||||
Icon="{x:Static ui:SegoeFluentIcons.QuietHours}"
|
VerticalAlignment="Top"
|
||||||
SwitchName="ToggleSwitchIsAutoUpdateWithSilence"
|
Margin="0,0,16,0"
|
||||||
ShowWhen="{Binding IsOn, ElementName=CardAutoUpdate}"
|
Background="{DynamicResource SystemControlHighlightAccentBrush}">
|
||||||
d:Visibility="Visible"
|
<ui:FontIcon x:Name="StatusIcon"
|
||||||
Toggled="ToggleSwitchIsAutoUpdateWithSilence_Toggled" />
|
FontSize="22"
|
||||||
|
Foreground="White"
|
||||||
|
Icon="{x:Static ui:SegoeFluentIcons.Completed}"
|
||||||
|
HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||||
|
</Border>
|
||||||
|
|
||||||
<ui:SettingsExpander x:Name="ExpanderSilentUpdateTime"
|
<ikw:SimpleStackPanel Grid.Column="1" Spacing="4">
|
||||||
Header="{i18n:I18n Key=Startup_SilentUpdateTimePeriod}"
|
<TextBlock x:Name="StatusTitle"
|
||||||
Visibility="{Binding IsOn, ElementName=CardSilentUpdate, Converter={StaticResource BooleanToVisibilityConverter}}"
|
FontSize="22" FontWeight="SemiBold"
|
||||||
IsExpanded="{Binding IsOn, ElementName=CardSilentUpdate, Mode=OneWay}"
|
Text="正在加载更新状态..." />
|
||||||
d:Visibility="Visible" d:IsExpanded="True">
|
<TextBlock x:Name="StatusSubtitle"
|
||||||
<ui:SettingsExpander.HeaderIcon>
|
Opacity="0.75"
|
||||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Settings}" />
|
TextWrapping="Wrap"
|
||||||
</ui:SettingsExpander.HeaderIcon>
|
Text="" />
|
||||||
<ui:SettingsExpander.Items>
|
|
||||||
<ui:SettingsCard Header="{i18n:I18n Key=Startup_SilentUpdateFullHint}">
|
<!-- 进度区 -->
|
||||||
<ui:SettingsCard.Content>
|
<ikw:SimpleStackPanel x:Name="ProgressPanel"
|
||||||
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="12">
|
Visibility="Collapsed"
|
||||||
<ikw:SimpleStackPanel Orientation="Horizontal">
|
Margin="0,8,0,0"
|
||||||
<TextBlock Margin="0,0,10,0" VerticalAlignment="Center"
|
Spacing="4">
|
||||||
Text="{i18n:I18n Key=Startup_StartTime}" FontSize="14" />
|
<TextBlock x:Name="ProgressText" FontSize="13" Opacity="0.8" />
|
||||||
<ComboBox x:Name="AutoUpdateWithSilenceStartTimeComboBox"
|
<ProgressBar x:Name="ProgressBar"
|
||||||
SelectionChanged="AutoUpdateWithSilenceStartTimeComboBox_SelectionChanged" />
|
Style="{DynamicResource DefaultProgressBarStyle}"
|
||||||
</ikw:SimpleStackPanel>
|
Height="6"
|
||||||
<ikw:SimpleStackPanel Orientation="Horizontal">
|
Minimum="0" Maximum="100"
|
||||||
<TextBlock Margin="0,0,10,0" VerticalAlignment="Center"
|
Value="0" />
|
||||||
Text="{i18n:I18n Key=Startup_EndTime}" FontSize="14" />
|
</ikw:SimpleStackPanel>
|
||||||
<ComboBox x:Name="AutoUpdateWithSilenceEndTimeComboBox"
|
|
||||||
SelectionChanged="AutoUpdateWithSilenceEndTimeComboBox_SelectionChanged" />
|
<!-- 操作按钮 -->
|
||||||
</ikw:SimpleStackPanel>
|
<ikw:SimpleStackPanel Orientation="Horizontal"
|
||||||
|
Margin="0,12,0,0"
|
||||||
|
Spacing="8">
|
||||||
|
<Button x:Name="CheckUpdateButton"
|
||||||
|
Style="{DynamicResource AccentButtonStyle}"
|
||||||
|
Click="CheckUpdateButton_Click">
|
||||||
|
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="6">
|
||||||
|
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Refresh}" FontSize="14" />
|
||||||
|
<TextBlock Text="检查更新" />
|
||||||
</ikw:SimpleStackPanel>
|
</ikw:SimpleStackPanel>
|
||||||
</ui:SettingsCard.Content>
|
</Button>
|
||||||
|
<Button x:Name="UpdateNowButton"
|
||||||
|
Visibility="Collapsed"
|
||||||
|
Style="{DynamicResource AccentButtonStyle}"
|
||||||
|
Click="UpdateNowButton_Click">
|
||||||
|
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="6">
|
||||||
|
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Download}" FontSize="14" />
|
||||||
|
<TextBlock Text="立即下载并安装" />
|
||||||
|
</ikw:SimpleStackPanel>
|
||||||
|
</Button>
|
||||||
|
<Button x:Name="UpdateLaterButton"
|
||||||
|
Visibility="Collapsed"
|
||||||
|
Click="UpdateLaterButton_Click">
|
||||||
|
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="6">
|
||||||
|
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.QuietHours}" FontSize="14" />
|
||||||
|
<TextBlock Text="退出时安装" />
|
||||||
|
</ikw:SimpleStackPanel>
|
||||||
|
</Button>
|
||||||
|
<Button x:Name="SkipVersionButton"
|
||||||
|
Visibility="Collapsed"
|
||||||
|
Click="SkipVersionButton_Click">
|
||||||
|
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="6">
|
||||||
|
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Cancel}" FontSize="14" />
|
||||||
|
<TextBlock Text="跳过此版本" />
|
||||||
|
</ikw:SimpleStackPanel>
|
||||||
|
</Button>
|
||||||
|
<Button x:Name="CancelDownloadButton"
|
||||||
|
Visibility="Collapsed"
|
||||||
|
Click="CancelDownloadButton_Click">
|
||||||
|
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="6">
|
||||||
|
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Cancel}" FontSize="14" />
|
||||||
|
<TextBlock Text="取消" />
|
||||||
|
</ikw:SimpleStackPanel>
|
||||||
|
</Button>
|
||||||
|
</ikw:SimpleStackPanel>
|
||||||
|
</ikw:SimpleStackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Tab 切换 -->
|
||||||
|
<TabControl x:Name="UpdateTabControl"
|
||||||
|
Margin="0,16,0,0"
|
||||||
|
HorizontalContentAlignment="Left"
|
||||||
|
Style="{StaticResource TabControlPivotStyle}"
|
||||||
|
BorderThickness="0"
|
||||||
|
Background="Transparent"
|
||||||
|
SelectionChanged="UpdateTabControl_SelectionChanged">
|
||||||
|
<TabControl.Resources>
|
||||||
|
<sys:Double x:Key="PivotHeaderItemFontSize">16</sys:Double>
|
||||||
|
</TabControl.Resources>
|
||||||
|
<!-- 更新日志 -->
|
||||||
|
<TabItem Header="更新日志">
|
||||||
|
<Border Margin="0,8,0,0"
|
||||||
|
Padding="16"
|
||||||
|
CornerRadius="8"
|
||||||
|
MinHeight="200"
|
||||||
|
Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}">
|
||||||
|
<ScrollViewer VerticalScrollBarVisibility="Auto"
|
||||||
|
HorizontalScrollBarVisibility="Disabled"
|
||||||
|
PanningMode="VerticalOnly"
|
||||||
|
MaxHeight="500">
|
||||||
|
<mdxam:MarkdownScrollViewer x:Name="ChangelogViewer"
|
||||||
|
MarkdownStyleName="GithubLike" />
|
||||||
|
</ScrollViewer>
|
||||||
|
</Border>
|
||||||
|
</TabItem>
|
||||||
|
|
||||||
|
<!-- 更新设置 -->
|
||||||
|
<TabItem Header="更新设置">
|
||||||
|
<ikw:SimpleStackPanel Margin="0,8,0,0"
|
||||||
|
Spacing="{StaticResource SettingsCardSpacing}">
|
||||||
|
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
|
||||||
|
Text="自动更新" />
|
||||||
|
|
||||||
|
<controls:LabeledSettingsCard x:Name="CardAutoUpdate"
|
||||||
|
Header="{i18n:I18n Key=Header_AutoUpdate}"
|
||||||
|
Description="{i18n:I18n Key=Startup_AutoUpdateHint}"
|
||||||
|
Icon="{x:Static ui:SegoeFluentIcons.Sync}"
|
||||||
|
SwitchName="ToggleSwitchIsAutoUpdate"
|
||||||
|
Toggled="ToggleSwitchIsAutoUpdate_Toggled" />
|
||||||
|
|
||||||
|
<controls:LabeledSettingsCard x:Name="CardSilentUpdate"
|
||||||
|
Header="{i18n:I18n Key=Header_SilentUpdate}"
|
||||||
|
Description="{i18n:I18n Key=Startup_SilentUpdateHint}"
|
||||||
|
Icon="{x:Static ui:SegoeFluentIcons.QuietHours}"
|
||||||
|
SwitchName="ToggleSwitchIsAutoUpdateWithSilence"
|
||||||
|
ShowWhen="{Binding IsOn, ElementName=CardAutoUpdate}"
|
||||||
|
d:Visibility="Visible"
|
||||||
|
Toggled="ToggleSwitchIsAutoUpdateWithSilence_Toggled" />
|
||||||
|
|
||||||
|
<ui:SettingsExpander x:Name="ExpanderSilentUpdateTime"
|
||||||
|
Header="{i18n:I18n Key=Startup_SilentUpdateTimePeriod}"
|
||||||
|
Visibility="{Binding IsOn, ElementName=CardSilentUpdate, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||||
|
IsExpanded="{Binding IsOn, ElementName=CardSilentUpdate, Mode=OneWay}"
|
||||||
|
d:Visibility="Visible" d:IsExpanded="True">
|
||||||
|
<ui:SettingsExpander.HeaderIcon>
|
||||||
|
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Settings}" />
|
||||||
|
</ui:SettingsExpander.HeaderIcon>
|
||||||
|
<ui:SettingsExpander.Items>
|
||||||
|
<ui:SettingsCard Header="{i18n:I18n Key=Startup_SilentUpdateFullHint}">
|
||||||
|
<ui:SettingsCard.Content>
|
||||||
|
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="12">
|
||||||
|
<ikw:SimpleStackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock Margin="0,0,10,0" VerticalAlignment="Center"
|
||||||
|
Text="{i18n:I18n Key=Startup_StartTime}" FontSize="14" />
|
||||||
|
<ComboBox x:Name="AutoUpdateWithSilenceStartTimeComboBox"
|
||||||
|
SelectionChanged="AutoUpdateWithSilenceStartTimeComboBox_SelectionChanged" />
|
||||||
|
</ikw:SimpleStackPanel>
|
||||||
|
<ikw:SimpleStackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock Margin="0,0,10,0" VerticalAlignment="Center"
|
||||||
|
Text="{i18n:I18n Key=Startup_EndTime}" FontSize="14" />
|
||||||
|
<ComboBox x:Name="AutoUpdateWithSilenceEndTimeComboBox"
|
||||||
|
SelectionChanged="AutoUpdateWithSilenceEndTimeComboBox_SelectionChanged" />
|
||||||
|
</ikw:SimpleStackPanel>
|
||||||
|
</ikw:SimpleStackPanel>
|
||||||
|
</ui:SettingsCard.Content>
|
||||||
|
</ui:SettingsCard>
|
||||||
|
<ui:SettingsCard Header="{i18n:I18n Key=Startup_TimePeriodHint}" />
|
||||||
|
</ui:SettingsExpander.Items>
|
||||||
|
</ui:SettingsExpander>
|
||||||
|
|
||||||
|
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
|
||||||
|
Text="更新通道" />
|
||||||
|
|
||||||
|
<ui:SettingsCard Header="{i18n:I18n Key=Startup_UpdateChannel}"
|
||||||
|
Description="{i18n:I18n Key=Startup_UpdateChannelHint}">
|
||||||
|
<ui:SettingsCard.HeaderIcon>
|
||||||
|
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.SyncFolder}" />
|
||||||
|
</ui:SettingsCard.HeaderIcon>
|
||||||
|
<ComboBox x:Name="UpdateChannelSelector"
|
||||||
|
SelectionChanged="UpdateChannelSelector_SelectionChanged">
|
||||||
|
<ComboBoxItem Content="{i18n:I18n Key=Update_Release}" Tag="Release" />
|
||||||
|
<ComboBoxItem Content="{i18n:I18n Key=Update_Preview}" Tag="Preview" />
|
||||||
|
<ComboBoxItem Content="{i18n:I18n Key=Update_Beta}" Tag="Beta" />
|
||||||
|
</ComboBox>
|
||||||
</ui:SettingsCard>
|
</ui:SettingsCard>
|
||||||
<ui:SettingsCard Header="{i18n:I18n Key=Startup_TimePeriodHint}" />
|
|
||||||
</ui:SettingsExpander.Items>
|
|
||||||
</ui:SettingsExpander>
|
|
||||||
|
|
||||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
|
<ui:SettingsCard Header="{i18n:I18n Key=Startup_UpdatePackageArchitecture}"
|
||||||
Text="更新通道" />
|
Description="{i18n:I18n Key=Startup_UpdatePackageArchitectureHint}">
|
||||||
|
<ui:SettingsCard.HeaderIcon>
|
||||||
|
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.DeveloperTools}" />
|
||||||
|
</ui:SettingsCard.HeaderIcon>
|
||||||
|
<ComboBox x:Name="UpdatePackageArchitectureSelector"
|
||||||
|
SelectionChanged="UpdatePackageArchitectureSelector_SelectionChanged">
|
||||||
|
<ComboBoxItem Content="{i18n:I18n Key=Update_PackageArch_X86}" Tag="X86" />
|
||||||
|
<ComboBoxItem Content="{i18n:I18n Key=Update_PackageArch_X64}" Tag="X64" />
|
||||||
|
</ComboBox>
|
||||||
|
</ui:SettingsCard>
|
||||||
|
|
||||||
<ui:SettingsCard Header="{i18n:I18n Key=Startup_UpdateChannel}"
|
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
|
||||||
Description="{i18n:I18n Key=Startup_UpdateChannelHint}">
|
Text="维护" />
|
||||||
<ui:SettingsCard.HeaderIcon>
|
|
||||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.SyncFolder}" />
|
|
||||||
</ui:SettingsCard.HeaderIcon>
|
|
||||||
<ComboBox x:Name="UpdateChannelSelector"
|
|
||||||
SelectionChanged="UpdateChannelSelector_SelectionChanged">
|
|
||||||
<ComboBoxItem Content="{i18n:I18n Key=Update_Release}" Tag="Release" />
|
|
||||||
<ComboBoxItem Content="{i18n:I18n Key=Update_Preview}" Tag="Preview" />
|
|
||||||
<ComboBoxItem Content="{i18n:I18n Key=Update_Beta}" Tag="Beta" />
|
|
||||||
</ComboBox>
|
|
||||||
</ui:SettingsCard>
|
|
||||||
|
|
||||||
<ui:SettingsCard Header="{i18n:I18n Key=Startup_UpdatePackageArchitecture}"
|
<ui:SettingsCard Header="{i18n:I18n Key=Btn_FixVersion}"
|
||||||
Description="{i18n:I18n Key=Startup_UpdatePackageArchitectureHint}">
|
Description="{i18n:I18n Key=Startup_FixVersionHint}">
|
||||||
<ui:SettingsCard.HeaderIcon>
|
<ui:SettingsCard.HeaderIcon>
|
||||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.DeveloperTools}" />
|
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Repair}" />
|
||||||
</ui:SettingsCard.HeaderIcon>
|
</ui:SettingsCard.HeaderIcon>
|
||||||
<ComboBox x:Name="UpdatePackageArchitectureSelector"
|
<Button x:Name="FixVersionButton" Content="{i18n:I18n Key=Btn_FixVersion}"
|
||||||
SelectionChanged="UpdatePackageArchitectureSelector_SelectionChanged">
|
Click="FixVersionButton_Click" />
|
||||||
<ComboBoxItem Content="{i18n:I18n Key=Update_PackageArch_X86}" Tag="X86" />
|
</ui:SettingsCard>
|
||||||
<ComboBoxItem Content="{i18n:I18n Key=Update_PackageArch_X64}" Tag="X64" />
|
</ikw:SimpleStackPanel>
|
||||||
</ComboBox>
|
</TabItem>
|
||||||
</ui:SettingsCard>
|
|
||||||
|
|
||||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
|
<!-- 历史版本 -->
|
||||||
Text="手动操作" />
|
<TabItem x:Name="HistoryTabItem" Header="历史版本">
|
||||||
|
<ikw:SimpleStackPanel Margin="0,8,0,0" Spacing="12">
|
||||||
|
<ui:SettingsCard Header="选择历史版本"
|
||||||
|
Description="选择一个旧版本进行回滚(将下载并安装该版本)。">
|
||||||
|
<ui:SettingsCard.HeaderIcon>
|
||||||
|
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.History}" />
|
||||||
|
</ui:SettingsCard.HeaderIcon>
|
||||||
|
<ComboBox x:Name="VersionComboBox"
|
||||||
|
MinWidth="220"
|
||||||
|
DisplayMemberPath="Version"
|
||||||
|
SelectionChanged="VersionComboBox_SelectionChanged" />
|
||||||
|
</ui:SettingsCard>
|
||||||
|
|
||||||
<ui:SettingsCard Header="{i18n:I18n Key=Btn_ManualUpdate}"
|
<Border Padding="16"
|
||||||
Description="{i18n:I18n Key=Startup_ManualUpdateHint}">
|
CornerRadius="8"
|
||||||
<ui:SettingsCard.HeaderIcon>
|
MinHeight="200"
|
||||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Search}" />
|
Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}">
|
||||||
</ui:SettingsCard.HeaderIcon>
|
<ScrollViewer x:Name="ReleaseNotesScrollViewer"
|
||||||
<Button x:Name="ManualUpdateButton" Content="{i18n:I18n Key=Btn_ManualUpdate}"
|
VerticalScrollBarVisibility="Auto"
|
||||||
Click="ManualUpdateButton_Click" />
|
HorizontalScrollBarVisibility="Disabled"
|
||||||
</ui:SettingsCard>
|
PanningMode="VerticalOnly"
|
||||||
|
MaxHeight="350">
|
||||||
|
<mdxam:MarkdownScrollViewer x:Name="ReleaseNotesViewer"
|
||||||
|
MarkdownStyleName="GithubLike"
|
||||||
|
IsHitTestVisible="False" />
|
||||||
|
</ScrollViewer>
|
||||||
|
</Border>
|
||||||
|
|
||||||
<ui:SettingsCard Header="{i18n:I18n Key=Btn_FixVersion}"
|
<Button x:Name="RollbackButton"
|
||||||
Description="{i18n:I18n Key=Startup_FixVersionHint}">
|
HorizontalAlignment="Left"
|
||||||
<ui:SettingsCard.HeaderIcon>
|
IsEnabled="False"
|
||||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Repair}" />
|
Style="{DynamicResource AccentButtonStyle}"
|
||||||
</ui:SettingsCard.HeaderIcon>
|
Click="RollbackButton_Click">
|
||||||
<Button x:Name="FixVersionButton" Content="{i18n:I18n Key=Btn_FixVersion}"
|
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="6">
|
||||||
Click="FixVersionButton_Click" />
|
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Undo}" FontSize="14" />
|
||||||
</ui:SettingsCard>
|
<TextBlock Text="回滚到此版本" />
|
||||||
|
</ikw:SimpleStackPanel>
|
||||||
|
</Button>
|
||||||
|
|
||||||
<ui:SettingsCard Header="{i18n:I18n Key=Btn_HistoryRollback}"
|
<Border x:Name="RollbackProgressPanel"
|
||||||
Description="{i18n:I18n Key=Startup_HistoryRollbackHint}">
|
Visibility="Collapsed"
|
||||||
<ui:SettingsCard.HeaderIcon>
|
Padding="12"
|
||||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.History}" />
|
CornerRadius="8"
|
||||||
</ui:SettingsCard.HeaderIcon>
|
Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}"
|
||||||
<Button Content="{i18n:I18n Key=Btn_HistoryRollback}"
|
BorderBrush="{DynamicResource SettingsPageBorderBrush}"
|
||||||
Click="HistoryRollbackButton_Click" />
|
BorderThickness="1">
|
||||||
</ui:SettingsCard>
|
<ikw:SimpleStackPanel Spacing="6">
|
||||||
|
<TextBlock x:Name="RollbackProgressText" Text="正在准备下载..." FontSize="13" />
|
||||||
|
<ProgressBar x:Name="RollbackProgressBar"
|
||||||
|
Style="{DynamicResource DefaultProgressBarStyle}"
|
||||||
|
Height="6"
|
||||||
|
Minimum="0" Maximum="100" Value="0" />
|
||||||
|
</ikw:SimpleStackPanel>
|
||||||
|
</Border>
|
||||||
|
</ikw:SimpleStackPanel>
|
||||||
|
</TabItem>
|
||||||
|
</TabControl>
|
||||||
|
|
||||||
<Rectangle Height="48" />
|
<Rectangle Height="48" />
|
||||||
|
|
||||||
</ikw:SimpleStackPanel>
|
</ikw:SimpleStackPanel>
|
||||||
</Grid>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</ui:Page>
|
</ui:Page>
|
||||||
@@ -1,9 +1,15 @@
|
|||||||
using Ink_Canvas.Helpers;
|
using Ink_Canvas.Helpers;
|
||||||
using Ink_Canvas.Windows.SettingsViews.Helpers;
|
using Ink_Canvas.Windows.SettingsViews.Helpers;
|
||||||
|
using iNKORE.UI.WPF.Modern.Common.IconKeys;
|
||||||
|
using iNKORE.UI.WPF.Modern.Controls;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Media;
|
||||||
using System.Windows.Threading;
|
using System.Windows.Threading;
|
||||||
using MessageBox = iNKORE.UI.WPF.Modern.Controls.MessageBox;
|
using MessageBox = iNKORE.UI.WPF.Modern.Controls.MessageBox;
|
||||||
|
|
||||||
@@ -11,9 +17,35 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
|
|||||||
{
|
{
|
||||||
public partial class UpdatePage : iNKORE.UI.WPF.Modern.Controls.Page
|
public partial class UpdatePage : iNKORE.UI.WPF.Modern.Controls.Page
|
||||||
{
|
{
|
||||||
private bool _isLoaded = false;
|
private enum UpdateUiState
|
||||||
private bool _isChangingUpdateChannelInternally = false;
|
{
|
||||||
private bool _isChangingUpdatePackageArchInternally = false;
|
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()
|
public UpdatePage()
|
||||||
{
|
{
|
||||||
@@ -21,12 +53,52 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
|
|||||||
Loaded += UpdatePage_Loaded;
|
Loaded += UpdatePage_Loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdatePage_Loaded(object sender, RoutedEventArgs e)
|
private async void UpdatePage_Loaded(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
LoadSettings();
|
LoadSettings();
|
||||||
_isLoaded = true;
|
_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()
|
private void LoadSettings()
|
||||||
{
|
{
|
||||||
_isLoaded = false;
|
_isLoaded = false;
|
||||||
@@ -82,6 +154,8 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
|
|||||||
_isLoaded = true;
|
_isLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region 自动更新事件处理
|
#region 自动更新事件处理
|
||||||
|
|
||||||
private void ToggleSwitchIsAutoUpdate_Toggled(object sender, RoutedEventArgs e)
|
private void ToggleSwitchIsAutoUpdate_Toggled(object sender, RoutedEventArgs e)
|
||||||
@@ -113,8 +187,7 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
bool newState = CardSilentUpdate.IsOn;
|
SettingsManager.Settings.Startup.IsAutoUpdateWithSilence = CardSilentUpdate.IsOn;
|
||||||
SettingsManager.Settings.Startup.IsAutoUpdateWithSilence = newState;
|
|
||||||
SettingsManager.SaveSettingsToFile();
|
SettingsManager.SaveSettingsToFile();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -197,7 +270,7 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
|
|||||||
if (isTestChannel && !SettingsManager.Settings.Startup.HasAcceptedTelemetryPrivacy)
|
if (isTestChannel && !SettingsManager.Settings.Startup.HasAcceptedTelemetryPrivacy)
|
||||||
{
|
{
|
||||||
MessageBox.Show(
|
MessageBox.Show(
|
||||||
"加入预览 / 测试通道前,请先在关于页面勾选\u201C我已阅读并同意 privacy 中的隐私说明\u201D。",
|
"加入预览 / 测试通道前,请先在关于页面勾选“我已阅读并同意 privacy 中的隐私说明”。",
|
||||||
"需要同意隐私说明",
|
"需要同意隐私说明",
|
||||||
MessageBoxButton.OK,
|
MessageBoxButton.OK,
|
||||||
MessageBoxImage.Warning);
|
MessageBoxImage.Warning);
|
||||||
@@ -238,6 +311,13 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
|
|||||||
LogHelper.WriteLogToFile($"Settings | Update channel changed to {SettingsManager.Settings.Startup.UpdateChannel}");
|
LogHelper.WriteLogToFile($"Settings | Update channel changed to {SettingsManager.Settings.Startup.UpdateChannel}");
|
||||||
SettingsManager.SaveSettingsToFile();
|
SettingsManager.SaveSettingsToFile();
|
||||||
|
|
||||||
|
// 通道切换后强制刷新更新日志和历史版本缓存
|
||||||
|
_isHistoryLoaded = false;
|
||||||
|
_versionList.Clear();
|
||||||
|
VersionComboBox.ItemsSource = null;
|
||||||
|
ReleaseNotesViewer.Markdown = "";
|
||||||
|
await LoadChangelogAsync();
|
||||||
|
|
||||||
if (SettingsManager.Settings.Startup.IsAutoUpdate)
|
if (SettingsManager.Settings.Startup.IsAutoUpdate)
|
||||||
{
|
{
|
||||||
LogHelper.WriteLogToFile($"AutoUpdate | Channel changed to {newChannel}, performing immediate update check");
|
LogHelper.WriteLogToFile($"AutoUpdate | Channel changed to {newChannel}, performing immediate update check");
|
||||||
@@ -262,10 +342,6 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
LogHelper.WriteLogToFile($"AutoUpdate | Channel changed to {newChannel}, but auto-update is disabled");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RevertChannelSelection(UpdateChannel targetChannel)
|
private void RevertChannelSelection(UpdateChannel targetChannel)
|
||||||
@@ -295,124 +371,520 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 手动操作事件处理
|
#region 更新状态机
|
||||||
|
|
||||||
private async void ManualUpdateButton_Click(object sender, RoutedEventArgs e)
|
private void ApplyState(UpdateUiState state, string customSubtitle = null)
|
||||||
{
|
{
|
||||||
ManualUpdateButton.IsEnabled = false;
|
_state = state;
|
||||||
ManualUpdateButton.Content = "正在检查更新...";
|
|
||||||
|
|
||||||
|
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 releases = await AutoUpdateHelper.GetAllGithubReleases(SettingsManager.Settings.Startup.UpdateChannel);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogHelper.WriteLogToFile($"UpdatePage | GitHub API 获取更新日志失败,回退到镜像源: {ex.Message}", LogHelper.LogType.Warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 回退到镜像源
|
||||||
|
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
|
try
|
||||||
{
|
{
|
||||||
LogHelper.WriteLogToFile("ManualUpdate | Manual update button clicked");
|
LogHelper.WriteLogToFile("ManualUpdate | Manual update button clicked");
|
||||||
|
|
||||||
var (remoteVersion, lineGroup, apiReleaseNotes) = await AutoUpdateHelper.CheckForUpdates(SettingsManager.Settings.Startup.UpdateChannel, true, false);
|
string remoteVersion = null;
|
||||||
|
string apiReleaseNotes = null;
|
||||||
|
AutoUpdateHelper.UpdateLineGroup lineGroup = null;
|
||||||
|
|
||||||
if (remoteVersion != null)
|
// 优先通过 GitHub Releases API 获取最新版本
|
||||||
|
try
|
||||||
{
|
{
|
||||||
LogHelper.WriteLogToFile($"ManualUpdate | Found new version: {remoteVersion}");
|
var releases = await AutoUpdateHelper.GetAllGithubReleases(SettingsManager.Settings.Startup.UpdateChannel);
|
||||||
|
var latest = releases?
|
||||||
string currentVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
.OrderByDescending(r => ParseVersionForSort(r.version))
|
||||||
|
.Select(r => Tuple.Create(r.version, r.downloadUrl, r.releaseNotes))
|
||||||
HasNewUpdateWindow updateWindow = new HasNewUpdateWindow(currentVersion, remoteVersion, "", apiReleaseNotes);
|
.FirstOrDefault();
|
||||||
updateWindow.Owner = Application.Current.MainWindow;
|
if (latest != null && !string.IsNullOrEmpty(latest.Item1))
|
||||||
bool? dialogResult = updateWindow.ShowDialog();
|
|
||||||
|
|
||||||
if (dialogResult != true)
|
|
||||||
{
|
{
|
||||||
LogHelper.WriteLogToFile("ManualUpdate | Update dialog closed without selection");
|
var localVersion = new Version(GetCurrentVersion());
|
||||||
return;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
var mainWindow = Application.Current.MainWindow as MainWindow;
|
// 回退:调用统一的 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;
|
||||||
|
}
|
||||||
|
|
||||||
switch (updateWindow.Result)
|
if (!string.IsNullOrEmpty(remoteVersion))
|
||||||
{
|
{
|
||||||
case HasNewUpdateWindow.UpdateResult.UpdateNow:
|
_remoteVersion = remoteVersion;
|
||||||
LogHelper.WriteLogToFile("ManualUpdate | User chose to update now");
|
_remoteLineGroup = lineGroup;
|
||||||
MessageBox.Show("开始下载更新,请稍候...", "正在更新", MessageBoxButton.OK, MessageBoxImage.Information);
|
_remoteReleaseNotes = apiReleaseNotes;
|
||||||
|
|
||||||
bool isDownloadSuccessful = mainWindow != null
|
if (!string.IsNullOrEmpty(apiReleaseNotes))
|
||||||
&& await mainWindow.DownloadUpdateWithFallback(remoteVersion, lineGroup, SettingsManager.Settings.Startup.UpdateChannel);
|
ChangelogViewer.Markdown = apiReleaseNotes;
|
||||||
|
|
||||||
if (isDownloadSuccessful)
|
ApplyState(UpdateUiState.UpdateAvailable);
|
||||||
{
|
|
||||||
MessageBoxResult result = MessageBox.Show("更新已下载完成,点击确定后将关闭软件并安装新版本!", "安装更新", MessageBoxButton.OKCancel, MessageBoxImage.Information);
|
|
||||||
|
|
||||||
if (result == MessageBoxResult.OK)
|
|
||||||
{
|
|
||||||
App.IsAppExitByUser = true;
|
|
||||||
AutoUpdateHelper.InstallNewVersionApp(remoteVersion, true);
|
|
||||||
Application.Current.Shutdown();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogHelper.WriteLogToFile("ManualUpdate | User cancelled update installation");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MessageBox.Show("更新下载失败,请检查网络连接后重试。", "下载失败", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case HasNewUpdateWindow.UpdateResult.UpdateLater:
|
|
||||||
LogHelper.WriteLogToFile("ManualUpdate | User chose to update later");
|
|
||||||
|
|
||||||
isDownloadSuccessful = mainWindow != null
|
|
||||||
&& await mainWindow.DownloadUpdateWithFallback(remoteVersion, lineGroup, SettingsManager.Settings.Startup.UpdateChannel);
|
|
||||||
|
|
||||||
if (isDownloadSuccessful)
|
|
||||||
{
|
|
||||||
LogHelper.WriteLogToFile("ManualUpdate | Update downloaded successfully, will install when application closes");
|
|
||||||
SettingsManager.Settings.Startup.IsAutoUpdate = true;
|
|
||||||
SettingsManager.Settings.Startup.IsAutoUpdateWithSilence = true;
|
|
||||||
|
|
||||||
if (mainWindow != null)
|
|
||||||
mainWindow.StartSilentUpdateTimer();
|
|
||||||
|
|
||||||
MessageBox.Show("更新已下载完成,将在软件关闭时自动安装。", "更新已准备就绪", MessageBoxButton.OK, MessageBoxImage.Information);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogHelper.WriteLogToFile("ManualUpdate | Update download failed", LogHelper.LogType.Error);
|
|
||||||
MessageBox.Show("更新下载失败,请检查网络连接后重试。", "下载失败", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case HasNewUpdateWindow.UpdateResult.SkipVersion:
|
|
||||||
LogHelper.WriteLogToFile($"ManualUpdate | User chose to skip version {remoteVersion}");
|
|
||||||
SettingsManager.Settings.Startup.SkippedVersion = remoteVersion;
|
|
||||||
SettingsManager.SaveSettingsToFile();
|
|
||||||
MessageBox.Show($"已设置跳过版本 {remoteVersion},在下次发布新版本之前不会再提示更新。",
|
|
||||||
"已跳过此版本",
|
|
||||||
MessageBoxButton.OK,
|
|
||||||
MessageBoxImage.Information);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LogHelper.WriteLogToFile("ManualUpdate | No updates available");
|
ApplyState(UpdateUiState.Idle);
|
||||||
MessageBox.Show("当前已是最新版本!", "无可用更新", MessageBoxButton.OK, MessageBoxImage.Information);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
LogHelper.WriteLogToFile($"Error in ManualUpdateButton_Click: {ex.Message}", LogHelper.LogType.Error);
|
LogHelper.WriteLogToFile($"Error in CheckUpdateButton_Click: {ex.Message}", LogHelper.LogType.Error);
|
||||||
MessageBox.Show(
|
ApplyState(UpdateUiState.NetworkError, ex.Message);
|
||||||
$"手动更新过程中发生错误: {ex.Message}",
|
|
||||||
"更新错误",
|
|
||||||
MessageBoxButton.OK,
|
|
||||||
MessageBoxImage.Error);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
ManualUpdateButton.IsEnabled = true;
|
|
||||||
ManualUpdateButton.Content = "手动更新";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
private async void FixVersionButton_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var confirm = MessageBox.Show(
|
var confirm = MessageBox.Show(
|
||||||
@@ -421,49 +893,39 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
|
|||||||
MessageBoxButton.YesNo,
|
MessageBoxButton.YesNo,
|
||||||
MessageBoxImage.Question);
|
MessageBoxImage.Question);
|
||||||
|
|
||||||
if (confirm == MessageBoxResult.Yes)
|
if (confirm != MessageBoxResult.Yes) return;
|
||||||
|
|
||||||
|
FixVersionButton.IsEnabled = false;
|
||||||
|
FixVersionButton.Content = "正在修复...";
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
FixVersionButton.IsEnabled = false;
|
bool result = await AutoUpdateHelper.FixVersion(SettingsManager.Settings.Startup.UpdateChannel);
|
||||||
FixVersionButton.Content = "正在修复...";
|
if (!result)
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
bool result = await AutoUpdateHelper.FixVersion(SettingsManager.Settings.Startup.UpdateChannel);
|
|
||||||
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
MessageBox.Show(
|
|
||||||
"版本修复失败,可能是网络问题或当前已是最新版本。",
|
|
||||||
"修复失败",
|
|
||||||
MessageBoxButton.OK,
|
|
||||||
MessageBoxImage.Error);
|
|
||||||
|
|
||||||
FixVersionButton.IsEnabled = true;
|
|
||||||
FixVersionButton.Content = "版本修复";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LogHelper.WriteLogToFile($"Error in FixVersionButton_Click: {ex.Message}", LogHelper.LogType.Error);
|
|
||||||
MessageBox.Show(
|
MessageBox.Show(
|
||||||
$"版本修复过程中发生错误: {ex.Message}",
|
"版本修复失败,可能是网络问题或当前已是最新版本。",
|
||||||
"修复错误",
|
"修复失败",
|
||||||
MessageBoxButton.OK,
|
MessageBoxButton.OK,
|
||||||
MessageBoxImage.Error);
|
MessageBoxImage.Error);
|
||||||
|
|
||||||
FixVersionButton.IsEnabled = true;
|
|
||||||
FixVersionButton.Content = "版本修复";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
catch (Exception ex)
|
||||||
|
{
|
||||||
private void HistoryRollbackButton_Click(object sender, RoutedEventArgs e)
|
LogHelper.WriteLogToFile($"Error in FixVersionButton_Click: {ex.Message}", LogHelper.LogType.Error);
|
||||||
{
|
MessageBox.Show(
|
||||||
var win = new HistoryRollbackWindow(SettingsManager.Settings.Startup.UpdateChannel);
|
$"版本修复过程中发生错误: {ex.Message}",
|
||||||
win.Owner = Application.Current.MainWindow;
|
"修复错误",
|
||||||
win.ShowDialog();
|
MessageBoxButton.OK,
|
||||||
|
MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
FixVersionButton.IsEnabled = true;
|
||||||
|
FixVersionButton.Content = "版本修复";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user