v2rayN/v2rayN/v2rayN/ViewModels/RoutingRuleSettingViewModel.cs
2023-04-28 10:23:48 +08:00

360 lines
No EOL
11 KiB
C#

using DynamicData.Binding;
using Microsoft.Win32;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using Splat;
using System.Reactive;
using System.Windows;
using v2rayN.Base;
using v2rayN.Handler;
using v2rayN.Mode;
using v2rayN.Resx;
using v2rayN.Views;
using Application = System.Windows.Application;
namespace v2rayN.ViewModels
{
public class RoutingRuleSettingViewModel : ReactiveObject
{
private static Config _config;
private NoticeHandler? _noticeHandler;
private Window _view;
private List<RulesItem> _rules;
[Reactive]
public RoutingItem SelectedRouting { get; set; }
private IObservableCollection<RulesItemModel> _rulesItems = new ObservableCollectionExtended<RulesItemModel>();
public IObservableCollection<RulesItemModel> RulesItems => _rulesItems;
[Reactive]
public RulesItemModel SelectedSource { get; set; }
public IList<RulesItemModel> SelectedSources { get; set; }
public ReactiveCommand<Unit, Unit> RuleAddCmd { get; }
public ReactiveCommand<Unit, Unit> ImportRulesFromFileCmd { get; }
public ReactiveCommand<Unit, Unit> ImportRulesFromClipboardCmd { get; }
public ReactiveCommand<Unit, Unit> ImportRulesFromUrlCmd { get; }
public ReactiveCommand<Unit, Unit> RuleRemoveCmd { get; }
public ReactiveCommand<Unit, Unit> RuleExportSelectedCmd { get; }
public ReactiveCommand<Unit, Unit> MoveTopCmd { get; }
public ReactiveCommand<Unit, Unit> MoveUpCmd { get; }
public ReactiveCommand<Unit, Unit> MoveDownCmd { get; }
public ReactiveCommand<Unit, Unit> MoveBottomCmd { get; }
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
public RoutingRuleSettingViewModel(RoutingItem routingItem, Window view)
{
_config = LazyConfig.Instance.GetConfig();
_noticeHandler = Locator.Current.GetService<NoticeHandler>();
_view = view;
SelectedSource = new();
if (routingItem.id.IsNullOrEmpty())
{
SelectedRouting = routingItem;
_rules = new();
}
else
{
SelectedRouting = routingItem;
_rules = Utils.FromJson<List<RulesItem>>(SelectedRouting.ruleSet);
}
RefreshRulesItems();
var canEditRemove = this.WhenAnyValue(
x => x.SelectedSource,
selectedSource => selectedSource != null && !selectedSource.outboundTag.IsNullOrEmpty());
RuleAddCmd = ReactiveCommand.Create(() =>
{
RuleEdit(true);
});
ImportRulesFromFileCmd = ReactiveCommand.Create(() =>
{
ImportRulesFromFile();
});
ImportRulesFromClipboardCmd = ReactiveCommand.Create(() =>
{
ImportRulesFromClipboard();
});
ImportRulesFromUrlCmd = ReactiveCommand.Create(() =>
{
ImportRulesFromUrl();
});
RuleRemoveCmd = ReactiveCommand.Create(() =>
{
RuleRemove();
}, canEditRemove);
RuleExportSelectedCmd = ReactiveCommand.Create(() =>
{
RuleExportSelected();
}, canEditRemove);
MoveTopCmd = ReactiveCommand.Create(() =>
{
MoveRule(EMove.Top);
}, canEditRemove);
MoveUpCmd = ReactiveCommand.Create(() =>
{
MoveRule(EMove.Up);
}, canEditRemove);
MoveDownCmd = ReactiveCommand.Create(() =>
{
MoveRule(EMove.Down);
}, canEditRemove);
MoveBottomCmd = ReactiveCommand.Create(() =>
{
MoveRule(EMove.Bottom);
}, canEditRemove);
SaveCmd = ReactiveCommand.Create(() =>
{
SaveRouting();
});
Utils.SetDarkBorder(view, _config.uiItem.colorModeDark);
}
public void RefreshRulesItems()
{
_rulesItems.Clear();
foreach (var item in _rules)
{
var it = new RulesItemModel()
{
id = item.id,
outboundTag = item.outboundTag,
port = item.port,
protocols = Utils.List2String(item.protocol),
inboundTags = Utils.List2String(item.inboundTag),
domains = Utils.List2String(item.domain),
ips = Utils.List2String(item.ip),
enabled = item.enabled,
};
_rulesItems.Add(it);
}
}
public void RuleEdit(bool blNew)
{
RulesItem item;
if (blNew)
{
item = new();
}
else
{
item = _rules.FirstOrDefault(t => t.id == SelectedSource?.id);
if (item is null)
{
return;
}
}
var ret = (new RoutingRuleDetailsWindow(item)).ShowDialog();
if (ret == true)
{
if (blNew)
{
_rules.Add(item);
}
RefreshRulesItems();
}
}
public void RuleRemove()
{
if (SelectedSource is null || SelectedSource.outboundTag.IsNullOrEmpty())
{
UI.Show(ResUI.PleaseSelectRules);
return;
}
if (UI.ShowYesNo(ResUI.RemoveRules) == MessageBoxResult.No)
{
return;
}
foreach (var it in SelectedSources)
{
var item = _rules.FirstOrDefault(t => t.id == it?.id);
if (item != null)
{
_rules.Remove(item);
}
}
RefreshRulesItems();
}
public void RuleExportSelected()
{
if (SelectedSource is null || SelectedSource.outboundTag.IsNullOrEmpty())
{
UI.Show(ResUI.PleaseSelectRules);
return;
}
var lst = new List<RulesItem>();
foreach (var it in SelectedSources)
{
var item = _rules.FirstOrDefault(t => t.id == it?.id);
if (item != null)
{
lst.Add(item);
}
}
if (lst.Count > 0)
{
Utils.SetClipboardData(Utils.ToJson(lst));
//UI.Show(ResUI.OperationSuccess"));
}
}
public void MoveRule(EMove eMove)
{
if (SelectedSource is null || SelectedSource.outboundTag.IsNullOrEmpty())
{
UI.Show(ResUI.PleaseSelectRules);
return;
}
var item = _rules.FirstOrDefault(t => t.id == SelectedSource?.id);
if (item == null)
{
return;
}
var index = _rules.IndexOf(item);
if (ConfigHandler.MoveRoutingRule(_rules, index, eMove) == 0)
{
RefreshRulesItems();
}
}
private void SaveRouting()
{
string remarks = SelectedRouting.remarks;
if (Utils.IsNullOrEmpty(remarks))
{
UI.Show(ResUI.PleaseFillRemarks);
return;
}
var item = SelectedRouting;
foreach (var it in _rules)
{
it.id = Utils.GetGUID(false);
}
item.ruleNum = _rules.Count;
item.ruleSet = Utils.ToJson(_rules, false);
if (ConfigHandler.SaveRoutingItem(ref _config, item) == 0)
{
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
_view.DialogResult = true;
}
else
{
UI.ShowWarning(ResUI.OperationFailed);
}
}
#region Import rules
private void ImportRulesFromFile()
{
OpenFileDialog fileDialog = new OpenFileDialog
{
Multiselect = false,
Filter = "Rules|*.json|All|*.*"
};
if (fileDialog.ShowDialog() != true)
{
return;
}
string fileName = fileDialog.FileName;
if (Utils.IsNullOrEmpty(fileName))
{
return;
}
string result = Utils.LoadResource(fileName);
if (Utils.IsNullOrEmpty(result))
{
return;
}
if (AddBatchRoutingRules(SelectedRouting, result) == 0)
{
RefreshRulesItems();
UI.Show(ResUI.OperationSuccess);
}
}
private void ImportRulesFromClipboard()
{
string clipboardData = Utils.GetClipboardData();
if (AddBatchRoutingRules(SelectedRouting, clipboardData) == 0)
{
RefreshRulesItems();
UI.Show(ResUI.OperationSuccess);
}
}
private async Task ImportRulesFromUrl()
{
var url = SelectedRouting.url;
if (Utils.IsNullOrEmpty(url))
{
UI.Show(ResUI.MsgNeedUrl);
return;
}
DownloadHandle downloadHandle = new DownloadHandle();
string result = await downloadHandle.TryDownloadString(url, true, "");
if (AddBatchRoutingRules(SelectedRouting, result) == 0)
{
Application.Current.Dispatcher.Invoke((Action)(() =>
{
RefreshRulesItems();
}));
UI.Show(ResUI.OperationSuccess);
}
}
private int AddBatchRoutingRules(RoutingItem routingItem, string clipboardData)
{
bool blReplace = false;
if (UI.ShowYesNo(ResUI.AddBatchRoutingRulesYesNo) == MessageBoxResult.No)
{
blReplace = true;
}
if (Utils.IsNullOrEmpty(clipboardData))
{
return -1;
}
var lstRules = Utils.FromJson<List<RulesItem>>(clipboardData);
if (lstRules == null)
{
return -1;
}
foreach (var rule in lstRules)
{
rule.id = Utils.GetGUID(false);
}
if (blReplace)
{
_rules = lstRules;
}
else
{
_rules.AddRange(lstRules);
}
return 0;
}
#endregion Import rules
}
}