csharp_style_namespace_declarations = file_scoped

This commit is contained in:
2dust
2025-04-02 11:44:23 +08:00
parent d92540121f
commit 4d3db56065
186 changed files with 23361 additions and 23546 deletions
@@ -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
+92 -93
View File
@@ -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;
}
}
}
+484 -485
View File
@@ -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;
}
}