add:更新面板
This commit is contained in:
@@ -10,140 +10,308 @@
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
||||
xmlns:controls="clr-namespace:Ink_Canvas.Controls;assembly=InkCanvas.Controls"
|
||||
xmlns:c="clr-namespace:Ink_Canvas.Converter"
|
||||
xmlns:mdxam="clr-namespace:MdXaml;assembly=MdXaml"
|
||||
mc:Ignorable="d"
|
||||
|
||||
Title="更新">
|
||||
|
||||
<ScrollViewer PanningMode="VerticalFirst">
|
||||
<Grid Margin="59,0,59,0">
|
||||
<FrameworkElement.Resources>
|
||||
<sys:Double x:Key="SettingsCardSpacing">4</sys:Double>
|
||||
<c:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
|
||||
<Style x:Key="SettingsSectionHeaderTextBlockStyle"
|
||||
BasedOn="{StaticResource BodyStrongTextBlockStyle}"
|
||||
TargetType="TextBlock">
|
||||
<Style.Setters>
|
||||
<sys:Double x:Key="SettingsCardSpacing">4</sys:Double>
|
||||
<c:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
|
||||
<Style x:Key="SettingsSectionHeaderTextBlockStyle"
|
||||
BasedOn="{StaticResource BodyStrongTextBlockStyle}"
|
||||
TargetType="TextBlock">
|
||||
<Setter Property="Margin" Value="1,30,0,6" />
|
||||
</Style.Setters>
|
||||
</Style>
|
||||
</FrameworkElement.Resources>
|
||||
<Grid>
|
||||
<ikw:SimpleStackPanel MaxWidth="1000"
|
||||
HorizontalAlignment="Stretch"
|
||||
Spacing="{StaticResource SettingsCardSpacing}">
|
||||
</Style>
|
||||
</FrameworkElement.Resources>
|
||||
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
|
||||
Text="自动更新" />
|
||||
<ikw:SimpleStackPanel MaxWidth="1000"
|
||||
HorizontalAlignment="Stretch"
|
||||
Spacing="{StaticResource SettingsCardSpacing}">
|
||||
|
||||
<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" />
|
||||
<!-- 状态横幅 -->
|
||||
<Border Margin="0,16,0,0"
|
||||
Padding="20"
|
||||
CornerRadius="8"
|
||||
Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<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" />
|
||||
<Border Grid.Column="0"
|
||||
Width="48" Height="48"
|
||||
CornerRadius="24"
|
||||
VerticalAlignment="Top"
|
||||
Margin="0,0,16,0"
|
||||
Background="{DynamicResource SystemControlHighlightAccentBrush}">
|
||||
<ui:FontIcon x:Name="StatusIcon"
|
||||
FontSize="22"
|
||||
Foreground="White"
|
||||
Icon="{x:Static ui:SegoeFluentIcons.Completed}"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</Border>
|
||||
|
||||
<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 Grid.Column="1" Spacing="4">
|
||||
<TextBlock x:Name="StatusTitle"
|
||||
FontSize="22" FontWeight="SemiBold"
|
||||
Text="正在加载更新状态..." />
|
||||
<TextBlock x:Name="StatusSubtitle"
|
||||
Opacity="0.75"
|
||||
TextWrapping="Wrap"
|
||||
Text="" />
|
||||
|
||||
<!-- 进度区 -->
|
||||
<ikw:SimpleStackPanel x:Name="ProgressPanel"
|
||||
Visibility="Collapsed"
|
||||
Margin="0,8,0,0"
|
||||
Spacing="4">
|
||||
<TextBlock x:Name="ProgressText" FontSize="13" Opacity="0.8" />
|
||||
<ProgressBar x:Name="ProgressBar"
|
||||
Style="{DynamicResource DefaultProgressBarStyle}"
|
||||
Height="6"
|
||||
Minimum="0" Maximum="100"
|
||||
Value="0" />
|
||||
</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>
|
||||
</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 Header="{i18n:I18n Key=Startup_TimePeriodHint}" />
|
||||
</ui:SettingsExpander.Items>
|
||||
</ui:SettingsExpander>
|
||||
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
|
||||
Text="更新通道" />
|
||||
<ui:SettingsCard Header="{i18n:I18n Key=Startup_UpdatePackageArchitecture}"
|
||||
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}"
|
||||
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>
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
|
||||
Text="维护" />
|
||||
|
||||
<ui:SettingsCard Header="{i18n:I18n Key=Startup_UpdatePackageArchitecture}"
|
||||
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=Btn_FixVersion}"
|
||||
Description="{i18n:I18n Key=Startup_FixVersionHint}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Repair}" />
|
||||
</ui:SettingsCard.HeaderIcon>
|
||||
<Button x:Name="FixVersionButton" Content="{i18n:I18n Key=Btn_FixVersion}"
|
||||
Click="FixVersionButton_Click" />
|
||||
</ui:SettingsCard>
|
||||
</ikw:SimpleStackPanel>
|
||||
</TabItem>
|
||||
|
||||
<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}"
|
||||
Description="{i18n:I18n Key=Startup_ManualUpdateHint}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Search}" />
|
||||
</ui:SettingsCard.HeaderIcon>
|
||||
<Button x:Name="ManualUpdateButton" Content="{i18n:I18n Key=Btn_ManualUpdate}"
|
||||
Click="ManualUpdateButton_Click" />
|
||||
</ui:SettingsCard>
|
||||
<Border Padding="16"
|
||||
CornerRadius="8"
|
||||
MinHeight="200"
|
||||
Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}">
|
||||
<ScrollViewer x:Name="ReleaseNotesScrollViewer"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
PanningMode="VerticalOnly"
|
||||
MaxHeight="350">
|
||||
<mdxam:MarkdownScrollViewer x:Name="ReleaseNotesViewer"
|
||||
MarkdownStyleName="GithubLike"
|
||||
IsHitTestVisible="False" />
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
|
||||
<ui:SettingsCard Header="{i18n:I18n Key=Btn_FixVersion}"
|
||||
Description="{i18n:I18n Key=Startup_FixVersionHint}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Repair}" />
|
||||
</ui:SettingsCard.HeaderIcon>
|
||||
<Button x:Name="FixVersionButton" Content="{i18n:I18n Key=Btn_FixVersion}"
|
||||
Click="FixVersionButton_Click" />
|
||||
</ui:SettingsCard>
|
||||
<Button x:Name="RollbackButton"
|
||||
HorizontalAlignment="Left"
|
||||
IsEnabled="False"
|
||||
Style="{DynamicResource AccentButtonStyle}"
|
||||
Click="RollbackButton_Click">
|
||||
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Undo}" FontSize="14" />
|
||||
<TextBlock Text="回滚到此版本" />
|
||||
</ikw:SimpleStackPanel>
|
||||
</Button>
|
||||
|
||||
<ui:SettingsCard Header="{i18n:I18n Key=Btn_HistoryRollback}"
|
||||
Description="{i18n:I18n Key=Startup_HistoryRollbackHint}">
|
||||
<ui:SettingsCard.HeaderIcon>
|
||||
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.History}" />
|
||||
</ui:SettingsCard.HeaderIcon>
|
||||
<Button Content="{i18n:I18n Key=Btn_HistoryRollback}"
|
||||
Click="HistoryRollbackButton_Click" />
|
||||
</ui:SettingsCard>
|
||||
<Border x:Name="RollbackProgressPanel"
|
||||
Visibility="Collapsed"
|
||||
Padding="12"
|
||||
CornerRadius="8"
|
||||
Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}"
|
||||
BorderBrush="{DynamicResource SettingsPageBorderBrush}"
|
||||
BorderThickness="1">
|
||||
<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>
|
||||
</Grid>
|
||||
</ikw:SimpleStackPanel>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</ui:Page>
|
||||
</ui:Page>
|
||||
@@ -1,9 +1,15 @@
|
||||
using Ink_Canvas.Helpers;
|
||||
using Ink_Canvas.Windows.SettingsViews.Helpers;
|
||||
using iNKORE.UI.WPF.Modern.Common.IconKeys;
|
||||
using iNKORE.UI.WPF.Modern.Controls;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Threading;
|
||||
using MessageBox = iNKORE.UI.WPF.Modern.Controls.MessageBox;
|
||||
|
||||
@@ -11,9 +17,35 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
|
||||
{
|
||||
public partial class UpdatePage : iNKORE.UI.WPF.Modern.Controls.Page
|
||||
{
|
||||
private bool _isLoaded = false;
|
||||
private bool _isChangingUpdateChannelInternally = false;
|
||||
private bool _isChangingUpdatePackageArchInternally = false;
|
||||
private enum UpdateUiState
|
||||
{
|
||||
Idle,
|
||||
Checking,
|
||||
UpdateAvailable,
|
||||
Downloading,
|
||||
Downloaded,
|
||||
NetworkError
|
||||
}
|
||||
|
||||
private class VersionItem
|
||||
{
|
||||
public string Version { get; set; }
|
||||
public string DownloadUrl { get; set; }
|
||||
public string ReleaseNotes { get; set; }
|
||||
}
|
||||
|
||||
private bool _isLoaded;
|
||||
private bool _isChangingUpdateChannelInternally;
|
||||
private bool _isChangingUpdatePackageArchInternally;
|
||||
|
||||
private UpdateUiState _state = UpdateUiState.Idle;
|
||||
private string _remoteVersion;
|
||||
private AutoUpdateHelper.UpdateLineGroup _remoteLineGroup;
|
||||
private string _remoteReleaseNotes;
|
||||
|
||||
private List<VersionItem> _versionList = new List<VersionItem>();
|
||||
private VersionItem _selectedHistoricalItem;
|
||||
private bool _isHistoryLoaded;
|
||||
|
||||
public UpdatePage()
|
||||
{
|
||||
@@ -21,12 +53,52 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
|
||||
Loaded += UpdatePage_Loaded;
|
||||
}
|
||||
|
||||
private void UpdatePage_Loaded(object sender, RoutedEventArgs e)
|
||||
private async void UpdatePage_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
LoadSettings();
|
||||
_isLoaded = true;
|
||||
|
||||
// 复用启动时自动检查的结果,避免二次检查
|
||||
var mainWindow = Application.Current.MainWindow as MainWindow;
|
||||
if (mainWindow != null && !string.IsNullOrEmpty(mainWindow.AvailableLatestVersion))
|
||||
{
|
||||
_remoteVersion = mainWindow.AvailableLatestVersion;
|
||||
_remoteLineGroup = mainWindow.AvailableLatestLineGroup;
|
||||
_remoteReleaseNotes = mainWindow.AvailableLatestReleaseNotes;
|
||||
|
||||
try
|
||||
{
|
||||
var statusFile = AutoUpdateHelper.GetUpdateDownloadStatusFilePath(_remoteVersion);
|
||||
if (System.IO.File.Exists(statusFile) &&
|
||||
System.IO.File.ReadAllText(statusFile).Trim().ToLower() == "true")
|
||||
{
|
||||
ApplyState(UpdateUiState.Downloaded);
|
||||
}
|
||||
else
|
||||
{
|
||||
ApplyState(UpdateUiState.UpdateAvailable);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
ApplyState(UpdateUiState.UpdateAvailable);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(_remoteReleaseNotes))
|
||||
ChangelogViewer.Markdown = _remoteReleaseNotes;
|
||||
else
|
||||
ChangelogViewer.Markdown = "切换到 *历史版本* 或点击 *检查更新* 查看具体更新日志。";
|
||||
return;
|
||||
}
|
||||
|
||||
// 没有缓存的检查结果时,仅展示空白;不主动联网,避免打开页面就触发请求
|
||||
ApplyState(UpdateUiState.Idle);
|
||||
ChangelogViewer.Markdown = "点击 *检查更新* 来获取最新版本及更新日志。";
|
||||
await System.Threading.Tasks.Task.CompletedTask;
|
||||
}
|
||||
|
||||
#region 更新设置加载
|
||||
|
||||
private void LoadSettings()
|
||||
{
|
||||
_isLoaded = false;
|
||||
@@ -82,6 +154,8 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
|
||||
_isLoaded = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 自动更新事件处理
|
||||
|
||||
private void ToggleSwitchIsAutoUpdate_Toggled(object sender, RoutedEventArgs e)
|
||||
@@ -113,8 +187,7 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
|
||||
|
||||
try
|
||||
{
|
||||
bool newState = CardSilentUpdate.IsOn;
|
||||
SettingsManager.Settings.Startup.IsAutoUpdateWithSilence = newState;
|
||||
SettingsManager.Settings.Startup.IsAutoUpdateWithSilence = CardSilentUpdate.IsOn;
|
||||
SettingsManager.SaveSettingsToFile();
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -197,7 +270,7 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
|
||||
if (isTestChannel && !SettingsManager.Settings.Startup.HasAcceptedTelemetryPrivacy)
|
||||
{
|
||||
MessageBox.Show(
|
||||
"加入预览 / 测试通道前,请先在关于页面勾选\u201C我已阅读并同意 privacy 中的隐私说明\u201D。",
|
||||
"加入预览 / 测试通道前,请先在关于页面勾选“我已阅读并同意 privacy 中的隐私说明”。",
|
||||
"需要同意隐私说明",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Warning);
|
||||
@@ -238,6 +311,13 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
|
||||
LogHelper.WriteLogToFile($"Settings | Update channel changed to {SettingsManager.Settings.Startup.UpdateChannel}");
|
||||
SettingsManager.SaveSettingsToFile();
|
||||
|
||||
// 通道切换后强制刷新更新日志和历史版本缓存
|
||||
_isHistoryLoaded = false;
|
||||
_versionList.Clear();
|
||||
VersionComboBox.ItemsSource = null;
|
||||
ReleaseNotesViewer.Markdown = "";
|
||||
await LoadChangelogAsync();
|
||||
|
||||
if (SettingsManager.Settings.Startup.IsAutoUpdate)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"AutoUpdate | Channel changed to {newChannel}, performing immediate update check");
|
||||
@@ -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)
|
||||
@@ -295,124 +371,520 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
|
||||
|
||||
#endregion
|
||||
|
||||
#region 手动操作事件处理
|
||||
#region 更新状态机
|
||||
|
||||
private async void ManualUpdateButton_Click(object sender, RoutedEventArgs e)
|
||||
private void ApplyState(UpdateUiState state, string customSubtitle = null)
|
||||
{
|
||||
ManualUpdateButton.IsEnabled = false;
|
||||
ManualUpdateButton.Content = "正在检查更新...";
|
||||
_state = state;
|
||||
|
||||
CheckUpdateButton.Visibility = Visibility.Collapsed;
|
||||
UpdateNowButton.Visibility = Visibility.Collapsed;
|
||||
UpdateLaterButton.Visibility = Visibility.Collapsed;
|
||||
SkipVersionButton.Visibility = Visibility.Collapsed;
|
||||
CancelDownloadButton.Visibility = Visibility.Collapsed;
|
||||
ProgressPanel.Visibility = Visibility.Collapsed;
|
||||
|
||||
CheckUpdateButton.IsEnabled = true;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case UpdateUiState.Idle:
|
||||
StatusIcon.Icon = SegoeFluentIcons.Completed;
|
||||
StatusTitle.Text = "已是最新版本";
|
||||
StatusSubtitle.Text = customSubtitle ?? BuildLastCheckSubtitle();
|
||||
CheckUpdateButton.Visibility = Visibility.Visible;
|
||||
break;
|
||||
|
||||
case UpdateUiState.Checking:
|
||||
StatusIcon.Icon = SegoeFluentIcons.Sync;
|
||||
StatusTitle.Text = "正在检查更新...";
|
||||
StatusSubtitle.Text = "";
|
||||
CheckUpdateButton.Visibility = Visibility.Visible;
|
||||
CheckUpdateButton.IsEnabled = false;
|
||||
ProgressPanel.Visibility = Visibility.Visible;
|
||||
ProgressText.Text = "正在连接更新服务器...";
|
||||
ProgressBar.IsIndeterminate = true;
|
||||
break;
|
||||
|
||||
case UpdateUiState.UpdateAvailable:
|
||||
StatusIcon.Icon = SegoeFluentIcons.Upload;
|
||||
StatusTitle.Text = $"检测到新版本 {_remoteVersion}";
|
||||
StatusSubtitle.Text = customSubtitle ?? $"当前版本 {GetCurrentVersion()} → {_remoteVersion}";
|
||||
UpdateNowButton.Visibility = Visibility.Visible;
|
||||
UpdateLaterButton.Visibility = Visibility.Visible;
|
||||
SkipVersionButton.Visibility = Visibility.Visible;
|
||||
break;
|
||||
|
||||
case UpdateUiState.Downloading:
|
||||
StatusIcon.Icon = SegoeFluentIcons.Download;
|
||||
StatusTitle.Text = "正在下载更新...";
|
||||
StatusSubtitle.Text = customSubtitle ?? $"目标版本 {_remoteVersion}";
|
||||
ProgressPanel.Visibility = Visibility.Visible;
|
||||
ProgressBar.IsIndeterminate = false;
|
||||
break;
|
||||
|
||||
case UpdateUiState.Downloaded:
|
||||
StatusIcon.Icon = SegoeFluentIcons.Download;
|
||||
StatusTitle.Text = "更新已下载完成";
|
||||
StatusSubtitle.Text = customSubtitle ?? $"将在软件关闭时自动安装 {_remoteVersion}";
|
||||
CheckUpdateButton.Visibility = Visibility.Visible;
|
||||
break;
|
||||
|
||||
case UpdateUiState.NetworkError:
|
||||
StatusIcon.Icon = SegoeFluentIcons.Error;
|
||||
StatusTitle.Text = "网络错误";
|
||||
StatusSubtitle.Text = customSubtitle ?? "请检查网络连接后重试。";
|
||||
CheckUpdateButton.Visibility = Visibility.Visible;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private string BuildLastCheckSubtitle()
|
||||
{
|
||||
string current = GetCurrentVersion();
|
||||
return $"当前版本 {current}";
|
||||
}
|
||||
|
||||
private static string GetCurrentVersion()
|
||||
{
|
||||
try
|
||||
{
|
||||
return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 更新日志
|
||||
|
||||
private async System.Threading.Tasks.Task LoadChangelogAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
ChangelogViewer.Markdown = "正在加载更新日志...";
|
||||
|
||||
// 优先尝试从 GitHub API 获取最新 Release 的 body
|
||||
try
|
||||
{
|
||||
var 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
|
||||
{
|
||||
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}");
|
||||
|
||||
string currentVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
||||
|
||||
HasNewUpdateWindow updateWindow = new HasNewUpdateWindow(currentVersion, remoteVersion, "", apiReleaseNotes);
|
||||
updateWindow.Owner = Application.Current.MainWindow;
|
||||
bool? dialogResult = updateWindow.ShowDialog();
|
||||
|
||||
if (dialogResult != true)
|
||||
var releases = await AutoUpdateHelper.GetAllGithubReleases(SettingsManager.Settings.Startup.UpdateChannel);
|
||||
var latest = releases?
|
||||
.OrderByDescending(r => ParseVersionForSort(r.version))
|
||||
.Select(r => Tuple.Create(r.version, r.downloadUrl, r.releaseNotes))
|
||||
.FirstOrDefault();
|
||||
if (latest != null && !string.IsNullOrEmpty(latest.Item1))
|
||||
{
|
||||
LogHelper.WriteLogToFile("ManualUpdate | Update dialog closed without selection");
|
||||
return;
|
||||
var localVersion = new Version(GetCurrentVersion());
|
||||
var remote = ParseVersionForSort(latest.Item1);
|
||||
if (remote > localVersion)
|
||||
{
|
||||
remoteVersion = latest.Item1.TrimStart('v', 'V');
|
||||
apiReleaseNotes = latest.Item3;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"UpdatePage | GitHub API 检查更新失败,回退到 CheckForUpdates: {ex.Message}", LogHelper.LogType.Warning);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
case HasNewUpdateWindow.UpdateResult.UpdateNow:
|
||||
LogHelper.WriteLogToFile("ManualUpdate | User chose to update now");
|
||||
MessageBox.Show("开始下载更新,请稍候...", "正在更新", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
if (!string.IsNullOrEmpty(remoteVersion))
|
||||
{
|
||||
_remoteVersion = remoteVersion;
|
||||
_remoteLineGroup = lineGroup;
|
||||
_remoteReleaseNotes = apiReleaseNotes;
|
||||
|
||||
bool isDownloadSuccessful = mainWindow != null
|
||||
&& await mainWindow.DownloadUpdateWithFallback(remoteVersion, lineGroup, SettingsManager.Settings.Startup.UpdateChannel);
|
||||
if (!string.IsNullOrEmpty(apiReleaseNotes))
|
||||
ChangelogViewer.Markdown = apiReleaseNotes;
|
||||
|
||||
if (isDownloadSuccessful)
|
||||
{
|
||||
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;
|
||||
}
|
||||
ApplyState(UpdateUiState.UpdateAvailable);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.WriteLogToFile("ManualUpdate | No updates available");
|
||||
MessageBox.Show("当前已是最新版本!", "无可用更新", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
ApplyState(UpdateUiState.Idle);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"Error in ManualUpdateButton_Click: {ex.Message}", LogHelper.LogType.Error);
|
||||
MessageBox.Show(
|
||||
$"手动更新过程中发生错误: {ex.Message}",
|
||||
"更新错误",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ManualUpdateButton.IsEnabled = true;
|
||||
ManualUpdateButton.Content = "手动更新";
|
||||
LogHelper.WriteLogToFile($"Error in CheckUpdateButton_Click: {ex.Message}", LogHelper.LogType.Error);
|
||||
ApplyState(UpdateUiState.NetworkError, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private bool _downloadCancelled;
|
||||
private static List<AutoUpdateHelper.UpdateLineGroup> _cachedOrderedGroups;
|
||||
private static UpdateChannel _cachedGroupsChannel;
|
||||
|
||||
private static async System.Threading.Tasks.Task<List<AutoUpdateHelper.UpdateLineGroup>> GetOrderedGroupsCachedAsync(UpdateChannel channel)
|
||||
{
|
||||
if (_cachedOrderedGroups != null && _cachedOrderedGroups.Count > 0 && _cachedGroupsChannel == channel)
|
||||
return _cachedOrderedGroups;
|
||||
var groups = await AutoUpdateHelper.GetAvailableLineGroupsOrdered(channel);
|
||||
_cachedOrderedGroups = groups;
|
||||
_cachedGroupsChannel = channel;
|
||||
return groups;
|
||||
}
|
||||
|
||||
private async System.Threading.Tasks.Task<bool> DownloadWithProgressAsync()
|
||||
{
|
||||
_downloadCancelled = false;
|
||||
var groups = await GetOrderedGroupsCachedAsync(SettingsManager.Settings.Startup.UpdateChannel);
|
||||
if (groups == null || groups.Count == 0)
|
||||
{
|
||||
LogHelper.WriteLogToFile("UpdatePage | 没有可用的下载线路组", LogHelper.LogType.Error);
|
||||
return false;
|
||||
}
|
||||
return await AutoUpdateHelper.DownloadSetupFileWithFallback(_remoteVersion, groups, (percent, text) =>
|
||||
{
|
||||
if (_downloadCancelled) return;
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
if (_state != UpdateUiState.Downloading) return;
|
||||
ProgressBar.IsIndeterminate = false;
|
||||
ProgressBar.Value = percent;
|
||||
ProgressText.Text = text;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private async void UpdateNowButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_remoteVersion)) return;
|
||||
|
||||
ApplyState(UpdateUiState.Downloading);
|
||||
CancelDownloadButton.Visibility = Visibility.Visible;
|
||||
ProgressBar.Value = 0;
|
||||
ProgressText.Text = "正在准备下载...";
|
||||
|
||||
try
|
||||
{
|
||||
bool ok = await DownloadWithProgressAsync();
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
ApplyState(UpdateUiState.NetworkError, "更新下载失败,请检查网络连接后重试。");
|
||||
return;
|
||||
}
|
||||
|
||||
MessageBoxResult result = MessageBox.Show(
|
||||
"更新已下载完成,点击确定后将关闭软件并安装新版本!",
|
||||
"安装更新",
|
||||
MessageBoxButton.OKCancel,
|
||||
MessageBoxImage.Information);
|
||||
|
||||
if (result == MessageBoxResult.OK)
|
||||
{
|
||||
App.IsAppExitByUser = true;
|
||||
AutoUpdateHelper.InstallNewVersionApp(_remoteVersion, true);
|
||||
Application.Current.Shutdown();
|
||||
}
|
||||
else
|
||||
{
|
||||
ApplyState(UpdateUiState.Downloaded);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"Error in UpdateNowButton_Click: {ex.Message}", LogHelper.LogType.Error);
|
||||
ApplyState(UpdateUiState.NetworkError, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private async void UpdateLaterButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_remoteVersion)) return;
|
||||
|
||||
var mainWindow = Application.Current.MainWindow as MainWindow;
|
||||
if (mainWindow == null) return;
|
||||
|
||||
ApplyState(UpdateUiState.Downloading);
|
||||
CancelDownloadButton.Visibility = Visibility.Visible;
|
||||
ProgressBar.Value = 0;
|
||||
ProgressText.Text = "正在后台下载...";
|
||||
|
||||
try
|
||||
{
|
||||
bool ok = await DownloadWithProgressAsync();
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
ApplyState(UpdateUiState.NetworkError, "更新下载失败,请检查网络连接后重试。");
|
||||
return;
|
||||
}
|
||||
|
||||
SettingsManager.Settings.Startup.IsAutoUpdate = true;
|
||||
SettingsManager.Settings.Startup.IsAutoUpdateWithSilence = true;
|
||||
SettingsManager.SaveSettingsToFile();
|
||||
CardAutoUpdate.IsOn = true;
|
||||
CardSilentUpdate.IsOn = true;
|
||||
|
||||
mainWindow.StartSilentUpdateTimer();
|
||||
ApplyState(UpdateUiState.Downloaded);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"Error in UpdateLaterButton_Click: {ex.Message}", LogHelper.LogType.Error);
|
||||
ApplyState(UpdateUiState.NetworkError, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void SkipVersionButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_remoteVersion)) return;
|
||||
|
||||
SettingsManager.Settings.Startup.SkippedVersion = _remoteVersion;
|
||||
SettingsManager.SaveSettingsToFile();
|
||||
LogHelper.WriteLogToFile($"ManualUpdate | User chose to skip version {_remoteVersion}");
|
||||
|
||||
ApplyState(UpdateUiState.Idle, $"已跳过版本 {_remoteVersion}");
|
||||
}
|
||||
|
||||
private void CancelDownloadButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_downloadCancelled = true;
|
||||
AutoUpdateHelper.RequestCancelDownload();
|
||||
ApplyState(UpdateUiState.UpdateAvailable);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 历史版本回滚
|
||||
|
||||
private async void UpdateTabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (e.OriginalSource != UpdateTabControl) return;
|
||||
if (UpdateTabControl.SelectedItem == HistoryTabItem && !_isHistoryLoaded)
|
||||
{
|
||||
await LoadHistoryAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async void HistoryTabItem_GotFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// 兼容用户首次切到历史版本 Tab 时再加载
|
||||
if (_isHistoryLoaded) return;
|
||||
await LoadHistoryAsync();
|
||||
}
|
||||
|
||||
private async System.Threading.Tasks.Task LoadHistoryAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_isHistoryLoaded = true;
|
||||
ReleaseNotesViewer.Markdown = "正在获取历史版本...";
|
||||
RollbackButton.IsEnabled = false;
|
||||
|
||||
var releases = await AutoUpdateHelper.GetAllGithubReleases(SettingsManager.Settings.Startup.UpdateChannel);
|
||||
_versionList = releases
|
||||
.Select(r => new VersionItem { Version = r.version, DownloadUrl = r.downloadUrl, ReleaseNotes = r.releaseNotes })
|
||||
.OrderByDescending(v => ParseVersionForSort(v.Version))
|
||||
.ToList();
|
||||
VersionComboBox.ItemsSource = _versionList;
|
||||
|
||||
if (_versionList.Count > 0)
|
||||
{
|
||||
VersionComboBox.SelectedIndex = 0;
|
||||
RollbackButton.IsEnabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ReleaseNotesViewer.Markdown = "未获取到历史版本信息。";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ReleaseNotesViewer.Markdown = $"加载历史版本失败:{ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
private static Version ParseVersionForSort(string version)
|
||||
{
|
||||
var v = (version ?? "").TrimStart('v', 'V');
|
||||
return Version.TryParse(v, out var result) ? result : new Version(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
private void VersionComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
_selectedHistoricalItem = VersionComboBox.SelectedItem as VersionItem;
|
||||
if (_selectedHistoricalItem != null)
|
||||
{
|
||||
ReleaseNotesViewer.Markdown = _selectedHistoricalItem.ReleaseNotes ?? "无更新日志";
|
||||
LogHelper.WriteLogToFile($"HistoryRollback | 用户选择版本: {_selectedHistoricalItem.Version}");
|
||||
}
|
||||
Keyboard.ClearFocus();
|
||||
}
|
||||
|
||||
private async void RollbackButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_selectedHistoricalItem == null) return;
|
||||
|
||||
int days = await AskPauseDaysAsync();
|
||||
if (days < 0)
|
||||
{
|
||||
LogHelper.WriteLogToFile("HistoryRollback | 用户取消了回滚操作");
|
||||
return;
|
||||
}
|
||||
|
||||
if (days == 0)
|
||||
{
|
||||
MainWindow.Settings.Startup.AutoUpdatePauseUntilDate = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
DateTime pauseUntilDate = DateTime.Now.AddDays(days);
|
||||
MainWindow.Settings.Startup.AutoUpdatePauseUntilDate = pauseUntilDate.ToString("yyyy-MM-dd");
|
||||
LogHelper.WriteLogToFile($"HistoryRollback | 用户选择暂停自动更新 {days} 天,截止日期: {pauseUntilDate:yyyy-MM-dd}");
|
||||
}
|
||||
MainWindow.SaveSettingsToFile();
|
||||
|
||||
LogHelper.WriteLogToFile($"HistoryRollback | 用户确认回滚,目标版本: {_selectedHistoricalItem.Version}");
|
||||
RollbackButton.IsEnabled = false;
|
||||
VersionComboBox.IsEnabled = false;
|
||||
RollbackProgressPanel.Visibility = Visibility.Visible;
|
||||
RollbackProgressBar.Value = 0;
|
||||
RollbackProgressText.Text = "正在准备下载...";
|
||||
|
||||
bool downloadSuccess = false;
|
||||
try
|
||||
{
|
||||
downloadSuccess = await AutoUpdateHelper.StartManualDownloadAndInstall(
|
||||
_selectedHistoricalItem.Version,
|
||||
SettingsManager.Settings.Startup.UpdateChannel,
|
||||
(percent, text) =>
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
RollbackProgressBar.Value = percent;
|
||||
RollbackProgressText.Text = text;
|
||||
});
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
RollbackProgressText.Text = $"下载失败: {ex.Message}";
|
||||
LogHelper.WriteLogToFile($"HistoryRollback | 下载异常: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
|
||||
if (downloadSuccess)
|
||||
{
|
||||
RollbackProgressBar.Value = 100;
|
||||
RollbackProgressText.Text = "下载完成,准备安装...";
|
||||
}
|
||||
else
|
||||
{
|
||||
RollbackProgressText.Text = "下载失败,请检查网络后重试。";
|
||||
RollbackButton.IsEnabled = true;
|
||||
VersionComboBox.IsEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private async System.Threading.Tasks.Task<int> AskPauseDaysAsync()
|
||||
{
|
||||
var dialog = new ContentDialog
|
||||
{
|
||||
Title = "暂停自动更新",
|
||||
PrimaryButtonText = "确定",
|
||||
SecondaryButtonText = "取消"
|
||||
};
|
||||
|
||||
var panel = new iNKORE.UI.WPF.Controls.SimpleStackPanel
|
||||
{
|
||||
Spacing = 16,
|
||||
Margin = new Thickness(0, 10, 0, 0)
|
||||
};
|
||||
|
||||
var textBlock = new TextBlock
|
||||
{
|
||||
Text = "请选择在回滚后多久不再接收自动更新:",
|
||||
FontSize = 14
|
||||
};
|
||||
|
||||
var daysComboBox = new ComboBox
|
||||
{
|
||||
Width = 200,
|
||||
Height = 36,
|
||||
HorizontalAlignment = HorizontalAlignment.Left
|
||||
};
|
||||
for (int i = 0; i <= 7; i++)
|
||||
{
|
||||
daysComboBox.Items.Add(new ComboBoxItem { Content = $"{i} 天", Tag = i });
|
||||
}
|
||||
daysComboBox.SelectedIndex = 0;
|
||||
|
||||
panel.Children.Add(textBlock);
|
||||
panel.Children.Add(daysComboBox);
|
||||
dialog.Content = panel;
|
||||
|
||||
var result = await dialog.ShowAsync();
|
||||
if (result != ContentDialogResult.Primary) return -1;
|
||||
|
||||
if (daysComboBox.SelectedItem is ComboBoxItem cbi && cbi.Tag is int days)
|
||||
return days;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 维护
|
||||
|
||||
private async void FixVersionButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var confirm = MessageBox.Show(
|
||||
@@ -421,49 +893,39 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
|
||||
MessageBoxButton.YesNo,
|
||||
MessageBoxImage.Question);
|
||||
|
||||
if (confirm == MessageBoxResult.Yes)
|
||||
if (confirm != MessageBoxResult.Yes) return;
|
||||
|
||||
FixVersionButton.IsEnabled = false;
|
||||
FixVersionButton.Content = "正在修复...";
|
||||
|
||||
try
|
||||
{
|
||||
FixVersionButton.IsEnabled = false;
|
||||
FixVersionButton.Content = "正在修复...";
|
||||
|
||||
try
|
||||
bool result = await AutoUpdateHelper.FixVersion(SettingsManager.Settings.Startup.UpdateChannel);
|
||||
if (!result)
|
||||
{
|
||||
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(
|
||||
$"版本修复过程中发生错误: {ex.Message}",
|
||||
"修复错误",
|
||||
"版本修复失败,可能是网络问题或当前已是最新版本。",
|
||||
"修复失败",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Error);
|
||||
|
||||
FixVersionButton.IsEnabled = true;
|
||||
FixVersionButton.Content = "版本修复";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HistoryRollbackButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var win = new HistoryRollbackWindow(SettingsManager.Settings.Startup.UpdateChannel);
|
||||
win.Owner = Application.Current.MainWindow;
|
||||
win.ShowDialog();
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"Error in FixVersionButton_Click: {ex.Message}", LogHelper.LogType.Error);
|
||||
MessageBox.Show(
|
||||
$"版本修复过程中发生错误: {ex.Message}",
|
||||
"修复错误",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
FixVersionButton.IsEnabled = true;
|
||||
FixVersionButton.Content = "版本修复";
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user