fix: remove Save/Cancel from routing settings, save edits immediately (#9133)

This commit is contained in:
Mangoo 2026-04-19 08:25:53 +02:00 committed by GitHub
parent 35b98f945f
commit b604a5b787
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 33 additions and 101 deletions

View file

@ -22,7 +22,6 @@ public class RoutingSettingViewModel : MyReactiveObject
public ReactiveCommand<Unit, Unit> RoutingAdvancedSetDefaultCmd { get; } public ReactiveCommand<Unit, Unit> RoutingAdvancedSetDefaultCmd { get; }
public ReactiveCommand<Unit, Unit> RoutingAdvancedImportRulesCmd { get; } public ReactiveCommand<Unit, Unit> RoutingAdvancedImportRulesCmd { get; }
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
public bool IsModified { get; set; } public bool IsModified { get; set; }
#endregion Reactive #endregion Reactive
@ -53,12 +52,19 @@ public class RoutingSettingViewModel : MyReactiveObject
await RoutingAdvancedImportRules(); await RoutingAdvancedImportRules();
}); });
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
{
await SaveRoutingAsync();
});
_ = Init(); _ = Init();
// Auto-save DomainStrategy when changed
this.WhenAnyValue(
x => x.DomainStrategy,
x => x.DomainStrategy4Singbox)
.Skip(1)
.DistinctUntilChanged()
.Subscribe(x =>
{
IsModified = true;
_ = SaveSettingsAsync();
});
} }
private async Task Init() private async Task Init()
@ -96,20 +102,14 @@ public class RoutingSettingViewModel : MyReactiveObject
} }
} }
private async Task SaveRoutingAsync() /// <summary>
/// Save DomainStrategy settings
/// </summary>
public async Task SaveSettingsAsync()
{ {
_config.RoutingBasicItem.DomainStrategy = DomainStrategy; _config.RoutingBasicItem.DomainStrategy = DomainStrategy;
_config.RoutingBasicItem.DomainStrategy4Singbox = DomainStrategy4Singbox; _config.RoutingBasicItem.DomainStrategy4Singbox = DomainStrategy4Singbox;
await ConfigHandler.SaveConfig(_config);
if (await ConfigHandler.SaveConfig(_config) == 0)
{
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
_updateView?.Invoke(EViewAction.CloseWindow, null);
}
else
{
NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
}
} }
#endregion Refresh Save #endregion Refresh Save

View file

@ -20,24 +20,6 @@
<MenuItem x:Name="menuRoutingAdvancedImportRules2" Header="{x:Static resx:ResUI.menuRoutingAdvancedImportRules}" /> <MenuItem x:Name="menuRoutingAdvancedImportRules2" Header="{x:Static resx:ResUI.menuRoutingAdvancedImportRules}" />
</Menu> </Menu>
<StackPanel
Margin="{StaticResource Margin4}"
HorizontalAlignment="Right"
DockPanel.Dock="Bottom"
Orientation="Horizontal">
<Button
x:Name="btnSave"
Width="100"
Content="{x:Static resx:ResUI.TbConfirm}"
IsDefault="True" />
<Button
x:Name="btnCancel"
Width="100"
Margin="{StaticResource MarginLr8}"
Content="{x:Static resx:ResUI.TbCancel}"
IsCancel="True" />
</StackPanel>
<Grid <Grid
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
ColumnDefinitions="Auto,Auto" ColumnDefinitions="Auto,Auto"

View file

