mirror of
https://github.com/2dust/v2rayN.git
synced 2026-10-20 04:05:00 +03:00
csharp_style_namespace_declarations = file_scoped
This commit is contained in:
@@ -2,115 +2,114 @@ using System.Reactive;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
|
||||
namespace ServiceLib.ViewModels
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
public class AddServer2ViewModel : MyReactiveObject
|
||||
{
|
||||
public class AddServer2ViewModel : MyReactiveObject
|
||||
[Reactive]
|
||||
public ProfileItem SelectedSource { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string? CoreType { get; set; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> BrowseServerCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> EditServerCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> SaveServerCmd { get; }
|
||||
public bool IsModified { get; set; }
|
||||
|
||||
public AddServer2ViewModel(ProfileItem profileItem, Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
[Reactive]
|
||||
public ProfileItem SelectedSource { get; set; }
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
|
||||
[Reactive]
|
||||
public string? CoreType { get; set; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> BrowseServerCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> EditServerCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> SaveServerCmd { get; }
|
||||
public bool IsModified { get; set; }
|
||||
|
||||
public AddServer2ViewModel(ProfileItem profileItem, Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
BrowseServerCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
|
||||
BrowseServerCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
_updateView?.Invoke(EViewAction.BrowseServer, null);
|
||||
await Task.CompletedTask;
|
||||
});
|
||||
EditServerCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await EditServer();
|
||||
});
|
||||
SaveServerCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await SaveServerAsync();
|
||||
});
|
||||
|
||||
SelectedSource = profileItem.IndexId.IsNullOrEmpty() ? profileItem : JsonUtils.DeepCopy(profileItem);
|
||||
CoreType = SelectedSource?.CoreType?.ToString();
|
||||
}
|
||||
|
||||
private async Task SaveServerAsync()
|
||||
{
|
||||
var remarks = SelectedSource.Remarks;
|
||||
if (remarks.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseFillRemarks);
|
||||
return;
|
||||
}
|
||||
|
||||
if (SelectedSource.Address.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillServerAddressCustom);
|
||||
return;
|
||||
}
|
||||
SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? null : (ECoreType)Enum.Parse(typeof(ECoreType), CoreType);
|
||||
|
||||
if (await ConfigHandler.EditCustomServer(_config, SelectedSource) == 0)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task BrowseServer(string fileName)
|
||||
{
|
||||
if (fileName.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var item = await AppHandler.Instance.GetProfileItem(SelectedSource.IndexId);
|
||||
item ??= SelectedSource;
|
||||
item.Address = fileName;
|
||||
if (await ConfigHandler.AddCustomServer(_config, item, false) == 0)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.SuccessfullyImportedCustomServer);
|
||||
if (item.IndexId.IsNotEmpty())
|
||||
{
|
||||
SelectedSource = JsonUtils.DeepCopy(item);
|
||||
}
|
||||
IsModified = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FailedImportedCustomServer);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task EditServer()
|
||||
{
|
||||
var address = SelectedSource.Address;
|
||||
if (address.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillServerAddressCustom);
|
||||
return;
|
||||
}
|
||||
|
||||
address = Utils.GetConfigPath(address);
|
||||
if (File.Exists(address))
|
||||
{
|
||||
ProcUtils.ProcessStart(address);
|
||||
}
|
||||
else
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FailedReadConfiguration);
|
||||
}
|
||||
_updateView?.Invoke(EViewAction.BrowseServer, null);
|
||||
await Task.CompletedTask;
|
||||
});
|
||||
EditServerCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await EditServer();
|
||||
});
|
||||
SaveServerCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await SaveServerAsync();
|
||||
});
|
||||
|
||||
SelectedSource = profileItem.IndexId.IsNullOrEmpty() ? profileItem : JsonUtils.DeepCopy(profileItem);
|
||||
CoreType = SelectedSource?.CoreType?.ToString();
|
||||
}
|
||||
|
||||
private async Task SaveServerAsync()
|
||||
{
|
||||
var remarks = SelectedSource.Remarks;
|
||||
if (remarks.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseFillRemarks);
|
||||
return;
|
||||
}
|
||||
|
||||
if (SelectedSource.Address.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillServerAddressCustom);
|
||||
return;
|
||||
}
|
||||
SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? null : (ECoreType)Enum.Parse(typeof(ECoreType), CoreType);
|
||||
|
||||
if (await ConfigHandler.EditCustomServer(_config, SelectedSource) == 0)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task BrowseServer(string fileName)
|
||||
{
|
||||
if (fileName.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var item = await AppHandler.Instance.GetProfileItem(SelectedSource.IndexId);
|
||||
item ??= SelectedSource;
|
||||
item.Address = fileName;
|
||||
if (await ConfigHandler.AddCustomServer(_config, item, false) == 0)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.SuccessfullyImportedCustomServer);
|
||||
if (item.IndexId.IsNotEmpty())
|
||||
{
|
||||
SelectedSource = JsonUtils.DeepCopy(item);
|
||||
}
|
||||
IsModified = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FailedImportedCustomServer);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task EditServer()
|
||||
{
|
||||
var address = SelectedSource.Address;
|
||||
if (address.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillServerAddressCustom);
|
||||
return;
|
||||
}
|
||||
|
||||
address = Utils.GetConfigPath(address);
|
||||
if (File.Exists(address))
|
||||
{
|
||||
ProcUtils.ProcessStart(address);
|
||||
}
|
||||
else
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FailedReadConfiguration);
|
||||
}
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,95 +2,94 @@ using System.Reactive;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
|
||||
namespace ServiceLib.ViewModels
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
public class AddServerViewModel : MyReactiveObject
|
||||
{
|
||||
public class AddServerViewModel : MyReactiveObject
|
||||
[Reactive]
|
||||
public ProfileItem SelectedSource { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string? CoreType { get; set; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||
|
||||
public AddServerViewModel(ProfileItem profileItem, Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
[Reactive]
|
||||
public ProfileItem SelectedSource { get; set; }
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
|
||||
[Reactive]
|
||||
public string? CoreType { get; set; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||
|
||||
public AddServerViewModel(ProfileItem profileItem, Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
await SaveServerAsync();
|
||||
});
|
||||
|
||||
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await SaveServerAsync();
|
||||
});
|
||||
if (profileItem.IndexId.IsNullOrEmpty())
|
||||
{
|
||||
profileItem.Network = Global.DefaultNetwork;
|
||||
profileItem.HeaderType = Global.None;
|
||||
profileItem.RequestHost = "";
|
||||
profileItem.StreamSecurity = "";
|
||||
SelectedSource = profileItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedSource = JsonUtils.DeepCopy(profileItem);
|
||||
}
|
||||
CoreType = SelectedSource?.CoreType?.ToString();
|
||||
}
|
||||
|
||||
if (profileItem.IndexId.IsNullOrEmpty())
|
||||
{
|
||||
profileItem.Network = Global.DefaultNetwork;
|
||||
profileItem.HeaderType = Global.None;
|
||||
profileItem.RequestHost = "";
|
||||
profileItem.StreamSecurity = "";
|
||||
SelectedSource = profileItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedSource = JsonUtils.DeepCopy(profileItem);
|
||||
}
|
||||
CoreType = SelectedSource?.CoreType?.ToString();
|
||||
private async Task SaveServerAsync()
|
||||
{
|
||||
if (SelectedSource.Remarks.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseFillRemarks);
|
||||
return;
|
||||
}
|
||||
|
||||
private async Task SaveServerAsync()
|
||||
if (SelectedSource.Address.IsNullOrEmpty())
|
||||
{
|
||||
if (SelectedSource.Remarks.IsNullOrEmpty())
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillServerAddress);
|
||||
return;
|
||||
}
|
||||
var port = SelectedSource.Port.ToString();
|
||||
if (port.IsNullOrEmpty() || !Utils.IsNumeric(port)
|
||||
|| SelectedSource.Port <= 0 || SelectedSource.Port >= Global.MaxPort)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillCorrectServerPort);
|
||||
return;
|
||||
}
|
||||
if (SelectedSource.ConfigType == EConfigType.Shadowsocks)
|
||||
{
|
||||
if (SelectedSource.Id.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseFillRemarks);
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillPassword);
|
||||
return;
|
||||
}
|
||||
if (SelectedSource.Security.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectEncryption);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (SelectedSource.ConfigType is not EConfigType.SOCKS and not EConfigType.HTTP)
|
||||
{
|
||||
if (SelectedSource.Id.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillUUID);
|
||||
return;
|
||||
}
|
||||
}
|
||||
SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? null : (ECoreType)Enum.Parse(typeof(ECoreType), CoreType);
|
||||
|
||||
if (SelectedSource.Address.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillServerAddress);
|
||||
return;
|
||||
}
|
||||
var port = SelectedSource.Port.ToString();
|
||||
if (port.IsNullOrEmpty() || !Utils.IsNumeric(port)
|
||||
|| SelectedSource.Port <= 0 || SelectedSource.Port >= Global.MaxPort)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillCorrectServerPort);
|
||||
return;
|
||||
}
|
||||
if (SelectedSource.ConfigType == EConfigType.Shadowsocks)
|
||||
{
|
||||
if (SelectedSource.Id.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillPassword);
|
||||
return;
|
||||
}
|
||||
if (SelectedSource.Security.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectEncryption);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (SelectedSource.ConfigType is not EConfigType.SOCKS and not EConfigType.HTTP)
|
||||
{
|
||||
if (SelectedSource.Id.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillUUID);
|
||||
return;
|
||||
}
|
||||
}
|
||||
SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? null : (ECoreType)Enum.Parse(typeof(ECoreType), CoreType);
|
||||
|
||||
if (await ConfigHandler.AddServer(_config, SelectedSource) == 0)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
|
||||
}
|
||||
if (await ConfigHandler.AddServer(_config, SelectedSource) == 0)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,180 +3,179 @@ using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using Splat;
|
||||
|
||||
namespace ServiceLib.ViewModels
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
public class BackupAndRestoreViewModel : MyReactiveObject
|
||||
{
|
||||
public class BackupAndRestoreViewModel : MyReactiveObject
|
||||
private readonly string _guiConfigs = "guiConfigs";
|
||||
private static string BackupFileName => $"backup_{DateTime.Now:yyyyMMddHHmmss}.zip";
|
||||
|
||||
public ReactiveCommand<Unit, Unit> RemoteBackupCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> RemoteRestoreCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> WebDavCheckCmd { get; }
|
||||
|
||||
[Reactive]
|
||||
public WebDavItem SelectedSource { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string OperationMsg { get; set; }
|
||||
|
||||
public BackupAndRestoreViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
private readonly string _guiConfigs = "guiConfigs";
|
||||
private static string BackupFileName => $"backup_{DateTime.Now:yyyyMMddHHmmss}.zip";
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
|
||||
public ReactiveCommand<Unit, Unit> RemoteBackupCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> RemoteRestoreCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> WebDavCheckCmd { get; }
|
||||
|
||||
[Reactive]
|
||||
public WebDavItem SelectedSource { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string OperationMsg { get; set; }
|
||||
|
||||
public BackupAndRestoreViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
WebDavCheckCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
await WebDavCheck();
|
||||
});
|
||||
RemoteBackupCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await RemoteBackup();
|
||||
});
|
||||
RemoteRestoreCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await RemoteRestore();
|
||||
});
|
||||
|
||||
WebDavCheckCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await WebDavCheck();
|
||||
});
|
||||
RemoteBackupCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await RemoteBackup();
|
||||
});
|
||||
RemoteRestoreCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await RemoteRestore();
|
||||
});
|
||||
SelectedSource = JsonUtils.DeepCopy(_config.WebDavItem);
|
||||
}
|
||||
|
||||
SelectedSource = JsonUtils.DeepCopy(_config.WebDavItem);
|
||||
private void DisplayOperationMsg(string msg = "")
|
||||
{
|
||||
OperationMsg = msg;
|
||||
}
|
||||
|
||||
private async Task WebDavCheck()
|
||||
{
|
||||
DisplayOperationMsg();
|
||||
_config.WebDavItem = SelectedSource;
|
||||
_ = await ConfigHandler.SaveConfig(_config);
|
||||
|
||||
var result = await WebDavHandler.Instance.CheckConnection();
|
||||
if (result)
|
||||
{
|
||||
DisplayOperationMsg(ResUI.OperationSuccess);
|
||||
}
|
||||
|
||||
private void DisplayOperationMsg(string msg = "")
|
||||
else
|
||||
{
|
||||
OperationMsg = msg;
|
||||
}
|
||||
|
||||
private async Task WebDavCheck()
|
||||
{
|
||||
DisplayOperationMsg();
|
||||
_config.WebDavItem = SelectedSource;
|
||||
_ = await ConfigHandler.SaveConfig(_config);
|
||||
|
||||
var result = await WebDavHandler.Instance.CheckConnection();
|
||||
if (result)
|
||||
{
|
||||
DisplayOperationMsg(ResUI.OperationSuccess);
|
||||
}
|
||||
else
|
||||
{
|
||||
DisplayOperationMsg(WebDavHandler.Instance.GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RemoteBackup()
|
||||
{
|
||||
DisplayOperationMsg();
|
||||
var fileName = Utils.GetBackupPath(BackupFileName);
|
||||
var result = await CreateZipFileFromDirectory(fileName);
|
||||
if (result)
|
||||
{
|
||||
var result2 = await WebDavHandler.Instance.PutFile(fileName);
|
||||
if (result2)
|
||||
{
|
||||
DisplayOperationMsg(ResUI.OperationSuccess);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DisplayOperationMsg(WebDavHandler.Instance.GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RemoteRestore()
|
||||
private async Task RemoteBackup()
|
||||
{
|
||||
DisplayOperationMsg();
|
||||
var fileName = Utils.GetBackupPath(BackupFileName);
|
||||
var result = await CreateZipFileFromDirectory(fileName);
|
||||
if (result)
|
||||
{
|
||||
DisplayOperationMsg();
|
||||
var fileName = Utils.GetTempPath(Utils.GetGuid());
|
||||
var result = await WebDavHandler.Instance.GetRawFile(fileName);
|
||||
if (result)
|
||||
var result2 = await WebDavHandler.Instance.PutFile(fileName);
|
||||
if (result2)
|
||||
{
|
||||
await LocalRestore(fileName);
|
||||
DisplayOperationMsg(ResUI.OperationSuccess);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DisplayOperationMsg(WebDavHandler.Instance.GetLastError());
|
||||
}
|
||||
|
||||
private async Task RemoteRestore()
|
||||
{
|
||||
DisplayOperationMsg();
|
||||
var fileName = Utils.GetTempPath(Utils.GetGuid());
|
||||
var result = await WebDavHandler.Instance.GetRawFile(fileName);
|
||||
if (result)
|
||||
{
|
||||
await LocalRestore(fileName);
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayOperationMsg(WebDavHandler.Instance.GetLastError());
|
||||
}
|
||||
|
||||
public async Task<bool> LocalBackup(string fileName)
|
||||
{
|
||||
DisplayOperationMsg();
|
||||
var result = await CreateZipFileFromDirectory(fileName);
|
||||
if (result)
|
||||
{
|
||||
DisplayOperationMsg(ResUI.OperationSuccess);
|
||||
}
|
||||
else
|
||||
{
|
||||
DisplayOperationMsg(WebDavHandler.Instance.GetLastError());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<bool> LocalBackup(string fileName)
|
||||
public async Task LocalRestore(string fileName)
|
||||
{
|
||||
DisplayOperationMsg();
|
||||
if (fileName.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
//exist
|
||||
if (!File.Exists(fileName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
//check
|
||||
var lstFiles = FileManager.GetFilesFromZip(fileName);
|
||||
if (lstFiles is null || !lstFiles.Any(t => t.Contains(_guiConfigs)))
|
||||
{
|
||||
DisplayOperationMsg();
|
||||
var result = await CreateZipFileFromDirectory(fileName);
|
||||
if (result)
|
||||
{
|
||||
DisplayOperationMsg(ResUI.OperationSuccess);
|
||||
}
|
||||
else
|
||||
{
|
||||
DisplayOperationMsg(WebDavHandler.Instance.GetLastError());
|
||||
}
|
||||
|
||||
return result;
|
||||
DisplayOperationMsg(ResUI.LocalRestoreInvalidZipTips);
|
||||
return;
|
||||
}
|
||||
|
||||
public async Task LocalRestore(string fileName)
|
||||
//backup first
|
||||
var fileBackup = Utils.GetBackupPath(BackupFileName);
|
||||
var result = await CreateZipFileFromDirectory(fileBackup);
|
||||
if (result)
|
||||
{
|
||||
DisplayOperationMsg();
|
||||
if (fileName.IsNullOrEmpty())
|
||||
var service = Locator.Current.GetService<MainWindowViewModel>();
|
||||
await service?.MyAppExitAsync(true);
|
||||
await SQLiteHelper.Instance.DisposeDbConnectionAsync();
|
||||
|
||||
var toPath = Utils.GetConfigPath();
|
||||
FileManager.ZipExtractToFile(fileName, toPath, "");
|
||||
|
||||
if (Utils.IsWindows())
|
||||
{
|
||||
return;
|
||||
ProcUtils.RebootAsAdmin(false);
|
||||
}
|
||||
//exist
|
||||
if (!File.Exists(fileName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
//check
|
||||
var lstFiles = FileManager.GetFilesFromZip(fileName);
|
||||
if (lstFiles is null || !lstFiles.Any(t => t.Contains(_guiConfigs)))
|
||||
{
|
||||
DisplayOperationMsg(ResUI.LocalRestoreInvalidZipTips);
|
||||
return;
|
||||
}
|
||||
|
||||
//backup first
|
||||
var fileBackup = Utils.GetBackupPath(BackupFileName);
|
||||
var result = await CreateZipFileFromDirectory(fileBackup);
|
||||
if (result)
|
||||
else
|
||||
{
|
||||
var service = Locator.Current.GetService<MainWindowViewModel>();
|
||||
await service?.MyAppExitAsync(true);
|
||||
await SQLiteHelper.Instance.DisposeDbConnectionAsync();
|
||||
|
||||
var toPath = Utils.GetConfigPath();
|
||||
FileManager.ZipExtractToFile(fileName, toPath, "");
|
||||
|
||||
if (Utils.IsWindows())
|
||||
if (Utils.UpgradeAppExists(out var upgradeFileName))
|
||||
{
|
||||
ProcUtils.RebootAsAdmin(false);
|
||||
_ = ProcUtils.ProcessStart(upgradeFileName, Global.RebootAs, Utils.StartupPath());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Utils.UpgradeAppExists(out var upgradeFileName))
|
||||
{
|
||||
_ = ProcUtils.ProcessStart(upgradeFileName, Global.RebootAs, Utils.StartupPath());
|
||||
}
|
||||
}
|
||||
service?.Shutdown(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
DisplayOperationMsg(WebDavHandler.Instance.GetLastError());
|
||||
}
|
||||
service?.Shutdown(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
DisplayOperationMsg(WebDavHandler.Instance.GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> CreateZipFileFromDirectory(string fileName)
|
||||
private async Task<bool> CreateZipFileFromDirectory(string fileName)
|
||||
{
|
||||
if (fileName.IsNullOrEmpty())
|
||||
{
|
||||
if (fileName.IsNullOrEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
var configDir = Utils.GetConfigPath();
|
||||
var configDirZipTemp = Utils.GetTempPath($"v2rayN_{DateTime.Now:yyyyMMddHHmmss}");
|
||||
var configDirTemp = Path.Combine(configDirZipTemp, _guiConfigs);
|
||||
var configDir = Utils.GetConfigPath();
|
||||
var configDirZipTemp = Utils.GetTempPath($"v2rayN_{DateTime.Now:yyyyMMddHHmmss}");
|
||||
var configDirTemp = Path.Combine(configDirZipTemp, _guiConfigs);
|
||||
|
||||
FileManager.CopyDirectory(configDir, configDirTemp, false, true, "");
|
||||
var ret = FileManager.CreateFromDirectory(configDirZipTemp, fileName);
|
||||
Directory.Delete(configDirZipTemp, true);
|
||||
return await Task.FromResult(ret);
|
||||
}
|
||||
FileManager.CopyDirectory(configDir, configDirTemp, false, true, "");
|
||||
var ret = FileManager.CreateFromDirectory(configDirZipTemp, fileName);
|
||||
Directory.Delete(configDirZipTemp, true);
|
||||
return await Task.FromResult(ret);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,311 +6,310 @@ using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using Splat;
|
||||
|
||||
namespace ServiceLib.ViewModels
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
public class CheckUpdateViewModel : MyReactiveObject
|
||||
{
|
||||
public class CheckUpdateViewModel : MyReactiveObject
|
||||
private const string _geo = "GeoFiles";
|
||||
private string _v2rayN = ECoreType.v2rayN.ToString();
|
||||
private List<CheckUpdateModel> _lstUpdated = [];
|
||||
|
||||
private IObservableCollection<CheckUpdateModel> _checkUpdateModel = new ObservableCollectionExtended<CheckUpdateModel>();
|
||||
public IObservableCollection<CheckUpdateModel> CheckUpdateModels => _checkUpdateModel;
|
||||
public ReactiveCommand<Unit, Unit> CheckUpdateCmd { get; }
|
||||
[Reactive] public bool EnableCheckPreReleaseUpdate { get; set; }
|
||||
|
||||
public CheckUpdateViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
private const string _geo = "GeoFiles";
|
||||
private string _v2rayN = ECoreType.v2rayN.ToString();
|
||||
private List<CheckUpdateModel> _lstUpdated = [];
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
|
||||
private IObservableCollection<CheckUpdateModel> _checkUpdateModel = new ObservableCollectionExtended<CheckUpdateModel>();
|
||||
public IObservableCollection<CheckUpdateModel> CheckUpdateModels => _checkUpdateModel;
|
||||
public ReactiveCommand<Unit, Unit> CheckUpdateCmd { get; }
|
||||
[Reactive] public bool EnableCheckPreReleaseUpdate { get; set; }
|
||||
|
||||
public CheckUpdateViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
CheckUpdateCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
await CheckUpdate();
|
||||
});
|
||||
|
||||
CheckUpdateCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await CheckUpdate();
|
||||
});
|
||||
EnableCheckPreReleaseUpdate = _config.CheckUpdateItem.CheckPreReleaseUpdate;
|
||||
|
||||
EnableCheckPreReleaseUpdate = _config.CheckUpdateItem.CheckPreReleaseUpdate;
|
||||
this.WhenAnyValue(
|
||||
x => x.EnableCheckPreReleaseUpdate,
|
||||
y => y == true)
|
||||
.Subscribe(c => { _config.CheckUpdateItem.CheckPreReleaseUpdate = EnableCheckPreReleaseUpdate; });
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.EnableCheckPreReleaseUpdate,
|
||||
y => y == true)
|
||||
.Subscribe(c => { _config.CheckUpdateItem.CheckPreReleaseUpdate = EnableCheckPreReleaseUpdate; });
|
||||
RefreshCheckUpdateItems();
|
||||
}
|
||||
|
||||
RefreshCheckUpdateItems();
|
||||
}
|
||||
private void RefreshCheckUpdateItems()
|
||||
{
|
||||
_checkUpdateModel.Clear();
|
||||
|
||||
private void RefreshCheckUpdateItems()
|
||||
if (RuntimeInformation.ProcessArchitecture != Architecture.X86)
|
||||
{
|
||||
_checkUpdateModel.Clear();
|
||||
|
||||
if (RuntimeInformation.ProcessArchitecture != Architecture.X86)
|
||||
_checkUpdateModel.Add(GetCheckUpdateModel(_v2rayN));
|
||||
//Not Windows and under Win10
|
||||
if (!(Utils.IsWindows() && Environment.OSVersion.Version.Major < 10))
|
||||
{
|
||||
_checkUpdateModel.Add(GetCheckUpdateModel(_v2rayN));
|
||||
//Not Windows and under Win10
|
||||
if (!(Utils.IsWindows() && Environment.OSVersion.Version.Major < 10))
|
||||
{
|
||||
_checkUpdateModel.Add(GetCheckUpdateModel(ECoreType.Xray.ToString()));
|
||||
_checkUpdateModel.Add(GetCheckUpdateModel(ECoreType.mihomo.ToString()));
|
||||
_checkUpdateModel.Add(GetCheckUpdateModel(ECoreType.sing_box.ToString()));
|
||||
}
|
||||
}
|
||||
_checkUpdateModel.Add(GetCheckUpdateModel(_geo));
|
||||
}
|
||||
|
||||
private CheckUpdateModel GetCheckUpdateModel(string coreType)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
IsSelected = _config.CheckUpdateItem.SelectedCoreTypes?.Contains(coreType) ?? true,
|
||||
CoreType = coreType,
|
||||
Remarks = ResUI.menuCheckUpdate,
|
||||
};
|
||||
}
|
||||
|
||||
private async Task SaveSelectedCoreTypes()
|
||||
{
|
||||
_config.CheckUpdateItem.SelectedCoreTypes = _checkUpdateModel.Where(t => t.IsSelected == true).Select(t => t.CoreType ?? "").ToList();
|
||||
await ConfigHandler.SaveConfig(_config);
|
||||
}
|
||||
|
||||
private async Task CheckUpdate()
|
||||
{
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
await CheckUpdateTask();
|
||||
});
|
||||
}
|
||||
|
||||
private async Task CheckUpdateTask()
|
||||
{
|
||||
_lstUpdated.Clear();
|
||||
_lstUpdated = _checkUpdateModel.Where(x => x.IsSelected == true)
|
||||
.Select(x => new CheckUpdateModel() { CoreType = x.CoreType }).ToList();
|
||||
await SaveSelectedCoreTypes();
|
||||
|
||||
for (var k = _checkUpdateModel.Count - 1; k >= 0; k--)
|
||||
{
|
||||
var item = _checkUpdateModel[k];
|
||||
if (item.IsSelected != true)
|
||||
continue;
|
||||
|
||||
UpdateView(item.CoreType, "...");
|
||||
if (item.CoreType == _geo)
|
||||
{
|
||||
await CheckUpdateGeo();
|
||||
}
|
||||
else if (item.CoreType == _v2rayN)
|
||||
{
|
||||
await CheckUpdateN(EnableCheckPreReleaseUpdate);
|
||||
}
|
||||
else if (item.CoreType == ECoreType.Xray.ToString())
|
||||
{
|
||||
await CheckUpdateCore(item, EnableCheckPreReleaseUpdate);
|
||||
}
|
||||
else
|
||||
{
|
||||
await CheckUpdateCore(item, false);
|
||||
}
|
||||
}
|
||||
|
||||
await UpdateFinished();
|
||||
}
|
||||
|
||||
private void UpdatedPlusPlus(string coreType, string fileName)
|
||||
{
|
||||
var item = _lstUpdated.FirstOrDefault(x => x.CoreType == coreType);
|
||||
if (item == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
item.IsFinished = true;
|
||||
if (!fileName.IsNullOrEmpty())
|
||||
{
|
||||
item.FileName = fileName;
|
||||
_checkUpdateModel.Add(GetCheckUpdateModel(ECoreType.Xray.ToString()));
|
||||
_checkUpdateModel.Add(GetCheckUpdateModel(ECoreType.mihomo.ToString()));
|
||||
_checkUpdateModel.Add(GetCheckUpdateModel(ECoreType.sing_box.ToString()));
|
||||
}
|
||||
}
|
||||
_checkUpdateModel.Add(GetCheckUpdateModel(_geo));
|
||||
}
|
||||
|
||||
private async Task CheckUpdateGeo()
|
||||
private CheckUpdateModel GetCheckUpdateModel(string coreType)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
void _updateUI(bool success, string msg)
|
||||
IsSelected = _config.CheckUpdateItem.SelectedCoreTypes?.Contains(coreType) ?? true,
|
||||
CoreType = coreType,
|
||||
Remarks = ResUI.menuCheckUpdate,
|
||||
};
|
||||
}
|
||||
|
||||
private async Task SaveSelectedCoreTypes()
|
||||
{
|
||||
_config.CheckUpdateItem.SelectedCoreTypes = _checkUpdateModel.Where(t => t.IsSelected == true).Select(t => t.CoreType ?? "").ToList();
|
||||
await ConfigHandler.SaveConfig(_config);
|
||||
}
|
||||
|
||||
private async Task CheckUpdate()
|
||||
{
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
await CheckUpdateTask();
|
||||
});
|
||||
}
|
||||
|
||||
private async Task CheckUpdateTask()
|
||||
{
|
||||
_lstUpdated.Clear();
|
||||
_lstUpdated = _checkUpdateModel.Where(x => x.IsSelected == true)
|
||||
.Select(x => new CheckUpdateModel() { CoreType = x.CoreType }).ToList();
|
||||
await SaveSelectedCoreTypes();
|
||||
|
||||
for (var k = _checkUpdateModel.Count - 1; k >= 0; k--)
|
||||
{
|
||||
var item = _checkUpdateModel[k];
|
||||
if (item.IsSelected != true)
|
||||
continue;
|
||||
|
||||
UpdateView(item.CoreType, "...");
|
||||
if (item.CoreType == _geo)
|
||||
{
|
||||
UpdateView(_geo, msg);
|
||||
if (success)
|
||||
{
|
||||
UpdatedPlusPlus(_geo, "");
|
||||
}
|
||||
await CheckUpdateGeo();
|
||||
}
|
||||
await (new UpdateService()).UpdateGeoFileAll(_config, _updateUI)
|
||||
.ContinueWith(t =>
|
||||
{
|
||||
UpdatedPlusPlus(_geo, "");
|
||||
});
|
||||
}
|
||||
|
||||
private async Task CheckUpdateN(bool preRelease)
|
||||
{
|
||||
void _updateUI(bool success, string msg)
|
||||
else if (item.CoreType == _v2rayN)
|
||||
{
|
||||
UpdateView(_v2rayN, msg);
|
||||
if (success)
|
||||
{
|
||||
UpdateView(_v2rayN, ResUI.OperationSuccess);
|
||||
UpdatedPlusPlus(_v2rayN, msg);
|
||||
}
|
||||
await CheckUpdateN(EnableCheckPreReleaseUpdate);
|
||||
}
|
||||
await (new UpdateService()).CheckUpdateGuiN(_config, _updateUI, preRelease)
|
||||
.ContinueWith(t =>
|
||||
{
|
||||
UpdatedPlusPlus(_v2rayN, "");
|
||||
});
|
||||
}
|
||||
|
||||
private async Task CheckUpdateCore(CheckUpdateModel model, bool preRelease)
|
||||
{
|
||||
void _updateUI(bool success, string msg)
|
||||
else if (item.CoreType == ECoreType.Xray.ToString())
|
||||
{
|
||||
UpdateView(model.CoreType, msg);
|
||||
if (success)
|
||||
{
|
||||
UpdateView(model.CoreType, ResUI.MsgUpdateV2rayCoreSuccessfullyMore);
|
||||
|
||||
UpdatedPlusPlus(model.CoreType, msg);
|
||||
}
|
||||
}
|
||||
var type = (ECoreType)Enum.Parse(typeof(ECoreType), model.CoreType);
|
||||
await (new UpdateService()).CheckUpdateCore(type, _config, _updateUI, preRelease)
|
||||
.ContinueWith(t =>
|
||||
{
|
||||
UpdatedPlusPlus(model.CoreType, "");
|
||||
});
|
||||
}
|
||||
|
||||
private async Task UpdateFinished()
|
||||
{
|
||||
if (_lstUpdated.Count > 0 && _lstUpdated.Count(x => x.IsFinished == true) == _lstUpdated.Count)
|
||||
{
|
||||
_updateView?.Invoke(EViewAction.DispatcherCheckUpdateFinished, false);
|
||||
await Task.Delay(2000);
|
||||
await UpgradeCore();
|
||||
|
||||
if (_lstUpdated.Any(x => x.CoreType == _v2rayN && x.IsFinished == true))
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
UpgradeN();
|
||||
}
|
||||
await Task.Delay(1000);
|
||||
_updateView?.Invoke(EViewAction.DispatcherCheckUpdateFinished, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateFinishedResult(bool blReload)
|
||||
{
|
||||
if (blReload)
|
||||
{
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.Reload();
|
||||
await CheckUpdateCore(item, EnableCheckPreReleaseUpdate);
|
||||
}
|
||||
else
|
||||
{
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.CloseCore();
|
||||
await CheckUpdateCore(item, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpgradeN()
|
||||
await UpdateFinished();
|
||||
}
|
||||
|
||||
private void UpdatedPlusPlus(string coreType, string fileName)
|
||||
{
|
||||
var item = _lstUpdated.FirstOrDefault(x => x.CoreType == coreType);
|
||||
if (item == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var fileName = _lstUpdated.FirstOrDefault(x => x.CoreType == _v2rayN)?.FileName;
|
||||
if (fileName.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!Utils.UpgradeAppExists(out _))
|
||||
{
|
||||
UpdateView(_v2rayN, ResUI.UpgradeAppNotExistTip);
|
||||
return;
|
||||
}
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.UpgradeApp(fileName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UpdateView(_v2rayN, ex.Message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
private async Task UpgradeCore()
|
||||
item.IsFinished = true;
|
||||
if (!fileName.IsNullOrEmpty())
|
||||
{
|
||||
foreach (var item in _lstUpdated)
|
||||
{
|
||||
if (item.FileName.IsNullOrEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var fileName = item.FileName;
|
||||
if (!File.Exists(fileName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var toPath = Utils.GetBinPath("", item.CoreType);
|
||||
|
||||
if (fileName.Contains(".tar.gz"))
|
||||
{
|
||||
FileManager.DecompressTarFile(fileName, toPath);
|
||||
var dir = new DirectoryInfo(toPath);
|
||||
if (dir.Exists)
|
||||
{
|
||||
foreach (var subDir in dir.GetDirectories())
|
||||
{
|
||||
FileManager.CopyDirectory(subDir.FullName, toPath, false, true);
|
||||
subDir.Delete(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (fileName.Contains(".gz"))
|
||||
{
|
||||
FileManager.DecompressFile(fileName, toPath, item.CoreType);
|
||||
}
|
||||
else
|
||||
{
|
||||
FileManager.ZipExtractToFile(fileName, toPath, "geo");
|
||||
}
|
||||
|
||||
if (Utils.IsNonWindows())
|
||||
{
|
||||
var filesList = (new DirectoryInfo(toPath)).GetFiles().Select(u => u.FullName).ToList();
|
||||
foreach (var file in filesList)
|
||||
{
|
||||
await Utils.SetLinuxChmod(Path.Combine(toPath, item.CoreType.ToLower()));
|
||||
}
|
||||
}
|
||||
|
||||
UpdateView(item.CoreType, ResUI.MsgUpdateV2rayCoreSuccessfully);
|
||||
|
||||
if (File.Exists(fileName))
|
||||
{
|
||||
File.Delete(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateView(string coreType, string msg)
|
||||
{
|
||||
var item = new CheckUpdateModel()
|
||||
{
|
||||
CoreType = coreType,
|
||||
Remarks = msg,
|
||||
};
|
||||
_updateView?.Invoke(EViewAction.DispatcherCheckUpdate, item);
|
||||
}
|
||||
|
||||
public void UpdateViewResult(CheckUpdateModel model)
|
||||
{
|
||||
var found = _checkUpdateModel.FirstOrDefault(t => t.CoreType == model.CoreType);
|
||||
if (found == null)
|
||||
return;
|
||||
var itemCopy = JsonUtils.DeepCopy(found);
|
||||
itemCopy.Remarks = model.Remarks;
|
||||
_checkUpdateModel.Replace(found, itemCopy);
|
||||
item.FileName = fileName;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CheckUpdateGeo()
|
||||
{
|
||||
void _updateUI(bool success, string msg)
|
||||
{
|
||||
UpdateView(_geo, msg);
|
||||
if (success)
|
||||
{
|
||||
UpdatedPlusPlus(_geo, "");
|
||||
}
|
||||
}
|
||||
await (new UpdateService()).UpdateGeoFileAll(_config, _updateUI)
|
||||
.ContinueWith(t =>
|
||||
{
|
||||
UpdatedPlusPlus(_geo, "");
|
||||
});
|
||||
}
|
||||
|
||||
private async Task CheckUpdateN(bool preRelease)
|
||||
{
|
||||
void _updateUI(bool success, string msg)
|
||||
{
|
||||
UpdateView(_v2rayN, msg);
|
||||
if (success)
|
||||
{
|
||||
UpdateView(_v2rayN, ResUI.OperationSuccess);
|
||||
UpdatedPlusPlus(_v2rayN, msg);
|
||||
}
|
||||
}
|
||||
await (new UpdateService()).CheckUpdateGuiN(_config, _updateUI, preRelease)
|
||||
.ContinueWith(t =>
|
||||
{
|
||||
UpdatedPlusPlus(_v2rayN, "");
|
||||
});
|
||||
}
|
||||
|
||||
private async Task CheckUpdateCore(CheckUpdateModel model, bool preRelease)
|
||||
{
|
||||
void _updateUI(bool success, string msg)
|
||||
{
|
||||
UpdateView(model.CoreType, msg);
|
||||
if (success)
|
||||
{
|
||||
UpdateView(model.CoreType, ResUI.MsgUpdateV2rayCoreSuccessfullyMore);
|
||||
|
||||
UpdatedPlusPlus(model.CoreType, msg);
|
||||
}
|
||||
}
|
||||
var type = (ECoreType)Enum.Parse(typeof(ECoreType), model.CoreType);
|
||||
await (new UpdateService()).CheckUpdateCore(type, _config, _updateUI, preRelease)
|
||||
.ContinueWith(t =>
|
||||
{
|
||||
UpdatedPlusPlus(model.CoreType, "");
|
||||
});
|
||||
}
|
||||
|
||||
private async Task UpdateFinished()
|
||||
{
|
||||
if (_lstUpdated.Count > 0 && _lstUpdated.Count(x => x.IsFinished == true) == _lstUpdated.Count)
|
||||
{
|
||||
_updateView?.Invoke(EViewAction.DispatcherCheckUpdateFinished, false);
|
||||
await Task.Delay(2000);
|
||||
await UpgradeCore();
|
||||
|
||||
if (_lstUpdated.Any(x => x.CoreType == _v2rayN && x.IsFinished == true))
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
UpgradeN();
|
||||
}
|
||||
await Task.Delay(1000);
|
||||
_updateView?.Invoke(EViewAction.DispatcherCheckUpdateFinished, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateFinishedResult(bool blReload)
|
||||
{
|
||||
if (blReload)
|
||||
{
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.Reload();
|
||||
}
|
||||
else
|
||||
{
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.CloseCore();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpgradeN()
|
||||
{
|
||||
try
|
||||
{
|
||||
var fileName = _lstUpdated.FirstOrDefault(x => x.CoreType == _v2rayN)?.FileName;
|
||||
if (fileName.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!Utils.UpgradeAppExists(out _))
|
||||
{
|
||||
UpdateView(_v2rayN, ResUI.UpgradeAppNotExistTip);
|
||||
return;
|
||||
}
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.UpgradeApp(fileName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UpdateView(_v2rayN, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpgradeCore()
|
||||
{
|
||||
foreach (var item in _lstUpdated)
|
||||
{
|
||||
if (item.FileName.IsNullOrEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var fileName = item.FileName;
|
||||
if (!File.Exists(fileName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var toPath = Utils.GetBinPath("", item.CoreType);
|
||||
|
||||
if (fileName.Contains(".tar.gz"))
|
||||
{
|
||||
FileManager.DecompressTarFile(fileName, toPath);
|
||||
var dir = new DirectoryInfo(toPath);
|
||||
if (dir.Exists)
|
||||
{
|
||||
foreach (var subDir in dir.GetDirectories())
|
||||
{
|
||||
FileManager.CopyDirectory(subDir.FullName, toPath, false, true);
|
||||
subDir.Delete(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (fileName.Contains(".gz"))
|
||||
{
|
||||
FileManager.DecompressFile(fileName, toPath, item.CoreType);
|
||||
}
|
||||
else
|
||||
{
|
||||
FileManager.ZipExtractToFile(fileName, toPath, "geo");
|
||||
}
|
||||
|
||||
if (Utils.IsNonWindows())
|
||||
{
|
||||
var filesList = (new DirectoryInfo(toPath)).GetFiles().Select(u => u.FullName).ToList();
|
||||
foreach (var file in filesList)
|
||||
{
|
||||
await Utils.SetLinuxChmod(Path.Combine(toPath, item.CoreType.ToLower()));
|
||||
}
|
||||
}
|
||||
|
||||
UpdateView(item.CoreType, ResUI.MsgUpdateV2rayCoreSuccessfully);
|
||||
|
||||
if (File.Exists(fileName))
|
||||
{
|
||||
File.Delete(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateView(string coreType, string msg)
|
||||
{
|
||||
var item = new CheckUpdateModel()
|
||||
{
|
||||
CoreType = coreType,
|
||||
Remarks = msg,
|
||||
};
|
||||
_updateView?.Invoke(EViewAction.DispatcherCheckUpdate, item);
|
||||
}
|
||||
|
||||
public void UpdateViewResult(CheckUpdateModel model)
|
||||
{
|
||||
var found = _checkUpdateModel.FirstOrDefault(t => t.CoreType == model.CoreType);
|
||||
if (found == null)
|
||||
return;
|
||||
var itemCopy = JsonUtils.DeepCopy(found);
|
||||
itemCopy.Remarks = model.Remarks;
|
||||
_checkUpdateModel.Replace(found, itemCopy);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,151 +5,150 @@ using DynamicData.Binding;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
|
||||
namespace ServiceLib.ViewModels
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
public class ClashConnectionsViewModel : MyReactiveObject
|
||||
{
|
||||
public class ClashConnectionsViewModel : MyReactiveObject
|
||||
private IObservableCollection<ClashConnectionModel> _connectionItems = new ObservableCollectionExtended<ClashConnectionModel>();
|
||||
public IObservableCollection<ClashConnectionModel> ConnectionItems => _connectionItems;
|
||||
|
||||
[Reactive]
|
||||
public ClashConnectionModel SelectedSource { get; set; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> ConnectionCloseCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> ConnectionCloseAllCmd { get; }
|
||||
|
||||
[Reactive]
|
||||
public string HostFilter { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool AutoRefresh { get; set; }
|
||||
|
||||
public ClashConnectionsViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
private IObservableCollection<ClashConnectionModel> _connectionItems = new ObservableCollectionExtended<ClashConnectionModel>();
|
||||
public IObservableCollection<ClashConnectionModel> ConnectionItems => _connectionItems;
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
AutoRefresh = _config.ClashUIItem.ConnectionsAutoRefresh;
|
||||
|
||||
[Reactive]
|
||||
public ClashConnectionModel SelectedSource { get; set; }
|
||||
var canEditRemove = this.WhenAnyValue(
|
||||
x => x.SelectedSource,
|
||||
selectedSource => selectedSource != null && selectedSource.Id.IsNotEmpty());
|
||||
|
||||
public ReactiveCommand<Unit, Unit> ConnectionCloseCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> ConnectionCloseAllCmd { get; }
|
||||
|
||||
[Reactive]
|
||||
public string HostFilter { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool AutoRefresh { get; set; }
|
||||
|
||||
public ClashConnectionsViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
this.WhenAnyValue(
|
||||
x => x.AutoRefresh,
|
||||
y => y == true)
|
||||
.Subscribe(c => { _config.ClashUIItem.ConnectionsAutoRefresh = AutoRefresh; });
|
||||
ConnectionCloseCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
AutoRefresh = _config.ClashUIItem.ConnectionsAutoRefresh;
|
||||
await ClashConnectionClose(false);
|
||||
}, canEditRemove);
|
||||
|
||||
var canEditRemove = this.WhenAnyValue(
|
||||
x => x.SelectedSource,
|
||||
selectedSource => selectedSource != null && selectedSource.Id.IsNotEmpty());
|
||||
ConnectionCloseAllCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await ClashConnectionClose(true);
|
||||
});
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.AutoRefresh,
|
||||
y => y == true)
|
||||
.Subscribe(c => { _config.ClashUIItem.ConnectionsAutoRefresh = AutoRefresh; });
|
||||
ConnectionCloseCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await ClashConnectionClose(false);
|
||||
}, canEditRemove);
|
||||
_ = Init();
|
||||
}
|
||||
|
||||
ConnectionCloseAllCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await ClashConnectionClose(true);
|
||||
});
|
||||
private async Task Init()
|
||||
{
|
||||
await DelayTestTask();
|
||||
}
|
||||
|
||||
_ = Init();
|
||||
private async Task GetClashConnections()
|
||||
{
|
||||
var ret = await ClashApiHandler.Instance.GetClashConnectionsAsync();
|
||||
if (ret == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
private async Task Init()
|
||||
_ = _updateView?.Invoke(EViewAction.DispatcherRefreshConnections, ret?.connections);
|
||||
}
|
||||
|
||||
public void RefreshConnections(List<ConnectionItem>? connections)
|
||||
{
|
||||
_connectionItems.Clear();
|
||||
|
||||
var dtNow = DateTime.Now;
|
||||
var lstModel = new List<ClashConnectionModel>();
|
||||
foreach (var item in connections ?? new())
|
||||
{
|
||||
await DelayTestTask();
|
||||
var host = $"{(item.metadata.host.IsNullOrEmpty() ? item.metadata.destinationIP : item.metadata.host)}:{item.metadata.destinationPort}";
|
||||
if (HostFilter.IsNotEmpty() && !host.Contains(HostFilter))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var model = new ClashConnectionModel
|
||||
{
|
||||
Id = item.id,
|
||||
Network = item.metadata.network,
|
||||
Type = item.metadata.type,
|
||||
Host = host,
|
||||
Time = (dtNow - item.start).TotalSeconds < 0 ? 1 : (dtNow - item.start).TotalSeconds,
|
||||
Elapsed = (dtNow - item.start).ToString(@"hh\:mm\:ss"),
|
||||
Chain = $"{item.rule} , {string.Join("->", item.chains ?? new())}"
|
||||
};
|
||||
|
||||
lstModel.Add(model);
|
||||
}
|
||||
if (lstModel.Count <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
private async Task GetClashConnections()
|
||||
_connectionItems.AddRange(lstModel);
|
||||
}
|
||||
|
||||
public async Task ClashConnectionClose(bool all)
|
||||
{
|
||||
var id = string.Empty;
|
||||
if (!all)
|
||||
{
|
||||
var ret = await ClashApiHandler.Instance.GetClashConnectionsAsync();
|
||||
if (ret == null)
|
||||
var item = SelectedSource;
|
||||
if (item is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_ = _updateView?.Invoke(EViewAction.DispatcherRefreshConnections, ret?.connections);
|
||||
id = item.Id;
|
||||
}
|
||||
|
||||
public void RefreshConnections(List<ConnectionItem>? connections)
|
||||
else
|
||||
{
|
||||
_connectionItems.Clear();
|
||||
}
|
||||
await ClashApiHandler.Instance.ClashConnectionClose(id);
|
||||
await GetClashConnections();
|
||||
}
|
||||
|
||||
var dtNow = DateTime.Now;
|
||||
var lstModel = new List<ClashConnectionModel>();
|
||||
foreach (var item in connections ?? new())
|
||||
public async Task DelayTestTask()
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
var numOfExecuted = 1;
|
||||
while (true)
|
||||
{
|
||||
var host = $"{(item.metadata.host.IsNullOrEmpty() ? item.metadata.destinationIP : item.metadata.host)}:{item.metadata.destinationPort}";
|
||||
if (HostFilter.IsNotEmpty() && !host.Contains(HostFilter))
|
||||
await Task.Delay(1000 * 5);
|
||||
numOfExecuted++;
|
||||
if (!(AutoRefresh && _config.UiItem.ShowInTaskbar && _config.IsRunningCore(ECoreType.sing_box)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var model = new ClashConnectionModel
|
||||
if (_config.ClashUIItem.ConnectionsRefreshInterval <= 0)
|
||||
{
|
||||
Id = item.id,
|
||||
Network = item.metadata.network,
|
||||
Type = item.metadata.type,
|
||||
Host = host,
|
||||
Time = (dtNow - item.start).TotalSeconds < 0 ? 1 : (dtNow - item.start).TotalSeconds,
|
||||
Elapsed = (dtNow - item.start).ToString(@"hh\:mm\:ss"),
|
||||
Chain = $"{item.rule} , {string.Join("->", item.chains ?? new())}"
|
||||
};
|
||||
|
||||
lstModel.Add(model);
|
||||
}
|
||||
if (lstModel.Count <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_connectionItems.AddRange(lstModel);
|
||||
}
|
||||
|
||||
public async Task ClashConnectionClose(bool all)
|
||||
{
|
||||
var id = string.Empty;
|
||||
if (!all)
|
||||
{
|
||||
var item = SelectedSource;
|
||||
if (item is null)
|
||||
{
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
id = item.Id;
|
||||
}
|
||||
else
|
||||
{
|
||||
_connectionItems.Clear();
|
||||
}
|
||||
await ClashApiHandler.Instance.ClashConnectionClose(id);
|
||||
await GetClashConnections();
|
||||
}
|
||||
|
||||
public async Task DelayTestTask()
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
var numOfExecuted = 1;
|
||||
while (true)
|
||||
if (numOfExecuted % _config.ClashUIItem.ConnectionsRefreshInterval != 0)
|
||||
{
|
||||
await Task.Delay(1000 * 5);
|
||||
numOfExecuted++;
|
||||
if (!(AutoRefresh && _config.UiItem.ShowInTaskbar && _config.IsRunningCore(ECoreType.sing_box)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_config.ClashUIItem.ConnectionsRefreshInterval <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (numOfExecuted % _config.ClashUIItem.ConnectionsRefreshInterval != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
await GetClashConnections();
|
||||
continue;
|
||||
}
|
||||
});
|
||||
await GetClashConnections();
|
||||
}
|
||||
});
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,436 +7,435 @@ using ReactiveUI.Fody.Helpers;
|
||||
using static ServiceLib.Models.ClashProviders;
|
||||
using static ServiceLib.Models.ClashProxies;
|
||||
|
||||
namespace ServiceLib.ViewModels
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
public class ClashProxiesViewModel : MyReactiveObject
|
||||
{
|
||||
public class ClashProxiesViewModel : MyReactiveObject
|
||||
private Dictionary<string, ProxiesItem>? _proxies;
|
||||
private Dictionary<string, ProvidersItem>? _providers;
|
||||
private readonly int _delayTimeout = 99999999;
|
||||
|
||||
private IObservableCollection<ClashProxyModel> _proxyGroups = new ObservableCollectionExtended<ClashProxyModel>();
|
||||
private IObservableCollection<ClashProxyModel> _proxyDetails = new ObservableCollectionExtended<ClashProxyModel>();
|
||||
|
||||
public IObservableCollection<ClashProxyModel> ProxyGroups => _proxyGroups;
|
||||
public IObservableCollection<ClashProxyModel> ProxyDetails => _proxyDetails;
|
||||
|
||||
[Reactive]
|
||||
public ClashProxyModel SelectedGroup { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public ClashProxyModel SelectedDetail { get; set; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> ProxiesReloadCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> ProxiesDelayTestCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> ProxiesDelayTestPartCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> ProxiesSelectActivityCmd { get; }
|
||||
|
||||
[Reactive]
|
||||
public int RuleModeSelected { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public int SortingSelected { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool AutoRefresh { get; set; }
|
||||
|
||||
public ClashProxiesViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
private Dictionary<string, ProxiesItem>? _proxies;
|
||||
private Dictionary<string, ProvidersItem>? _providers;
|
||||
private readonly int _delayTimeout = 99999999;
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
|
||||
private IObservableCollection<ClashProxyModel> _proxyGroups = new ObservableCollectionExtended<ClashProxyModel>();
|
||||
private IObservableCollection<ClashProxyModel> _proxyDetails = new ObservableCollectionExtended<ClashProxyModel>();
|
||||
|
||||
public IObservableCollection<ClashProxyModel> ProxyGroups => _proxyGroups;
|
||||
public IObservableCollection<ClashProxyModel> ProxyDetails => _proxyDetails;
|
||||
|
||||
[Reactive]
|
||||
public ClashProxyModel SelectedGroup { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public ClashProxyModel SelectedDetail { get; set; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> ProxiesReloadCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> ProxiesDelayTestCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> ProxiesDelayTestPartCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> ProxiesSelectActivityCmd { get; }
|
||||
|
||||
[Reactive]
|
||||
public int RuleModeSelected { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public int SortingSelected { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool AutoRefresh { get; set; }
|
||||
|
||||
public ClashProxiesViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
ProxiesReloadCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
await ProxiesReload();
|
||||
});
|
||||
ProxiesDelayTestCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await ProxiesDelayTest(true);
|
||||
});
|
||||
|
||||
ProxiesReloadCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await ProxiesReload();
|
||||
});
|
||||
ProxiesDelayTestCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await ProxiesDelayTest(true);
|
||||
});
|
||||
ProxiesDelayTestPartCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await ProxiesDelayTest(false);
|
||||
});
|
||||
ProxiesSelectActivityCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await SetActiveProxy();
|
||||
});
|
||||
|
||||
ProxiesDelayTestPartCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await ProxiesDelayTest(false);
|
||||
});
|
||||
ProxiesSelectActivityCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await SetActiveProxy();
|
||||
});
|
||||
SelectedGroup = new();
|
||||
SelectedDetail = new();
|
||||
AutoRefresh = _config.ClashUIItem.ProxiesAutoRefresh;
|
||||
SortingSelected = _config.ClashUIItem.ProxiesSorting;
|
||||
RuleModeSelected = (int)_config.ClashUIItem.RuleMode;
|
||||
|
||||
SelectedGroup = new();
|
||||
SelectedDetail = new();
|
||||
AutoRefresh = _config.ClashUIItem.ProxiesAutoRefresh;
|
||||
SortingSelected = _config.ClashUIItem.ProxiesSorting;
|
||||
RuleModeSelected = (int)_config.ClashUIItem.RuleMode;
|
||||
this.WhenAnyValue(
|
||||
x => x.SelectedGroup,
|
||||
y => y != null && y.Name.IsNotEmpty())
|
||||
.Subscribe(c => RefreshProxyDetails(c));
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.SelectedGroup,
|
||||
y => y != null && y.Name.IsNotEmpty())
|
||||
.Subscribe(c => RefreshProxyDetails(c));
|
||||
this.WhenAnyValue(
|
||||
x => x.RuleModeSelected,
|
||||
y => y >= 0)
|
||||
.Subscribe(async c => await DoRuleModeSelected(c));
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.RuleModeSelected,
|
||||
y => y >= 0)
|
||||
.Subscribe(async c => await DoRuleModeSelected(c));
|
||||
this.WhenAnyValue(
|
||||
x => x.SortingSelected,
|
||||
y => y >= 0)
|
||||
.Subscribe(c => DoSortingSelected(c));
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.SortingSelected,
|
||||
y => y >= 0)
|
||||
.Subscribe(c => DoSortingSelected(c));
|
||||
this.WhenAnyValue(
|
||||
x => x.AutoRefresh,
|
||||
y => y == true)
|
||||
.Subscribe(c => { _config.ClashUIItem.ProxiesAutoRefresh = AutoRefresh; });
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.AutoRefresh,
|
||||
y => y == true)
|
||||
.Subscribe(c => { _config.ClashUIItem.ProxiesAutoRefresh = AutoRefresh; });
|
||||
_ = Init();
|
||||
}
|
||||
|
||||
_ = Init();
|
||||
private async Task Init()
|
||||
{
|
||||
await DelayTestTask();
|
||||
}
|
||||
|
||||
private async Task DoRuleModeSelected(bool c)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (_config.ClashUIItem.RuleMode == (ERuleMode)RuleModeSelected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
await SetRuleModeCheck((ERuleMode)RuleModeSelected);
|
||||
}
|
||||
|
||||
public async Task SetRuleModeCheck(ERuleMode mode)
|
||||
{
|
||||
if (_config.ClashUIItem.RuleMode == mode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
await SetRuleMode(mode);
|
||||
}
|
||||
|
||||
private void DoSortingSelected(bool c)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (SortingSelected != _config.ClashUIItem.ProxiesSorting)
|
||||
{
|
||||
_config.ClashUIItem.ProxiesSorting = SortingSelected;
|
||||
}
|
||||
|
||||
private async Task Init()
|
||||
RefreshProxyDetails(c);
|
||||
}
|
||||
|
||||
public async Task ProxiesReload()
|
||||
{
|
||||
await GetClashProxies(true);
|
||||
await ProxiesDelayTest();
|
||||
}
|
||||
|
||||
#region proxy function
|
||||
|
||||
private async Task SetRuleMode(ERuleMode mode)
|
||||
{
|
||||
_config.ClashUIItem.RuleMode = mode;
|
||||
|
||||
if (mode != ERuleMode.Unchanged)
|
||||
{
|
||||
await DelayTestTask();
|
||||
}
|
||||
|
||||
private async Task DoRuleModeSelected(bool c)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (_config.ClashUIItem.RuleMode == (ERuleMode)RuleModeSelected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
await SetRuleModeCheck((ERuleMode)RuleModeSelected);
|
||||
}
|
||||
|
||||
public async Task SetRuleModeCheck(ERuleMode mode)
|
||||
{
|
||||
if (_config.ClashUIItem.RuleMode == mode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
await SetRuleMode(mode);
|
||||
}
|
||||
|
||||
private void DoSortingSelected(bool c)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (SortingSelected != _config.ClashUIItem.ProxiesSorting)
|
||||
{
|
||||
_config.ClashUIItem.ProxiesSorting = SortingSelected;
|
||||
}
|
||||
|
||||
RefreshProxyDetails(c);
|
||||
}
|
||||
|
||||
public async Task ProxiesReload()
|
||||
{
|
||||
await GetClashProxies(true);
|
||||
await ProxiesDelayTest();
|
||||
}
|
||||
|
||||
#region proxy function
|
||||
|
||||
private async Task SetRuleMode(ERuleMode mode)
|
||||
{
|
||||
_config.ClashUIItem.RuleMode = mode;
|
||||
|
||||
if (mode != ERuleMode.Unchanged)
|
||||
{
|
||||
Dictionary<string, string> headers = new()
|
||||
Dictionary<string, string> headers = new()
|
||||
{
|
||||
{ "mode", mode.ToString().ToLower() }
|
||||
};
|
||||
await ClashApiHandler.Instance.ClashConfigUpdate(headers);
|
||||
}
|
||||
await ClashApiHandler.Instance.ClashConfigUpdate(headers);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task GetClashProxies(bool refreshUI)
|
||||
private async Task GetClashProxies(bool refreshUI)
|
||||
{
|
||||
var ret = await ClashApiHandler.Instance.GetClashProxiesAsync();
|
||||
if (ret?.Item1 == null || ret.Item2 == null)
|
||||
{
|
||||
var ret = await ClashApiHandler.Instance.GetClashProxiesAsync();
|
||||
if (ret?.Item1 == null || ret.Item2 == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_proxies = ret.Item1.proxies;
|
||||
_providers = ret?.Item2.providers;
|
||||
|
||||
if (refreshUI)
|
||||
{
|
||||
_updateView?.Invoke(EViewAction.DispatcherRefreshProxyGroups, null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
_proxies = ret.Item1.proxies;
|
||||
_providers = ret?.Item2.providers;
|
||||
|
||||
public void RefreshProxyGroups()
|
||||
if (refreshUI)
|
||||
{
|
||||
var selectedName = SelectedGroup?.Name;
|
||||
_proxyGroups.Clear();
|
||||
_updateView?.Invoke(EViewAction.DispatcherRefreshProxyGroups, null);
|
||||
}
|
||||
}
|
||||
|
||||
var proxyGroups = ClashApiHandler.Instance.GetClashProxyGroups();
|
||||
if (proxyGroups != null && proxyGroups.Count > 0)
|
||||
{
|
||||
foreach (var it in proxyGroups)
|
||||
{
|
||||
if (it.name.IsNullOrEmpty() || !_proxies.ContainsKey(it.name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var item = _proxies[it.name];
|
||||
if (!Global.allowSelectType.Contains(item.type.ToLower()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
_proxyGroups.Add(new ClashProxyModel()
|
||||
{
|
||||
Now = item.now,
|
||||
Name = item.name,
|
||||
Type = item.type
|
||||
});
|
||||
}
|
||||
}
|
||||
public void RefreshProxyGroups()
|
||||
{
|
||||
var selectedName = SelectedGroup?.Name;
|
||||
_proxyGroups.Clear();
|
||||
|
||||
//from api
|
||||
foreach (KeyValuePair<string, ProxiesItem> kv in _proxies)
|
||||
var proxyGroups = ClashApiHandler.Instance.GetClashProxyGroups();
|
||||
if (proxyGroups != null && proxyGroups.Count > 0)
|
||||
{
|
||||
foreach (var it in proxyGroups)
|
||||
{
|
||||
if (!Global.allowSelectType.Contains(kv.Value.type.ToLower()))
|
||||
if (it.name.IsNullOrEmpty() || !_proxies.ContainsKey(it.name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var item = _proxyGroups.FirstOrDefault(t => t.Name == kv.Key);
|
||||
if (item != null && item.Name.IsNotEmpty())
|
||||
var item = _proxies[it.name];
|
||||
if (!Global.allowSelectType.Contains(item.type.ToLower()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
_proxyGroups.Add(new ClashProxyModel()
|
||||
{
|
||||
Now = kv.Value.now,
|
||||
Name = kv.Key,
|
||||
Type = kv.Value.type
|
||||
Now = item.now,
|
||||
Name = item.name,
|
||||
Type = item.type
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (_proxyGroups != null && _proxyGroups.Count > 0)
|
||||
//from api
|
||||
foreach (KeyValuePair<string, ProxiesItem> kv in _proxies)
|
||||
{
|
||||
if (!Global.allowSelectType.Contains(kv.Value.type.ToLower()))
|
||||
{
|
||||
if (selectedName != null && _proxyGroups.Any(t => t.Name == selectedName))
|
||||
{
|
||||
SelectedGroup = _proxyGroups.FirstOrDefault(t => t.Name == selectedName);
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedGroup = _proxyGroups.First();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
var item = _proxyGroups.FirstOrDefault(t => t.Name == kv.Key);
|
||||
if (item != null && item.Name.IsNotEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
_proxyGroups.Add(new ClashProxyModel()
|
||||
{
|
||||
Now = kv.Value.now,
|
||||
Name = kv.Key,
|
||||
Type = kv.Value.type
|
||||
});
|
||||
}
|
||||
|
||||
if (_proxyGroups != null && _proxyGroups.Count > 0)
|
||||
{
|
||||
if (selectedName != null && _proxyGroups.Any(t => t.Name == selectedName))
|
||||
{
|
||||
SelectedGroup = _proxyGroups.FirstOrDefault(t => t.Name == selectedName);
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedGroup = new();
|
||||
SelectedGroup = _proxyGroups.First();
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshProxyDetails(bool c)
|
||||
else
|
||||
{
|
||||
_proxyDetails.Clear();
|
||||
if (!c)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var name = SelectedGroup?.Name;
|
||||
if (name.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (_proxies == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
SelectedGroup = new();
|
||||
}
|
||||
}
|
||||
|
||||
_proxies.TryGetValue(name, out var proxy);
|
||||
if (proxy?.all == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var lstDetails = new List<ClashProxyModel>();
|
||||
foreach (var item in proxy.all)
|
||||
{
|
||||
var proxy2 = TryGetProxy(item);
|
||||
if (proxy2 == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var delay = proxy2.history?.Count > 0 ? proxy2.history.Last().delay : -1;
|
||||
|
||||
lstDetails.Add(new ClashProxyModel()
|
||||
{
|
||||
IsActive = item == proxy.now,
|
||||
Name = item,
|
||||
Type = proxy2.type,
|
||||
Delay = delay <= 0 ? _delayTimeout : delay,
|
||||
DelayName = delay <= 0 ? string.Empty : $"{delay}ms",
|
||||
});
|
||||
}
|
||||
//sort
|
||||
switch (SortingSelected)
|
||||
{
|
||||
case 0:
|
||||
lstDetails = lstDetails.OrderBy(t => t.Delay).ToList();
|
||||
break;
|
||||
|
||||
case 1:
|
||||
lstDetails = lstDetails.OrderBy(t => t.Name).ToList();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
_proxyDetails.AddRange(lstDetails);
|
||||
private void RefreshProxyDetails(bool c)
|
||||
{
|
||||
_proxyDetails.Clear();
|
||||
if (!c)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var name = SelectedGroup?.Name;
|
||||
if (name.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (_proxies == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
private ProxiesItem? TryGetProxy(string name)
|
||||
_proxies.TryGetValue(name, out var proxy);
|
||||
if (proxy?.all == null)
|
||||
{
|
||||
if (_proxies is null)
|
||||
return null;
|
||||
_proxies.TryGetValue(name, out var proxy2);
|
||||
if (proxy2 != null)
|
||||
return;
|
||||
}
|
||||
var lstDetails = new List<ClashProxyModel>();
|
||||
foreach (var item in proxy.all)
|
||||
{
|
||||
var proxy2 = TryGetProxy(item);
|
||||
if (proxy2 == null)
|
||||
{
|
||||
return proxy2;
|
||||
continue;
|
||||
}
|
||||
//from providers
|
||||
if (_providers != null)
|
||||
var delay = proxy2.history?.Count > 0 ? proxy2.history.Last().delay : -1;
|
||||
|
||||
lstDetails.Add(new ClashProxyModel()
|
||||
{
|
||||
foreach (KeyValuePair<string, ProvidersItem> kv in _providers)
|
||||
IsActive = item == proxy.now,
|
||||
Name = item,
|
||||
Type = proxy2.type,
|
||||
Delay = delay <= 0 ? _delayTimeout : delay,
|
||||
DelayName = delay <= 0 ? string.Empty : $"{delay}ms",
|
||||
});
|
||||
}
|
||||
//sort
|
||||
switch (SortingSelected)
|
||||
{
|
||||
case 0:
|
||||
lstDetails = lstDetails.OrderBy(t => t.Delay).ToList();
|
||||
break;
|
||||
|
||||
case 1:
|
||||
lstDetails = lstDetails.OrderBy(t => t.Name).ToList();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
_proxyDetails.AddRange(lstDetails);
|
||||
}
|
||||
|
||||
private ProxiesItem? TryGetProxy(string name)
|
||||
{
|
||||
if (_proxies is null)
|
||||
return null;
|
||||
_proxies.TryGetValue(name, out var proxy2);
|
||||
if (proxy2 != null)
|
||||
{
|
||||
return proxy2;
|
||||
}
|
||||
//from providers
|
||||
if (_providers != null)
|
||||
{
|
||||
foreach (KeyValuePair<string, ProvidersItem> kv in _providers)
|
||||
{
|
||||
if (Global.proxyVehicleType.Contains(kv.Value.vehicleType.ToLower()))
|
||||
{
|
||||
if (Global.proxyVehicleType.Contains(kv.Value.vehicleType.ToLower()))
|
||||
var proxy3 = kv.Value.proxies.FirstOrDefault(t => t.name == name);
|
||||
if (proxy3 != null)
|
||||
{
|
||||
var proxy3 = kv.Value.proxies.FirstOrDefault(t => t.name == name);
|
||||
if (proxy3 != null)
|
||||
{
|
||||
return proxy3;
|
||||
}
|
||||
return proxy3;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task SetActiveProxy()
|
||||
{
|
||||
if (SelectedGroup == null || SelectedGroup.Name.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (SelectedDetail == null || SelectedDetail.Name.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var name = SelectedGroup.Name;
|
||||
if (name.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var nameNode = SelectedDetail.Name;
|
||||
if (nameNode.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var selectedProxy = TryGetProxy(name);
|
||||
if (selectedProxy == null || selectedProxy.type != "Selector")
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
|
||||
return;
|
||||
}
|
||||
|
||||
await ClashApiHandler.Instance.ClashSetActiveProxy(name, nameNode);
|
||||
|
||||
selectedProxy.now = nameNode;
|
||||
var group = _proxyGroups.FirstOrDefault(it => it.Name == SelectedGroup.Name);
|
||||
if (group != null)
|
||||
{
|
||||
group.Now = nameNode;
|
||||
var group2 = JsonUtils.DeepCopy(group);
|
||||
_proxyGroups.Replace(group, group2);
|
||||
|
||||
SelectedGroup = group2;
|
||||
}
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
}
|
||||
|
||||
private async Task ProxiesDelayTest(bool blAll = true)
|
||||
{
|
||||
ClashApiHandler.Instance.ClashProxiesDelayTest(blAll, _proxyDetails.ToList(), (item, result) =>
|
||||
{
|
||||
if (item == null || result.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_updateView?.Invoke(EViewAction.DispatcherProxiesDelayTest, new SpeedTestResult() { IndexId = item.Name, Delay = result });
|
||||
});
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void ProxiesDelayTestResult(SpeedTestResult result)
|
||||
{
|
||||
//UpdateHandler(false, $"{item.name}={result}");
|
||||
var detail = _proxyDetails.FirstOrDefault(it => it.Name == result.IndexId);
|
||||
if (detail == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var dicResult = JsonUtils.Deserialize<Dictionary<string, object>>(result.Delay);
|
||||
if (dicResult != null && dicResult.TryGetValue("delay", out var value))
|
||||
{
|
||||
detail.Delay = Convert.ToInt32(value.ToString());
|
||||
detail.DelayName = $"{detail.Delay}ms";
|
||||
}
|
||||
else if (dicResult != null && dicResult.TryGetValue("message", out var value1))
|
||||
{
|
||||
detail.Delay = _delayTimeout;
|
||||
detail.DelayName = $"{value1}";
|
||||
}
|
||||
else
|
||||
{
|
||||
detail.Delay = _delayTimeout;
|
||||
detail.DelayName = string.Empty;
|
||||
}
|
||||
_proxyDetails.Replace(detail, JsonUtils.DeepCopy(detail));
|
||||
}
|
||||
|
||||
#endregion proxy function
|
||||
|
||||
#region task
|
||||
|
||||
public async Task DelayTestTask()
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
var numOfExecuted = 1;
|
||||
while (true)
|
||||
{
|
||||
await Task.Delay(1000 * 60);
|
||||
numOfExecuted++;
|
||||
if (!(AutoRefresh && _config.UiItem.ShowInTaskbar && _config.IsRunningCore(ECoreType.sing_box)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (_config.ClashUIItem.ProxiesAutoDelayTestInterval <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (numOfExecuted % _config.ClashUIItem.ProxiesAutoDelayTestInterval != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
await ProxiesDelayTest();
|
||||
}
|
||||
});
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
#endregion task
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task SetActiveProxy()
|
||||
{
|
||||
if (SelectedGroup == null || SelectedGroup.Name.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (SelectedDetail == null || SelectedDetail.Name.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var name = SelectedGroup.Name;
|
||||
if (name.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var nameNode = SelectedDetail.Name;
|
||||
if (nameNode.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var selectedProxy = TryGetProxy(name);
|
||||
if (selectedProxy == null || selectedProxy.type != "Selector")
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
|
||||
return;
|
||||
}
|
||||
|
||||
await ClashApiHandler.Instance.ClashSetActiveProxy(name, nameNode);
|
||||
|
||||
selectedProxy.now = nameNode;
|
||||
var group = _proxyGroups.FirstOrDefault(it => it.Name == SelectedGroup.Name);
|
||||
if (group != null)
|
||||
{
|
||||
group.Now = nameNode;
|
||||
var group2 = JsonUtils.DeepCopy(group);
|
||||
_proxyGroups.Replace(group, group2);
|
||||
|
||||
SelectedGroup = group2;
|
||||
}
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
}
|
||||
|
||||
private async Task ProxiesDelayTest(bool blAll = true)
|
||||
{
|
||||
ClashApiHandler.Instance.ClashProxiesDelayTest(blAll, _proxyDetails.ToList(), (item, result) =>
|
||||
{
|
||||
if (item == null || result.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_updateView?.Invoke(EViewAction.DispatcherProxiesDelayTest, new SpeedTestResult() { IndexId = item.Name, Delay = result });
|
||||
});
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void ProxiesDelayTestResult(SpeedTestResult result)
|
||||
{
|
||||
//UpdateHandler(false, $"{item.name}={result}");
|
||||
var detail = _proxyDetails.FirstOrDefault(it => it.Name == result.IndexId);
|
||||
if (detail == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var dicResult = JsonUtils.Deserialize<Dictionary<string, object>>(result.Delay);
|
||||
if (dicResult != null && dicResult.TryGetValue("delay", out var value))
|
||||
{
|
||||
detail.Delay = Convert.ToInt32(value.ToString());
|
||||
detail.DelayName = $"{detail.Delay}ms";
|
||||
}
|
||||
else if (dicResult != null && dicResult.TryGetValue("message", out var value1))
|
||||
{
|
||||
detail.Delay = _delayTimeout;
|
||||
detail.DelayName = $"{value1}";
|
||||
}
|
||||
else
|
||||
{
|
||||
detail.Delay = _delayTimeout;
|
||||
detail.DelayName = string.Empty;
|
||||
}
|
||||
_proxyDetails.Replace(detail, JsonUtils.DeepCopy(detail));
|
||||
}
|
||||
|
||||
#endregion proxy function
|
||||
|
||||
#region task
|
||||
|
||||
public async Task DelayTestTask()
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
var numOfExecuted = 1;
|
||||
while (true)
|
||||
{
|
||||
await Task.Delay(1000 * 60);
|
||||
numOfExecuted++;
|
||||
if (!(AutoRefresh && _config.UiItem.ShowInTaskbar && _config.IsRunningCore(ECoreType.sing_box)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (_config.ClashUIItem.ProxiesAutoDelayTestInterval <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (numOfExecuted % _config.ClashUIItem.ProxiesAutoDelayTestInterval != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
await ProxiesDelayTest();
|
||||
}
|
||||
});
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
#endregion task
|
||||
}
|
||||
|
||||
@@ -2,116 +2,115 @@ using System.Reactive;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
|
||||
namespace ServiceLib.ViewModels
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
public class DNSSettingViewModel : MyReactiveObject
|
||||
{
|
||||
public class DNSSettingViewModel : MyReactiveObject
|
||||
[Reactive] public bool UseSystemHosts { get; set; }
|
||||
[Reactive] public string DomainStrategy4Freedom { get; set; }
|
||||
[Reactive] public string DomainDNSAddress { get; set; }
|
||||
[Reactive] public string NormalDNS { get; set; }
|
||||
|
||||
[Reactive] public string DomainStrategy4Freedom2 { get; set; }
|
||||
[Reactive] public string DomainDNSAddress2 { get; set; }
|
||||
[Reactive] public string NormalDNS2 { get; set; }
|
||||
[Reactive] public string TunDNS2 { get; set; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> ImportDefConfig4V2rayCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> ImportDefConfig4SingboxCmd { get; }
|
||||
|
||||
public DNSSettingViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
[Reactive] public bool UseSystemHosts { get; set; }
|
||||
[Reactive] public string DomainStrategy4Freedom { get; set; }
|
||||
[Reactive] public string DomainDNSAddress { get; set; }
|
||||
[Reactive] public string NormalDNS { get; set; }
|
||||
|
||||
[Reactive] public string DomainStrategy4Freedom2 { get; set; }
|
||||
[Reactive] public string DomainDNSAddress2 { get; set; }
|
||||
[Reactive] public string NormalDNS2 { get; set; }
|
||||
[Reactive] public string TunDNS2 { get; set; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> ImportDefConfig4V2rayCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> ImportDefConfig4SingboxCmd { get; }
|
||||
|
||||
public DNSSettingViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await SaveSettingAsync();
|
||||
});
|
||||
await SaveSettingAsync();
|
||||
});
|
||||
|
||||
ImportDefConfig4V2rayCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
NormalDNS = EmbedUtils.GetEmbedText(Global.DNSV2rayNormalFileName);
|
||||
await Task.CompletedTask;
|
||||
});
|
||||
|
||||
ImportDefConfig4SingboxCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
NormalDNS2 = EmbedUtils.GetEmbedText(Global.DNSSingboxNormalFileName);
|
||||
TunDNS2 = EmbedUtils.GetEmbedText(Global.TunSingboxDNSFileName);
|
||||
await Task.CompletedTask;
|
||||
});
|
||||
|
||||
_ = Init();
|
||||
}
|
||||
|
||||
private async Task Init()
|
||||
ImportDefConfig4V2rayCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
var item = await AppHandler.Instance.GetDNSItem(ECoreType.Xray);
|
||||
UseSystemHosts = item.UseSystemHosts;
|
||||
DomainStrategy4Freedom = item?.DomainStrategy4Freedom ?? string.Empty;
|
||||
DomainDNSAddress = item?.DomainDNSAddress ?? string.Empty;
|
||||
NormalDNS = item?.NormalDNS ?? string.Empty;
|
||||
NormalDNS = EmbedUtils.GetEmbedText(Global.DNSV2rayNormalFileName);
|
||||
await Task.CompletedTask;
|
||||
});
|
||||
|
||||
var item2 = await AppHandler.Instance.GetDNSItem(ECoreType.sing_box);
|
||||
DomainStrategy4Freedom2 = item2?.DomainStrategy4Freedom ?? string.Empty;
|
||||
DomainDNSAddress2 = item2?.DomainDNSAddress ?? string.Empty;
|
||||
NormalDNS2 = item2?.NormalDNS ?? string.Empty;
|
||||
TunDNS2 = item2?.TunDNS ?? string.Empty;
|
||||
}
|
||||
|
||||
private async Task SaveSettingAsync()
|
||||
ImportDefConfig4SingboxCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
if (NormalDNS.IsNotEmpty())
|
||||
NormalDNS2 = EmbedUtils.GetEmbedText(Global.DNSSingboxNormalFileName);
|
||||
TunDNS2 = EmbedUtils.GetEmbedText(Global.TunSingboxDNSFileName);
|
||||
await Task.CompletedTask;
|
||||
});
|
||||
|
||||
_ = Init();
|
||||
}
|
||||
|
||||
private async Task Init()
|
||||
{
|
||||
var item = await AppHandler.Instance.GetDNSItem(ECoreType.Xray);
|
||||
UseSystemHosts = item.UseSystemHosts;
|
||||
DomainStrategy4Freedom = item?.DomainStrategy4Freedom ?? string.Empty;
|
||||
DomainDNSAddress = item?.DomainDNSAddress ?? string.Empty;
|
||||
NormalDNS = item?.NormalDNS ?? string.Empty;
|
||||
|
||||
var item2 = await AppHandler.Instance.GetDNSItem(ECoreType.sing_box);
|
||||
DomainStrategy4Freedom2 = item2?.DomainStrategy4Freedom ?? string.Empty;
|
||||
DomainDNSAddress2 = item2?.DomainDNSAddress ?? string.Empty;
|
||||
NormalDNS2 = item2?.NormalDNS ?? string.Empty;
|
||||
TunDNS2 = item2?.TunDNS ?? string.Empty;
|
||||
}
|
||||
|
||||
private async Task SaveSettingAsync()
|
||||
{
|
||||
if (NormalDNS.IsNotEmpty())
|
||||
{
|
||||
var obj = JsonUtils.ParseJson(NormalDNS);
|
||||
if (obj != null && obj["servers"] != null)
|
||||
{
|
||||
var obj = JsonUtils.ParseJson(NormalDNS);
|
||||
if (obj != null && obj["servers"] != null)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
if (NormalDNS.Contains('{') || NormalDNS.Contains('}'))
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillCorrectDNSText);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (NormalDNS2.IsNotEmpty())
|
||||
else
|
||||
{
|
||||
var obj2 = JsonUtils.Deserialize<Dns4Sbox>(NormalDNS2);
|
||||
if (obj2 == null)
|
||||
if (NormalDNS.Contains('{') || NormalDNS.Contains('}'))
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillCorrectDNSText);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (TunDNS2.IsNotEmpty())
|
||||
{
|
||||
var obj2 = JsonUtils.Deserialize<Dns4Sbox>(TunDNS2);
|
||||
if (obj2 == null)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillCorrectDNSText);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var item = await AppHandler.Instance.GetDNSItem(ECoreType.Xray);
|
||||
item.DomainStrategy4Freedom = DomainStrategy4Freedom;
|
||||
item.DomainDNSAddress = DomainDNSAddress;
|
||||
item.UseSystemHosts = UseSystemHosts;
|
||||
item.NormalDNS = NormalDNS;
|
||||
await ConfigHandler.SaveDNSItems(_config, item);
|
||||
|
||||
var item2 = await AppHandler.Instance.GetDNSItem(ECoreType.sing_box);
|
||||
item2.DomainStrategy4Freedom = DomainStrategy4Freedom2;
|
||||
item2.DomainDNSAddress = DomainDNSAddress2;
|
||||
item2.NormalDNS = JsonUtils.Serialize(JsonUtils.ParseJson(NormalDNS2));
|
||||
item2.TunDNS = JsonUtils.Serialize(JsonUtils.ParseJson(TunDNS2));
|
||||
await ConfigHandler.SaveDNSItems(_config, item2);
|
||||
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
_ = _updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||
}
|
||||
if (NormalDNS2.IsNotEmpty())
|
||||
{
|
||||
var obj2 = JsonUtils.Deserialize<Dns4Sbox>(NormalDNS2);
|
||||
if (obj2 == null)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillCorrectDNSText);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (TunDNS2.IsNotEmpty())
|
||||
{
|
||||
var obj2 = JsonUtils.Deserialize<Dns4Sbox>(TunDNS2);
|
||||
if (obj2 == null)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillCorrectDNSText);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var item = await AppHandler.Instance.GetDNSItem(ECoreType.Xray);
|
||||
item.DomainStrategy4Freedom = DomainStrategy4Freedom;
|
||||
item.DomainDNSAddress = DomainDNSAddress;
|
||||
item.UseSystemHosts = UseSystemHosts;
|
||||
item.NormalDNS = NormalDNS;
|
||||
await ConfigHandler.SaveDNSItems(_config, item);
|
||||
|
||||
var item2 = await AppHandler.Instance.GetDNSItem(ECoreType.sing_box);
|
||||
item2.DomainStrategy4Freedom = DomainStrategy4Freedom2;
|
||||
item2.DomainDNSAddress = DomainDNSAddress2;
|
||||
item2.NormalDNS = JsonUtils.Serialize(JsonUtils.ParseJson(NormalDNS2));
|
||||
item2.TunDNS = JsonUtils.Serialize(JsonUtils.ParseJson(TunDNS2));
|
||||
await ConfigHandler.SaveDNSItems(_config, item2);
|
||||
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
_ = _updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,65 +1,64 @@
|
||||
using System.Reactive;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace ServiceLib.ViewModels
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
public class GlobalHotkeySettingViewModel : MyReactiveObject
|
||||
{
|
||||
public class GlobalHotkeySettingViewModel : MyReactiveObject
|
||||
private readonly List<KeyEventItem> _globalHotkeys;
|
||||
|
||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||
|
||||
public GlobalHotkeySettingViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
private readonly List<KeyEventItem> _globalHotkeys;
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
|
||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||
_globalHotkeys = JsonUtils.DeepCopy(_config.GlobalHotkeys);
|
||||
|
||||
public GlobalHotkeySettingViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
await SaveSettingAsync();
|
||||
});
|
||||
}
|
||||
|
||||
_globalHotkeys = JsonUtils.DeepCopy(_config.GlobalHotkeys);
|
||||
|
||||
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await SaveSettingAsync();
|
||||
});
|
||||
}
|
||||
|
||||
public KeyEventItem GetKeyEventItem(EGlobalHotkey eg)
|
||||
public KeyEventItem GetKeyEventItem(EGlobalHotkey eg)
|
||||
{
|
||||
var item = _globalHotkeys.FirstOrDefault((it) => it.EGlobalHotkey == eg);
|
||||
if (item != null)
|
||||
{
|
||||
var item = _globalHotkeys.FirstOrDefault((it) => it.EGlobalHotkey == eg);
|
||||
if (item != null)
|
||||
{
|
||||
return item;
|
||||
}
|
||||
|
||||
item = new()
|
||||
{
|
||||
EGlobalHotkey = eg,
|
||||
Control = false,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
KeyCode = null
|
||||
};
|
||||
_globalHotkeys.Add(item);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public void ResetKeyEventItem()
|
||||
item = new()
|
||||
{
|
||||
_globalHotkeys.Clear();
|
||||
EGlobalHotkey = eg,
|
||||
Control = false,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
KeyCode = null
|
||||
};
|
||||
_globalHotkeys.Add(item);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public void ResetKeyEventItem()
|
||||
{
|
||||
_globalHotkeys.Clear();
|
||||
}
|
||||
|
||||
private async Task SaveSettingAsync()
|
||||
{
|
||||
_config.GlobalHotkeys = _globalHotkeys;
|
||||
|
||||
if (await ConfigHandler.SaveConfig(_config) == 0)
|
||||
{
|
||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||
}
|
||||
|
||||
private async Task SaveSettingAsync()
|
||||
else
|
||||
{
|
||||
_config.GlobalHotkeys = _globalHotkeys;
|
||||
|
||||
if (await ConfigHandler.SaveConfig(_config) == 0)
|
||||
{
|
||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
|
||||
}
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,120 +3,119 @@ using System.Text.RegularExpressions;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
|
||||
namespace ServiceLib.ViewModels
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
public class MsgViewModel : MyReactiveObject
|
||||
{
|
||||
public class MsgViewModel : MyReactiveObject
|
||||
private ConcurrentQueue<string> _queueMsg = new();
|
||||
private int _numMaxMsg = 500;
|
||||
private bool _lastMsgFilterNotAvailable;
|
||||
private bool _blLockShow = false;
|
||||
|
||||
[Reactive]
|
||||
public string MsgFilter { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool AutoRefresh { get; set; }
|
||||
|
||||
public MsgViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
private ConcurrentQueue<string> _queueMsg = new();
|
||||
private int _numMaxMsg = 500;
|
||||
private bool _lastMsgFilterNotAvailable;
|
||||
private bool _blLockShow = false;
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
MsgFilter = _config.MsgUIItem.MainMsgFilter ?? string.Empty;
|
||||
AutoRefresh = _config.MsgUIItem.AutoRefresh ?? true;
|
||||
|
||||
[Reactive]
|
||||
public string MsgFilter { get; set; }
|
||||
this.WhenAnyValue(
|
||||
x => x.MsgFilter)
|
||||
.Subscribe(c => DoMsgFilter());
|
||||
|
||||
[Reactive]
|
||||
public bool AutoRefresh { get; set; }
|
||||
this.WhenAnyValue(
|
||||
x => x.AutoRefresh,
|
||||
y => y == true)
|
||||
.Subscribe(c => { _config.MsgUIItem.AutoRefresh = AutoRefresh; });
|
||||
|
||||
public MsgViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
MessageBus.Current.Listen<string>(EMsgCommand.SendMsgView.ToString()).Subscribe(OnNext);
|
||||
}
|
||||
|
||||
private async void OnNext(string x)
|
||||
{
|
||||
await AppendQueueMsg(x);
|
||||
}
|
||||
|
||||
private async Task AppendQueueMsg(string msg)
|
||||
{
|
||||
//if (msg == Global.CommandClearMsg)
|
||||
//{
|
||||
// ClearMsg();
|
||||
// return;
|
||||
//}
|
||||
if (AutoRefresh == false)
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
MsgFilter = _config.MsgUIItem.MainMsgFilter ?? string.Empty;
|
||||
AutoRefresh = _config.MsgUIItem.AutoRefresh ?? true;
|
||||
return;
|
||||
}
|
||||
_ = EnqueueQueueMsg(msg);
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.MsgFilter)
|
||||
.Subscribe(c => DoMsgFilter());
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.AutoRefresh,
|
||||
y => y == true)
|
||||
.Subscribe(c => { _config.MsgUIItem.AutoRefresh = AutoRefresh; });
|
||||
|
||||
MessageBus.Current.Listen<string>(EMsgCommand.SendMsgView.ToString()).Subscribe(OnNext);
|
||||
if (_blLockShow)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!_config.UiItem.ShowInTaskbar)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
private async void OnNext(string x)
|
||||
_blLockShow = true;
|
||||
|
||||
await Task.Delay(500);
|
||||
var txt = string.Join("", _queueMsg.ToArray());
|
||||
await _updateView?.Invoke(EViewAction.DispatcherShowMsg, txt);
|
||||
|
||||
_blLockShow = false;
|
||||
}
|
||||
|
||||
private async Task EnqueueQueueMsg(string msg)
|
||||
{
|
||||
//filter msg
|
||||
if (MsgFilter.IsNotEmpty() && !_lastMsgFilterNotAvailable)
|
||||
{
|
||||
await AppendQueueMsg(x);
|
||||
}
|
||||
|
||||
private async Task AppendQueueMsg(string msg)
|
||||
{
|
||||
//if (msg == Global.CommandClearMsg)
|
||||
//{
|
||||
// ClearMsg();
|
||||
// return;
|
||||
//}
|
||||
if (AutoRefresh == false)
|
||||
try
|
||||
{
|
||||
return;
|
||||
}
|
||||
_ = EnqueueQueueMsg(msg);
|
||||
|
||||
if (_blLockShow)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!_config.UiItem.ShowInTaskbar)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_blLockShow = true;
|
||||
|
||||
await Task.Delay(500);
|
||||
var txt = string.Join("", _queueMsg.ToArray());
|
||||
await _updateView?.Invoke(EViewAction.DispatcherShowMsg, txt);
|
||||
|
||||
_blLockShow = false;
|
||||
}
|
||||
|
||||
private async Task EnqueueQueueMsg(string msg)
|
||||
{
|
||||
//filter msg
|
||||
if (MsgFilter.IsNotEmpty() && !_lastMsgFilterNotAvailable)
|
||||
{
|
||||
try
|
||||
if (!Regex.IsMatch(msg, MsgFilter))
|
||||
{
|
||||
if (!Regex.IsMatch(msg, MsgFilter))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_queueMsg.Enqueue(ex.Message);
|
||||
_lastMsgFilterNotAvailable = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//Enqueue
|
||||
if (_queueMsg.Count > _numMaxMsg)
|
||||
catch (Exception ex)
|
||||
{
|
||||
for (int k = 0; k < _queueMsg.Count - _numMaxMsg; k++)
|
||||
{
|
||||
_queueMsg.TryDequeue(out _);
|
||||
}
|
||||
_queueMsg.Enqueue(ex.Message);
|
||||
_lastMsgFilterNotAvailable = true;
|
||||
}
|
||||
_queueMsg.Enqueue(msg);
|
||||
if (!msg.EndsWith(Environment.NewLine))
|
||||
}
|
||||
|
||||
//Enqueue
|
||||
if (_queueMsg.Count > _numMaxMsg)
|
||||
{
|
||||
for (int k = 0; k < _queueMsg.Count - _numMaxMsg; k++)
|
||||
{
|
||||
_queueMsg.Enqueue(Environment.NewLine);
|
||||
_queueMsg.TryDequeue(out _);
|
||||
}
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void ClearMsg()
|
||||
_queueMsg.Enqueue(msg);
|
||||
if (!msg.EndsWith(Environment.NewLine))
|
||||
{
|
||||
_queueMsg.Clear();
|
||||
_queueMsg.Enqueue(Environment.NewLine);
|
||||
}
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void DoMsgFilter()
|
||||
{
|
||||
_config.MsgUIItem.MainMsgFilter = MsgFilter;
|
||||
_lastMsgFilterNotAvailable = false;
|
||||
}
|
||||
public void ClearMsg()
|
||||
{
|
||||
_queueMsg.Clear();
|
||||
}
|
||||
|
||||
private void DoMsgFilter()
|
||||
{
|
||||
_config.MsgUIItem.MainMsgFilter = MsgFilter;
|
||||
_lastMsgFilterNotAvailable = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,417 +2,416 @@ using System.Reactive;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
|
||||
namespace ServiceLib.ViewModels
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
public class OptionSettingViewModel : MyReactiveObject
|
||||
{
|
||||
public class OptionSettingViewModel : MyReactiveObject
|
||||
#region Core
|
||||
|
||||
[Reactive] public int localPort { get; set; }
|
||||
[Reactive] public bool SecondLocalPortEnabled { get; set; }
|
||||
[Reactive] public bool udpEnabled { get; set; }
|
||||
[Reactive] public bool sniffingEnabled { get; set; }
|
||||
public IList<string> destOverride { get; set; }
|
||||
[Reactive] public bool routeOnly { get; set; }
|
||||
[Reactive] public bool allowLANConn { get; set; }
|
||||
[Reactive] public bool newPort4LAN { get; set; }
|
||||
[Reactive] public string user { get; set; }
|
||||
[Reactive] public string pass { get; set; }
|
||||
[Reactive] public bool muxEnabled { get; set; }
|
||||
[Reactive] public bool logEnabled { get; set; }
|
||||
[Reactive] public string loglevel { get; set; }
|
||||
[Reactive] public bool defAllowInsecure { get; set; }
|
||||
[Reactive] public string defFingerprint { get; set; }
|
||||
[Reactive] public string defUserAgent { get; set; }
|
||||
[Reactive] public string mux4SboxProtocol { get; set; }
|
||||
[Reactive] public bool enableCacheFile4Sbox { get; set; }
|
||||
[Reactive] public int hyUpMbps { get; set; }
|
||||
[Reactive] public int hyDownMbps { get; set; }
|
||||
[Reactive] public bool enableFragment { get; set; }
|
||||
|
||||
#endregion Core
|
||||
|
||||
#region Core KCP
|
||||
|
||||
//[Reactive] public int Kcpmtu { get; set; }
|
||||
//[Reactive] public int Kcptti { get; set; }
|
||||
//[Reactive] public int KcpuplinkCapacity { get; set; }
|
||||
//[Reactive] public int KcpdownlinkCapacity { get; set; }
|
||||
//[Reactive] public int KcpreadBufferSize { get; set; }
|
||||
//[Reactive] public int KcpwriteBufferSize { get; set; }
|
||||
//[Reactive] public bool Kcpcongestion { get; set; }
|
||||
|
||||
#endregion Core KCP
|
||||
|
||||
#region UI
|
||||
|
||||
[Reactive] public bool AutoRun { get; set; }
|
||||
[Reactive] public bool EnableStatistics { get; set; }
|
||||
[Reactive] public bool KeepOlderDedupl { get; set; }
|
||||
[Reactive] public bool DisplayRealTimeSpeed { get; set; }
|
||||
[Reactive] public bool EnableAutoAdjustMainLvColWidth { get; set; }
|
||||
[Reactive] public bool EnableUpdateSubOnlyRemarksExist { get; set; }
|
||||
[Reactive] public bool EnableSecurityProtocolTls13 { get; set; }
|
||||
[Reactive] public bool AutoHideStartup { get; set; }
|
||||
[Reactive] public bool Hide2TrayWhenClose { get; set; }
|
||||
[Reactive] public bool EnableDragDropSort { get; set; }
|
||||
[Reactive] public bool DoubleClick2Activate { get; set; }
|
||||
[Reactive] public int AutoUpdateInterval { get; set; }
|
||||
[Reactive] public int TrayMenuServersLimit { get; set; }
|
||||
[Reactive] public string CurrentFontFamily { get; set; }
|
||||
[Reactive] public int SpeedTestTimeout { get; set; }
|
||||
[Reactive] public string SpeedTestUrl { get; set; }
|
||||
[Reactive] public string SpeedPingTestUrl { get; set; }
|
||||
[Reactive] public int MixedConcurrencyCount { get; set; }
|
||||
[Reactive] public bool EnableHWA { get; set; }
|
||||
[Reactive] public string SubConvertUrl { get; set; }
|
||||
[Reactive] public int MainGirdOrientation { get; set; }
|
||||
[Reactive] public string GeoFileSourceUrl { get; set; }
|
||||
[Reactive] public string SrsFileSourceUrl { get; set; }
|
||||
[Reactive] public string RoutingRulesSourceUrl { get; set; }
|
||||
|
||||
#endregion UI
|
||||
|
||||
#region System proxy
|
||||
|
||||
[Reactive] public bool notProxyLocalAddress { get; set; }
|
||||
[Reactive] public string systemProxyAdvancedProtocol { get; set; }
|
||||
[Reactive] public string systemProxyExceptions { get; set; }
|
||||
|
||||
#endregion System proxy
|
||||
|
||||
#region Tun mode
|
||||
|
||||
[Reactive] public bool TunStrictRoute { get; set; }
|
||||
[Reactive] public string TunStack { get; set; }
|
||||
[Reactive] public int TunMtu { get; set; }
|
||||
[Reactive] public bool TunEnableExInbound { get; set; }
|
||||
[Reactive] public bool TunEnableIPv6Address { get; set; }
|
||||
[Reactive] public string TunLinuxSudoPassword { get; set; }
|
||||
|
||||
#endregion Tun mode
|
||||
|
||||
#region CoreType
|
||||
|
||||
[Reactive] public string CoreType1 { get; set; }
|
||||
[Reactive] public string CoreType2 { get; set; }
|
||||
[Reactive] public string CoreType3 { get; set; }
|
||||
[Reactive] public string CoreType4 { get; set; }
|
||||
[Reactive] public string CoreType5 { get; set; }
|
||||
[Reactive] public string CoreType6 { get; set; }
|
||||
|
||||
#endregion CoreType
|
||||
|
||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||
|
||||
public OptionSettingViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
|
||||
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await SaveSettingAsync();
|
||||
});
|
||||
|
||||
_ = Init();
|
||||
}
|
||||
|
||||
private async Task Init()
|
||||
{
|
||||
await _updateView?.Invoke(EViewAction.InitSettingFont, null);
|
||||
|
||||
#region Core
|
||||
|
||||
[Reactive] public int localPort { get; set; }
|
||||
[Reactive] public bool SecondLocalPortEnabled { get; set; }
|
||||
[Reactive] public bool udpEnabled { get; set; }
|
||||
[Reactive] public bool sniffingEnabled { get; set; }
|
||||
public IList<string> destOverride { get; set; }
|
||||
[Reactive] public bool routeOnly { get; set; }
|
||||
[Reactive] public bool allowLANConn { get; set; }
|
||||
[Reactive] public bool newPort4LAN { get; set; }
|
||||
[Reactive] public string user { get; set; }
|
||||
[Reactive] public string pass { get; set; }
|
||||
[Reactive] public bool muxEnabled { get; set; }
|
||||
[Reactive] public bool logEnabled { get; set; }
|
||||
[Reactive] public string loglevel { get; set; }
|
||||
[Reactive] public bool defAllowInsecure { get; set; }
|
||||
[Reactive] public string defFingerprint { get; set; }
|
||||
[Reactive] public string defUserAgent { get; set; }
|
||||
[Reactive] public string mux4SboxProtocol { get; set; }
|
||||
[Reactive] public bool enableCacheFile4Sbox { get; set; }
|
||||
[Reactive] public int hyUpMbps { get; set; }
|
||||
[Reactive] public int hyDownMbps { get; set; }
|
||||
[Reactive] public bool enableFragment { get; set; }
|
||||
var inbound = _config.Inbound.First();
|
||||
localPort = inbound.LocalPort;
|
||||
SecondLocalPortEnabled = inbound.SecondLocalPortEnabled;
|
||||
udpEnabled = inbound.UdpEnabled;
|
||||
sniffingEnabled = inbound.SniffingEnabled;
|
||||
routeOnly = inbound.RouteOnly;
|
||||
allowLANConn = inbound.AllowLANConn;
|
||||
newPort4LAN = inbound.NewPort4LAN;
|
||||
user = inbound.User;
|
||||
pass = inbound.Pass;
|
||||
muxEnabled = _config.CoreBasicItem.MuxEnabled;
|
||||
logEnabled = _config.CoreBasicItem.LogEnabled;
|
||||
loglevel = _config.CoreBasicItem.Loglevel;
|
||||
defAllowInsecure = _config.CoreBasicItem.DefAllowInsecure;
|
||||
defFingerprint = _config.CoreBasicItem.DefFingerprint;
|
||||
defUserAgent = _config.CoreBasicItem.DefUserAgent;
|
||||
mux4SboxProtocol = _config.Mux4SboxItem.Protocol;
|
||||
enableCacheFile4Sbox = _config.CoreBasicItem.EnableCacheFile4Sbox;
|
||||
hyUpMbps = _config.HysteriaItem.UpMbps;
|
||||
hyDownMbps = _config.HysteriaItem.DownMbps;
|
||||
enableFragment = _config.CoreBasicItem.EnableFragment;
|
||||
|
||||
#endregion Core
|
||||
|
||||
#region Core KCP
|
||||
|
||||
//[Reactive] public int Kcpmtu { get; set; }
|
||||
//[Reactive] public int Kcptti { get; set; }
|
||||
//[Reactive] public int KcpuplinkCapacity { get; set; }
|
||||
//[Reactive] public int KcpdownlinkCapacity { get; set; }
|
||||
//[Reactive] public int KcpreadBufferSize { get; set; }
|
||||
//[Reactive] public int KcpwriteBufferSize { get; set; }
|
||||
//[Reactive] public bool Kcpcongestion { get; set; }
|
||||
//Kcpmtu = _config.kcpItem.mtu;
|
||||
//Kcptti = _config.kcpItem.tti;
|
||||
//KcpuplinkCapacity = _config.kcpItem.uplinkCapacity;
|
||||
//KcpdownlinkCapacity = _config.kcpItem.downlinkCapacity;
|
||||
//KcpreadBufferSize = _config.kcpItem.readBufferSize;
|
||||
//KcpwriteBufferSize = _config.kcpItem.writeBufferSize;
|
||||
//Kcpcongestion = _config.kcpItem.congestion;
|
||||
|
||||
#endregion Core KCP
|
||||
|
||||
#region UI
|
||||
|
||||
[Reactive] public bool AutoRun { get; set; }
|
||||
[Reactive] public bool EnableStatistics { get; set; }
|
||||
[Reactive] public bool KeepOlderDedupl { get; set; }
|
||||
[Reactive] public bool DisplayRealTimeSpeed { get; set; }
|
||||
[Reactive] public bool EnableAutoAdjustMainLvColWidth { get; set; }
|
||||
[Reactive] public bool EnableUpdateSubOnlyRemarksExist { get; set; }
|
||||
[Reactive] public bool EnableSecurityProtocolTls13 { get; set; }
|
||||
[Reactive] public bool AutoHideStartup { get; set; }
|
||||
[Reactive] public bool Hide2TrayWhenClose { get; set; }
|
||||
[Reactive] public bool EnableDragDropSort { get; set; }
|
||||
[Reactive] public bool DoubleClick2Activate { get; set; }
|
||||
[Reactive] public int AutoUpdateInterval { get; set; }
|
||||
[Reactive] public int TrayMenuServersLimit { get; set; }
|
||||
[Reactive] public string CurrentFontFamily { get; set; }
|
||||
[Reactive] public int SpeedTestTimeout { get; set; }
|
||||
[Reactive] public string SpeedTestUrl { get; set; }
|
||||
[Reactive] public string SpeedPingTestUrl { get; set; }
|
||||
[Reactive] public int MixedConcurrencyCount { get; set; }
|
||||
[Reactive] public bool EnableHWA { get; set; }
|
||||
[Reactive] public string SubConvertUrl { get; set; }
|
||||
[Reactive] public int MainGirdOrientation { get; set; }
|
||||
[Reactive] public string GeoFileSourceUrl { get; set; }
|
||||
[Reactive] public string SrsFileSourceUrl { get; set; }
|
||||
[Reactive] public string RoutingRulesSourceUrl { get; set; }
|
||||
AutoRun = _config.GuiItem.AutoRun;
|
||||
EnableStatistics = _config.GuiItem.EnableStatistics;
|
||||
DisplayRealTimeSpeed = _config.GuiItem.DisplayRealTimeSpeed;
|
||||
KeepOlderDedupl = _config.GuiItem.KeepOlderDedupl;
|
||||
EnableAutoAdjustMainLvColWidth = _config.UiItem.EnableAutoAdjustMainLvColWidth;
|
||||
EnableUpdateSubOnlyRemarksExist = _config.UiItem.EnableUpdateSubOnlyRemarksExist;
|
||||
EnableSecurityProtocolTls13 = _config.GuiItem.EnableSecurityProtocolTls13;
|
||||
AutoHideStartup = _config.UiItem.AutoHideStartup;
|
||||
Hide2TrayWhenClose = _config.UiItem.Hide2TrayWhenClose;
|
||||
EnableDragDropSort = _config.UiItem.EnableDragDropSort;
|
||||
DoubleClick2Activate = _config.UiItem.DoubleClick2Activate;
|
||||
AutoUpdateInterval = _config.GuiItem.AutoUpdateInterval;
|
||||
TrayMenuServersLimit = _config.GuiItem.TrayMenuServersLimit;
|
||||
CurrentFontFamily = _config.UiItem.CurrentFontFamily;
|
||||
SpeedTestTimeout = _config.SpeedTestItem.SpeedTestTimeout;
|
||||
SpeedTestUrl = _config.SpeedTestItem.SpeedTestUrl;
|
||||
MixedConcurrencyCount = _config.SpeedTestItem.MixedConcurrencyCount;
|
||||
SpeedPingTestUrl = _config.SpeedTestItem.SpeedPingTestUrl;
|
||||
EnableHWA = _config.GuiItem.EnableHWA;
|
||||
SubConvertUrl = _config.ConstItem.SubConvertUrl;
|
||||
MainGirdOrientation = (int)_config.UiItem.MainGirdOrientation;
|
||||
GeoFileSourceUrl = _config.ConstItem.GeoSourceUrl;
|
||||
SrsFileSourceUrl = _config.ConstItem.SrsSourceUrl;
|
||||
RoutingRulesSourceUrl = _config.ConstItem.RouteRulesTemplateSourceUrl;
|
||||
|
||||
#endregion UI
|
||||
|
||||
#region System proxy
|
||||
|
||||
[Reactive] public bool notProxyLocalAddress { get; set; }
|
||||
[Reactive] public string systemProxyAdvancedProtocol { get; set; }
|
||||
[Reactive] public string systemProxyExceptions { get; set; }
|
||||
notProxyLocalAddress = _config.SystemProxyItem.NotProxyLocalAddress;
|
||||
systemProxyAdvancedProtocol = _config.SystemProxyItem.SystemProxyAdvancedProtocol;
|
||||
systemProxyExceptions = _config.SystemProxyItem.SystemProxyExceptions;
|
||||
|
||||
#endregion System proxy
|
||||
|
||||
#region Tun mode
|
||||
|
||||
[Reactive] public bool TunStrictRoute { get; set; }
|
||||
[Reactive] public string TunStack { get; set; }
|
||||
[Reactive] public int TunMtu { get; set; }
|
||||
[Reactive] public bool TunEnableExInbound { get; set; }
|
||||
[Reactive] public bool TunEnableIPv6Address { get; set; }
|
||||
[Reactive] public string TunLinuxSudoPassword { get; set; }
|
||||
TunStrictRoute = _config.TunModeItem.StrictRoute;
|
||||
TunStack = _config.TunModeItem.Stack;
|
||||
TunMtu = _config.TunModeItem.Mtu;
|
||||
TunEnableExInbound = _config.TunModeItem.EnableExInbound;
|
||||
TunEnableIPv6Address = _config.TunModeItem.EnableIPv6Address;
|
||||
TunLinuxSudoPassword = _config.TunModeItem.LinuxSudoPwd;
|
||||
|
||||
#endregion Tun mode
|
||||
|
||||
#region CoreType
|
||||
await InitCoreType();
|
||||
}
|
||||
|
||||
[Reactive] public string CoreType1 { get; set; }
|
||||
[Reactive] public string CoreType2 { get; set; }
|
||||
[Reactive] public string CoreType3 { get; set; }
|
||||
[Reactive] public string CoreType4 { get; set; }
|
||||
[Reactive] public string CoreType5 { get; set; }
|
||||
[Reactive] public string CoreType6 { get; set; }
|
||||
|
||||
#endregion CoreType
|
||||
|
||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||
|
||||
public OptionSettingViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
private async Task InitCoreType()
|
||||
{
|
||||
if (_config.CoreTypeItem == null)
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
_config.CoreTypeItem = new List<CoreTypeItem>();
|
||||
}
|
||||
|
||||
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
foreach (EConfigType it in Enum.GetValues(typeof(EConfigType)))
|
||||
{
|
||||
if (_config.CoreTypeItem.FindIndex(t => t.ConfigType == it) >= 0)
|
||||
{
|
||||
await SaveSettingAsync();
|
||||
continue;
|
||||
}
|
||||
|
||||
_config.CoreTypeItem.Add(new CoreTypeItem()
|
||||
{
|
||||
ConfigType = it,
|
||||
CoreType = ECoreType.Xray
|
||||
});
|
||||
}
|
||||
_config.CoreTypeItem.ForEach(it =>
|
||||
{
|
||||
var type = it.CoreType.ToString();
|
||||
switch ((int)it.ConfigType)
|
||||
{
|
||||
case 1:
|
||||
CoreType1 = type;
|
||||
break;
|
||||
|
||||
_ = Init();
|
||||
case 2:
|
||||
CoreType2 = type;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
CoreType3 = type;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
CoreType4 = type;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
CoreType5 = type;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
CoreType6 = type;
|
||||
break;
|
||||
}
|
||||
});
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task SaveSettingAsync()
|
||||
{
|
||||
if (localPort.ToString().IsNullOrEmpty() || !Utils.IsNumeric(localPort.ToString())
|
||||
|| localPort <= 0 || localPort >= Global.MaxPort)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillLocalListeningPort);
|
||||
return;
|
||||
}
|
||||
var needReboot = (EnableStatistics != _config.GuiItem.EnableStatistics
|
||||
|| DisplayRealTimeSpeed != _config.GuiItem.DisplayRealTimeSpeed
|
||||
|| EnableDragDropSort != _config.UiItem.EnableDragDropSort
|
||||
|| EnableHWA != _config.GuiItem.EnableHWA
|
||||
|| CurrentFontFamily != _config.UiItem.CurrentFontFamily
|
||||
|| MainGirdOrientation != (int)_config.UiItem.MainGirdOrientation);
|
||||
|
||||
//if (Utile.IsNullOrEmpty(Kcpmtu.ToString()) || !Utile.IsNumeric(Kcpmtu.ToString())
|
||||
// || Utile.IsNullOrEmpty(Kcptti.ToString()) || !Utile.IsNumeric(Kcptti.ToString())
|
||||
// || Utile.IsNullOrEmpty(KcpuplinkCapacity.ToString()) || !Utile.IsNumeric(KcpuplinkCapacity.ToString())
|
||||
// || Utile.IsNullOrEmpty(KcpdownlinkCapacity.ToString()) || !Utile.IsNumeric(KcpdownlinkCapacity.ToString())
|
||||
// || Utile.IsNullOrEmpty(KcpreadBufferSize.ToString()) || !Utile.IsNumeric(KcpreadBufferSize.ToString())
|
||||
// || Utile.IsNullOrEmpty(KcpwriteBufferSize.ToString()) || !Utile.IsNumeric(KcpwriteBufferSize.ToString()))
|
||||
//{
|
||||
// NoticeHandler.Instance.Enqueue(ResUI.FillKcpParameters);
|
||||
// return;
|
||||
//}
|
||||
|
||||
//Core
|
||||
_config.Inbound.First().LocalPort = localPort;
|
||||
_config.Inbound.First().SecondLocalPortEnabled = SecondLocalPortEnabled;
|
||||
_config.Inbound.First().UdpEnabled = udpEnabled;
|
||||
_config.Inbound.First().SniffingEnabled = sniffingEnabled;
|
||||
_config.Inbound.First().DestOverride = destOverride?.ToList();
|
||||
_config.Inbound.First().RouteOnly = routeOnly;
|
||||
_config.Inbound.First().AllowLANConn = allowLANConn;
|
||||
_config.Inbound.First().NewPort4LAN = newPort4LAN;
|
||||
_config.Inbound.First().User = user;
|
||||
_config.Inbound.First().Pass = pass;
|
||||
if (_config.Inbound.Count > 1)
|
||||
{
|
||||
_config.Inbound.RemoveAt(1);
|
||||
}
|
||||
_config.CoreBasicItem.LogEnabled = logEnabled;
|
||||
_config.CoreBasicItem.Loglevel = loglevel;
|
||||
_config.CoreBasicItem.MuxEnabled = muxEnabled;
|
||||
_config.CoreBasicItem.DefAllowInsecure = defAllowInsecure;
|
||||
_config.CoreBasicItem.DefFingerprint = defFingerprint;
|
||||
_config.CoreBasicItem.DefUserAgent = defUserAgent;
|
||||
_config.Mux4SboxItem.Protocol = mux4SboxProtocol;
|
||||
_config.CoreBasicItem.EnableCacheFile4Sbox = enableCacheFile4Sbox;
|
||||
_config.HysteriaItem.UpMbps = hyUpMbps;
|
||||
_config.HysteriaItem.DownMbps = hyDownMbps;
|
||||
_config.CoreBasicItem.EnableFragment = enableFragment;
|
||||
|
||||
_config.GuiItem.AutoRun = AutoRun;
|
||||
_config.GuiItem.EnableStatistics = EnableStatistics;
|
||||
_config.GuiItem.DisplayRealTimeSpeed = DisplayRealTimeSpeed;
|
||||
_config.GuiItem.KeepOlderDedupl = KeepOlderDedupl;
|
||||
_config.UiItem.EnableAutoAdjustMainLvColWidth = EnableAutoAdjustMainLvColWidth;
|
||||
_config.UiItem.EnableUpdateSubOnlyRemarksExist = EnableUpdateSubOnlyRemarksExist;
|
||||
_config.GuiItem.EnableSecurityProtocolTls13 = EnableSecurityProtocolTls13;
|
||||
_config.UiItem.AutoHideStartup = AutoHideStartup;
|
||||
_config.UiItem.Hide2TrayWhenClose = Hide2TrayWhenClose;
|
||||
_config.GuiItem.AutoUpdateInterval = AutoUpdateInterval;
|
||||
_config.UiItem.EnableDragDropSort = EnableDragDropSort;
|
||||
_config.UiItem.DoubleClick2Activate = DoubleClick2Activate;
|
||||
_config.GuiItem.TrayMenuServersLimit = TrayMenuServersLimit;
|
||||
_config.UiItem.CurrentFontFamily = CurrentFontFamily;
|
||||
_config.SpeedTestItem.SpeedTestTimeout = SpeedTestTimeout;
|
||||
_config.SpeedTestItem.MixedConcurrencyCount = MixedConcurrencyCount;
|
||||
_config.SpeedTestItem.SpeedTestUrl = SpeedTestUrl;
|
||||
_config.SpeedTestItem.SpeedPingTestUrl = SpeedPingTestUrl;
|
||||
_config.GuiItem.EnableHWA = EnableHWA;
|
||||
_config.ConstItem.SubConvertUrl = SubConvertUrl;
|
||||
_config.UiItem.MainGirdOrientation = (EGirdOrientation)MainGirdOrientation;
|
||||
_config.ConstItem.GeoSourceUrl = GeoFileSourceUrl;
|
||||
_config.ConstItem.SrsSourceUrl = SrsFileSourceUrl;
|
||||
_config.ConstItem.RouteRulesTemplateSourceUrl = RoutingRulesSourceUrl;
|
||||
|
||||
//systemProxy
|
||||
_config.SystemProxyItem.SystemProxyExceptions = systemProxyExceptions;
|
||||
_config.SystemProxyItem.NotProxyLocalAddress = notProxyLocalAddress;
|
||||
_config.SystemProxyItem.SystemProxyAdvancedProtocol = systemProxyAdvancedProtocol;
|
||||
|
||||
//tun mode
|
||||
_config.TunModeItem.StrictRoute = TunStrictRoute;
|
||||
_config.TunModeItem.Stack = TunStack;
|
||||
_config.TunModeItem.Mtu = TunMtu;
|
||||
_config.TunModeItem.EnableExInbound = TunEnableExInbound;
|
||||
_config.TunModeItem.EnableIPv6Address = TunEnableIPv6Address;
|
||||
if (TunLinuxSudoPassword != _config.TunModeItem.LinuxSudoPwd)
|
||||
{
|
||||
_config.TunModeItem.LinuxSudoPwd = DesUtils.Encrypt(TunLinuxSudoPassword);
|
||||
}
|
||||
|
||||
private async Task Init()
|
||||
//coreType
|
||||
await SaveCoreType();
|
||||
|
||||
if (await ConfigHandler.SaveConfig(_config) == 0)
|
||||
{
|
||||
await _updateView?.Invoke(EViewAction.InitSettingFont, null);
|
||||
await AutoStartupHandler.UpdateTask(_config);
|
||||
AppHandler.Instance.Reset();
|
||||
|
||||
#region Core
|
||||
|
||||
var inbound = _config.Inbound.First();
|
||||
localPort = inbound.LocalPort;
|
||||
SecondLocalPortEnabled = inbound.SecondLocalPortEnabled;
|
||||
udpEnabled = inbound.UdpEnabled;
|
||||
sniffingEnabled = inbound.SniffingEnabled;
|
||||
routeOnly = inbound.RouteOnly;
|
||||
allowLANConn = inbound.AllowLANConn;
|
||||
newPort4LAN = inbound.NewPort4LAN;
|
||||
user = inbound.User;
|
||||
pass = inbound.Pass;
|
||||
muxEnabled = _config.CoreBasicItem.MuxEnabled;
|
||||
logEnabled = _config.CoreBasicItem.LogEnabled;
|
||||
loglevel = _config.CoreBasicItem.Loglevel;
|
||||
defAllowInsecure = _config.CoreBasicItem.DefAllowInsecure;
|
||||
defFingerprint = _config.CoreBasicItem.DefFingerprint;
|
||||
defUserAgent = _config.CoreBasicItem.DefUserAgent;
|
||||
mux4SboxProtocol = _config.Mux4SboxItem.Protocol;
|
||||
enableCacheFile4Sbox = _config.CoreBasicItem.EnableCacheFile4Sbox;
|
||||
hyUpMbps = _config.HysteriaItem.UpMbps;
|
||||
hyDownMbps = _config.HysteriaItem.DownMbps;
|
||||
enableFragment = _config.CoreBasicItem.EnableFragment;
|
||||
|
||||
#endregion Core
|
||||
|
||||
#region Core KCP
|
||||
|
||||
//Kcpmtu = _config.kcpItem.mtu;
|
||||
//Kcptti = _config.kcpItem.tti;
|
||||
//KcpuplinkCapacity = _config.kcpItem.uplinkCapacity;
|
||||
//KcpdownlinkCapacity = _config.kcpItem.downlinkCapacity;
|
||||
//KcpreadBufferSize = _config.kcpItem.readBufferSize;
|
||||
//KcpwriteBufferSize = _config.kcpItem.writeBufferSize;
|
||||
//Kcpcongestion = _config.kcpItem.congestion;
|
||||
|
||||
#endregion Core KCP
|
||||
|
||||
#region UI
|
||||
|
||||
AutoRun = _config.GuiItem.AutoRun;
|
||||
EnableStatistics = _config.GuiItem.EnableStatistics;
|
||||
DisplayRealTimeSpeed = _config.GuiItem.DisplayRealTimeSpeed;
|
||||
KeepOlderDedupl = _config.GuiItem.KeepOlderDedupl;
|
||||
EnableAutoAdjustMainLvColWidth = _config.UiItem.EnableAutoAdjustMainLvColWidth;
|
||||
EnableUpdateSubOnlyRemarksExist = _config.UiItem.EnableUpdateSubOnlyRemarksExist;
|
||||
EnableSecurityProtocolTls13 = _config.GuiItem.EnableSecurityProtocolTls13;
|
||||
AutoHideStartup = _config.UiItem.AutoHideStartup;
|
||||
Hide2TrayWhenClose = _config.UiItem.Hide2TrayWhenClose;
|
||||
EnableDragDropSort = _config.UiItem.EnableDragDropSort;
|
||||
DoubleClick2Activate = _config.UiItem.DoubleClick2Activate;
|
||||
AutoUpdateInterval = _config.GuiItem.AutoUpdateInterval;
|
||||
TrayMenuServersLimit = _config.GuiItem.TrayMenuServersLimit;
|
||||
CurrentFontFamily = _config.UiItem.CurrentFontFamily;
|
||||
SpeedTestTimeout = _config.SpeedTestItem.SpeedTestTimeout;
|
||||
SpeedTestUrl = _config.SpeedTestItem.SpeedTestUrl;
|
||||
MixedConcurrencyCount = _config.SpeedTestItem.MixedConcurrencyCount;
|
||||
SpeedPingTestUrl = _config.SpeedTestItem.SpeedPingTestUrl;
|
||||
EnableHWA = _config.GuiItem.EnableHWA;
|
||||
SubConvertUrl = _config.ConstItem.SubConvertUrl;
|
||||
MainGirdOrientation = (int)_config.UiItem.MainGirdOrientation;
|
||||
GeoFileSourceUrl = _config.ConstItem.GeoSourceUrl;
|
||||
SrsFileSourceUrl = _config.ConstItem.SrsSourceUrl;
|
||||
RoutingRulesSourceUrl = _config.ConstItem.RouteRulesTemplateSourceUrl;
|
||||
|
||||
#endregion UI
|
||||
|
||||
#region System proxy
|
||||
|
||||
notProxyLocalAddress = _config.SystemProxyItem.NotProxyLocalAddress;
|
||||
systemProxyAdvancedProtocol = _config.SystemProxyItem.SystemProxyAdvancedProtocol;
|
||||
systemProxyExceptions = _config.SystemProxyItem.SystemProxyExceptions;
|
||||
|
||||
#endregion System proxy
|
||||
|
||||
#region Tun mode
|
||||
|
||||
TunStrictRoute = _config.TunModeItem.StrictRoute;
|
||||
TunStack = _config.TunModeItem.Stack;
|
||||
TunMtu = _config.TunModeItem.Mtu;
|
||||
TunEnableExInbound = _config.TunModeItem.EnableExInbound;
|
||||
TunEnableIPv6Address = _config.TunModeItem.EnableIPv6Address;
|
||||
TunLinuxSudoPassword = _config.TunModeItem.LinuxSudoPwd;
|
||||
|
||||
#endregion Tun mode
|
||||
|
||||
await InitCoreType();
|
||||
NoticeHandler.Instance.Enqueue(needReboot ? ResUI.NeedRebootTips : ResUI.OperationSuccess);
|
||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||
}
|
||||
|
||||
private async Task InitCoreType()
|
||||
else
|
||||
{
|
||||
if (_config.CoreTypeItem == null)
|
||||
{
|
||||
_config.CoreTypeItem = new List<CoreTypeItem>();
|
||||
}
|
||||
|
||||
foreach (EConfigType it in Enum.GetValues(typeof(EConfigType)))
|
||||
{
|
||||
if (_config.CoreTypeItem.FindIndex(t => t.ConfigType == it) >= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_config.CoreTypeItem.Add(new CoreTypeItem()
|
||||
{
|
||||
ConfigType = it,
|
||||
CoreType = ECoreType.Xray
|
||||
});
|
||||
}
|
||||
_config.CoreTypeItem.ForEach(it =>
|
||||
{
|
||||
var type = it.CoreType.ToString();
|
||||
switch ((int)it.ConfigType)
|
||||
{
|
||||
case 1:
|
||||
CoreType1 = type;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
CoreType2 = type;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
CoreType3 = type;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
CoreType4 = type;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
CoreType5 = type;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
CoreType6 = type;
|
||||
break;
|
||||
}
|
||||
});
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task SaveSettingAsync()
|
||||
{
|
||||
if (localPort.ToString().IsNullOrEmpty() || !Utils.IsNumeric(localPort.ToString())
|
||||
|| localPort <= 0 || localPort >= Global.MaxPort)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillLocalListeningPort);
|
||||
return;
|
||||
}
|
||||
var needReboot = (EnableStatistics != _config.GuiItem.EnableStatistics
|
||||
|| DisplayRealTimeSpeed != _config.GuiItem.DisplayRealTimeSpeed
|
||||
|| EnableDragDropSort != _config.UiItem.EnableDragDropSort
|
||||
|| EnableHWA != _config.GuiItem.EnableHWA
|
||||
|| CurrentFontFamily != _config.UiItem.CurrentFontFamily
|
||||
|| MainGirdOrientation != (int)_config.UiItem.MainGirdOrientation);
|
||||
|
||||
//if (Utile.IsNullOrEmpty(Kcpmtu.ToString()) || !Utile.IsNumeric(Kcpmtu.ToString())
|
||||
// || Utile.IsNullOrEmpty(Kcptti.ToString()) || !Utile.IsNumeric(Kcptti.ToString())
|
||||
// || Utile.IsNullOrEmpty(KcpuplinkCapacity.ToString()) || !Utile.IsNumeric(KcpuplinkCapacity.ToString())
|
||||
// || Utile.IsNullOrEmpty(KcpdownlinkCapacity.ToString()) || !Utile.IsNumeric(KcpdownlinkCapacity.ToString())
|
||||
// || Utile.IsNullOrEmpty(KcpreadBufferSize.ToString()) || !Utile.IsNumeric(KcpreadBufferSize.ToString())
|
||||
// || Utile.IsNullOrEmpty(KcpwriteBufferSize.ToString()) || !Utile.IsNumeric(KcpwriteBufferSize.ToString()))
|
||||
//{
|
||||
// NoticeHandler.Instance.Enqueue(ResUI.FillKcpParameters);
|
||||
// return;
|
||||
//}
|
||||
|
||||
//Core
|
||||
_config.Inbound.First().LocalPort = localPort;
|
||||
_config.Inbound.First().SecondLocalPortEnabled = SecondLocalPortEnabled;
|
||||
_config.Inbound.First().UdpEnabled = udpEnabled;
|
||||
_config.Inbound.First().SniffingEnabled = sniffingEnabled;
|
||||
_config.Inbound.First().DestOverride = destOverride?.ToList();
|
||||
_config.Inbound.First().RouteOnly = routeOnly;
|
||||
_config.Inbound.First().AllowLANConn = allowLANConn;
|
||||
_config.Inbound.First().NewPort4LAN = newPort4LAN;
|
||||
_config.Inbound.First().User = user;
|
||||
_config.Inbound.First().Pass = pass;
|
||||
if (_config.Inbound.Count > 1)
|
||||
{
|
||||
_config.Inbound.RemoveAt(1);
|
||||
}
|
||||
_config.CoreBasicItem.LogEnabled = logEnabled;
|
||||
_config.CoreBasicItem.Loglevel = loglevel;
|
||||
_config.CoreBasicItem.MuxEnabled = muxEnabled;
|
||||
_config.CoreBasicItem.DefAllowInsecure = defAllowInsecure;
|
||||
_config.CoreBasicItem.DefFingerprint = defFingerprint;
|
||||
_config.CoreBasicItem.DefUserAgent = defUserAgent;
|
||||
_config.Mux4SboxItem.Protocol = mux4SboxProtocol;
|
||||
_config.CoreBasicItem.EnableCacheFile4Sbox = enableCacheFile4Sbox;
|
||||
_config.HysteriaItem.UpMbps = hyUpMbps;
|
||||
_config.HysteriaItem.DownMbps = hyDownMbps;
|
||||
_config.CoreBasicItem.EnableFragment = enableFragment;
|
||||
|
||||
_config.GuiItem.AutoRun = AutoRun;
|
||||
_config.GuiItem.EnableStatistics = EnableStatistics;
|
||||
_config.GuiItem.DisplayRealTimeSpeed = DisplayRealTimeSpeed;
|
||||
_config.GuiItem.KeepOlderDedupl = KeepOlderDedupl;
|
||||
_config.UiItem.EnableAutoAdjustMainLvColWidth = EnableAutoAdjustMainLvColWidth;
|
||||
_config.UiItem.EnableUpdateSubOnlyRemarksExist = EnableUpdateSubOnlyRemarksExist;
|
||||
_config.GuiItem.EnableSecurityProtocolTls13 = EnableSecurityProtocolTls13;
|
||||
_config.UiItem.AutoHideStartup = AutoHideStartup;
|
||||
_config.UiItem.Hide2TrayWhenClose = Hide2TrayWhenClose;
|
||||
_config.GuiItem.AutoUpdateInterval = AutoUpdateInterval;
|
||||
_config.UiItem.EnableDragDropSort = EnableDragDropSort;
|
||||
_config.UiItem.DoubleClick2Activate = DoubleClick2Activate;
|
||||
_config.GuiItem.TrayMenuServersLimit = TrayMenuServersLimit;
|
||||
_config.UiItem.CurrentFontFamily = CurrentFontFamily;
|
||||
_config.SpeedTestItem.SpeedTestTimeout = SpeedTestTimeout;
|
||||
_config.SpeedTestItem.MixedConcurrencyCount = MixedConcurrencyCount;
|
||||
_config.SpeedTestItem.SpeedTestUrl = SpeedTestUrl;
|
||||
_config.SpeedTestItem.SpeedPingTestUrl = SpeedPingTestUrl;
|
||||
_config.GuiItem.EnableHWA = EnableHWA;
|
||||
_config.ConstItem.SubConvertUrl = SubConvertUrl;
|
||||
_config.UiItem.MainGirdOrientation = (EGirdOrientation)MainGirdOrientation;
|
||||
_config.ConstItem.GeoSourceUrl = GeoFileSourceUrl;
|
||||
_config.ConstItem.SrsSourceUrl = SrsFileSourceUrl;
|
||||
_config.ConstItem.RouteRulesTemplateSourceUrl = RoutingRulesSourceUrl;
|
||||
|
||||
//systemProxy
|
||||
_config.SystemProxyItem.SystemProxyExceptions = systemProxyExceptions;
|
||||
_config.SystemProxyItem.NotProxyLocalAddress = notProxyLocalAddress;
|
||||
_config.SystemProxyItem.SystemProxyAdvancedProtocol = systemProxyAdvancedProtocol;
|
||||
|
||||
//tun mode
|
||||
_config.TunModeItem.StrictRoute = TunStrictRoute;
|
||||
_config.TunModeItem.Stack = TunStack;
|
||||
_config.TunModeItem.Mtu = TunMtu;
|
||||
_config.TunModeItem.EnableExInbound = TunEnableExInbound;
|
||||
_config.TunModeItem.EnableIPv6Address = TunEnableIPv6Address;
|
||||
if (TunLinuxSudoPassword != _config.TunModeItem.LinuxSudoPwd)
|
||||
{
|
||||
_config.TunModeItem.LinuxSudoPwd = DesUtils.Encrypt(TunLinuxSudoPassword);
|
||||
}
|
||||
|
||||
//coreType
|
||||
await SaveCoreType();
|
||||
|
||||
if (await ConfigHandler.SaveConfig(_config) == 0)
|
||||
{
|
||||
await AutoStartupHandler.UpdateTask(_config);
|
||||
AppHandler.Instance.Reset();
|
||||
|
||||
NoticeHandler.Instance.Enqueue(needReboot ? ResUI.NeedRebootTips : ResUI.OperationSuccess);
|
||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SaveCoreType()
|
||||
{
|
||||
for (int k = 1; k <= _config.CoreTypeItem.Count; k++)
|
||||
{
|
||||
var item = _config.CoreTypeItem[k - 1];
|
||||
var type = string.Empty;
|
||||
switch ((int)item.ConfigType)
|
||||
{
|
||||
case 1:
|
||||
type = CoreType1;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
type = CoreType2;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
type = CoreType3;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
type = CoreType4;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
type = CoreType5;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
type = CoreType6;
|
||||
break;
|
||||
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
item.CoreType = (ECoreType)Enum.Parse(typeof(ECoreType), type);
|
||||
}
|
||||
await Task.CompletedTask;
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SaveCoreType()
|
||||
{
|
||||
for (int k = 1; k <= _config.CoreTypeItem.Count; k++)
|
||||
{
|
||||
var item = _config.CoreTypeItem[k - 1];
|
||||
var type = string.Empty;
|
||||
switch ((int)item.ConfigType)
|
||||
{
|
||||
case 1:
|
||||
type = CoreType1;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
type = CoreType2;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
type = CoreType3;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
type = CoreType4;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
type = CoreType5;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
type = CoreType6;
|
||||
break;
|
||||
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
item.CoreType = (ECoreType)Enum.Parse(typeof(ECoreType), type);
|
||||
}
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,92 +2,91 @@ using System.Reactive;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
|
||||
namespace ServiceLib.ViewModels
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
public class RoutingRuleDetailsViewModel : MyReactiveObject
|
||||
{
|
||||
public class RoutingRuleDetailsViewModel : MyReactiveObject
|
||||
public IList<string> ProtocolItems { get; set; }
|
||||
public IList<string> InboundTagItems { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public RulesItem SelectedSource { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string Domain { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string IP { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string Process { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool AutoSort { get; set; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||
|
||||
public RoutingRuleDetailsViewModel(RulesItem rulesItem, Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
public IList<string> ProtocolItems { get; set; }
|
||||
public IList<string> InboundTagItems { get; set; }
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
|
||||
[Reactive]
|
||||
public RulesItem SelectedSource { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string Domain { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string IP { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string Process { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool AutoSort { get; set; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||
|
||||
public RoutingRuleDetailsViewModel(RulesItem rulesItem, Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
await SaveRulesAsync();
|
||||
});
|
||||
|
||||
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await SaveRulesAsync();
|
||||
});
|
||||
|
||||
if (rulesItem.Id.IsNullOrEmpty())
|
||||
{
|
||||
rulesItem.Id = Utils.GetGuid(false);
|
||||
rulesItem.OutboundTag = Global.ProxyTag;
|
||||
rulesItem.Enabled = true;
|
||||
SelectedSource = rulesItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedSource = rulesItem;
|
||||
}
|
||||
|
||||
Domain = Utils.List2String(SelectedSource.Domain, true);
|
||||
IP = Utils.List2String(SelectedSource.Ip, true);
|
||||
Process = Utils.List2String(SelectedSource.Process, true);
|
||||
if (rulesItem.Id.IsNullOrEmpty())
|
||||
{
|
||||
rulesItem.Id = Utils.GetGuid(false);
|
||||
rulesItem.OutboundTag = Global.ProxyTag;
|
||||
rulesItem.Enabled = true;
|
||||
SelectedSource = rulesItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedSource = rulesItem;
|
||||
}
|
||||
|
||||
private async Task SaveRulesAsync()
|
||||
Domain = Utils.List2String(SelectedSource.Domain, true);
|
||||
IP = Utils.List2String(SelectedSource.Ip, true);
|
||||
Process = Utils.List2String(SelectedSource.Process, true);
|
||||
}
|
||||
|
||||
private async Task SaveRulesAsync()
|
||||
{
|
||||
Domain = Utils.Convert2Comma(Domain);
|
||||
IP = Utils.Convert2Comma(IP);
|
||||
Process = Utils.Convert2Comma(Process);
|
||||
|
||||
if (AutoSort)
|
||||
{
|
||||
Domain = Utils.Convert2Comma(Domain);
|
||||
IP = Utils.Convert2Comma(IP);
|
||||
Process = Utils.Convert2Comma(Process);
|
||||
|
||||
if (AutoSort)
|
||||
{
|
||||
SelectedSource.Domain = Utils.String2ListSorted(Domain);
|
||||
SelectedSource.Ip = Utils.String2ListSorted(IP);
|
||||
SelectedSource.Process = Utils.String2ListSorted(Process);
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedSource.Domain = Utils.String2List(Domain);
|
||||
SelectedSource.Ip = Utils.String2List(IP);
|
||||
SelectedSource.Process = Utils.String2List(Process);
|
||||
}
|
||||
SelectedSource.Protocol = ProtocolItems?.ToList();
|
||||
SelectedSource.InboundTag = InboundTagItems?.ToList();
|
||||
|
||||
var hasRule = SelectedSource.Domain?.Count > 0
|
||||
|| SelectedSource.Ip?.Count > 0
|
||||
|| SelectedSource.Protocol?.Count > 0
|
||||
|| SelectedSource.Process?.Count > 0
|
||||
|| SelectedSource.Port.IsNotEmpty()
|
||||
|| SelectedSource.Network.IsNotEmpty();
|
||||
|
||||
if (!hasRule)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(string.Format(ResUI.RoutingRuleDetailRequiredTips, "Network/Port/Protocol/Domain/IP/Process"));
|
||||
return;
|
||||
}
|
||||
//NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
await _updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||
SelectedSource.Domain = Utils.String2ListSorted(Domain);
|
||||
SelectedSource.Ip = Utils.String2ListSorted(IP);
|
||||
SelectedSource.Process = Utils.String2ListSorted(Process);
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedSource.Domain = Utils.String2List(Domain);
|
||||
SelectedSource.Ip = Utils.String2List(IP);
|
||||
SelectedSource.Process = Utils.String2List(Process);
|
||||
}
|
||||
SelectedSource.Protocol = ProtocolItems?.ToList();
|
||||
SelectedSource.InboundTag = InboundTagItems?.ToList();
|
||||
|
||||
var hasRule = SelectedSource.Domain?.Count > 0
|
||||
|| SelectedSource.Ip?.Count > 0
|
||||
|| SelectedSource.Protocol?.Count > 0
|
||||
|| SelectedSource.Process?.Count > 0
|
||||
|| SelectedSource.Port.IsNotEmpty()
|
||||
|| SelectedSource.Network.IsNotEmpty();
|
||||
|
||||
if (!hasRule)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(string.Format(ResUI.RoutingRuleDetailRequiredTips, "Network/Port/Protocol/Domain/IP/Process"));
|
||||
return;
|
||||
}
|
||||
//NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
await _updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,337 +5,336 @@ using DynamicData.Binding;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
|
||||
namespace ServiceLib.ViewModels
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
public class RoutingRuleSettingViewModel : MyReactiveObject
|
||||
{
|
||||
public class RoutingRuleSettingViewModel : MyReactiveObject
|
||||
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, Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
private List<RulesItem> _rules;
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
|
||||
[Reactive]
|
||||
public RoutingItem SelectedRouting { get; set; }
|
||||
var canEditRemove = this.WhenAnyValue(
|
||||
x => x.SelectedSource,
|
||||
selectedSource => selectedSource != null && !selectedSource.OutboundTag.IsNullOrEmpty());
|
||||
|
||||
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, Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
RuleAddCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
await RuleEditAsync(true);
|
||||
});
|
||||
ImportRulesFromFileCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await _updateView?.Invoke(EViewAction.ImportRulesFromFile, null);
|
||||
});
|
||||
ImportRulesFromClipboardCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await ImportRulesFromClipboardAsync(null);
|
||||
});
|
||||
ImportRulesFromUrlCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await ImportRulesFromUrl();
|
||||
});
|
||||
|
||||
var canEditRemove = this.WhenAnyValue(
|
||||
x => x.SelectedSource,
|
||||
selectedSource => selectedSource != null && !selectedSource.OutboundTag.IsNullOrEmpty());
|
||||
RuleRemoveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await RuleRemoveAsync();
|
||||
}, canEditRemove);
|
||||
RuleExportSelectedCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await RuleExportSelectedAsync();
|
||||
}, canEditRemove);
|
||||
|
||||
RuleAddCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await RuleEditAsync(true);
|
||||
});
|
||||
ImportRulesFromFileCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await _updateView?.Invoke(EViewAction.ImportRulesFromFile, null);
|
||||
});
|
||||
ImportRulesFromClipboardCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await ImportRulesFromClipboardAsync(null);
|
||||
});
|
||||
ImportRulesFromUrlCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await ImportRulesFromUrl();
|
||||
});
|
||||
MoveTopCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await MoveRule(EMove.Top);
|
||||
}, canEditRemove);
|
||||
MoveUpCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await MoveRule(EMove.Up);
|
||||
}, canEditRemove);
|
||||
MoveDownCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await MoveRule(EMove.Down);
|
||||
}, canEditRemove);
|
||||
MoveBottomCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await MoveRule(EMove.Bottom);
|
||||
}, canEditRemove);
|
||||
|
||||
RuleRemoveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await RuleRemoveAsync();
|
||||
}, canEditRemove);
|
||||
RuleExportSelectedCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await RuleExportSelectedAsync();
|
||||
}, canEditRemove);
|
||||
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await SaveRoutingAsync();
|
||||
});
|
||||
|
||||
MoveTopCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await MoveRule(EMove.Top);
|
||||
}, canEditRemove);
|
||||
MoveUpCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await MoveRule(EMove.Up);
|
||||
}, canEditRemove);
|
||||
MoveDownCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await MoveRule(EMove.Down);
|
||||
}, canEditRemove);
|
||||
MoveBottomCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await MoveRule(EMove.Bottom);
|
||||
}, canEditRemove);
|
||||
SelectedSource = new();
|
||||
SelectedRouting = routingItem;
|
||||
_rules = routingItem.Id.IsNullOrEmpty() ? new() : JsonUtils.Deserialize<List<RulesItem>>(SelectedRouting.RuleSet);
|
||||
|
||||
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
RefreshRulesItems();
|
||||
}
|
||||
|
||||
public void RefreshRulesItems()
|
||||
{
|
||||
_rulesItems.Clear();
|
||||
|
||||
foreach (var item in _rules)
|
||||
{
|
||||
var it = new RulesItemModel()
|
||||
{
|
||||
await SaveRoutingAsync();
|
||||
});
|
||||
|
||||
SelectedSource = new();
|
||||
SelectedRouting = routingItem;
|
||||
_rules = routingItem.Id.IsNullOrEmpty() ? new() : JsonUtils.Deserialize<List<RulesItem>>(SelectedRouting.RuleSet);
|
||||
|
||||
RefreshRulesItems();
|
||||
Id = item.Id,
|
||||
OutboundTag = item.OutboundTag,
|
||||
Port = item.Port,
|
||||
Network = item.Network,
|
||||
Protocols = Utils.List2String(item.Protocol),
|
||||
InboundTags = Utils.List2String(item.InboundTag),
|
||||
Domains = Utils.List2String(item.Domain),
|
||||
Ips = Utils.List2String(item.Ip),
|
||||
Enabled = item.Enabled,
|
||||
Remarks = item.Remarks,
|
||||
};
|
||||
_rulesItems.Add(it);
|
||||
}
|
||||
}
|
||||
|
||||
public void RefreshRulesItems()
|
||||
public async Task RuleEditAsync(bool blNew)
|
||||
{
|
||||
RulesItem? item;
|
||||
if (blNew)
|
||||
{
|
||||
_rulesItems.Clear();
|
||||
|
||||
foreach (var item in _rules)
|
||||
item = new();
|
||||
}
|
||||
else
|
||||
{
|
||||
item = _rules.FirstOrDefault(t => t.Id == SelectedSource?.Id);
|
||||
if (item is null)
|
||||
{
|
||||
var it = new RulesItemModel()
|
||||
{
|
||||
Id = item.Id,
|
||||
OutboundTag = item.OutboundTag,
|
||||
Port = item.Port,
|
||||
Network = item.Network,
|
||||
Protocols = Utils.List2String(item.Protocol),
|
||||
InboundTags = Utils.List2String(item.InboundTag),
|
||||
Domains = Utils.List2String(item.Domain),
|
||||
Ips = Utils.List2String(item.Ip),
|
||||
Enabled = item.Enabled,
|
||||
Remarks = item.Remarks,
|
||||
};
|
||||
_rulesItems.Add(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task RuleEditAsync(bool blNew)
|
||||
if (await _updateView?.Invoke(EViewAction.RoutingRuleDetailsWindow, item) == true)
|
||||
{
|
||||
RulesItem? item;
|
||||
if (blNew)
|
||||
{
|
||||
item = new();
|
||||
_rules.Insert(0, item);
|
||||
}
|
||||
else
|
||||
{
|
||||
item = _rules.FirstOrDefault(t => t.Id == SelectedSource?.Id);
|
||||
if (item is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (await _updateView?.Invoke(EViewAction.RoutingRuleDetailsWindow, item) == true)
|
||||
{
|
||||
if (blNew)
|
||||
{
|
||||
_rules.Insert(0, item);
|
||||
}
|
||||
RefreshRulesItems();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task RuleRemoveAsync()
|
||||
{
|
||||
if (SelectedSource is null || SelectedSource.OutboundTag.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectRules);
|
||||
return;
|
||||
}
|
||||
if (await _updateView?.Invoke(EViewAction.ShowYesNo, null) == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
foreach (var it in SelectedSources ?? [SelectedSource])
|
||||
{
|
||||
var item = _rules.FirstOrDefault(t => t.Id == it?.Id);
|
||||
if (item != null)
|
||||
{
|
||||
_rules.Remove(item);
|
||||
}
|
||||
}
|
||||
|
||||
RefreshRulesItems();
|
||||
}
|
||||
|
||||
public async Task RuleExportSelectedAsync()
|
||||
{
|
||||
if (SelectedSource is null || SelectedSource.OutboundTag.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectRules);
|
||||
return;
|
||||
}
|
||||
|
||||
var lst = new List<RulesItem>();
|
||||
var sources = SelectedSources ?? [SelectedSource];
|
||||
foreach (var it in _rules)
|
||||
{
|
||||
if (sources.Any(t => t.Id == it?.Id))
|
||||
{
|
||||
var item2 = JsonUtils.DeepCopy(it);
|
||||
item2.Id = null;
|
||||
lst.Add(item2 ?? new());
|
||||
}
|
||||
}
|
||||
if (lst.Count > 0)
|
||||
{
|
||||
var options = new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
};
|
||||
await _updateView?.Invoke(EViewAction.SetClipboardData, JsonUtils.Serialize(lst, options));
|
||||
}
|
||||
}
|
||||
|
||||
public async Task MoveRule(EMove eMove)
|
||||
{
|
||||
if (SelectedSource is null || SelectedSource.OutboundTag.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectRules);
|
||||
return;
|
||||
}
|
||||
|
||||
var item = _rules.FirstOrDefault(t => t.Id == SelectedSource?.Id);
|
||||
if (item == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var index = _rules.IndexOf(item);
|
||||
if (await ConfigHandler.MoveRoutingRule(_rules, index, eMove) == 0)
|
||||
{
|
||||
RefreshRulesItems();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SaveRoutingAsync()
|
||||
{
|
||||
string remarks = SelectedRouting.Remarks;
|
||||
if (remarks.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseFillRemarks);
|
||||
return;
|
||||
}
|
||||
var item = SelectedRouting;
|
||||
foreach (var it in _rules)
|
||||
{
|
||||
it.Id = Utils.GetGuid(false);
|
||||
}
|
||||
item.RuleNum = _rules.Count;
|
||||
item.RuleSet = JsonUtils.Serialize(_rules, false);
|
||||
|
||||
if (await ConfigHandler.SaveRoutingItem(_config, item) == 0)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
|
||||
}
|
||||
}
|
||||
|
||||
#region Import rules
|
||||
|
||||
public async Task ImportRulesFromFileAsync(string fileName)
|
||||
{
|
||||
if (fileName.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var result = EmbedUtils.LoadResource(fileName);
|
||||
if (result.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var ret = await AddBatchRoutingRulesAsync(SelectedRouting, result);
|
||||
if (ret == 0)
|
||||
{
|
||||
RefreshRulesItems();
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ImportRulesFromClipboardAsync(string? clipboardData)
|
||||
{
|
||||
if (clipboardData == null)
|
||||
{
|
||||
await _updateView?.Invoke(EViewAction.ImportRulesFromClipboard, null);
|
||||
return;
|
||||
}
|
||||
var ret = await AddBatchRoutingRulesAsync(SelectedRouting, clipboardData);
|
||||
if (ret == 0)
|
||||
{
|
||||
RefreshRulesItems();
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ImportRulesFromUrl()
|
||||
{
|
||||
var url = SelectedRouting.Url;
|
||||
if (url.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.MsgNeedUrl);
|
||||
return;
|
||||
}
|
||||
|
||||
DownloadService downloadHandle = new DownloadService();
|
||||
var result = await downloadHandle.TryDownloadString(url, true, "");
|
||||
var ret = await AddBatchRoutingRulesAsync(SelectedRouting, result);
|
||||
if (ret == 0)
|
||||
{
|
||||
RefreshRulesItems();
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<int> AddBatchRoutingRulesAsync(RoutingItem routingItem, string? clipboardData)
|
||||
{
|
||||
bool blReplace = false;
|
||||
if (await _updateView?.Invoke(EViewAction.AddBatchRoutingRulesYesNo, null) == false)
|
||||
{
|
||||
blReplace = true;
|
||||
}
|
||||
if (clipboardData.IsNullOrEmpty())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
var lstRules = JsonUtils.Deserialize<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
|
||||
}
|
||||
|
||||
public async Task RuleRemoveAsync()
|
||||
{
|
||||
if (SelectedSource is null || SelectedSource.OutboundTag.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectRules);
|
||||
return;
|
||||
}
|
||||
if (await _updateView?.Invoke(EViewAction.ShowYesNo, null) == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
foreach (var it in SelectedSources ?? [SelectedSource])
|
||||
{
|
||||
var item = _rules.FirstOrDefault(t => t.Id == it?.Id);
|
||||
if (item != null)
|
||||
{
|
||||
_rules.Remove(item);
|
||||
}
|
||||
}
|
||||
|
||||
RefreshRulesItems();
|
||||
}
|
||||
|
||||
public async Task RuleExportSelectedAsync()
|
||||
{
|
||||
if (SelectedSource is null || SelectedSource.OutboundTag.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectRules);
|
||||
return;
|
||||
}
|
||||
|
||||
var lst = new List<RulesItem>();
|
||||
var sources = SelectedSources ?? [SelectedSource];
|
||||
foreach (var it in _rules)
|
||||
{
|
||||
if (sources.Any(t => t.Id == it?.Id))
|
||||
{
|
||||
var item2 = JsonUtils.DeepCopy(it);
|
||||
item2.Id = null;
|
||||
lst.Add(item2 ?? new());
|
||||
}
|
||||
}
|
||||
if (lst.Count > 0)
|
||||
{
|
||||
var options = new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
};
|
||||
await _updateView?.Invoke(EViewAction.SetClipboardData, JsonUtils.Serialize(lst, options));
|
||||
}
|
||||
}
|
||||
|
||||
public async Task MoveRule(EMove eMove)
|
||||
{
|
||||
if (SelectedSource is null || SelectedSource.OutboundTag.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectRules);
|
||||
return;
|
||||
}
|
||||
|
||||
var item = _rules.FirstOrDefault(t => t.Id == SelectedSource?.Id);
|
||||
if (item == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var index = _rules.IndexOf(item);
|
||||
if (await ConfigHandler.MoveRoutingRule(_rules, index, eMove) == 0)
|
||||
{
|
||||
RefreshRulesItems();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SaveRoutingAsync()
|
||||
{
|
||||
string remarks = SelectedRouting.Remarks;
|
||||
if (remarks.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseFillRemarks);
|
||||
return;
|
||||
}
|
||||
var item = SelectedRouting;
|
||||
foreach (var it in _rules)
|
||||
{
|
||||
it.Id = Utils.GetGuid(false);
|
||||
}
|
||||
item.RuleNum = _rules.Count;
|
||||
item.RuleSet = JsonUtils.Serialize(_rules, false);
|
||||
|
||||
if (await ConfigHandler.SaveRoutingItem(_config, item) == 0)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
|
||||
}
|
||||
}
|
||||
|
||||
#region Import rules
|
||||
|
||||
public async Task ImportRulesFromFileAsync(string fileName)
|
||||
{
|
||||
if (fileName.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var result = EmbedUtils.LoadResource(fileName);
|
||||
if (result.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var ret = await AddBatchRoutingRulesAsync(SelectedRouting, result);
|
||||
if (ret == 0)
|
||||
{
|
||||
RefreshRulesItems();
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ImportRulesFromClipboardAsync(string? clipboardData)
|
||||
{
|
||||
if (clipboardData == null)
|
||||
{
|
||||
await _updateView?.Invoke(EViewAction.ImportRulesFromClipboard, null);
|
||||
return;
|
||||
}
|
||||
var ret = await AddBatchRoutingRulesAsync(SelectedRouting, clipboardData);
|
||||
if (ret == 0)
|
||||
{
|
||||
RefreshRulesItems();
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ImportRulesFromUrl()
|
||||
{
|
||||
var url = SelectedRouting.Url;
|
||||
if (url.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.MsgNeedUrl);
|
||||
return;
|
||||
}
|
||||
|
||||
DownloadService downloadHandle = new DownloadService();
|
||||
var result = await downloadHandle.TryDownloadString(url, true, "");
|
||||
var ret = await AddBatchRoutingRulesAsync(SelectedRouting, result);
|
||||
if (ret == 0)
|
||||
{
|
||||
RefreshRulesItems();
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<int> AddBatchRoutingRulesAsync(RoutingItem routingItem, string? clipboardData)
|
||||
{
|
||||
bool blReplace = false;
|
||||
if (await _updateView?.Invoke(EViewAction.AddBatchRoutingRulesYesNo, null) == false)
|
||||
{
|
||||
blReplace = true;
|
||||
}
|
||||
if (clipboardData.IsNullOrEmpty())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
var lstRules = JsonUtils.Deserialize<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
|
||||
}
|
||||
|
||||
@@ -3,199 +3,198 @@ using DynamicData.Binding;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
|
||||
namespace ServiceLib.ViewModels
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
public class RoutingSettingViewModel : MyReactiveObject
|
||||
{
|
||||
public class RoutingSettingViewModel : MyReactiveObject
|
||||
#region Reactive
|
||||
|
||||
private IObservableCollection<RoutingItemModel> _routingItems = new ObservableCollectionExtended<RoutingItemModel>();
|
||||
public IObservableCollection<RoutingItemModel> RoutingItems => _routingItems;
|
||||
|
||||
[Reactive]
|
||||
public RoutingItemModel SelectedSource { get; set; }
|
||||
|
||||
public IList<RoutingItemModel> SelectedSources { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string DomainStrategy { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string DomainMatcher { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string DomainStrategy4Singbox { get; set; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> RoutingAdvancedAddCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> RoutingAdvancedRemoveCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> RoutingAdvancedSetDefaultCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> RoutingAdvancedImportRulesCmd { get; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||
public bool IsModified { get; set; }
|
||||
|
||||
#endregion Reactive
|
||||
|
||||
public RoutingSettingViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
#region Reactive
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
|
||||
private IObservableCollection<RoutingItemModel> _routingItems = new ObservableCollectionExtended<RoutingItemModel>();
|
||||
public IObservableCollection<RoutingItemModel> RoutingItems => _routingItems;
|
||||
var canEditRemove = this.WhenAnyValue(
|
||||
x => x.SelectedSource,
|
||||
selectedSource => selectedSource != null && !selectedSource.Remarks.IsNullOrEmpty());
|
||||
|
||||
[Reactive]
|
||||
public RoutingItemModel SelectedSource { get; set; }
|
||||
|
||||
public IList<RoutingItemModel> SelectedSources { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string DomainStrategy { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string DomainMatcher { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string DomainStrategy4Singbox { get; set; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> RoutingAdvancedAddCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> RoutingAdvancedRemoveCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> RoutingAdvancedSetDefaultCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> RoutingAdvancedImportRulesCmd { get; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||
public bool IsModified { get; set; }
|
||||
|
||||
#endregion Reactive
|
||||
|
||||
public RoutingSettingViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
RoutingAdvancedAddCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
await RoutingAdvancedEditAsync(true);
|
||||
});
|
||||
RoutingAdvancedRemoveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await RoutingAdvancedRemoveAsync();
|
||||
}, canEditRemove);
|
||||
RoutingAdvancedSetDefaultCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await RoutingAdvancedSetDefault();
|
||||
}, canEditRemove);
|
||||
RoutingAdvancedImportRulesCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await RoutingAdvancedImportRules();
|
||||
});
|
||||
|
||||
var canEditRemove = this.WhenAnyValue(
|
||||
x => x.SelectedSource,
|
||||
selectedSource => selectedSource != null && !selectedSource.Remarks.IsNullOrEmpty());
|
||||
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await SaveRoutingAsync();
|
||||
});
|
||||
|
||||
RoutingAdvancedAddCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await RoutingAdvancedEditAsync(true);
|
||||
});
|
||||
RoutingAdvancedRemoveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await RoutingAdvancedRemoveAsync();
|
||||
}, canEditRemove);
|
||||
RoutingAdvancedSetDefaultCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await RoutingAdvancedSetDefault();
|
||||
}, canEditRemove);
|
||||
RoutingAdvancedImportRulesCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await RoutingAdvancedImportRules();
|
||||
});
|
||||
_ = Init();
|
||||
}
|
||||
|
||||
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await SaveRoutingAsync();
|
||||
});
|
||||
private async Task Init()
|
||||
{
|
||||
SelectedSource = new();
|
||||
|
||||
_ = Init();
|
||||
DomainStrategy = _config.RoutingBasicItem.DomainStrategy;
|
||||
DomainMatcher = _config.RoutingBasicItem.DomainMatcher;
|
||||
DomainStrategy4Singbox = _config.RoutingBasicItem.DomainStrategy4Singbox;
|
||||
|
||||
await ConfigHandler.InitBuiltinRouting(_config);
|
||||
await RefreshRoutingItems();
|
||||
}
|
||||
|
||||
#region Refresh Save
|
||||
|
||||
public async Task RefreshRoutingItems()
|
||||
{
|
||||
_routingItems.Clear();
|
||||
|
||||
var routings = await AppHandler.Instance.RoutingItems();
|
||||
foreach (var item in routings)
|
||||
{
|
||||
var def = item.Id == _config.RoutingBasicItem.RoutingIndexId;
|
||||
|
||||
var it = new RoutingItemModel()
|
||||
{
|
||||
IsActive = def,
|
||||
RuleNum = item.RuleNum,
|
||||
Id = item.Id,
|
||||
Remarks = item.Remarks,
|
||||
Url = item.Url,
|
||||
CustomIcon = item.CustomIcon,
|
||||
CustomRulesetPath4Singbox = item.CustomRulesetPath4Singbox,
|
||||
Sort = item.Sort,
|
||||
};
|
||||
_routingItems.Add(it);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Init()
|
||||
private async Task SaveRoutingAsync()
|
||||
{
|
||||
_config.RoutingBasicItem.DomainStrategy = DomainStrategy;
|
||||
_config.RoutingBasicItem.DomainMatcher = DomainMatcher;
|
||||
_config.RoutingBasicItem.DomainStrategy4Singbox = DomainStrategy4Singbox;
|
||||
|
||||
if (await ConfigHandler.SaveConfig(_config) == 0)
|
||||
{
|
||||
SelectedSource = new();
|
||||
|
||||
DomainStrategy = _config.RoutingBasicItem.DomainStrategy;
|
||||
DomainMatcher = _config.RoutingBasicItem.DomainMatcher;
|
||||
DomainStrategy4Singbox = _config.RoutingBasicItem.DomainStrategy4Singbox;
|
||||
|
||||
await ConfigHandler.InitBuiltinRouting(_config);
|
||||
await RefreshRoutingItems();
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||
}
|
||||
|
||||
#region Refresh Save
|
||||
|
||||
public async Task RefreshRoutingItems()
|
||||
else
|
||||
{
|
||||
_routingItems.Clear();
|
||||
|
||||
var routings = await AppHandler.Instance.RoutingItems();
|
||||
foreach (var item in routings)
|
||||
{
|
||||
var def = item.Id == _config.RoutingBasicItem.RoutingIndexId;
|
||||
|
||||
var it = new RoutingItemModel()
|
||||
{
|
||||
IsActive = def,
|
||||
RuleNum = item.RuleNum,
|
||||
Id = item.Id,
|
||||
Remarks = item.Remarks,
|
||||
Url = item.Url,
|
||||
CustomIcon = item.CustomIcon,
|
||||
CustomRulesetPath4Singbox = item.CustomRulesetPath4Singbox,
|
||||
Sort = item.Sort,
|
||||
};
|
||||
_routingItems.Add(it);
|
||||
}
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SaveRoutingAsync()
|
||||
#endregion Refresh Save
|
||||
|
||||
public async Task RoutingAdvancedEditAsync(bool blNew)
|
||||
{
|
||||
RoutingItem item;
|
||||
if (blNew)
|
||||
{
|
||||
_config.RoutingBasicItem.DomainStrategy = DomainStrategy;
|
||||
_config.RoutingBasicItem.DomainMatcher = DomainMatcher;
|
||||
_config.RoutingBasicItem.DomainStrategy4Singbox = DomainStrategy4Singbox;
|
||||
|
||||
if (await ConfigHandler.SaveConfig(_config) == 0)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
|
||||
}
|
||||
item = new();
|
||||
}
|
||||
|
||||
#endregion Refresh Save
|
||||
|
||||
public async Task RoutingAdvancedEditAsync(bool blNew)
|
||||
else
|
||||
{
|
||||
RoutingItem item;
|
||||
if (blNew)
|
||||
{
|
||||
item = new();
|
||||
}
|
||||
else
|
||||
{
|
||||
item = await AppHandler.Instance.GetRoutingItem(SelectedSource?.Id);
|
||||
if (item is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (await _updateView?.Invoke(EViewAction.RoutingRuleSettingWindow, item) == true)
|
||||
{
|
||||
await RefreshRoutingItems();
|
||||
IsModified = true;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task RoutingAdvancedRemoveAsync()
|
||||
{
|
||||
if (SelectedSource is null || SelectedSource.Remarks.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectRules);
|
||||
return;
|
||||
}
|
||||
if (await _updateView?.Invoke(EViewAction.ShowYesNo, null) == false)
|
||||
item = await AppHandler.Instance.GetRoutingItem(SelectedSource?.Id);
|
||||
if (item is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
foreach (var it in SelectedSources ?? [SelectedSource])
|
||||
{
|
||||
var item = await AppHandler.Instance.GetRoutingItem(it?.Id);
|
||||
if (item != null)
|
||||
{
|
||||
await ConfigHandler.RemoveRoutingItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (await _updateView?.Invoke(EViewAction.RoutingRuleSettingWindow, item) == true)
|
||||
{
|
||||
await RefreshRoutingItems();
|
||||
IsModified = true;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task RoutingAdvancedSetDefault()
|
||||
public async Task RoutingAdvancedRemoveAsync()
|
||||
{
|
||||
if (SelectedSource is null || SelectedSource.Remarks.IsNullOrEmpty())
|
||||
{
|
||||
var item = await AppHandler.Instance.GetRoutingItem(SelectedSource?.Id);
|
||||
if (item is null)
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectRules);
|
||||
return;
|
||||
}
|
||||
if (await _updateView?.Invoke(EViewAction.ShowYesNo, null) == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
foreach (var it in SelectedSources ?? [SelectedSource])
|
||||
{
|
||||
var item = await AppHandler.Instance.GetRoutingItem(it?.Id);
|
||||
if (item != null)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectRules);
|
||||
return;
|
||||
}
|
||||
|
||||
if (await ConfigHandler.SetDefaultRouting(_config, item) == 0)
|
||||
{
|
||||
await RefreshRoutingItems();
|
||||
IsModified = true;
|
||||
await ConfigHandler.RemoveRoutingItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RoutingAdvancedImportRules()
|
||||
await RefreshRoutingItems();
|
||||
IsModified = true;
|
||||
}
|
||||
|
||||
public async Task RoutingAdvancedSetDefault()
|
||||
{
|
||||
var item = await AppHandler.Instance.GetRoutingItem(SelectedSource?.Id);
|
||||
if (item is null)
|
||||
{
|
||||
if (await ConfigHandler.InitRouting(_config, true) == 0)
|
||||
{
|
||||
await RefreshRoutingItems();
|
||||
IsModified = true;
|
||||
}
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectRules);
|
||||
return;
|
||||
}
|
||||
|
||||
if (await ConfigHandler.SetDefaultRouting(_config, item) == 0)
|
||||
{
|
||||
await RefreshRoutingItems();
|
||||
IsModified = true;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RoutingAdvancedImportRules()
|
||||
{
|
||||
if (await ConfigHandler.InitRouting(_config, true) == 0)
|
||||
{
|
||||
await RefreshRoutingItems();
|
||||
IsModified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,504 +5,503 @@ using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using Splat;
|
||||
|
||||
namespace ServiceLib.ViewModels
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
public class StatusBarViewModel : MyReactiveObject
|
||||
{
|
||||
public class StatusBarViewModel : MyReactiveObject
|
||||
#region ObservableCollection
|
||||
|
||||
private IObservableCollection<RoutingItem> _routingItems = new ObservableCollectionExtended<RoutingItem>();
|
||||
public IObservableCollection<RoutingItem> RoutingItems => _routingItems;
|
||||
|
||||
private IObservableCollection<ComboItem> _servers = new ObservableCollectionExtended<ComboItem>();
|
||||
public IObservableCollection<ComboItem> Servers => _servers;
|
||||
|
||||
[Reactive]
|
||||
public RoutingItem SelectedRouting { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public ComboItem SelectedServer { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool BlServers { get; set; }
|
||||
|
||||
#endregion ObservableCollection
|
||||
|
||||
public ReactiveCommand<Unit, Unit> AddServerViaClipboardCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> AddServerViaScanCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> SubUpdateCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> SubUpdateViaProxyCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> CopyProxyCmdToClipboardCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> NotifyLeftClickCmd { get; }
|
||||
|
||||
#region System Proxy
|
||||
|
||||
[Reactive]
|
||||
public bool BlSystemProxyClear { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool BlSystemProxySet { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool BlSystemProxyNothing { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool BlSystemProxyPac { get; set; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> SystemProxyClearCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> SystemProxySetCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> SystemProxyNothingCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> SystemProxyPacCmd { get; }
|
||||
|
||||
[Reactive]
|
||||
public bool BlRouting { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public int SystemProxySelected { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool BlSystemProxyPacVisible { get; set; }
|
||||
|
||||
#endregion System Proxy
|
||||
|
||||
#region UI
|
||||
|
||||
[Reactive]
|
||||
public string InboundDisplay { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string InboundLanDisplay { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string RunningServerDisplay { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string RunningServerToolTipText { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string RunningInfoDisplay { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string SpeedProxyDisplay { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string SpeedDirectDisplay { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool EnableTun { get; set; }
|
||||
|
||||
#endregion UI
|
||||
|
||||
public StatusBarViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
#region ObservableCollection
|
||||
_config = AppHandler.Instance.Config;
|
||||
SelectedRouting = new();
|
||||
SelectedServer = new();
|
||||
RunningServerToolTipText = "-";
|
||||
BlSystemProxyPacVisible = Utils.IsWindows();
|
||||
|
||||
private IObservableCollection<RoutingItem> _routingItems = new ObservableCollectionExtended<RoutingItem>();
|
||||
public IObservableCollection<RoutingItem> RoutingItems => _routingItems;
|
||||
|
||||
private IObservableCollection<ComboItem> _servers = new ObservableCollectionExtended<ComboItem>();
|
||||
public IObservableCollection<ComboItem> Servers => _servers;
|
||||
|
||||
[Reactive]
|
||||
public RoutingItem SelectedRouting { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public ComboItem SelectedServer { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool BlServers { get; set; }
|
||||
|
||||
#endregion ObservableCollection
|
||||
|
||||
public ReactiveCommand<Unit, Unit> AddServerViaClipboardCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> AddServerViaScanCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> SubUpdateCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> SubUpdateViaProxyCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> CopyProxyCmdToClipboardCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> NotifyLeftClickCmd { get; }
|
||||
|
||||
#region System Proxy
|
||||
|
||||
[Reactive]
|
||||
public bool BlSystemProxyClear { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool BlSystemProxySet { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool BlSystemProxyNothing { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool BlSystemProxyPac { get; set; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> SystemProxyClearCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> SystemProxySetCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> SystemProxyNothingCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> SystemProxyPacCmd { get; }
|
||||
|
||||
[Reactive]
|
||||
public bool BlRouting { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public int SystemProxySelected { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool BlSystemProxyPacVisible { get; set; }
|
||||
|
||||
#endregion System Proxy
|
||||
|
||||
#region UI
|
||||
|
||||
[Reactive]
|
||||
public string InboundDisplay { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string InboundLanDisplay { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string RunningServerDisplay { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string RunningServerToolTipText { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string RunningInfoDisplay { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string SpeedProxyDisplay { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string SpeedDirectDisplay { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool EnableTun { get; set; }
|
||||
|
||||
#endregion UI
|
||||
|
||||
public StatusBarViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
if (_config.TunModeItem.EnableTun && AllowEnableTun())
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
SelectedRouting = new();
|
||||
SelectedServer = new();
|
||||
RunningServerToolTipText = "-";
|
||||
BlSystemProxyPacVisible = Utils.IsWindows();
|
||||
|
||||
if (_config.TunModeItem.EnableTun && AllowEnableTun())
|
||||
{
|
||||
EnableTun = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_config.TunModeItem.EnableTun = EnableTun = false;
|
||||
}
|
||||
|
||||
#region WhenAnyValue && ReactiveCommand
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.SelectedRouting,
|
||||
y => y != null && !y.Remarks.IsNullOrEmpty())
|
||||
.Subscribe(async c => await RoutingSelectedChangedAsync(c));
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.SelectedServer,
|
||||
y => y != null && !y.Text.IsNullOrEmpty())
|
||||
.Subscribe(c => ServerSelectedChanged(c));
|
||||
|
||||
SystemProxySelected = (int)_config.SystemProxyItem.SysProxyType;
|
||||
this.WhenAnyValue(
|
||||
x => x.SystemProxySelected,
|
||||
y => y >= 0)
|
||||
.Subscribe(async c => await DoSystemProxySelected(c));
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.EnableTun,
|
||||
y => y == true)
|
||||
.Subscribe(async c => await DoEnableTun(c));
|
||||
|
||||
CopyProxyCmdToClipboardCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await CopyProxyCmdToClipboard();
|
||||
});
|
||||
|
||||
NotifyLeftClickCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.ShowHideWindow(null);
|
||||
await Task.CompletedTask;
|
||||
});
|
||||
|
||||
AddServerViaClipboardCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await AddServerViaClipboard();
|
||||
});
|
||||
AddServerViaScanCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await AddServerViaScan();
|
||||
});
|
||||
SubUpdateCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await UpdateSubscriptionProcess(false);
|
||||
});
|
||||
SubUpdateViaProxyCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await UpdateSubscriptionProcess(true);
|
||||
});
|
||||
|
||||
//System proxy
|
||||
SystemProxyClearCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await SetListenerType(ESysProxyType.ForcedClear);
|
||||
});
|
||||
SystemProxySetCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await SetListenerType(ESysProxyType.ForcedChange);
|
||||
});
|
||||
SystemProxyNothingCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await SetListenerType(ESysProxyType.Unchanged);
|
||||
});
|
||||
SystemProxyPacCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await SetListenerType(ESysProxyType.Pac);
|
||||
});
|
||||
|
||||
#endregion WhenAnyValue && ReactiveCommand
|
||||
|
||||
if (updateView != null)
|
||||
{
|
||||
InitUpdateView(updateView);
|
||||
}
|
||||
_ = Init();
|
||||
EnableTun = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_config.TunModeItem.EnableTun = EnableTun = false;
|
||||
}
|
||||
|
||||
private async Task Init()
|
||||
#region WhenAnyValue && ReactiveCommand
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.SelectedRouting,
|
||||
y => y != null && !y.Remarks.IsNullOrEmpty())
|
||||
.Subscribe(async c => await RoutingSelectedChangedAsync(c));
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.SelectedServer,
|
||||
y => y != null && !y.Text.IsNullOrEmpty())
|
||||
.Subscribe(c => ServerSelectedChanged(c));
|
||||
|
||||
SystemProxySelected = (int)_config.SystemProxyItem.SysProxyType;
|
||||
this.WhenAnyValue(
|
||||
x => x.SystemProxySelected,
|
||||
y => y >= 0)
|
||||
.Subscribe(async c => await DoSystemProxySelected(c));
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.EnableTun,
|
||||
y => y == true)
|
||||
.Subscribe(async c => await DoEnableTun(c));
|
||||
|
||||
CopyProxyCmdToClipboardCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await RefreshRoutingsMenu();
|
||||
await InboundDisplayStatus();
|
||||
await ChangeSystemProxyAsync(_config.SystemProxyItem.SysProxyType, true);
|
||||
}
|
||||
await CopyProxyCmdToClipboard();
|
||||
});
|
||||
|
||||
public void InitUpdateView(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
NotifyLeftClickCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
_updateView = updateView;
|
||||
if (_updateView != null)
|
||||
{
|
||||
MessageBus.Current.Listen<string>(EMsgCommand.RefreshProfiles.ToString()).Subscribe(OnNext);
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnNext(string x)
|
||||
{
|
||||
await _updateView?.Invoke(EViewAction.DispatcherRefreshServersBiz, null);
|
||||
}
|
||||
|
||||
private async Task CopyProxyCmdToClipboard()
|
||||
{
|
||||
var cmd = Utils.IsWindows() ? "set" : "export";
|
||||
var address = $"{Global.Loopback}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)}";
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine($"{cmd} http_proxy={Global.HttpProtocol}{address}");
|
||||
sb.AppendLine($"{cmd} https_proxy={Global.HttpProtocol}{address}");
|
||||
sb.AppendLine($"{cmd} all_proxy={Global.Socks5Protocol}{address}");
|
||||
sb.AppendLine("");
|
||||
sb.AppendLine($"{cmd} HTTP_PROXY={Global.HttpProtocol}{address}");
|
||||
sb.AppendLine($"{cmd} HTTPS_PROXY={Global.HttpProtocol}{address}");
|
||||
sb.AppendLine($"{cmd} ALL_PROXY={Global.Socks5Protocol}{address}");
|
||||
|
||||
await _updateView?.Invoke(EViewAction.SetClipboardData, sb.ToString());
|
||||
}
|
||||
|
||||
private async Task AddServerViaClipboard()
|
||||
{
|
||||
var service = Locator.Current.GetService<MainWindowViewModel>();
|
||||
if (service != null)
|
||||
await service.AddServerViaClipboardAsync(null);
|
||||
}
|
||||
|
||||
private async Task AddServerViaScan()
|
||||
{
|
||||
var service = Locator.Current.GetService<MainWindowViewModel>();
|
||||
if (service != null)
|
||||
await service.AddServerViaScanAsync();
|
||||
}
|
||||
|
||||
private async Task UpdateSubscriptionProcess(bool blProxy)
|
||||
{
|
||||
var service = Locator.Current.GetService<MainWindowViewModel>();
|
||||
if (service != null)
|
||||
await service.UpdateSubscriptionProcess("", blProxy);
|
||||
}
|
||||
|
||||
public async Task RefreshServersBiz()
|
||||
{
|
||||
await RefreshServersMenu();
|
||||
|
||||
//display running server
|
||||
var running = await ConfigHandler.GetDefaultServer(_config);
|
||||
if (running != null)
|
||||
{
|
||||
RunningServerDisplay =
|
||||
RunningServerToolTipText = running.GetSummary();
|
||||
}
|
||||
else
|
||||
{
|
||||
RunningServerDisplay =
|
||||
RunningServerToolTipText = ResUI.CheckServerSettings;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RefreshServersMenu()
|
||||
{
|
||||
var lstModel = await AppHandler.Instance.ProfileItems(_config.SubIndexId, "");
|
||||
|
||||
_servers.Clear();
|
||||
if (lstModel.Count > _config.GuiItem.TrayMenuServersLimit)
|
||||
{
|
||||
BlServers = false;
|
||||
return;
|
||||
}
|
||||
|
||||
BlServers = true;
|
||||
for (int k = 0; k < lstModel.Count; k++)
|
||||
{
|
||||
ProfileItem it = lstModel[k];
|
||||
string name = it.GetSummary();
|
||||
|
||||
var item = new ComboItem() { ID = it.IndexId, Text = name };
|
||||
_servers.Add(item);
|
||||
if (_config.IndexId == it.IndexId)
|
||||
{
|
||||
SelectedServer = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ServerSelectedChanged(bool c)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (SelectedServer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (SelectedServer.ID.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
Locator.Current.GetService<ProfilesViewModel>()?.SetDefaultServer(SelectedServer.ID);
|
||||
}
|
||||
|
||||
public async Task TestServerAvailability()
|
||||
{
|
||||
var item = await ConfigHandler.GetDefaultServer(_config);
|
||||
if (item == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_updateView?.Invoke(EViewAction.DispatcherServerAvailability, ResUI.Speedtesting);
|
||||
|
||||
var msg = await (new UpdateService()).RunAvailabilityCheck();
|
||||
|
||||
NoticeHandler.Instance.SendMessageEx(msg);
|
||||
_updateView?.Invoke(EViewAction.DispatcherServerAvailability, msg);
|
||||
}
|
||||
|
||||
public void TestServerAvailabilityResult(string msg)
|
||||
{
|
||||
RunningInfoDisplay = msg;
|
||||
}
|
||||
|
||||
#region System proxy and Routings
|
||||
|
||||
public async Task SetListenerType(ESysProxyType type)
|
||||
{
|
||||
if (_config.SystemProxyItem.SysProxyType == type)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_config.SystemProxyItem.SysProxyType = type;
|
||||
await ChangeSystemProxyAsync(type, true);
|
||||
NoticeHandler.Instance.SendMessageEx($"{ResUI.TipChangeSystemProxy} - {_config.SystemProxyItem.SysProxyType.ToString()}");
|
||||
|
||||
SystemProxySelected = (int)_config.SystemProxyItem.SysProxyType;
|
||||
await ConfigHandler.SaveConfig(_config);
|
||||
}
|
||||
|
||||
public async Task ChangeSystemProxyAsync(ESysProxyType type, bool blChange)
|
||||
{
|
||||
await SysProxyHandler.UpdateSysProxy(_config, false);
|
||||
|
||||
BlSystemProxyClear = (type == ESysProxyType.ForcedClear);
|
||||
BlSystemProxySet = (type == ESysProxyType.ForcedChange);
|
||||
BlSystemProxyNothing = (type == ESysProxyType.Unchanged);
|
||||
BlSystemProxyPac = (type == ESysProxyType.Pac);
|
||||
|
||||
if (blChange)
|
||||
{
|
||||
_updateView?.Invoke(EViewAction.DispatcherRefreshIcon, null);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task RefreshRoutingsMenu()
|
||||
{
|
||||
_routingItems.Clear();
|
||||
|
||||
BlRouting = true;
|
||||
var routings = await AppHandler.Instance.RoutingItems();
|
||||
foreach (var item in routings)
|
||||
{
|
||||
_routingItems.Add(item);
|
||||
if (item.Id == _config.RoutingBasicItem.RoutingIndexId)
|
||||
{
|
||||
SelectedRouting = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RoutingSelectedChangedAsync(bool c)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (SelectedRouting == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var item = await AppHandler.Instance.GetRoutingItem(SelectedRouting?.Id);
|
||||
if (item is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (_config.RoutingBasicItem.RoutingIndexId == item.Id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (await ConfigHandler.SetDefaultRouting(_config, item) == 0)
|
||||
{
|
||||
NoticeHandler.Instance.SendMessageEx(ResUI.TipChangeRouting);
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.Reload();
|
||||
_updateView?.Invoke(EViewAction.DispatcherRefreshIcon, null);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DoSystemProxySelected(bool c)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (_config.SystemProxyItem.SysProxyType == (ESysProxyType)SystemProxySelected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
await SetListenerType((ESysProxyType)SystemProxySelected);
|
||||
}
|
||||
|
||||
private async Task DoEnableTun(bool c)
|
||||
{
|
||||
if (_config.TunModeItem.EnableTun != EnableTun)
|
||||
{
|
||||
_config.TunModeItem.EnableTun = EnableTun;
|
||||
// When running as a non-administrator, reboot to administrator mode
|
||||
if (EnableTun && AllowEnableTun() == false)
|
||||
{
|
||||
if (Utils.IsWindows())
|
||||
{
|
||||
_config.TunModeItem.EnableTun = false;
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.RebootAsAdmin();
|
||||
return;
|
||||
}
|
||||
else if (Utils.IsOSX())
|
||||
{
|
||||
_config.TunModeItem.EnableTun = false;
|
||||
NoticeHandler.Instance.SendMessageAndEnqueue(ResUI.TbSettingsLinuxSudoPasswordIsEmpty);
|
||||
return;
|
||||
}
|
||||
}
|
||||
await ConfigHandler.SaveConfig(_config);
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.Reload();
|
||||
}
|
||||
}
|
||||
|
||||
private bool AllowEnableTun()
|
||||
{
|
||||
if (Utils.IsWindows())
|
||||
{
|
||||
return AppHandler.Instance.IsAdministrator;
|
||||
}
|
||||
else if (Utils.IsLinux())
|
||||
{
|
||||
return _config.TunModeItem.LinuxSudoPwd.IsNotEmpty();
|
||||
}
|
||||
else if (Utils.IsOSX())
|
||||
{
|
||||
return _config.TunModeItem.LinuxSudoPwd.IsNotEmpty();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion System proxy and Routings
|
||||
|
||||
#region UI
|
||||
|
||||
public async Task InboundDisplayStatus()
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
sb.Append($"[{EInboundProtocol.mixed}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)}");
|
||||
if (_config.Inbound.First().SecondLocalPortEnabled)
|
||||
{
|
||||
sb.Append($",{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks2)}");
|
||||
}
|
||||
sb.Append(']');
|
||||
InboundDisplay = $"{ResUI.LabLocal}:{sb}";
|
||||
|
||||
if (_config.Inbound.First().AllowLANConn)
|
||||
{
|
||||
var lan = _config.Inbound.First().NewPort4LAN
|
||||
? $"[{EInboundProtocol.mixed}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks3)}]"
|
||||
: $"[{EInboundProtocol.mixed}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)}]";
|
||||
InboundLanDisplay = $"{ResUI.LabLAN}:{lan}";
|
||||
}
|
||||
else
|
||||
{
|
||||
InboundLanDisplay = $"{ResUI.LabLAN}:{Global.None}";
|
||||
}
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.ShowHideWindow(null);
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
});
|
||||
|
||||
public void UpdateStatistics(ServerSpeedItem update)
|
||||
AddServerViaClipboardCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
SpeedProxyDisplay = string.Format(ResUI.SpeedDisplayText, Global.ProxyTag, Utils.HumanFy(update.ProxyUp), Utils.HumanFy(update.ProxyDown));
|
||||
SpeedDirectDisplay = string.Format(ResUI.SpeedDisplayText, Global.DirectTag, Utils.HumanFy(update.DirectUp), Utils.HumanFy(update.DirectDown));
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
await AddServerViaClipboard();
|
||||
});
|
||||
AddServerViaScanCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await AddServerViaScan();
|
||||
});
|
||||
SubUpdateCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await UpdateSubscriptionProcess(false);
|
||||
});
|
||||
SubUpdateViaProxyCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await UpdateSubscriptionProcess(true);
|
||||
});
|
||||
|
||||
//System proxy
|
||||
SystemProxyClearCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await SetListenerType(ESysProxyType.ForcedClear);
|
||||
});
|
||||
SystemProxySetCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await SetListenerType(ESysProxyType.ForcedChange);
|
||||
});
|
||||
SystemProxyNothingCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await SetListenerType(ESysProxyType.Unchanged);
|
||||
});
|
||||
SystemProxyPacCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await SetListenerType(ESysProxyType.Pac);
|
||||
});
|
||||
|
||||
#endregion WhenAnyValue && ReactiveCommand
|
||||
|
||||
if (updateView != null)
|
||||
{
|
||||
InitUpdateView(updateView);
|
||||
}
|
||||
_ = Init();
|
||||
}
|
||||
|
||||
private async Task Init()
|
||||
{
|
||||
await RefreshRoutingsMenu();
|
||||
await InboundDisplayStatus();
|
||||
await ChangeSystemProxyAsync(_config.SystemProxyItem.SysProxyType, true);
|
||||
}
|
||||
|
||||
public void InitUpdateView(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
_updateView = updateView;
|
||||
if (_updateView != null)
|
||||
{
|
||||
MessageBus.Current.Listen<string>(EMsgCommand.RefreshProfiles.ToString()).Subscribe(OnNext);
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnNext(string x)
|
||||
{
|
||||
await _updateView?.Invoke(EViewAction.DispatcherRefreshServersBiz, null);
|
||||
}
|
||||
|
||||
private async Task CopyProxyCmdToClipboard()
|
||||
{
|
||||
var cmd = Utils.IsWindows() ? "set" : "export";
|
||||
var address = $"{Global.Loopback}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)}";
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine($"{cmd} http_proxy={Global.HttpProtocol}{address}");
|
||||
sb.AppendLine($"{cmd} https_proxy={Global.HttpProtocol}{address}");
|
||||
sb.AppendLine($"{cmd} all_proxy={Global.Socks5Protocol}{address}");
|
||||
sb.AppendLine("");
|
||||
sb.AppendLine($"{cmd} HTTP_PROXY={Global.HttpProtocol}{address}");
|
||||
sb.AppendLine($"{cmd} HTTPS_PROXY={Global.HttpProtocol}{address}");
|
||||
sb.AppendLine($"{cmd} ALL_PROXY={Global.Socks5Protocol}{address}");
|
||||
|
||||
await _updateView?.Invoke(EViewAction.SetClipboardData, sb.ToString());
|
||||
}
|
||||
|
||||
private async Task AddServerViaClipboard()
|
||||
{
|
||||
var service = Locator.Current.GetService<MainWindowViewModel>();
|
||||
if (service != null)
|
||||
await service.AddServerViaClipboardAsync(null);
|
||||
}
|
||||
|
||||
private async Task AddServerViaScan()
|
||||
{
|
||||
var service = Locator.Current.GetService<MainWindowViewModel>();
|
||||
if (service != null)
|
||||
await service.AddServerViaScanAsync();
|
||||
}
|
||||
|
||||
private async Task UpdateSubscriptionProcess(bool blProxy)
|
||||
{
|
||||
var service = Locator.Current.GetService<MainWindowViewModel>();
|
||||
if (service != null)
|
||||
await service.UpdateSubscriptionProcess("", blProxy);
|
||||
}
|
||||
|
||||
public async Task RefreshServersBiz()
|
||||
{
|
||||
await RefreshServersMenu();
|
||||
|
||||
//display running server
|
||||
var running = await ConfigHandler.GetDefaultServer(_config);
|
||||
if (running != null)
|
||||
{
|
||||
RunningServerDisplay =
|
||||
RunningServerToolTipText = running.GetSummary();
|
||||
}
|
||||
else
|
||||
{
|
||||
RunningServerDisplay =
|
||||
RunningServerToolTipText = ResUI.CheckServerSettings;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RefreshServersMenu()
|
||||
{
|
||||
var lstModel = await AppHandler.Instance.ProfileItems(_config.SubIndexId, "");
|
||||
|
||||
_servers.Clear();
|
||||
if (lstModel.Count > _config.GuiItem.TrayMenuServersLimit)
|
||||
{
|
||||
BlServers = false;
|
||||
return;
|
||||
}
|
||||
|
||||
#endregion UI
|
||||
BlServers = true;
|
||||
for (int k = 0; k < lstModel.Count; k++)
|
||||
{
|
||||
ProfileItem it = lstModel[k];
|
||||
string name = it.GetSummary();
|
||||
|
||||
var item = new ComboItem() { ID = it.IndexId, Text = name };
|
||||
_servers.Add(item);
|
||||
if (_config.IndexId == it.IndexId)
|
||||
{
|
||||
SelectedServer = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ServerSelectedChanged(bool c)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (SelectedServer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (SelectedServer.ID.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
Locator.Current.GetService<ProfilesViewModel>()?.SetDefaultServer(SelectedServer.ID);
|
||||
}
|
||||
|
||||
public async Task TestServerAvailability()
|
||||
{
|
||||
var item = await ConfigHandler.GetDefaultServer(_config);
|
||||
if (item == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_updateView?.Invoke(EViewAction.DispatcherServerAvailability, ResUI.Speedtesting);
|
||||
|
||||
var msg = await (new UpdateService()).RunAvailabilityCheck();
|
||||
|
||||
NoticeHandler.Instance.SendMessageEx(msg);
|
||||
_updateView?.Invoke(EViewAction.DispatcherServerAvailability, msg);
|
||||
}
|
||||
|
||||
public void TestServerAvailabilityResult(string msg)
|
||||
{
|
||||
RunningInfoDisplay = msg;
|
||||
}
|
||||
|
||||
#region System proxy and Routings
|
||||
|
||||
public async Task SetListenerType(ESysProxyType type)
|
||||
{
|
||||
if (_config.SystemProxyItem.SysProxyType == type)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_config.SystemProxyItem.SysProxyType = type;
|
||||
await ChangeSystemProxyAsync(type, true);
|
||||
NoticeHandler.Instance.SendMessageEx($"{ResUI.TipChangeSystemProxy} - {_config.SystemProxyItem.SysProxyType.ToString()}");
|
||||
|
||||
SystemProxySelected = (int)_config.SystemProxyItem.SysProxyType;
|
||||
await ConfigHandler.SaveConfig(_config);
|
||||
}
|
||||
|
||||
public async Task ChangeSystemProxyAsync(ESysProxyType type, bool blChange)
|
||||
{
|
||||
await SysProxyHandler.UpdateSysProxy(_config, false);
|
||||
|
||||
BlSystemProxyClear = (type == ESysProxyType.ForcedClear);
|
||||
BlSystemProxySet = (type == ESysProxyType.ForcedChange);
|
||||
BlSystemProxyNothing = (type == ESysProxyType.Unchanged);
|
||||
BlSystemProxyPac = (type == ESysProxyType.Pac);
|
||||
|
||||
if (blChange)
|
||||
{
|
||||
_updateView?.Invoke(EViewAction.DispatcherRefreshIcon, null);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task RefreshRoutingsMenu()
|
||||
{
|
||||
_routingItems.Clear();
|
||||
|
||||
BlRouting = true;
|
||||
var routings = await AppHandler.Instance.RoutingItems();
|
||||
foreach (var item in routings)
|
||||
{
|
||||
_routingItems.Add(item);
|
||||
if (item.Id == _config.RoutingBasicItem.RoutingIndexId)
|
||||
{
|
||||
SelectedRouting = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RoutingSelectedChangedAsync(bool c)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (SelectedRouting == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var item = await AppHandler.Instance.GetRoutingItem(SelectedRouting?.Id);
|
||||
if (item is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (_config.RoutingBasicItem.RoutingIndexId == item.Id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (await ConfigHandler.SetDefaultRouting(_config, item) == 0)
|
||||
{
|
||||
NoticeHandler.Instance.SendMessageEx(ResUI.TipChangeRouting);
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.Reload();
|
||||
_updateView?.Invoke(EViewAction.DispatcherRefreshIcon, null);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DoSystemProxySelected(bool c)
|
||||
{
|
||||
if (!c)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (_config.SystemProxyItem.SysProxyType == (ESysProxyType)SystemProxySelected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
await SetListenerType((ESysProxyType)SystemProxySelected);
|
||||
}
|
||||
|
||||
private async Task DoEnableTun(bool c)
|
||||
{
|
||||
if (_config.TunModeItem.EnableTun != EnableTun)
|
||||
{
|
||||
_config.TunModeItem.EnableTun = EnableTun;
|
||||
// When running as a non-administrator, reboot to administrator mode
|
||||
if (EnableTun && AllowEnableTun() == false)
|
||||
{
|
||||
if (Utils.IsWindows())
|
||||
{
|
||||
_config.TunModeItem.EnableTun = false;
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.RebootAsAdmin();
|
||||
return;
|
||||
}
|
||||
else if (Utils.IsOSX())
|
||||
{
|
||||
_config.TunModeItem.EnableTun = false;
|
||||
NoticeHandler.Instance.SendMessageAndEnqueue(ResUI.TbSettingsLinuxSudoPasswordIsEmpty);
|
||||
return;
|
||||
}
|
||||
}
|
||||
await ConfigHandler.SaveConfig(_config);
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.Reload();
|
||||
}
|
||||
}
|
||||
|
||||
private bool AllowEnableTun()
|
||||
{
|
||||
if (Utils.IsWindows())
|
||||
{
|
||||
return AppHandler.Instance.IsAdministrator;
|
||||
}
|
||||
else if (Utils.IsLinux())
|
||||
{
|
||||
return _config.TunModeItem.LinuxSudoPwd.IsNotEmpty();
|
||||
}
|
||||
else if (Utils.IsOSX())
|
||||
{
|
||||
return _config.TunModeItem.LinuxSudoPwd.IsNotEmpty();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion System proxy and Routings
|
||||
|
||||
#region UI
|
||||
|
||||
public async Task InboundDisplayStatus()
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
sb.Append($"[{EInboundProtocol.mixed}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)}");
|
||||
if (_config.Inbound.First().SecondLocalPortEnabled)
|
||||
{
|
||||
sb.Append($",{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks2)}");
|
||||
}
|
||||
sb.Append(']');
|
||||
InboundDisplay = $"{ResUI.LabLocal}:{sb}";
|
||||
|
||||
if (_config.Inbound.First().AllowLANConn)
|
||||
{
|
||||
var lan = _config.Inbound.First().NewPort4LAN
|
||||
? $"[{EInboundProtocol.mixed}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks3)}]"
|
||||
: $"[{EInboundProtocol.mixed}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)}]";
|
||||
InboundLanDisplay = $"{ResUI.LabLAN}:{lan}";
|
||||
}
|
||||
else
|
||||
{
|
||||
InboundLanDisplay = $"{ResUI.LabLAN}:{Global.None}";
|
||||
}
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void UpdateStatistics(ServerSpeedItem update)
|
||||
{
|
||||
try
|
||||
{
|
||||
SpeedProxyDisplay = string.Format(ResUI.SpeedDisplayText, Global.ProxyTag, Utils.HumanFy(update.ProxyUp), Utils.HumanFy(update.ProxyDown));
|
||||
SpeedDirectDisplay = string.Format(ResUI.SpeedDisplayText, Global.DirectTag, Utils.HumanFy(update.DirectUp), Utils.HumanFy(update.DirectDown));
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#endregion UI
|
||||
}
|
||||
|
||||
@@ -2,63 +2,62 @@ using System.Reactive;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
|
||||
namespace ServiceLib.ViewModels
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
public class SubEditViewModel : MyReactiveObject
|
||||
{
|
||||
public class SubEditViewModel : MyReactiveObject
|
||||
[Reactive]
|
||||
public SubItem SelectedSource { get; set; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||
|
||||
public SubEditViewModel(SubItem subItem, Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
[Reactive]
|
||||
public SubItem SelectedSource { get; set; }
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
|
||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||
|
||||
public SubEditViewModel(SubItem subItem, Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
await SaveSubAsync();
|
||||
});
|
||||
|
||||
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await SaveSubAsync();
|
||||
});
|
||||
SelectedSource = subItem.Id.IsNullOrEmpty() ? subItem : JsonUtils.DeepCopy(subItem);
|
||||
}
|
||||
|
||||
SelectedSource = subItem.Id.IsNullOrEmpty() ? subItem : JsonUtils.DeepCopy(subItem);
|
||||
private async Task SaveSubAsync()
|
||||
{
|
||||
var remarks = SelectedSource.Remarks;
|
||||
if (remarks.IsNullOrEmpty())
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseFillRemarks);
|
||||
return;
|
||||
}
|
||||
|
||||
private async Task SaveSubAsync()
|
||||
var url = SelectedSource.Url;
|
||||
if (url.IsNotEmpty())
|
||||
{
|
||||
var remarks = SelectedSource.Remarks;
|
||||
if (remarks.IsNullOrEmpty())
|
||||
var uri = Utils.TryUri(url);
|
||||
if (uri == null)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.PleaseFillRemarks);
|
||||
NoticeHandler.Instance.Enqueue(ResUI.InvalidUrlTip);
|
||||
return;
|
||||
}
|
||||
//Do not allow http protocol
|
||||
if (url.StartsWith(Global.HttpProtocol) && !Utils.IsPrivateNetwork(uri.IdnHost))
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.InsecureUrlProtocol);
|
||||
//return;
|
||||
}
|
||||
}
|
||||
|
||||
var url = SelectedSource.Url;
|
||||
if (url.IsNotEmpty())
|
||||
{
|
||||
var uri = Utils.TryUri(url);
|
||||
if (uri == null)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.InvalidUrlTip);
|
||||
return;
|
||||
}
|
||||
//Do not allow http protocol
|
||||
if (url.StartsWith(Global.HttpProtocol) && !Utils.IsPrivateNetwork(uri.IdnHost))
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.InsecureUrlProtocol);
|
||||
//return;
|
||||
}
|
||||
}
|
||||
|
||||
if (await ConfigHandler.AddSubItem(_config, SelectedSource) == 0)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
|
||||
}
|
||||
if (await ConfigHandler.AddSubItem(_config, SelectedSource) == 0)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationFailed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,102 +4,101 @@ using DynamicData.Binding;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
|
||||
namespace ServiceLib.ViewModels
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
public class SubSettingViewModel : MyReactiveObject
|
||||
{
|
||||
public class SubSettingViewModel : MyReactiveObject
|
||||
private IObservableCollection<SubItem> _subItems = new ObservableCollectionExtended<SubItem>();
|
||||
public IObservableCollection<SubItem> SubItems => _subItems;
|
||||
|
||||
[Reactive]
|
||||
public SubItem SelectedSource { get; set; }
|
||||
|
||||
public IList<SubItem> SelectedSources { get; set; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> SubAddCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> SubDeleteCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> SubEditCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> SubShareCmd { get; }
|
||||
public bool IsModified { get; set; }
|
||||
|
||||
public SubSettingViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
private IObservableCollection<SubItem> _subItems = new ObservableCollectionExtended<SubItem>();
|
||||
public IObservableCollection<SubItem> SubItems => _subItems;
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
|
||||
[Reactive]
|
||||
public SubItem SelectedSource { get; set; }
|
||||
var canEditRemove = this.WhenAnyValue(
|
||||
x => x.SelectedSource,
|
||||
selectedSource => selectedSource != null && !selectedSource.Id.IsNullOrEmpty());
|
||||
|
||||
public IList<SubItem> SelectedSources { get; set; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> SubAddCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> SubDeleteCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> SubEditCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> SubShareCmd { get; }
|
||||
public bool IsModified { get; set; }
|
||||
|
||||
public SubSettingViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
SubAddCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
await EditSubAsync(true);
|
||||
});
|
||||
SubDeleteCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await DeleteSubAsync();
|
||||
}, canEditRemove);
|
||||
SubEditCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await EditSubAsync(false);
|
||||
}, canEditRemove);
|
||||
SubShareCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await _updateView?.Invoke(EViewAction.ShareSub, SelectedSource?.Url);
|
||||
}, canEditRemove);
|
||||
|
||||
var canEditRemove = this.WhenAnyValue(
|
||||
x => x.SelectedSource,
|
||||
selectedSource => selectedSource != null && !selectedSource.Id.IsNullOrEmpty());
|
||||
_ = Init();
|
||||
}
|
||||
|
||||
SubAddCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await EditSubAsync(true);
|
||||
});
|
||||
SubDeleteCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await DeleteSubAsync();
|
||||
}, canEditRemove);
|
||||
SubEditCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await EditSubAsync(false);
|
||||
}, canEditRemove);
|
||||
SubShareCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await _updateView?.Invoke(EViewAction.ShareSub, SelectedSource?.Url);
|
||||
}, canEditRemove);
|
||||
private async Task Init()
|
||||
{
|
||||
SelectedSource = new();
|
||||
|
||||
_ = Init();
|
||||
await RefreshSubItems();
|
||||
}
|
||||
|
||||
public async Task RefreshSubItems()
|
||||
{
|
||||
_subItems.Clear();
|
||||
_subItems.AddRange(await AppHandler.Instance.SubItems());
|
||||
}
|
||||
|
||||
public async Task EditSubAsync(bool blNew)
|
||||
{
|
||||
SubItem item;
|
||||
if (blNew)
|
||||
{
|
||||
item = new();
|
||||
}
|
||||
|
||||
private async Task Init()
|
||||
else
|
||||
{
|
||||
SelectedSource = new();
|
||||
|
||||
await RefreshSubItems();
|
||||
}
|
||||
|
||||
public async Task RefreshSubItems()
|
||||
{
|
||||
_subItems.Clear();
|
||||
_subItems.AddRange(await AppHandler.Instance.SubItems());
|
||||
}
|
||||
|
||||
public async Task EditSubAsync(bool blNew)
|
||||
{
|
||||
SubItem item;
|
||||
if (blNew)
|
||||
{
|
||||
item = new();
|
||||
}
|
||||
else
|
||||
{
|
||||
item = await AppHandler.Instance.GetSubItem(SelectedSource?.Id);
|
||||
if (item is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (await _updateView?.Invoke(EViewAction.SubEditWindow, item) == true)
|
||||
{
|
||||
await RefreshSubItems();
|
||||
IsModified = true;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeleteSubAsync()
|
||||
{
|
||||
if (await _updateView?.Invoke(EViewAction.ShowYesNo, null) == false)
|
||||
item = await AppHandler.Instance.GetSubItem(SelectedSource?.Id);
|
||||
if (item is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var it in SelectedSources ?? [SelectedSource])
|
||||
{
|
||||
await ConfigHandler.DeleteSubItem(_config, it.Id);
|
||||
}
|
||||
}
|
||||
if (await _updateView?.Invoke(EViewAction.SubEditWindow, item) == true)
|
||||
{
|
||||
await RefreshSubItems();
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
IsModified = true;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeleteSubAsync()
|
||||
{
|
||||
if (await _updateView?.Invoke(EViewAction.ShowYesNo, null) == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var it in SelectedSources ?? [SelectedSource])
|
||||
{
|
||||
await ConfigHandler.DeleteSubItem(_config, it.Id);
|
||||
}
|
||||
await RefreshSubItems();
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
IsModified = true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user