Add per-core prerelease update selection

Replaces the global prerelease toggle with per-core prerelease options in both WPF and Avalonia update views, and persists selections via `CheckPreReleaseCoreTypes` in config. Update checks now resolve prerelease mode per selected core (including auto-check flow), and the sing-box max-version lock is removed to allow newer versions.
This commit is contained in:
2dust
2026-10-03 14:58:45 +08:00
parent 3666fc549d
commit f85326cf0e
10 changed files with 70 additions and 56 deletions
+1 -1
View File
@@ -229,7 +229,7 @@ public sealed class CoreInfoManager
DownloadUrlLinuxLoong64 = urlSingbox + "/download/{0}/sing-box-{1}-linux-loong64.tar.gz", DownloadUrlLinuxLoong64 = urlSingbox + "/download/{0}/sing-box-{1}-linux-loong64.tar.gz",
DownloadUrlOSX64 = urlSingbox + "/download/{0}/sing-box-{1}-darwin-amd64.tar.gz", DownloadUrlOSX64 = urlSingbox + "/download/{0}/sing-box-{1}-darwin-amd64.tar.gz",
DownloadUrlOSXArm64 = urlSingbox + "/download/{0}/sing-box-{1}-darwin-arm64.tar.gz", DownloadUrlOSXArm64 = urlSingbox + "/download/{0}/sing-box-{1}-darwin-arm64.tar.gz",
LockedMaxVersion = new SemanticVersion(1, 14, int.MaxValue), //LockedMaxVersion = new SemanticVersion(1, 14, int.MaxValue),
Match = "sing-box", Match = "sing-box",
VersionArg = "version", VersionArg = "version",
}, },
+1 -1
View File
@@ -134,7 +134,7 @@ public class TaskManager
var updateService = new UpdateService(_config, async (success, msg) => await Task.CompletedTask); var updateService = new UpdateService(_config, async (success, msg) => await Task.CompletedTask);
var msgs = await updateService.CheckHasUpdateOnlyAll(_config.CheckUpdateItem.CheckPreReleaseUpdate, _config.CheckUpdateItem.UpdateViaProxy); var msgs = await updateService.CheckHasUpdateOnlyAll(_config.CheckUpdateItem.UpdateViaProxy);
foreach (var msg in msgs) foreach (var msg in msgs)
{ {
await _updateFunc?.Invoke(false, msg); await _updateFunc?.Invoke(false, msg);
@@ -242,9 +242,9 @@ public class WebDavItem
[Serializable] [Serializable]
public class CheckUpdateItem public class CheckUpdateItem
{ {
public bool CheckPreReleaseUpdate { get; set; }
public bool UpdateViaProxy { get; set; } = true; public bool UpdateViaProxy { get; set; } = true;
public List<string>? SelectedCoreTypes { get; set; } public List<string>? SelectedCoreTypes { get; set; }
public List<string>? CheckPreReleaseCoreTypes { get; set; }
} }
[Serializable] [Serializable]
@@ -3,6 +3,8 @@ namespace ServiceLib.Models.Dto;
public partial class CheckUpdateModel : ReactiveObject public partial class CheckUpdateModel : ReactiveObject
{ {
public bool? IsSelected { get; set; } public bool? IsSelected { get; set; }
public bool IsCheckPreRelease { get; set; }
public bool ShowCheckPreRelease { get; set; }
public ECoreType? CoreType { get; set; } public ECoreType? CoreType { get; set; }
[Reactive] public partial string? Remarks { get; set; } [Reactive] public partial string? Remarks { get; set; }
public string? FileName { get; set; } public string? FileName { get; set; }
+2 -1
View File
@@ -111,7 +111,7 @@ public partial class UpdateService(Config config, Func<bool, string, Task> updat
return await CheckUpdateAsync(downloadHandle, type, checkPreRelease, blProxy, cancellationToken); return await CheckUpdateAsync(downloadHandle, type, checkPreRelease, blProxy, cancellationToken);
} }
public async Task<List<string>> CheckHasUpdateOnlyAll(bool preRelease, bool blProxy = true, CancellationToken cancellationToken = default) public async Task<List<string>> CheckHasUpdateOnlyAll(bool blProxy = true, CancellationToken cancellationToken = default)
{ {
var msgs = new List<string>(); var msgs = new List<string>();
foreach (var type in CoreInfoManager.Instance.GetCheckUpdateCoreTypes()) foreach (var type in CoreInfoManager.Instance.GetCheckUpdateCoreTypes())
@@ -121,6 +121,7 @@ public partial class UpdateService(Config config, Func<bool, string, Task> updat
continue; continue;
} }
var preRelease = _config.CheckUpdateItem.CheckPreReleaseCoreTypes?.Contains(type.ToString()) ?? false;
var result = await CheckHasUpdateOnly(type, preRelease, blProxy, cancellationToken); var result = await CheckHasUpdateOnly(type, preRelease, blProxy, cancellationToken);
if (result.Success && result.Version != null) if (result.Success && result.Version != null)
{ {
@@ -12,7 +12,7 @@ public partial class CheckUpdateViewModel : MyReactiveObject
public BulkObservableCollection<CheckUpdateModel> CheckUpdateModels { get; } = []; public BulkObservableCollection<CheckUpdateModel> CheckUpdateModels { get; } = [];
public ReactiveCommand<RxVoid, RxVoid> CheckUpdateCmd { get; } public ReactiveCommand<RxVoid, RxVoid> CheckUpdateCmd { get; }
public ReactiveCommand<RxVoid, RxVoid> CheckOnlyCmd { get; } public ReactiveCommand<RxVoid, RxVoid> CheckOnlyCmd { get; }
[Reactive] public partial bool EnableCheckPreReleaseUpdate { get; set; }
[Reactive] public partial bool EnableUpdateViaProxy { get; set; } [Reactive] public partial bool EnableUpdateViaProxy { get; set; }
public CheckUpdateViewModel() public CheckUpdateViewModel()
@@ -33,12 +33,8 @@ public partial class CheckUpdateViewModel : MyReactiveObject
_ = UpdateView(_v2rayN, ex.Message); _ = UpdateView(_v2rayN, ex.Message);
}); });
EnableCheckPreReleaseUpdate = _config.CheckUpdateItem.CheckPreReleaseUpdate;
EnableUpdateViaProxy = _config.CheckUpdateItem.UpdateViaProxy; EnableUpdateViaProxy = _config.CheckUpdateItem.UpdateViaProxy;
this.WhenAnyValue(x => x.EnableCheckPreReleaseUpdate)
.SubscribeAsync(async _ => await OnCheckPreReleaseUpdateChanged());
this.WhenAnyValue(x => x.EnableUpdateViaProxy) this.WhenAnyValue(x => x.EnableUpdateViaProxy)
.Subscribe(c => _ = OnUpdateViaProxyChanged()); .Subscribe(c => _ = OnUpdateViaProxyChanged());
@@ -73,6 +69,8 @@ public partial class CheckUpdateViewModel : MyReactiveObject
return new() return new()
{ {
IsSelected = false, IsSelected = false,
IsCheckPreRelease = false,
ShowCheckPreRelease = false,
CoreType = coreType, CoreType = coreType,
IsGeoFile = false, IsGeoFile = false,
Remarks = ResUI.menuCheckUpdate + $" ({ResUI.MsgNotSupport})", Remarks = ResUI.menuCheckUpdate + $" ({ResUI.MsgNotSupport})",
@@ -83,6 +81,8 @@ public partial class CheckUpdateViewModel : MyReactiveObject
return new() return new()
{ {
IsSelected = _config.CheckUpdateItem.SelectedCoreTypes?.Contains(coreType.ToString()) ?? true, IsSelected = _config.CheckUpdateItem.SelectedCoreTypes?.Contains(coreType.ToString()) ?? true,
IsCheckPreRelease = _config.CheckUpdateItem.CheckPreReleaseCoreTypes?.Contains(coreType.ToString()) ?? false,
ShowCheckPreRelease = CoreInfoManager.Instance.GetCheckPreRelease(coreType, true),
CoreType = coreType, CoreType = coreType,
IsGeoFile = false, IsGeoFile = false,
Remarks = lastResult ?? ResUI.menuCheckUpdate, Remarks = lastResult ?? ResUI.menuCheckUpdate,
@@ -94,22 +94,14 @@ public partial class CheckUpdateViewModel : MyReactiveObject
return new() return new()
{ {
IsSelected = _config.CheckUpdateItem.SelectedCoreTypes?.Contains(_geo) ?? true, IsSelected = _config.CheckUpdateItem.SelectedCoreTypes?.Contains(_geo) ?? true,
IsCheckPreRelease = false,
ShowCheckPreRelease = false,
CoreType = null, CoreType = null,
IsGeoFile = true, IsGeoFile = true,
Remarks = ResUI.menuCheckUpdate, Remarks = ResUI.menuCheckUpdate,
}; };
} }
private async Task OnCheckPreReleaseUpdateChanged()
{
if (_config.CheckUpdateItem.CheckPreReleaseUpdate == EnableCheckPreReleaseUpdate)
{
return;
}
_config.CheckUpdateItem.CheckPreReleaseUpdate = EnableCheckPreReleaseUpdate;
await SaveSelectedCoreTypes();
}
private async Task OnUpdateViaProxyChanged() private async Task OnUpdateViaProxyChanged()
{ {
if (_config.CheckUpdateItem.UpdateViaProxy == EnableUpdateViaProxy) if (_config.CheckUpdateItem.UpdateViaProxy == EnableUpdateViaProxy)
@@ -127,6 +119,11 @@ public partial class CheckUpdateViewModel : MyReactiveObject
.Select(t => t.CoreTypeForStorage) .Select(t => t.CoreTypeForStorage)
.ToList(); .ToList();
_config.CheckUpdateItem.CheckPreReleaseCoreTypes =
CheckUpdateModels.Where(t => t.IsCheckPreRelease == true)
.Select(t => t.CoreTypeForStorage)
.ToList();
await ConfigHandler.SaveConfig(_config); await ConfigHandler.SaveConfig(_config);
} }
@@ -167,7 +164,7 @@ public partial class CheckUpdateViewModel : MyReactiveObject
} }
var updateService = new UpdateService(_config, async (success, msg) => await Task.CompletedTask); var updateService = new UpdateService(_config, async (success, msg) => await Task.CompletedTask);
var result = await updateService.CheckHasUpdateOnly(item.CoreType.Value, EnableCheckPreReleaseUpdate, EnableUpdateViaProxy); var result = await updateService.CheckHasUpdateOnly(item.CoreType.Value, item.IsCheckPreRelease, EnableUpdateViaProxy);
if (result.Success && result.Version != null) if (result.Success && result.Version != null)
{ {
await UpdateView(item.CoreType, string.Format(ResUI.MsgCheckUpdateHasNewVersion, item.CoreType, result.Version)); await UpdateView(item.CoreType, string.Format(ResUI.MsgCheckUpdateHasNewVersion, item.CoreType, result.Version));
@@ -213,15 +210,11 @@ public partial class CheckUpdateViewModel : MyReactiveObject
await UpdateView(_v2rayN, ResUI.MsgNotSupport); await UpdateView(_v2rayN, ResUI.MsgNotSupport);
continue; continue;
} }
await CheckUpdateN(EnableCheckPreReleaseUpdate); await CheckUpdateN(item.IsCheckPreRelease);
}
else if (item.CoreType is ECoreType.Xray or ECoreType.sing_box)
{
await CheckUpdateCore(item, EnableCheckPreReleaseUpdate);
} }
else if (item.CoreType.HasValue) else if (item.CoreType.HasValue)
{ {
await CheckUpdateCore(item, false); await CheckUpdateCore(item, item.IsCheckPreRelease);
} }
} }
@@ -35,16 +35,6 @@
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" /> HorizontalAlignment="Left" />
<TextBlock
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbSettingsEnableCheckPreReleaseUpdate}" />
<ToggleSwitch
x:Name="togEnableCheckPreReleaseUpdate"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
<Button <Button
x:Name="btnCheckOnly" x:Name="btnCheckOnly"
Width="100" Width="100"
@@ -75,8 +65,8 @@
</ItemsControl.ItemsPanel> </ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<DataTemplate> <DataTemplate>
<Panel Width="500" VerticalAlignment="Center"> <Panel Width="680" VerticalAlignment="Center">
<Grid ColumnDefinitions="1*,1*,3*" RowDefinitions="Auto"> <Grid ColumnDefinitions="1*,3*,1*,3*" RowDefinitions="Auto">
<ToggleSwitch <ToggleSwitch
x:Name="togAutoRefresh" x:Name="togAutoRefresh"
Grid.Column="0" Grid.Column="0"
@@ -84,13 +74,31 @@
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center" VerticalAlignment="Center"
IsChecked="{Binding IsSelected}" /> IsChecked="{Binding IsSelected}" />
<TextBlock
<StackPanel
Grid.Column="1" Grid.Column="1"
Margin="{StaticResource Margin4}"
IsVisible="{Binding ShowCheckPreRelease}"
Orientation="Horizontal">
<ToggleSwitch
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
IsChecked="{Binding IsCheckPreRelease}"
OffContent=""
OnContent="" />
<TextBlock
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbSettingsEnableCheckPreReleaseUpdate}" />
</StackPanel>
<TextBlock
Grid.Column="2"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{Binding CoreTypeForStorage}" /> Text="{Binding CoreTypeForStorage}" />
<TextBlock <TextBlock
Grid.Column="2" Grid.Column="3"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{Binding Remarks}" Text="{Binding Remarks}"
@@ -10,7 +10,6 @@ public partial class CheckUpdateView : ReactiveUserControl<CheckUpdateViewModel>
{ {
this.OneWayBind(ViewModel, vm => vm.CheckUpdateModels, v => v.lstCheckUpdates.ItemsSource).DisposeWith(disposables); this.OneWayBind(ViewModel, vm => vm.CheckUpdateModels, v => v.lstCheckUpdates.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableCheckPreReleaseUpdate, v => v.togEnableCheckPreReleaseUpdate.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableUpdateViaProxy, v => v.togEnableUpdateViaProxy.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.EnableUpdateViaProxy, v => v.togEnableUpdateViaProxy.IsChecked).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.CheckOnlyCmd, v => v.btnCheckOnly).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.CheckOnlyCmd, v => v.btnCheckOnly).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.CheckUpdateCmd, v => v.btnCheckUpdate).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.CheckUpdateCmd, v => v.btnCheckUpdate).DisposeWith(disposables);
+26 -14
View File
@@ -14,7 +14,9 @@
x:TypeArguments="vms:CheckUpdateViewModel" x:TypeArguments="vms:CheckUpdateViewModel"
Style="{StaticResource ViewGlobal}" Style="{StaticResource ViewGlobal}"
mc:Ignorable="d"> mc:Ignorable="d">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisConverter" />
</UserControl.Resources>
<DockPanel Margin="{StaticResource Margin8}"> <DockPanel Margin="{StaticResource Margin8}">
<StackPanel <StackPanel
Margin="{StaticResource Margin8}" Margin="{StaticResource Margin8}"
@@ -32,16 +34,6 @@
Margin="{StaticResource Margin8}" Margin="{StaticResource Margin8}"
HorizontalAlignment="Left" /> HorizontalAlignment="Left" />
<TextBlock
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbSettingsEnableCheckPreReleaseUpdate}" />
<ToggleButton
x:Name="togEnableCheckPreReleaseUpdate"
Margin="{StaticResource Margin8}"
HorizontalAlignment="Left" />
<Button <Button
x:Name="btnCheckOnly" x:Name="btnCheckOnly"
Width="100" Width="100"
@@ -71,7 +63,7 @@
<ListView.ItemTemplate> <ListView.ItemTemplate>
<DataTemplate> <DataTemplate>
<Border <Border
Width="500" Width="660"
Margin="{StaticResource Margin8}" Margin="{StaticResource Margin8}"
Padding="0" Padding="0"
VerticalAlignment="Center"> VerticalAlignment="Center">
@@ -81,6 +73,7 @@
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" /> <ColumnDefinition Width="1*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="1*" /> <ColumnDefinition Width="1*" />
<ColumnDefinition Width="3*" /> <ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
@@ -91,12 +84,31 @@
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center" VerticalAlignment="Center"
IsChecked="{Binding IsSelected}" /> IsChecked="{Binding IsSelected}" />
<TextBlock
<StackPanel
Grid.Column="1" Grid.Column="1"
Margin="{StaticResource MarginLeftRight4}"
Orientation="Horizontal"
Visibility="{Binding ShowCheckPreRelease, Converter={StaticResource BoolToVisConverter}}">
<ToggleButton
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
IsChecked="{Binding IsCheckPreRelease}" />
<TextBlock
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbSettingsEnableCheckPreReleaseUpdate}" />
</StackPanel>
<TextBlock
Grid.Column="2"
VerticalAlignment="Center"
Style="{StaticResource ListItemTitle}" Style="{StaticResource ListItemTitle}"
Text="{Binding CoreTypeForStorage}" /> Text="{Binding CoreTypeForStorage}" />
<TextBlock <TextBlock
Grid.Column="2" Grid.Column="3"
VerticalAlignment="Center"
Style="{StaticResource ListItemSubTitle}" Style="{StaticResource ListItemSubTitle}"
Text="{Binding Remarks}" Text="{Binding Remarks}"
TextWrapping="WrapWithOverflow" /> TextWrapping="WrapWithOverflow" />
@@ -10,7 +10,6 @@ public partial class CheckUpdateView
{ {
this.OneWayBind(ViewModel, vm => vm.CheckUpdateModels, v => v.lstCheckUpdates.ItemsSource).DisposeWith(disposables); this.OneWayBind(ViewModel, vm => vm.CheckUpdateModels, v => v.lstCheckUpdates.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableCheckPreReleaseUpdate, v => v.togEnableCheckPreReleaseUpdate.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableUpdateViaProxy, v => v.togEnableUpdateViaProxy.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.EnableUpdateViaProxy, v => v.togEnableUpdateViaProxy.IsChecked).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.CheckOnlyCmd, v => v.btnCheckOnly).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.CheckOnlyCmd, v => v.btnCheckOnly).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.CheckUpdateCmd, v => v.btnCheckUpdate).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.CheckUpdateCmd, v => v.btnCheckUpdate).DisposeWith(disposables);