@ -5,15 +5,12 @@ namespace v2rayN.Desktop.Views;
public partial class RoutingSettingWindow : WindowBase<RoutingSettingViewModel> public partial class RoutingSettingWindow : WindowBase<RoutingSettingViewModel>
{ {
private bool _manualClose = false;
public RoutingSettingWindow() public RoutingSettingWindow()
{ {
InitializeComponent(); InitializeComponent();
Loaded += Window_Loaded; Loaded += Window_Loaded;
Closing += RoutingSettingWindow_Closing; Closing += RoutingSettingWindow_Closing;
btnCancel.Click += (s, e) => Close();
KeyDown += RoutingSettingWindow_KeyDown; KeyDown += RoutingSettingWindow_KeyDown;
lstRoutings.SelectionChanged += lstRoutings_SelectionChanged; lstRoutings.SelectionChanged += lstRoutings_SelectionChanged;
lstRoutings.DoubleTapped += LstRoutings_DoubleTapped; lstRoutings.DoubleTapped += LstRoutings_DoubleTapped;
@ -38,8 +35,6 @@ public partial class RoutingSettingWindow : WindowBase<RoutingSettingViewModel>
this.BindCommand(ViewModel, vm => vm.RoutingAdvancedSetDefaultCmd, v => v.menuRoutingAdvancedSetDefault).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.RoutingAdvancedSetDefaultCmd, v => v.menuRoutingAdvancedSetDefault).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RoutingAdvancedImportRulesCmd, v => v.menuRoutingAdvancedImportRules).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.RoutingAdvancedImportRulesCmd, v => v.menuRoutingAdvancedImportRules).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RoutingAdvancedImportRulesCmd, v => v.menuRoutingAdvancedImportRules2).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.RoutingAdvancedImportRulesCmd, v => v.menuRoutingAdvancedImportRules2).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
}); });
} }
@ -47,10 +42,6 @@ public partial class RoutingSettingWindow : WindowBase<RoutingSettingViewModel>
{ {
switch (action) switch (action)
{ {
case EViewAction.CloseWindow:
Close(true);
break;
case EViewAction.ShowYesNo: case EViewAction.ShowYesNo:
if (await UI.ShowYesNo(this, ResUI.RemoveRules) != ButtonResult.Yes) if (await UI.ShowYesNo(this, ResUI.RemoveRules) != ButtonResult.Yes)
{ {
@ -69,6 +60,21 @@ public partial class RoutingSettingWindow : WindowBase<RoutingSettingViewModel>
return await Task.FromResult(true); return await Task.FromResult(true);
} }
private bool _closed = false;
private void RoutingSettingWindow_Closing(object? sender, WindowClosingEventArgs e)
{
if (_closed) return;
// DomainStrategy is auto-saved reactively; just ensure the caller knows changes were made
if (ViewModel?.IsModified == true)
{
e.Cancel = true;
_closed = true;
Close(true);
}
}
private void RoutingSettingWindow_KeyDown(object? sender, KeyEventArgs e) private void RoutingSettingWindow_KeyDown(object? sender, KeyEventArgs e)
{ {
if (e.KeyModifiers is KeyModifiers.Control or KeyModifiers.Meta) if (e.KeyModifiers is KeyModifiers.Control or KeyModifiers.Meta)
@ -125,25 +131,7 @@ public partial class RoutingSettingWindow : WindowBase<RoutingSettingViewModel>
ProcUtils.ProcessStart("https://sing-box.sagernet.org/zh/configuration/route/rule_action/#strategy"); ProcUtils.ProcessStart("https://sing-box.sagernet.org/zh/configuration/route/rule_action/#strategy");
} }
private void btnCancel_Click(object? sender, RoutedEventArgs e)
{
_manualClose = true;
Close(ViewModel?.IsModified);
}
private void RoutingSettingWindow_Closing(object? sender, WindowClosingEventArgs e)
{
if (ViewModel?.IsModified == true)
{
if (!_manualClose)
{
btnCancel_Click(null, null);
}
}
}
private void Window_Loaded(object? sender, RoutedEventArgs e) private void Window_Loaded(object? sender, RoutedEventArgs e)
{ {
btnCancel.Focus();
} }
} }

View file

@ -52,26 +52,6 @@
</ToolBar> </ToolBar>
</ToolBarTray> </ToolBarTray>
<StackPanel
Margin="{StaticResource Margin8}"
HorizontalAlignment="Right"
DockPanel.Dock="Bottom"
Orientation="Horizontal">
<Button
x:Name="btnSave"
Width="100"
Content="{x:Static resx:ResUI.TbConfirm}"
IsDefault="True"
Style="{StaticResource DefButton}" />
<Button
x:Name="btnCancel"
Width="100"
Margin="{StaticResource MarginLeftRight8}"
Content="{x:Static resx:ResUI.TbCancel}"
IsCancel="true"
Style="{StaticResource DefButton}" />
</StackPanel>
<Grid Margin="{StaticResource Margin8}" DockPanel.Dock="Top"> <Grid Margin="{StaticResource Margin8}" DockPanel.Dock="Top">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />

View file

@ -12,7 +12,6 @@ public partial class RoutingSettingWindow
lstRoutings.SelectionChanged += lstRoutings_SelectionChanged; lstRoutings.SelectionChanged += lstRoutings_SelectionChanged;
lstRoutings.MouseDoubleClick += LstRoutings_MouseDoubleClick; lstRoutings.MouseDoubleClick += LstRoutings_MouseDoubleClick;
menuRoutingAdvancedSelectAll.Click += menuRoutingAdvancedSelectAll_Click; menuRoutingAdvancedSelectAll.Click += menuRoutingAdvancedSelectAll_Click;
btnCancel.Click += btnCancel_Click;
ViewModel = new RoutingSettingViewModel(UpdateViewHandler); ViewModel = new RoutingSettingViewModel(UpdateViewHandler);
@ -33,8 +32,6 @@ public partial class RoutingSettingWindow
this.BindCommand(ViewModel, vm => vm.RoutingAdvancedSetDefaultCmd, v => v.menuRoutingAdvancedSetDefault).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.RoutingAdvancedSetDefaultCmd, v => v.menuRoutingAdvancedSetDefault).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RoutingAdvancedImportRulesCmd, v => v.menuRoutingAdvancedImportRules).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.RoutingAdvancedImportRulesCmd, v => v.menuRoutingAdvancedImportRules).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RoutingAdvancedImportRulesCmd, v => v.menuRoutingAdvancedImportRules2).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.RoutingAdvancedImportRulesCmd, v => v.menuRoutingAdvancedImportRules2).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
}); });
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme); WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
} }
@ -43,10 +40,6 @@ public partial class RoutingSettingWindow
{ {
switch (action) switch (action)
{ {
case EViewAction.CloseWindow:
DialogResult = true;
break;
case EViewAction.ShowYesNo: case EViewAction.ShowYesNo:
if (UI.ShowYesNo(ResUI.RemoveRules) == MessageBoxResult.No) if (UI.ShowYesNo(ResUI.RemoveRules) == MessageBoxResult.No)
{ {
@ -68,6 +61,7 @@ public partial class RoutingSettingWindow
private void RoutingSettingWindow_Closing(object? sender, System.ComponentModel.CancelEventArgs e) private void RoutingSettingWindow_Closing(object? sender, System.ComponentModel.CancelEventArgs e)
{ {
// DomainStrategy is auto-saved reactively; just ensure the caller knows changes were made
if (ViewModel?.IsModified == true) if (ViewModel?.IsModified == true)
{ {
DialogResult = true; DialogResult = true;
@ -129,16 +123,4 @@ public partial class RoutingSettingWindow
{ {
ProcUtils.ProcessStart("https://sing-box.sagernet.org/zh/configuration/route/rule_action/#strategy"); ProcUtils.ProcessStart("https://sing-box.sagernet.org/zh/configuration/route/rule_action/#strategy");
} }
private void btnCancel_Click(object sender, System.Windows.RoutedEventArgs e)
{
if (ViewModel?.IsModified == true)
{
DialogResult = true;
}
else
{
Close();
}
}
} }