mirror of
https://github.com/2dust/v2rayN.git
synced 2026-05-30 01:34:08 +00:00
Show routing import errors in modal dialog
This commit is contained in:
parent
807f0aba06
commit
1520343c29
8 changed files with 123 additions and 3 deletions
|
|
@ -33,4 +33,5 @@ public enum EViewAction
|
||||||
DispatcherRefreshServersBiz,
|
DispatcherRefreshServersBiz,
|
||||||
DispatcherRefreshIcon,
|
DispatcherRefreshIcon,
|
||||||
DispatcherShowMsg,
|
DispatcherShowMsg,
|
||||||
|
ShowMessage,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -305,13 +305,16 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
|
||||||
}
|
}
|
||||||
if (clipboardData.IsNullOrEmpty())
|
if (clipboardData.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
|
await ShowImportErrorAsync($"{ResUI.OperationFailed}: {ResUI.FailedReadConfiguration}");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
var lstRules = JsonUtils.Deserialize<List<RulesItem>>(clipboardData);
|
|
||||||
if (lstRules == null)
|
if (!TryDeserializeRoutingRules(clipboardData, out var lstRules, out var errorMsg))
|
||||||
{
|
{
|
||||||
|
await ShowImportErrorAsync($"{ResUI.OperationFailed}: {errorMsg}");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var rule in lstRules)
|
foreach (var rule in lstRules)
|
||||||
{
|
{
|
||||||
rule.Id = Utils.GetGuid(false);
|
rule.Id = Utils.GetGuid(false);
|
||||||
|
|
@ -328,5 +331,74 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task ShowImportErrorAsync(string message)
|
||||||
|
{
|
||||||
|
if (_updateView != null)
|
||||||
|
{
|
||||||
|
await _updateView.Invoke(EViewAction.ShowMessage, message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NoticeManager.Instance.Enqueue(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryDeserializeRoutingRules(string clipboardData, out List<RulesItem> rules, out string errorMsg)
|
||||||
|
{
|
||||||
|
rules = [];
|
||||||
|
errorMsg = string.Empty;
|
||||||
|
|
||||||
|
var trimmed = clipboardData.Trim();
|
||||||
|
if (trimmed.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
errorMsg = ResUI.FailedReadConfiguration;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trimmed.StartsWith('{'))
|
||||||
|
{
|
||||||
|
if (trimmed.Contains("\"routing\"", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| trimmed.Contains("\"route\"", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
errorMsg = "你粘贴的是完整配置文件,不是路由规则数组。请只粘贴 routing.rules 或 route.rules 的 JSON 数组。";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errorMsg = "当前内容是 JSON 对象。路由规则导入需要顶层 JSON 数组,例如 [ { \"outboundTag\": \"proxy\" } ]。";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var options = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
PropertyNameCaseInsensitive = true,
|
||||||
|
ReadCommentHandling = JsonCommentHandling.Skip,
|
||||||
|
};
|
||||||
|
|
||||||
|
rules = JsonSerializer.Deserialize<List<RulesItem>>(trimmed, options) ?? [];
|
||||||
|
if (rules.Count == 0)
|
||||||
|
{
|
||||||
|
errorMsg = "未解析到任何路由规则,请确认内容是非空 JSON 数组。";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (JsonException ex)
|
||||||
|
{
|
||||||
|
var location = ex.LineNumber is not null && ex.BytePositionInLine is not null
|
||||||
|
? $"第 {ex.LineNumber + 1} 行,第 {ex.BytePositionInLine + 1} 列"
|
||||||
|
: "未知位置";
|
||||||
|
errorMsg = $"JSON 解析错误,{location}。{ex.Message}";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
errorMsg = ex.Message;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endregion Import rules
|
#endregion Import rules
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,12 @@ internal class UI
|
||||||
{
|
{
|
||||||
private static readonly string caption = Global.AppName;
|
private static readonly string caption = Global.AppName;
|
||||||
|
|
||||||
|
public static async Task Show(Window owner, string msg)
|
||||||
|
{
|
||||||
|
var box = new MessageBoxDialog(caption, msg, true);
|
||||||
|
await box.ShowDialog<ButtonResult>(owner);
|
||||||
|
}
|
||||||
|
|
||||||
public static async Task<ButtonResult> ShowYesNo(Window owner, string msg)
|
public static async Task<ButtonResult> ShowYesNo(Window owner, string msg)
|
||||||
{
|
{
|
||||||
var box = new MessageBoxDialog(caption, msg);
|
var box = new MessageBoxDialog(caption, msg);
|
||||||
|
|
|
||||||
|
|
@ -247,6 +247,15 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
||||||
case EViewAction.AddServerViaClipboard:
|
case EViewAction.AddServerViaClipboard:
|
||||||
await AddServerViaClipboardAsync();
|
await AddServerViaClipboardAsync();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case EViewAction.ShowMessage:
|
||||||
|
if (obj is null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
await UI.Show(this, obj.ToString() ?? string.Empty);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return await Task.FromResult(true);
|
return await Task.FromResult(true);
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,18 @@ public partial class MessageBoxDialog : Window
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public MessageBoxDialog(string caption, string message)
|
public MessageBoxDialog(string caption, string message, bool okOnly = false)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
Title = caption;
|
Title = caption;
|
||||||
txtMessage.Text = message;
|
txtMessage.Text = message;
|
||||||
|
|
||||||
|
if (okOnly)
|
||||||
|
{
|
||||||
|
btnNo.IsVisible = false;
|
||||||
|
}
|
||||||
|
|
||||||
btnYes.Click += BtnYes_Click;
|
btnYes.Click += BtnYes_Click;
|
||||||
btnNo.Click += BtnNo_Click;
|
btnNo.Click += BtnNo_Click;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,15 @@ public partial class RoutingRuleSettingWindow : WindowBase<RoutingRuleSettingVie
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case EViewAction.ShowMessage:
|
||||||
|
if (obj is null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
await UI.Show(this, obj.ToString() ?? string.Empty);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return await Task.FromResult(true);
|
return await Task.FromResult(true);
|
||||||
|
|
|
||||||
|
|
@ -242,6 +242,15 @@ public partial class MainWindow
|
||||||
case EViewAction.AddServerViaClipboard:
|
case EViewAction.AddServerViaClipboard:
|
||||||
await AddServerViaClipboardAsync();
|
await AddServerViaClipboardAsync();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case EViewAction.ShowMessage:
|
||||||
|
if (obj is null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UI.Show(obj.ToString() ?? string.Empty);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return await Task.FromResult(true);
|
return await Task.FromResult(true);
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,15 @@ public partial class RoutingRuleSettingWindow
|
||||||
ViewModel?.ImportRulesFromClipboardAsync(clipboardData);
|
ViewModel?.ImportRulesFromClipboardAsync(clipboardData);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case EViewAction.ShowMessage:
|
||||||
|
if (obj is null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UI.Show(obj.ToString() ?? string.Empty);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return await Task.FromResult(true);
|
return await Task.FromResult(true);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue