v2rayN/v2rayN/ServiceLib/ViewModels/RoutingRuleSettingViewModel.cs

343 lines
11 KiB
C#
Raw Normal View History

2023-01-01 11:42:01 +00:00
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
2024-10-07 02:39:43 +00:00
using ServiceLib.Services;
2023-01-01 11:42:01 +00:00
using Splat;
using System.Reactive;
2024-08-20 06:15:29 +00:00
namespace ServiceLib.ViewModels
2023-01-01 11:42:01 +00:00
{
public class RoutingRuleSettingViewModel : MyReactiveObject
2023-01-01 11:42:01 +00:00
{
private List<RulesItem> _rules;
[Reactive]
public RoutingItem SelectedRouting { get; set; }
private IObservableCollection<RulesItemModel> _rulesItems = new ObservableCollectionExtended<RulesItemModel>();
public IObservableCollection<RulesItemModel> RulesItems => _rulesItems;
2023-04-14 12:49:36 +00:00
2023-01-01 11:42:01 +00:00
[Reactive]
public RulesItemModel SelectedSource { get; set; }
2023-04-14 12:49:36 +00:00
2023-01-01 11:42:01 +00:00
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; }
2024-08-22 11:51:10 +00:00
public RoutingRuleSettingViewModel(RoutingItem routingItem, Func<EViewAction, object?, Task<bool>>? updateView)
2023-01-01 11:42:01 +00:00
{
2024-10-07 01:51:41 +00:00
_config = AppHandler.Instance.Config;
2023-01-01 11:42:01 +00:00
_noticeHandler = Locator.Current.GetService<NoticeHandler>();
_updateView = updateView;
2023-01-01 11:42:01 +00:00
SelectedSource = new();
if (routingItem.id.IsNullOrEmpty())
{
SelectedRouting = routingItem;
_rules = new();
}
else
{
SelectedRouting = routingItem;
2024-03-26 06:26:03 +00:00
_rules = JsonUtils.Deserialize<List<RulesItem>>(SelectedRouting.ruleSet);
2023-01-01 11:42:01 +00:00
}
RefreshRulesItems();
var canEditRemove = this.WhenAnyValue(
x => x.SelectedSource,
selectedSource => selectedSource != null && !selectedSource.outboundTag.IsNullOrEmpty());
RuleAddCmd = ReactiveCommand.Create(() =>
{
2024-08-22 11:51:10 +00:00
RuleEditAsync(true);
2023-01-01 11:42:01 +00:00
});
2024-08-22 11:51:10 +00:00
ImportRulesFromFileCmd = ReactiveCommand.CreateFromTask(async () =>
2023-01-01 11:42:01 +00:00
{
2024-08-22 11:51:10 +00:00
await _updateView?.Invoke(EViewAction.ImportRulesFromFile, null);
2023-01-01 11:42:01 +00:00
});
ImportRulesFromClipboardCmd = ReactiveCommand.Create(() =>
{
2024-08-22 11:51:10 +00:00
ImportRulesFromClipboardAsync(null);
2023-01-01 11:42:01 +00:00
});
ImportRulesFromUrlCmd = ReactiveCommand.Create(() =>
2023-01-01 11:42:01 +00:00
{
ImportRulesFromUrl();
2023-01-01 11:42:01 +00:00
});
RuleRemoveCmd = ReactiveCommand.Create(() =>
{
2024-08-22 11:51:10 +00:00
RuleRemoveAsync();
2023-01-01 11:42:01 +00:00
}, canEditRemove);
RuleExportSelectedCmd = ReactiveCommand.Create(() =>
{
2024-08-22 11:51:10 +00:00
RuleExportSelectedAsync();
2023-01-01 11:42:01 +00:00
}, 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(() =>
{
2024-08-22 11:51:10 +00:00
SaveRoutingAsync();
2023-01-01 11:42:01 +00:00
});
}
public void RefreshRulesItems()
{
_rulesItems.Clear();
foreach (var item in _rules)
{
var it = new RulesItemModel()
{
id = item.id,
outboundTag = item.outboundTag,
port = item.port,
network = item.network,
2024-03-26 06:26:03 +00:00
protocols = Utils.List2String(item.protocol),
inboundTags = Utils.List2String(item.inboundTag),
domains = Utils.List2String(item.domain),
ips = Utils.List2String(item.ip),
2023-01-01 11:42:01 +00:00
enabled = item.enabled,
2024-10-03 05:56:06 +00:00
remarks = item.remarks,
2023-01-01 11:42:01 +00:00
};
_rulesItems.Add(it);
}
}
2024-08-22 11:51:10 +00:00
public async Task RuleEditAsync(bool blNew)
2023-01-01 11:42:01 +00:00
{
2024-01-10 09:44:55 +00:00
RulesItem? item;
2023-01-01 11:42:01 +00:00
if (blNew)
{
item = new();
}
else
{
item = _rules.FirstOrDefault(t => t.id == SelectedSource?.id);
if (item is null)
{
return;
}
}
2024-08-22 11:51:10 +00:00
if (await _updateView?.Invoke(EViewAction.RoutingRuleDetailsWindow, item) == true)
2023-01-01 11:42:01 +00:00
{
if (blNew)
{
_rules.Add(item);
}
RefreshRulesItems();
}
}
2024-08-22 11:51:10 +00:00
public async Task RuleRemoveAsync()
2023-01-01 11:42:01 +00:00
{
if (SelectedSource is null || SelectedSource.outboundTag.IsNullOrEmpty())
{
2024-03-04 02:41:42 +00:00
_noticeHandler?.Enqueue(ResUI.PleaseSelectRules);
2023-01-01 11:42:01 +00:00
return;
}
2024-08-22 11:51:10 +00:00
if (await _updateView?.Invoke(EViewAction.ShowYesNo, null) == false)
2023-01-01 11:42:01 +00:00
{
return;
}
2024-08-21 13:12:01 +00:00
foreach (var it in SelectedSources ?? [SelectedSource])
2023-01-01 11:42:01 +00:00
{
var item = _rules.FirstOrDefault(t => t.id == it?.id);
if (item != null)
{
_rules.Remove(item);
}
}
RefreshRulesItems();
}
2023-04-14 12:49:36 +00:00
2024-08-22 11:51:10 +00:00
public async Task RuleExportSelectedAsync()
2023-01-01 11:42:01 +00:00
{
if (SelectedSource is null || SelectedSource.outboundTag.IsNullOrEmpty())
{
2024-03-04 02:41:42 +00:00
_noticeHandler?.Enqueue(ResUI.PleaseSelectRules);
2023-01-01 11:42:01 +00:00
return;
}
var lst = new List<RulesItem4Ray>();
2024-08-21 13:12:01 +00:00
foreach (var it in SelectedSources ?? [SelectedSource])
2023-01-01 11:42:01 +00:00
{
var item = _rules.FirstOrDefault(t => t.id == it?.id);
if (item != null)
{
var item2 = JsonUtils.Deserialize<RulesItem4Ray>(JsonUtils.Serialize(item));
lst.Add(item2 ?? new());
2023-01-01 11:42:01 +00:00
}
}
if (lst.Count > 0)
{
2024-08-22 11:51:10 +00:00
await _updateView?.Invoke(EViewAction.SetClipboardData, JsonUtils.Serialize(lst));
2023-01-01 11:42:01 +00:00
}
}
public void MoveRule(EMove eMove)
{
if (SelectedSource is null || SelectedSource.outboundTag.IsNullOrEmpty())
{
2024-03-04 02:41:42 +00:00
_noticeHandler?.Enqueue(ResUI.PleaseSelectRules);
2023-01-01 11:42:01 +00:00
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();
}
}
2024-08-22 11:51:10 +00:00
private async Task SaveRoutingAsync()
2023-01-01 11:42:01 +00:00
{
string remarks = SelectedRouting.remarks;
2024-03-26 06:26:03 +00:00
if (Utils.IsNullOrEmpty(remarks))
2023-01-01 11:42:01 +00:00
{
2024-03-04 02:41:42 +00:00
_noticeHandler?.Enqueue(ResUI.PleaseFillRemarks);
2023-01-01 11:42:01 +00:00
return;
}
var item = SelectedRouting;
foreach (var it in _rules)
{
2024-03-26 06:26:03 +00:00
it.id = Utils.GetGUID(false);
2023-01-01 11:42:01 +00:00
}
item.ruleNum = _rules.Count;
2024-03-26 06:26:03 +00:00
item.ruleSet = JsonUtils.Serialize(_rules, false);
2023-01-01 11:42:01 +00:00
2023-12-22 08:03:25 +00:00
if (ConfigHandler.SaveRoutingItem(_config, item) == 0)
2023-01-01 11:42:01 +00:00
{
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
2024-08-22 11:51:10 +00:00
await _updateView?.Invoke(EViewAction.CloseWindow, null);
2023-01-01 11:42:01 +00:00
}
else
{
2024-03-04 02:41:42 +00:00
_noticeHandler?.Enqueue(ResUI.OperationFailed);
2023-01-01 11:42:01 +00:00
}
}
#region Import rules
2024-08-22 11:51:10 +00:00
public async Task ImportRulesFromFileAsync(string fileName)
2023-01-01 11:42:01 +00:00
{
2024-03-26 06:26:03 +00:00
if (Utils.IsNullOrEmpty(fileName))
2023-01-01 11:42:01 +00:00
{
return;
}
2024-01-11 10:24:32 +00:00
2024-03-26 06:26:03 +00:00
string result = Utils.LoadResource(fileName);
if (Utils.IsNullOrEmpty(result))
2023-01-01 11:42:01 +00:00
{
return;
}
2024-08-22 11:51:10 +00:00
var ret = await AddBatchRoutingRulesAsync(SelectedRouting, result);
if (ret == 0)
2023-01-01 11:42:01 +00:00
{
RefreshRulesItems();
2024-03-04 02:41:42 +00:00
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
2023-01-01 11:42:01 +00:00
}
}
2024-08-22 11:51:10 +00:00
public async Task ImportRulesFromClipboardAsync(string? clipboardData)
2023-01-01 11:42:01 +00:00
{
2024-08-20 06:15:29 +00:00
if (clipboardData == null)
{
2024-08-22 11:51:10 +00:00
await _updateView?.Invoke(EViewAction.ImportRulesFromClipboard, null);
2024-08-20 06:15:29 +00:00
return;
}
2024-08-22 11:51:10 +00:00
var ret = await AddBatchRoutingRulesAsync(SelectedRouting, clipboardData);
if (ret == 0)
2023-01-01 11:42:01 +00:00
{
RefreshRulesItems();
2024-03-04 02:41:42 +00:00
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
2023-01-01 11:42:01 +00:00
}
}
2023-04-14 12:49:36 +00:00
2024-08-22 11:51:10 +00:00
private async void ImportRulesFromUrl()
2023-01-01 11:42:01 +00:00
{
var url = SelectedRouting.url;
2024-03-26 06:26:03 +00:00
if (Utils.IsNullOrEmpty(url))
2023-01-01 11:42:01 +00:00
{
2024-03-04 02:41:42 +00:00
_noticeHandler?.Enqueue(ResUI.MsgNeedUrl);
2023-01-01 11:42:01 +00:00
return;
}
2024-10-07 02:39:43 +00:00
DownloadService downloadHandle = new DownloadService();
2024-08-22 11:51:10 +00:00
var result = await downloadHandle.TryDownloadString(url, true, "");
var ret = await AddBatchRoutingRulesAsync(SelectedRouting, result);
if (ret == 0)
2023-01-01 11:42:01 +00:00
{
RefreshRulesItems();
2024-03-04 02:41:42 +00:00
_noticeHandler?.Enqueue(ResUI.OperationSuccess);
2023-03-03 02:16:04 +00:00
}
2023-01-01 11:42:01 +00:00
}
2023-04-14 12:49:36 +00:00
2024-08-22 11:51:10 +00:00
private async Task<int> AddBatchRoutingRulesAsync(RoutingItem routingItem, string? clipboardData)
2023-01-01 11:42:01 +00:00
{
bool blReplace = false;
2024-08-22 11:51:10 +00:00
if (await _updateView?.Invoke(EViewAction.AddBatchRoutingRulesYesNo, null) == false)
2023-01-01 11:42:01 +00:00
{
blReplace = true;
}
2024-03-26 06:26:03 +00:00
if (Utils.IsNullOrEmpty(clipboardData))
2023-01-01 11:42:01 +00:00
{
return -1;
}
2024-03-26 06:26:03 +00:00
var lstRules = JsonUtils.Deserialize<List<RulesItem>>(clipboardData);
2023-01-01 11:42:01 +00:00
if (lstRules == null)
{
return -1;
}
foreach (var rule in lstRules)
{
2024-03-26 06:26:03 +00:00
rule.id = Utils.GetGUID(false);
2023-01-01 11:42:01 +00:00
}
if (blReplace)
{
_rules = lstRules;
}
else
{
_rules.AddRange(lstRules);
}
return 0;
}
2023-04-14 12:49:36 +00:00
#endregion Import rules
2023-01-01 11:42:01 +00:00
}
2023-04-14 12:49:36 +00:00
}