Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7f3a7b1a7 | ||
|
|
71eb5f0813 | ||
|
|
b4f50258a7 | ||
|
|
346a9c5fcc | ||
|
|
61635db5b5 | ||
|
|
4c0a59a715 | ||
|
|
aa829a66ea | ||
|
|
885f193a00 | ||
|
|
0a9bbf526d | ||
|
|
ff6716b39d | ||
|
|
8505f2db96 | ||
|
|
233d605256 | ||
|
|
c4e01d20a0 | ||
|
|
07156cb55c | ||
|
|
8361b4e4a0 | ||
|
|
bb90671979 | ||
|
|
beddc71ed8 | ||
|
|
d5f1cc99ac | ||
|
|
d03a86292e | ||
|
|
c061a24948 | ||
|
|
9c7446f820 | ||
|
|
8be1730719 | ||
|
|
7ceaf5c071 | ||
|
|
7901a59aa4 | ||
|
|
8343a1002f | ||
|
|
c83dce3cc0 | ||
|
|
14afeab2bb | ||
|
|
c22b57927c | ||
|
|
81b84d235c | ||
|
|
488e8aab00 | ||
|
|
fc43a9a726 | ||
|
|
31947fdcb3 | ||
|
|
34c7963d28 | ||
|
|
d91b0afb9a | ||
|
|
82eb3fd6bd | ||
|
|
3d3d3f83df | ||
|
|
6879c75bc8 |
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.Protobuf" Version="3.28.0" />
|
||||
<PackageReference Include="Google.Protobuf" Version="3.28.1" />
|
||||
<PackageReference Include="Grpc.Net.Client" Version="2.65.0" />
|
||||
<PackageReference Include="Grpc.Tools" Version="2.66.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace ServiceLib.Common
|
||||
Uri uri = new(url);
|
||||
//Authorization Header
|
||||
var headers = new WebHeaderCollection();
|
||||
if (!Utils.IsNullOrEmpty(uri.UserInfo))
|
||||
if (Utils.IsNotEmpty(uri.UserInfo))
|
||||
{
|
||||
headers.Add(HttpRequestHeader.Authorization, "Basic " + Utils.Base64Encode(uri.UserInfo));
|
||||
}
|
||||
@@ -176,7 +176,7 @@ namespace ServiceLib.Common
|
||||
};
|
||||
|
||||
using var cts = new CancellationTokenSource();
|
||||
await downloader.DownloadFileTaskAsync(url, fileName, cts.Token).WaitAsync(TimeSpan.FromSeconds(timeout), cts.Token);
|
||||
await downloader.DownloadFileTaskAsync(url, fileName, cts.Token);
|
||||
|
||||
downloadOpt = null;
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace ServiceLib.Common
|
||||
}
|
||||
try
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(ignoredName) && entry.Name.Contains(ignoredName))
|
||||
if (Utils.IsNotEmpty(ignoredName) && entry.Name.Contains(ignoredName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -102,11 +102,34 @@ namespace ServiceLib.Common
|
||||
return true;
|
||||
}
|
||||
|
||||
public static List<string>? GetFilesFromZip(string fileName)
|
||||
{
|
||||
if (!File.Exists(fileName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
using ZipArchive archive = ZipFile.OpenRead(fileName);
|
||||
return archive.Entries.Select(entry => entry.FullName).ToList();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool CreateFromDirectory(string sourceDirectoryName, string destinationArchiveFileName)
|
||||
{
|
||||
try
|
||||
{
|
||||
ZipFile.CreateFromDirectory(sourceDirectoryName, destinationArchiveFileName);
|
||||
if (File.Exists(destinationArchiveFileName))
|
||||
{
|
||||
File.Delete(destinationArchiveFileName);
|
||||
}
|
||||
|
||||
ZipFile.CreateFromDirectory(sourceDirectoryName, destinationArchiveFileName, CompressionLevel.SmallestSize, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -115,6 +138,46 @@ namespace ServiceLib.Common
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public static void CopyDirectory(string sourceDir, string destinationDir, bool recursive, string ignoredName)
|
||||
{
|
||||
// Get information about the source directory
|
||||
var dir = new DirectoryInfo(sourceDir);
|
||||
|
||||
// Check if the source directory exists
|
||||
if (!dir.Exists)
|
||||
throw new DirectoryNotFoundException($"Source directory not found: {dir.FullName}");
|
||||
|
||||
// Cache directories before we start copying
|
||||
DirectoryInfo[] dirs = dir.GetDirectories();
|
||||
|
||||
// Create the destination directory
|
||||
Directory.CreateDirectory(destinationDir);
|
||||
|
||||
// Get the files in the source directory and copy to the destination directory
|
||||
foreach (FileInfo file in dir.GetFiles())
|
||||
{
|
||||
if (Utils.IsNotEmpty(ignoredName) && file.Name.Contains(ignoredName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (file.Extension == file.Name)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
string targetFilePath = Path.Combine(destinationDir, file.Name);
|
||||
file.CopyTo(targetFilePath);
|
||||
}
|
||||
|
||||
// If recursive and copying subdirectories, recursively call this method
|
||||
if (recursive)
|
||||
{
|
||||
foreach (DirectoryInfo subDir in dirs)
|
||||
{
|
||||
string newDestinationDir = Path.Combine(destinationDir, subDir.Name);
|
||||
CopyDirectory(subDir.FullName, newDestinationDir, true, ignoredName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ namespace ServiceLib.Common
|
||||
|
||||
public async Task<string?> TryGetAsync(string url)
|
||||
{
|
||||
if (string.IsNullOrEmpty(url))
|
||||
if (Utils.IsNullOrEmpty(url))
|
||||
return null;
|
||||
|
||||
try
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
this.minor = int.Parse(parts[1]);
|
||||
this.patch = 0;
|
||||
}
|
||||
else if (parts.Length == 3)
|
||||
else if (parts.Length == 3 || parts.Length == 4)
|
||||
{
|
||||
this.major = int.Parse(parts[0]);
|
||||
this.minor = int.Parse(parts[1]);
|
||||
|
||||
@@ -14,6 +14,11 @@ namespace ServiceLib.Common
|
||||
return string.IsNullOrWhiteSpace(value);
|
||||
}
|
||||
|
||||
public static bool IsNotEmpty([NotNullWhen(false)] this string? value)
|
||||
{
|
||||
return !string.IsNullOrEmpty(value);
|
||||
}
|
||||
|
||||
public static bool BeginWithAny(this string s, IEnumerable<char> chars)
|
||||
{
|
||||
if (s.IsNullOrEmpty()) return false;
|
||||
|
||||
@@ -417,6 +417,11 @@ namespace ServiceLib.Common
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsNotEmpty(string? text)
|
||||
{
|
||||
return !string.IsNullOrEmpty(text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证IP地址是否合法
|
||||
/// </summary>
|
||||
@@ -567,13 +572,12 @@ namespace ServiceLib.Common
|
||||
{
|
||||
try
|
||||
{
|
||||
string location = GetExePath();
|
||||
if (blFull)
|
||||
{
|
||||
return string.Format("{0} - V{1} - {2}",
|
||||
Global.AppName,
|
||||
GetVersionInfo(),
|
||||
File.GetLastWriteTime(location).ToString("yyyy/MM/dd"));
|
||||
File.GetLastWriteTime(GetExePath()).ToString("yyyy/MM/dd"));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -593,8 +597,7 @@ namespace ServiceLib.Common
|
||||
{
|
||||
try
|
||||
{
|
||||
string location = GetExePath();
|
||||
return FileVersionInfo.GetVersionInfo(location)?.FileVersion ?? "0.0";
|
||||
return Assembly.GetExecutingAssembly()?.GetName()?.Version?.ToString(3) ?? "0.0";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using YamlDotNet.Serialization;
|
||||
using YamlDotNet.Core;
|
||||
using YamlDotNet.Serialization;
|
||||
using YamlDotNet.Serialization.NamingConventions;
|
||||
|
||||
namespace ServiceLib.Common
|
||||
@@ -35,13 +36,17 @@ namespace ServiceLib.Common
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <returns></returns>
|
||||
public static string ToYaml(Object obj)
|
||||
public static string ToYaml(Object? obj)
|
||||
{
|
||||
string result = string.Empty;
|
||||
if (obj == null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
var serializer = new SerializerBuilder()
|
||||
.WithNamingConvention(HyphenatedNamingConvention.Instance)
|
||||
.Build();
|
||||
|
||||
string result = string.Empty;
|
||||
try
|
||||
{
|
||||
result = serializer.Serialize(obj);
|
||||
@@ -53,6 +58,24 @@ namespace ServiceLib.Common
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string? PreprocessYaml(string str)
|
||||
{
|
||||
var deserializer = new DeserializerBuilder()
|
||||
.WithNamingConvention(PascalCaseNamingConvention.Instance)
|
||||
.Build();
|
||||
try
|
||||
{
|
||||
var mergingParser = new MergingParser(new Parser(new StringReader(str)));
|
||||
var obj = new DeserializerBuilder().Build().Deserialize(mergingParser);
|
||||
return ToYaml(obj);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog("PreprocessYaml", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion YAML
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,7 @@
|
||||
DispatcherRefreshServersBiz,
|
||||
DispatcherRefreshIcon,
|
||||
DispatcherCheckUpdate,
|
||||
DispatcherCheckUpdateFinished,
|
||||
DispatcherCheckUpdateFinished,
|
||||
DispatcherShowMsg,
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ namespace ServiceLib.Handler
|
||||
{
|
||||
//载入配置文件
|
||||
var result = Utils.LoadResource(Utils.GetConfigPath(configRes));
|
||||
if (!Utils.IsNullOrEmpty(result))
|
||||
if (Utils.IsNotEmpty(result))
|
||||
{
|
||||
//转成Json
|
||||
config = JsonUtils.Deserialize<Config>(result);
|
||||
@@ -124,20 +124,16 @@ namespace ServiceLib.Handler
|
||||
mtu = 9000,
|
||||
};
|
||||
}
|
||||
if (config.guiItem == null)
|
||||
config.guiItem ??= new()
|
||||
{
|
||||
config.guiItem = new()
|
||||
{
|
||||
enableStatistics = false,
|
||||
};
|
||||
}
|
||||
if (config.uiItem == null)
|
||||
enableStatistics = false,
|
||||
};
|
||||
config.msgUIItem ??= new();
|
||||
|
||||
config.uiItem ??= new UIItem()
|
||||
{
|
||||
config.uiItem = new UIItem()
|
||||
{
|
||||
enableAutoAdjustMainLvColWidth = true
|
||||
};
|
||||
}
|
||||
enableAutoAdjustMainLvColWidth = true
|
||||
};
|
||||
if (config.uiItem.mainColumnItem == null)
|
||||
{
|
||||
config.uiItem.mainColumnItem = new();
|
||||
@@ -174,11 +170,11 @@ namespace ServiceLib.Handler
|
||||
}
|
||||
|
||||
config.mux4RayItem ??= new()
|
||||
{
|
||||
concurrency = 8,
|
||||
xudpConcurrency = 16,
|
||||
xudpProxyUDP443 = "reject"
|
||||
};
|
||||
{
|
||||
concurrency = 8,
|
||||
xudpConcurrency = 16,
|
||||
xudpProxyUDP443 = "reject"
|
||||
};
|
||||
|
||||
if (config.mux4SboxItem == null)
|
||||
{
|
||||
@@ -208,6 +204,8 @@ namespace ServiceLib.Handler
|
||||
};
|
||||
}
|
||||
|
||||
config.webDavItem ??= new();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1009,7 +1007,7 @@ namespace ServiceLib.Handler
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(profileItem.security) && profileItem.security != Global.None)
|
||||
if (Utils.IsNotEmpty(profileItem.security) && profileItem.security != Global.None)
|
||||
{
|
||||
profileItem.security = Global.None;
|
||||
}
|
||||
@@ -1047,7 +1045,7 @@ namespace ServiceLib.Handler
|
||||
{
|
||||
profileItem.configVersion = 2;
|
||||
|
||||
if (!Utils.IsNullOrEmpty(profileItem.streamSecurity))
|
||||
if (Utils.IsNotEmpty(profileItem.streamSecurity))
|
||||
{
|
||||
if (profileItem.streamSecurity != Global.StreamSecurity
|
||||
&& profileItem.streamSecurity != Global.StreamSecurityReality)
|
||||
@@ -1067,7 +1065,7 @@ namespace ServiceLib.Handler
|
||||
}
|
||||
}
|
||||
|
||||
if (!Utils.IsNullOrEmpty(profileItem.network) && !Global.Networks.Contains(profileItem.network))
|
||||
if (Utils.IsNotEmpty(profileItem.network) && !Global.Networks.Contains(profileItem.network))
|
||||
{
|
||||
profileItem.network = Global.DefaultNetwork;
|
||||
}
|
||||
@@ -1188,7 +1186,7 @@ namespace ServiceLib.Handler
|
||||
|
||||
string subFilter = string.Empty;
|
||||
//remove sub items
|
||||
if (isSub && !Utils.IsNullOrEmpty(subid))
|
||||
if (isSub && Utils.IsNotEmpty(subid))
|
||||
{
|
||||
RemoveServerViaSubid(config, subid, isSub);
|
||||
subFilter = LazyConfig.Instance.GetSubItem(subid)?.filter ?? "";
|
||||
@@ -1221,7 +1219,7 @@ namespace ServiceLib.Handler
|
||||
}
|
||||
|
||||
//exist sub items
|
||||
if (isSub && !Utils.IsNullOrEmpty(subid))
|
||||
if (isSub && Utils.IsNotEmpty(subid))
|
||||
{
|
||||
var existItem = lstOriSub?.FirstOrDefault(t => t.isSub == isSub
|
||||
&& config.uiItem.enableUpdateSubOnlyRemarksExist ? t.remarks == profileItem.remarks : CompareProfileItem(t, profileItem, true));
|
||||
@@ -1243,7 +1241,7 @@ namespace ServiceLib.Handler
|
||||
}
|
||||
}
|
||||
//filter
|
||||
if (!Utils.IsNullOrEmpty(subFilter))
|
||||
if (Utils.IsNotEmpty(subFilter))
|
||||
{
|
||||
if (!Regex.IsMatch(profileItem.remarks, subFilter))
|
||||
{
|
||||
@@ -1307,7 +1305,7 @@ namespace ServiceLib.Handler
|
||||
}
|
||||
if (lstProfiles != null && lstProfiles.Count > 0)
|
||||
{
|
||||
if (isSub && !Utils.IsNullOrEmpty(subid))
|
||||
if (isSub && Utils.IsNotEmpty(subid))
|
||||
{
|
||||
RemoveServerViaSubid(config, subid, isSub);
|
||||
}
|
||||
@@ -1363,7 +1361,7 @@ namespace ServiceLib.Handler
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (isSub && !Utils.IsNullOrEmpty(subid))
|
||||
if (isSub && Utils.IsNotEmpty(subid))
|
||||
{
|
||||
RemoveServerViaSubid(config, subid, isSub);
|
||||
}
|
||||
@@ -1391,7 +1389,7 @@ namespace ServiceLib.Handler
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (isSub && !Utils.IsNullOrEmpty(subid))
|
||||
if (isSub && Utils.IsNotEmpty(subid))
|
||||
{
|
||||
RemoveServerViaSubid(config, subid, isSub);
|
||||
}
|
||||
@@ -1423,7 +1421,7 @@ namespace ServiceLib.Handler
|
||||
return -1;
|
||||
}
|
||||
List<ProfileItem>? lstOriSub = null;
|
||||
if (isSub && !Utils.IsNullOrEmpty(subid))
|
||||
if (isSub && Utils.IsNotEmpty(subid))
|
||||
{
|
||||
lstOriSub = LazyConfig.Instance.ProfileItems(subid);
|
||||
}
|
||||
@@ -1510,6 +1508,7 @@ namespace ServiceLib.Handler
|
||||
item.userAgent = subItem.userAgent;
|
||||
item.sort = subItem.sort;
|
||||
item.filter = subItem.filter;
|
||||
item.updateTime = subItem.updateTime;
|
||||
item.convertTarget = subItem.convertTarget;
|
||||
item.prevProfile = subItem.prevProfile;
|
||||
item.nextProfile = subItem.nextProfile;
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
}
|
||||
|
||||
string addressFileName = node.address;
|
||||
if (string.IsNullOrEmpty(addressFileName))
|
||||
if (Utils.IsNullOrEmpty(addressFileName))
|
||||
{
|
||||
msg = ResUI.FailedGetDefaultConfiguration;
|
||||
return -1;
|
||||
@@ -64,6 +64,12 @@
|
||||
var txtFile = File.ReadAllText(addressFileName);
|
||||
txtFile = txtFile.Replace(tagYamlStr1, tagYamlStr2);
|
||||
|
||||
//YAML anchors
|
||||
if (txtFile.Contains("<<:") && txtFile.Contains("*") && txtFile.Contains("&"))
|
||||
{
|
||||
txtFile = YamlUtils.PreprocessYaml(txtFile);
|
||||
}
|
||||
|
||||
var fileContent = YamlUtils.FromYaml<Dictionary<string, object>>(txtFile);
|
||||
if (fileContent == null)
|
||||
{
|
||||
@@ -111,7 +117,7 @@
|
||||
if (_config.tunModeItem.enableTun)
|
||||
{
|
||||
string tun = Utils.GetEmbedText(Global.ClashTunYaml);
|
||||
if (!string.IsNullOrEmpty(tun))
|
||||
if (Utils.IsNotEmpty(tun))
|
||||
{
|
||||
var tunContent = YamlUtils.FromYaml<Dictionary<string, object>>(tun);
|
||||
if (tunContent != null)
|
||||
|
||||
@@ -370,7 +370,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
}
|
||||
|
||||
string addressFileName = node.address;
|
||||
if (string.IsNullOrEmpty(addressFileName))
|
||||
if (Utils.IsNullOrEmpty(addressFileName))
|
||||
{
|
||||
msg = ResUI.FailedGetDefaultConfiguration;
|
||||
return -1;
|
||||
@@ -489,7 +489,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
if (_config.routingBasicItem.enableRoutingAdvanced)
|
||||
{
|
||||
var routing = ConfigHandler.GetDefaultRouting(_config);
|
||||
if (!Utils.IsNullOrEmpty(routing.domainStrategy4Singbox))
|
||||
if (Utils.IsNotEmpty(routing.domainStrategy4Singbox))
|
||||
{
|
||||
inbound.domain_strategy = routing.domainStrategy4Singbox;
|
||||
}
|
||||
@@ -512,7 +512,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
singboxConfig.inbounds.Add(inbound4);
|
||||
|
||||
//auth
|
||||
if (!Utils.IsNullOrEmpty(_config.inbound[0].user) && !Utils.IsNullOrEmpty(_config.inbound[0].pass))
|
||||
if (Utils.IsNotEmpty(_config.inbound[0].user) && Utils.IsNotEmpty(_config.inbound[0].pass))
|
||||
{
|
||||
inbound3.users = new() { new() { username = _config.inbound[0].user, password = _config.inbound[0].pass } };
|
||||
inbound4.users = new() { new() { username = _config.inbound[0].user, password = _config.inbound[0].pass } };
|
||||
@@ -604,8 +604,8 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
case EConfigType.Socks:
|
||||
{
|
||||
outbound.version = "5";
|
||||
if (!Utils.IsNullOrEmpty(node.security)
|
||||
&& !Utils.IsNullOrEmpty(node.id))
|
||||
if (Utils.IsNotEmpty(node.security)
|
||||
&& Utils.IsNotEmpty(node.id))
|
||||
{
|
||||
outbound.username = node.security;
|
||||
outbound.password = node.id;
|
||||
@@ -614,8 +614,8 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
}
|
||||
case EConfigType.Http:
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(node.security)
|
||||
&& !Utils.IsNullOrEmpty(node.id))
|
||||
if (Utils.IsNotEmpty(node.security)
|
||||
&& Utils.IsNotEmpty(node.id))
|
||||
{
|
||||
outbound.username = node.security;
|
||||
outbound.password = node.id;
|
||||
@@ -649,7 +649,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
{
|
||||
outbound.password = node.id;
|
||||
|
||||
if (!Utils.IsNullOrEmpty(node.path))
|
||||
if (Utils.IsNotEmpty(node.path))
|
||||
{
|
||||
outbound.obfs = new()
|
||||
{
|
||||
@@ -695,7 +695,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_config.coreBasicItem.muxEnabled && !Utils.IsNullOrEmpty(_config.mux4SboxItem.protocol))
|
||||
if (_config.coreBasicItem.muxEnabled && Utils.IsNotEmpty(_config.mux4SboxItem.protocol))
|
||||
{
|
||||
var mux = new Multiplex4Sbox()
|
||||
{
|
||||
@@ -721,11 +721,11 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
if (node.streamSecurity == Global.StreamSecurityReality || node.streamSecurity == Global.StreamSecurity)
|
||||
{
|
||||
var server_name = string.Empty;
|
||||
if (!Utils.IsNullOrEmpty(node.sni))
|
||||
if (Utils.IsNotEmpty(node.sni))
|
||||
{
|
||||
server_name = node.sni;
|
||||
}
|
||||
else if (!Utils.IsNullOrEmpty(node.requestHost))
|
||||
else if (Utils.IsNotEmpty(node.requestHost))
|
||||
{
|
||||
server_name = Utils.String2List(node.requestHost)[0];
|
||||
}
|
||||
@@ -736,7 +736,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
insecure = Utils.ToBool(node.allowInsecure.IsNullOrEmpty() ? _config.coreBasicItem.defAllowInsecure.ToString().ToLower() : node.allowInsecure),
|
||||
alpn = node.GetAlpn(),
|
||||
};
|
||||
if (!Utils.IsNullOrEmpty(node.fingerprint))
|
||||
if (Utils.IsNotEmpty(node.fingerprint))
|
||||
{
|
||||
tls.utls = new Utls4Sbox()
|
||||
{
|
||||
@@ -798,7 +798,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
case nameof(ETransport.ws):
|
||||
transport.type = nameof(ETransport.ws);
|
||||
transport.path = Utils.IsNullOrEmpty(node.path) ? null : node.path;
|
||||
if (!Utils.IsNullOrEmpty(node.requestHost))
|
||||
if (Utils.IsNotEmpty(node.requestHost))
|
||||
{
|
||||
transport.headers = new()
|
||||
{
|
||||
@@ -1020,7 +1020,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
outbound = item.outboundTag,
|
||||
};
|
||||
|
||||
if (!Utils.IsNullOrEmpty(item.port))
|
||||
if (Utils.IsNotEmpty(item.port))
|
||||
{
|
||||
if (item.port.Contains("-"))
|
||||
{
|
||||
@@ -1031,7 +1031,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
rule.port = new List<int> { Utils.ToInt(item.port) };
|
||||
}
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.network))
|
||||
if (Utils.IsNotEmpty(item.network))
|
||||
{
|
||||
rule.network = Utils.String2List(item.network);
|
||||
}
|
||||
@@ -1221,7 +1221,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
});
|
||||
|
||||
var lstDomain = singboxConfig.outbounds
|
||||
.Where(t => !Utils.IsNullOrEmpty(t.server) && Utils.IsDomain(t.server))
|
||||
.Where(t => Utils.IsNotEmpty(t.server) && Utils.IsDomain(t.server))
|
||||
.Select(t => t.server)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
@@ -1250,7 +1250,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
|
||||
private int GenExperimental(SingboxConfig singboxConfig)
|
||||
{
|
||||
if (_config.guiItem.enableStatistics)
|
||||
//if (_config.guiItem.enableStatistics)
|
||||
{
|
||||
singboxConfig.experimental ??= new Experimental4Sbox();
|
||||
singboxConfig.experimental.clash_api = new Clash_Api4Sbox()
|
||||
@@ -1324,10 +1324,10 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
if (_config.routingBasicItem.enableRoutingAdvanced)
|
||||
{
|
||||
var routing = ConfigHandler.GetDefaultRouting(_config);
|
||||
if (!Utils.IsNullOrEmpty(routing.customRulesetPath4Singbox))
|
||||
if (Utils.IsNotEmpty(routing.customRulesetPath4Singbox))
|
||||
{
|
||||
var result = Utils.LoadResource(routing.customRulesetPath4Singbox);
|
||||
if (!Utils.IsNullOrEmpty(result))
|
||||
if (Utils.IsNotEmpty(result))
|
||||
{
|
||||
customRulesets = (JsonUtils.Deserialize<List<Ruleset4Sbox>>(result) ?? [])
|
||||
.Where(t => t.tag != null)
|
||||
|
||||
@@ -392,7 +392,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
v2rayConfig.inbounds.Add(inbound4);
|
||||
|
||||
//auth
|
||||
if (!Utils.IsNullOrEmpty(_config.inbound[0].user) && !Utils.IsNullOrEmpty(_config.inbound[0].pass))
|
||||
if (Utils.IsNotEmpty(_config.inbound[0].user) && Utils.IsNotEmpty(_config.inbound[0].pass))
|
||||
{
|
||||
inbound3.settings.auth = "password";
|
||||
inbound3.settings.accounts = new List<AccountsItem4Ray> { new AccountsItem4Ray() { user = _config.inbound[0].user, pass = _config.inbound[0].pass } };
|
||||
@@ -453,7 +453,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
var routing = ConfigHandler.GetDefaultRouting(_config);
|
||||
if (routing != null)
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(routing.domainStrategy))
|
||||
if (Utils.IsNotEmpty(routing.domainStrategy))
|
||||
{
|
||||
v2rayConfig.routing.domainStrategy = routing.domainStrategy;
|
||||
}
|
||||
@@ -550,7 +550,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
}
|
||||
if (!hasDomainIp)
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(rule.port)
|
||||
if (Utils.IsNotEmpty(rule.port)
|
||||
|| rule.protocol?.Count > 0
|
||||
|| rule.inboundTag?.Count > 0
|
||||
)
|
||||
@@ -660,8 +660,8 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
serversItem.method = null;
|
||||
serversItem.password = null;
|
||||
|
||||
if (!Utils.IsNullOrEmpty(node.security)
|
||||
&& !Utils.IsNullOrEmpty(node.id))
|
||||
if (Utils.IsNotEmpty(node.security)
|
||||
&& Utils.IsNotEmpty(node.id))
|
||||
{
|
||||
SocksUsersItem4Ray socksUsersItem = new()
|
||||
{
|
||||
@@ -712,7 +712,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
if (node.streamSecurity == Global.StreamSecurityReality
|
||||
|| node.streamSecurity == Global.StreamSecurity)
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(node.flow))
|
||||
if (Utils.IsNotEmpty(node.flow))
|
||||
{
|
||||
usersItem.flow = node.flow;
|
||||
|
||||
@@ -818,11 +818,11 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
alpn = node.GetAlpn(),
|
||||
fingerprint = node.fingerprint.IsNullOrEmpty() ? _config.coreBasicItem.defFingerprint : node.fingerprint
|
||||
};
|
||||
if (!Utils.IsNullOrEmpty(sni))
|
||||
if (Utils.IsNotEmpty(sni))
|
||||
{
|
||||
tlsSettings.serverName = sni;
|
||||
}
|
||||
else if (!Utils.IsNullOrEmpty(host))
|
||||
else if (Utils.IsNotEmpty(host))
|
||||
{
|
||||
tlsSettings.serverName = Utils.String2List(host)[0];
|
||||
}
|
||||
@@ -867,7 +867,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
{
|
||||
type = node.headerType
|
||||
};
|
||||
if (!Utils.IsNullOrEmpty(node.path))
|
||||
if (Utils.IsNotEmpty(node.path))
|
||||
{
|
||||
kcpSettings.seed = node.path;
|
||||
}
|
||||
@@ -878,15 +878,15 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
WsSettings4Ray wsSettings = new();
|
||||
wsSettings.headers = new Headers4Ray();
|
||||
string path = node.path;
|
||||
if (!Utils.IsNullOrEmpty(host))
|
||||
if (Utils.IsNotEmpty(host))
|
||||
{
|
||||
wsSettings.headers.Host = host;
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(path))
|
||||
if (Utils.IsNotEmpty(path))
|
||||
{
|
||||
wsSettings.path = path;
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(useragent))
|
||||
if (Utils.IsNotEmpty(useragent))
|
||||
{
|
||||
wsSettings.headers.UserAgent = useragent;
|
||||
}
|
||||
@@ -897,11 +897,11 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
case nameof(ETransport.httpupgrade):
|
||||
HttpupgradeSettings4Ray httpupgradeSettings = new();
|
||||
|
||||
if (!Utils.IsNullOrEmpty(node.path))
|
||||
if (Utils.IsNotEmpty(node.path))
|
||||
{
|
||||
httpupgradeSettings.path = node.path;
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(host))
|
||||
if (Utils.IsNotEmpty(host))
|
||||
{
|
||||
httpupgradeSettings.host = host;
|
||||
}
|
||||
@@ -916,11 +916,11 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
maxConcurrentUploads = 10
|
||||
};
|
||||
|
||||
if (!Utils.IsNullOrEmpty(node.path))
|
||||
if (Utils.IsNotEmpty(node.path))
|
||||
{
|
||||
splithttpSettings.path = node.path;
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(host))
|
||||
if (Utils.IsNotEmpty(host))
|
||||
{
|
||||
splithttpSettings.host = host;
|
||||
}
|
||||
@@ -931,7 +931,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
case nameof(ETransport.h2):
|
||||
HttpSettings4Ray httpSettings = new();
|
||||
|
||||
if (!Utils.IsNullOrEmpty(host))
|
||||
if (Utils.IsNotEmpty(host))
|
||||
{
|
||||
httpSettings.host = Utils.String2List(host);
|
||||
}
|
||||
@@ -954,7 +954,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
streamSettings.quicSettings = quicsettings;
|
||||
if (node.streamSecurity == Global.StreamSecurity)
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(sni))
|
||||
if (Utils.IsNotEmpty(sni))
|
||||
{
|
||||
streamSettings.tlsSettings.serverName = sni;
|
||||
}
|
||||
@@ -1000,7 +1000,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
request = request.Replace("$requestUserAgent$", $"\"{useragent}\"");
|
||||
//Path
|
||||
string pathHttp = @"/";
|
||||
if (!Utils.IsNullOrEmpty(node.path))
|
||||
if (Utils.IsNotEmpty(node.path))
|
||||
{
|
||||
string[] arrPath = node.path.Split(',');
|
||||
pathHttp = string.Join("\",\"", arrPath);
|
||||
@@ -1033,7 +1033,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
}
|
||||
|
||||
//Outbound Freedom domainStrategy
|
||||
if (!Utils.IsNullOrEmpty(domainStrategy4Freedom))
|
||||
if (Utils.IsNotEmpty(domainStrategy4Freedom))
|
||||
{
|
||||
var outbound = v2rayConfig.outbounds[1];
|
||||
outbound.settings.domainStrategy = domainStrategy4Freedom;
|
||||
@@ -1097,7 +1097,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
address = Utils.IsNullOrEmpty(dNSItem?.domainDNSAddress) ? Global.DomainDNSAddress.FirstOrDefault() : dNSItem?.domainDNSAddress,
|
||||
domains = [node.address]
|
||||
};
|
||||
servers.AsArray().Insert(0, JsonUtils.SerializeToNode(dnsServer));
|
||||
servers.AsArray().Add(JsonUtils.SerializeToNode(dnsServer));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -1157,7 +1157,7 @@ namespace ServiceLib.Handler.CoreConfig
|
||||
{
|
||||
//fragment proxy
|
||||
if (_config.coreBasicItem.enableFragment
|
||||
&& !Utils.IsNullOrEmpty(v2rayConfig.outbounds[0].streamSettings?.security))
|
||||
&& Utils.IsNotEmpty(v2rayConfig.outbounds[0].streamSettings?.security))
|
||||
{
|
||||
var fragmentOutbound = new Outbounds4Ray
|
||||
{
|
||||
|
||||
@@ -302,7 +302,7 @@ namespace ServiceLib.Handler
|
||||
{
|
||||
proc.OutputDataReceived += (sender, e) =>
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(e.Data))
|
||||
if (Utils.IsNotEmpty(e.Data))
|
||||
{
|
||||
string msg = e.Data + Environment.NewLine;
|
||||
ShowMsg(false, msg);
|
||||
@@ -310,7 +310,7 @@ namespace ServiceLib.Handler
|
||||
};
|
||||
proc.ErrorDataReceived += (sender, e) =>
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(e.Data))
|
||||
if (Utils.IsNotEmpty(e.Data))
|
||||
{
|
||||
string msg = e.Data + Environment.NewLine;
|
||||
ShowMsg(false, msg);
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace ServiceLib.Handler
|
||||
return 0;
|
||||
}
|
||||
|
||||
public async Task DownloadFileAsync(string url, bool blProxy, int downloadTimeout)
|
||||
public async Task DownloadFileAsync(string url, string fileName, bool blProxy, int downloadTimeout)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -74,7 +74,7 @@ namespace ServiceLib.Handler
|
||||
var webProxy = GetWebProxy(blProxy);
|
||||
await DownloaderHelper.Instance.DownloadFileAsync(webProxy,
|
||||
url,
|
||||
Utils.GetTempPath(Utils.GetDownloadFileName(url)),
|
||||
fileName,
|
||||
progress,
|
||||
downloadTimeout);
|
||||
}
|
||||
@@ -117,7 +117,7 @@ namespace ServiceLib.Handler
|
||||
try
|
||||
{
|
||||
var result1 = await DownloadStringAsync(url, blProxy, userAgent);
|
||||
if (!Utils.IsNullOrEmpty(result1))
|
||||
if (Utils.IsNotEmpty(result1))
|
||||
{
|
||||
return result1;
|
||||
}
|
||||
@@ -135,7 +135,7 @@ namespace ServiceLib.Handler
|
||||
try
|
||||
{
|
||||
var result2 = await DownloadStringViaDownloader(url, blProxy, userAgent);
|
||||
if (!Utils.IsNullOrEmpty(result2))
|
||||
if (Utils.IsNotEmpty(result2))
|
||||
{
|
||||
return result2;
|
||||
}
|
||||
@@ -155,7 +155,7 @@ namespace ServiceLib.Handler
|
||||
using var wc = new WebClient();
|
||||
wc.Proxy = GetWebProxy(blProxy);
|
||||
var result3 = await wc.DownloadStringTaskAsync(url);
|
||||
if (!Utils.IsNullOrEmpty(result3))
|
||||
if (Utils.IsNotEmpty(result3))
|
||||
{
|
||||
return result3;
|
||||
}
|
||||
@@ -197,7 +197,7 @@ namespace ServiceLib.Handler
|
||||
|
||||
Uri uri = new(url);
|
||||
//Authorization Header
|
||||
if (!Utils.IsNullOrEmpty(uri.UserInfo))
|
||||
if (Utils.IsNotEmpty(uri.UserInfo))
|
||||
{
|
||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Utils.Base64Encode(uri.UserInfo));
|
||||
}
|
||||
|
||||
@@ -16,12 +16,12 @@ namespace ServiceLib.Handler.Fmt
|
||||
|
||||
protected static int GetStdTransport(ProfileItem item, string? securityDef, ref Dictionary<string, string> dicQuery)
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(item.flow))
|
||||
if (Utils.IsNotEmpty(item.flow))
|
||||
{
|
||||
dicQuery.Add("flow", item.flow);
|
||||
}
|
||||
|
||||
if (!Utils.IsNullOrEmpty(item.streamSecurity))
|
||||
if (Utils.IsNotEmpty(item.streamSecurity))
|
||||
{
|
||||
dicQuery.Add("security", item.streamSecurity);
|
||||
}
|
||||
@@ -32,27 +32,27 @@ namespace ServiceLib.Handler.Fmt
|
||||
dicQuery.Add("security", securityDef);
|
||||
}
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.sni))
|
||||
if (Utils.IsNotEmpty(item.sni))
|
||||
{
|
||||
dicQuery.Add("sni", item.sni);
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.alpn))
|
||||
if (Utils.IsNotEmpty(item.alpn))
|
||||
{
|
||||
dicQuery.Add("alpn", Utils.UrlEncode(item.alpn));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.fingerprint))
|
||||
if (Utils.IsNotEmpty(item.fingerprint))
|
||||
{
|
||||
dicQuery.Add("fp", Utils.UrlEncode(item.fingerprint));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.publicKey))
|
||||
if (Utils.IsNotEmpty(item.publicKey))
|
||||
{
|
||||
dicQuery.Add("pbk", Utils.UrlEncode(item.publicKey));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.shortId))
|
||||
if (Utils.IsNotEmpty(item.shortId))
|
||||
{
|
||||
dicQuery.Add("sid", Utils.UrlEncode(item.shortId));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.spiderX))
|
||||
if (Utils.IsNotEmpty(item.spiderX))
|
||||
{
|
||||
dicQuery.Add("spx", Utils.UrlEncode(item.spiderX));
|
||||
}
|
||||
@@ -61,21 +61,21 @@ namespace ServiceLib.Handler.Fmt
|
||||
dicQuery.Add("allowInsecure", "1");
|
||||
}
|
||||
|
||||
dicQuery.Add("type", !Utils.IsNullOrEmpty(item.network) ? item.network : nameof(ETransport.tcp));
|
||||
dicQuery.Add("type", Utils.IsNotEmpty(item.network) ? item.network : nameof(ETransport.tcp));
|
||||
|
||||
switch (item.network)
|
||||
{
|
||||
case nameof(ETransport.tcp):
|
||||
dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : Global.None);
|
||||
if (!Utils.IsNullOrEmpty(item.requestHost))
|
||||
dicQuery.Add("headerType", Utils.IsNotEmpty(item.headerType) ? item.headerType : Global.None);
|
||||
if (Utils.IsNotEmpty(item.requestHost))
|
||||
{
|
||||
dicQuery.Add("host", Utils.UrlEncode(item.requestHost));
|
||||
}
|
||||
break;
|
||||
|
||||
case nameof(ETransport.kcp):
|
||||
dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : Global.None);
|
||||
if (!Utils.IsNullOrEmpty(item.path))
|
||||
dicQuery.Add("headerType", Utils.IsNotEmpty(item.headerType) ? item.headerType : Global.None);
|
||||
if (Utils.IsNotEmpty(item.path))
|
||||
{
|
||||
dicQuery.Add("seed", Utils.UrlEncode(item.path));
|
||||
}
|
||||
@@ -84,11 +84,11 @@ namespace ServiceLib.Handler.Fmt
|
||||
case nameof(ETransport.ws):
|
||||
case nameof(ETransport.httpupgrade):
|
||||
case nameof(ETransport.splithttp):
|
||||
if (!Utils.IsNullOrEmpty(item.requestHost))
|
||||
if (Utils.IsNotEmpty(item.requestHost))
|
||||
{
|
||||
dicQuery.Add("host", Utils.UrlEncode(item.requestHost));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.path))
|
||||
if (Utils.IsNotEmpty(item.path))
|
||||
{
|
||||
dicQuery.Add("path", Utils.UrlEncode(item.path));
|
||||
}
|
||||
@@ -97,24 +97,24 @@ namespace ServiceLib.Handler.Fmt
|
||||
case nameof(ETransport.http):
|
||||
case nameof(ETransport.h2):
|
||||
dicQuery["type"] = nameof(ETransport.http);
|
||||
if (!Utils.IsNullOrEmpty(item.requestHost))
|
||||
if (Utils.IsNotEmpty(item.requestHost))
|
||||
{
|
||||
dicQuery.Add("host", Utils.UrlEncode(item.requestHost));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.path))
|
||||
if (Utils.IsNotEmpty(item.path))
|
||||
{
|
||||
dicQuery.Add("path", Utils.UrlEncode(item.path));
|
||||
}
|
||||
break;
|
||||
|
||||
case nameof(ETransport.quic):
|
||||
dicQuery.Add("headerType", !Utils.IsNullOrEmpty(item.headerType) ? item.headerType : Global.None);
|
||||
dicQuery.Add("headerType", Utils.IsNotEmpty(item.headerType) ? item.headerType : Global.None);
|
||||
dicQuery.Add("quicSecurity", Utils.UrlEncode(item.requestHost));
|
||||
dicQuery.Add("key", Utils.UrlEncode(item.path));
|
||||
break;
|
||||
|
||||
case nameof(ETransport.grpc):
|
||||
if (!Utils.IsNullOrEmpty(item.path))
|
||||
if (Utils.IsNotEmpty(item.path))
|
||||
{
|
||||
dicQuery.Add("authority", Utils.UrlEncode(item.requestHost));
|
||||
dicQuery.Add("serviceName", Utils.UrlEncode(item.path));
|
||||
|
||||
@@ -31,20 +31,20 @@
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (!Utils.IsNullOrEmpty(item.remarks))
|
||||
if (Utils.IsNotEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
if (!Utils.IsNullOrEmpty(item.sni))
|
||||
if (Utils.IsNotEmpty(item.sni))
|
||||
{
|
||||
dicQuery.Add("sni", item.sni);
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.alpn))
|
||||
if (Utils.IsNotEmpty(item.alpn))
|
||||
{
|
||||
dicQuery.Add("alpn", Utils.UrlEncode(item.alpn));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.path))
|
||||
if (Utils.IsNotEmpty(item.path))
|
||||
{
|
||||
dicQuery.Add("obfs", "salamander");
|
||||
dicQuery.Add("obfs-password", Utils.UrlEncode(item.path));
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace ServiceLib.Handler.Fmt
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (!Utils.IsNullOrEmpty(item.remarks))
|
||||
if (Utils.IsNotEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
@@ -59,7 +59,7 @@ namespace ServiceLib.Handler.Fmt
|
||||
ProfileItem item = new();
|
||||
var base64 = match.Groups["base64"].Value.TrimEnd('/');
|
||||
var tag = match.Groups["tag"].Value;
|
||||
if (!Utils.IsNullOrEmpty(tag))
|
||||
if (Utils.IsNotEmpty(tag))
|
||||
{
|
||||
item.remarks = Utils.UrlDecode(tag);
|
||||
}
|
||||
@@ -128,7 +128,7 @@ namespace ServiceLib.Handler.Fmt
|
||||
{
|
||||
//obfs-host exists
|
||||
var obfsHost = queryParameters["plugin"]?.Split(';').FirstOrDefault(t => t.Contains("obfs-host"));
|
||||
if (queryParameters["plugin"].Contains("obfs=http") && !Utils.IsNullOrEmpty(obfsHost))
|
||||
if (queryParameters["plugin"].Contains("obfs=http") && Utils.IsNotEmpty(obfsHost))
|
||||
{
|
||||
obfsHost = obfsHost?.Replace("obfs-host=", "");
|
||||
item.network = Global.DefaultNetwork;
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (!Utils.IsNullOrEmpty(item.remarks))
|
||||
if (Utils.IsNotEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (!Utils.IsNullOrEmpty(item.remarks))
|
||||
if (Utils.IsNotEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
|
||||
@@ -36,16 +36,16 @@
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (!Utils.IsNullOrEmpty(item.remarks))
|
||||
if (Utils.IsNotEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
if (!Utils.IsNullOrEmpty(item.sni))
|
||||
if (Utils.IsNotEmpty(item.sni))
|
||||
{
|
||||
dicQuery.Add("sni", item.sni);
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.alpn))
|
||||
if (Utils.IsNotEmpty(item.alpn))
|
||||
{
|
||||
dicQuery.Add("alpn", Utils.UrlEncode(item.alpn));
|
||||
}
|
||||
|
||||
@@ -33,12 +33,12 @@
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (!Utils.IsNullOrEmpty(item.remarks))
|
||||
if (Utils.IsNotEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
if (!Utils.IsNullOrEmpty(item.security))
|
||||
if (Utils.IsNotEmpty(item.security))
|
||||
{
|
||||
dicQuery.Add("encryption", item.security);
|
||||
}
|
||||
|
||||
@@ -78,12 +78,12 @@
|
||||
item.alterId = Utils.ToInt(vmessQRCode.aid);
|
||||
item.security = Utils.ToString(vmessQRCode.scy);
|
||||
|
||||
item.security = !Utils.IsNullOrEmpty(vmessQRCode.scy) ? vmessQRCode.scy : Global.DefaultSecurity;
|
||||
if (!Utils.IsNullOrEmpty(vmessQRCode.net))
|
||||
item.security = Utils.IsNotEmpty(vmessQRCode.scy) ? vmessQRCode.scy : Global.DefaultSecurity;
|
||||
if (Utils.IsNotEmpty(vmessQRCode.net))
|
||||
{
|
||||
item.network = vmessQRCode.net;
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(vmessQRCode.type))
|
||||
if (Utils.IsNotEmpty(vmessQRCode.type))
|
||||
{
|
||||
item.headerType = vmessQRCode.type;
|
||||
}
|
||||
|
||||
@@ -34,25 +34,25 @@
|
||||
string url = string.Empty;
|
||||
|
||||
string remark = string.Empty;
|
||||
if (!Utils.IsNullOrEmpty(item.remarks))
|
||||
if (Utils.IsNotEmpty(item.remarks))
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.remarks);
|
||||
}
|
||||
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
if (!Utils.IsNullOrEmpty(item.publicKey))
|
||||
if (Utils.IsNotEmpty(item.publicKey))
|
||||
{
|
||||
dicQuery.Add("publickey", Utils.UrlEncode(item.publicKey));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.path))
|
||||
if (Utils.IsNotEmpty(item.path))
|
||||
{
|
||||
dicQuery.Add("reserved", Utils.UrlEncode(item.path));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.requestHost))
|
||||
if (Utils.IsNotEmpty(item.requestHost))
|
||||
{
|
||||
dicQuery.Add("address", Utils.UrlEncode(item.requestHost));
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(item.shortId))
|
||||
if (Utils.IsNotEmpty(item.shortId))
|
||||
{
|
||||
dicQuery.Add("mtu", Utils.UrlEncode(item.shortId));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ServiceLib.Handler
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
public sealed class LazyConfig
|
||||
{
|
||||
@@ -106,11 +104,11 @@ namespace ServiceLib.Handler
|
||||
from ProfileItem a
|
||||
left join SubItem b on a.subid = b.id
|
||||
where 1=1 ";
|
||||
if (!Utils.IsNullOrEmpty(subid))
|
||||
if (Utils.IsNotEmpty(subid))
|
||||
{
|
||||
sql += $" and a.subid = '{subid}'";
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(filter))
|
||||
if (Utils.IsNotEmpty(filter))
|
||||
{
|
||||
if (filter.Contains('\''))
|
||||
{
|
||||
|
||||
@@ -22,13 +22,13 @@ namespace ServiceLib.Handler
|
||||
MessageBus.Current.SendMessage(content, Global.CommandSendMsgView);
|
||||
}
|
||||
|
||||
public void SendMessage(string? content, bool time)
|
||||
public void SendMessageEx(string? content)
|
||||
{
|
||||
if (content.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
content = $"{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")} {content}";
|
||||
content = $"{DateTime.Now:yyyy/MM/dd HH:mm:ss} {content}";
|
||||
SendMessage(content);
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace ServiceLib.Handler
|
||||
|
||||
private void IndexIdEnqueue(string indexId)
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(indexId) && !_queIndexIds.Contains(indexId))
|
||||
if (Utils.IsNotEmpty(indexId) && !_queIndexIds.Contains(indexId))
|
||||
{
|
||||
_queIndexIds.Enqueue(indexId);
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ namespace ServiceLib.Handler.Statistics
|
||||
while (!res.CloseStatus.HasValue)
|
||||
{
|
||||
var result = Encoding.UTF8.GetString(buffer, 0, res.Count);
|
||||
if (!Utils.IsNullOrEmpty(result))
|
||||
if (Utils.IsNotEmpty(result))
|
||||
{
|
||||
ParseOutput(result, out ulong up, out ulong down);
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
{
|
||||
if ((dtNow - autoUpdateGeoTime).Hours % config.guiItem.autoUpdateInterval == 0)
|
||||
{
|
||||
updateHandle.UpdateGeoFileAll(config, (bool success, string msg) =>
|
||||
await updateHandle.UpdateGeoFileAll(config, (bool success, string msg) =>
|
||||
{
|
||||
update(false, msg);
|
||||
});
|
||||
|
||||
@@ -9,10 +9,9 @@ namespace ServiceLib.Handler
|
||||
{
|
||||
private Action<bool, string> _updateFunc;
|
||||
private Config _config;
|
||||
private int _timeout = 30;
|
||||
|
||||
public event EventHandler<ResultEventArgs> AbsoluteCompleted;
|
||||
|
||||
public class ResultEventArgs : EventArgs
|
||||
private class ResultEventArgs
|
||||
{
|
||||
public bool Success;
|
||||
public string Msg;
|
||||
@@ -26,11 +25,12 @@ namespace ServiceLib.Handler
|
||||
}
|
||||
}
|
||||
|
||||
public void CheckUpdateGuiN(Config config, Action<bool, string> update, bool preRelease)
|
||||
public async Task CheckUpdateGuiN(Config config, Action<bool, string> update, bool preRelease)
|
||||
{
|
||||
_config = config;
|
||||
_updateFunc = update;
|
||||
var url = string.Empty;
|
||||
var fileName = string.Empty;
|
||||
|
||||
DownloadHandler downloadHandle = new();
|
||||
downloadHandle.UpdateCompleted += (sender2, args) =>
|
||||
@@ -38,9 +38,7 @@ namespace ServiceLib.Handler
|
||||
if (args.Success)
|
||||
{
|
||||
_updateFunc(false, ResUI.MsgDownloadV2rayCoreSuccessfully);
|
||||
string fileName = Utils.GetTempPath(Utils.GetDownloadFileName(url));
|
||||
fileName = Utils.UrlEncode(fileName);
|
||||
_updateFunc(true, fileName);
|
||||
_updateFunc(true, Utils.UrlEncode(fileName));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -50,36 +48,31 @@ namespace ServiceLib.Handler
|
||||
downloadHandle.Error += (sender2, args) =>
|
||||
{
|
||||
_updateFunc(false, args.GetException().Message);
|
||||
_updateFunc(false, "");
|
||||
};
|
||||
AbsoluteCompleted += (sender2, args) =>
|
||||
{
|
||||
if (args.Success)
|
||||
{
|
||||
_updateFunc(false, string.Format(ResUI.MsgParsingSuccessfully, ECoreType.v2rayN));
|
||||
_updateFunc(false, args.Msg);
|
||||
|
||||
url = args.Url;
|
||||
AskToDownload(downloadHandle, url, true).ContinueWith(task =>
|
||||
{
|
||||
_updateFunc(false, "");
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
_updateFunc(false, args.Msg);
|
||||
_updateFunc(false, "");
|
||||
}
|
||||
};
|
||||
_updateFunc(false, string.Format(ResUI.MsgStartUpdating, ECoreType.v2rayN));
|
||||
CheckUpdateAsync(downloadHandle, ECoreType.v2rayN, preRelease);
|
||||
var args = await CheckUpdateAsync(downloadHandle, ECoreType.v2rayN, preRelease);
|
||||
if (args.Success)
|
||||
{
|
||||
_updateFunc(false, string.Format(ResUI.MsgParsingSuccessfully, ECoreType.v2rayN));
|
||||
_updateFunc(false, args.Msg);
|
||||
|
||||
url = args.Url;
|
||||
fileName = Utils.GetTempPath(Utils.GetGUID());
|
||||
await downloadHandle.DownloadFileAsync(url, fileName, true, _timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
_updateFunc(false, args.Msg);
|
||||
}
|
||||
}
|
||||
|
||||
public async void CheckUpdateCore(ECoreType type, Config config, Action<bool, string> update, bool preRelease)
|
||||
public async Task CheckUpdateCore(ECoreType type, Config config, Action<bool, string> update, bool preRelease)
|
||||
{
|
||||
_config = config;
|
||||
_updateFunc = update;
|
||||
var url = string.Empty;
|
||||
var fileName = string.Empty;
|
||||
|
||||
DownloadHandler downloadHandle = new();
|
||||
downloadHandle.UpdateCompleted += (sender2, args) =>
|
||||
@@ -91,7 +84,7 @@ namespace ServiceLib.Handler
|
||||
|
||||
try
|
||||
{
|
||||
_updateFunc(true, url);
|
||||
_updateFunc(true, fileName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -105,31 +98,27 @@ namespace ServiceLib.Handler
|
||||
};
|
||||
downloadHandle.Error += (sender2, args) =>
|
||||
{
|
||||
_updateFunc(false, args.GetException().Message);
|
||||
_updateFunc(false, "");
|
||||
_updateFunc(false, args.GetException().Message);
|
||||
};
|
||||
|
||||
AbsoluteCompleted += (sender2, args) =>
|
||||
{
|
||||
if (args.Success)
|
||||
{
|
||||
_updateFunc(false, string.Format(ResUI.MsgParsingSuccessfully, type));
|
||||
_updateFunc(false, args.Msg);
|
||||
|
||||
url = args.Url;
|
||||
AskToDownload(downloadHandle, url, true).ContinueWith(task =>
|
||||
{
|
||||
_updateFunc(false, "");
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
_updateFunc(false, args.Msg);
|
||||
_updateFunc(false, "");
|
||||
}
|
||||
};
|
||||
_updateFunc(false, string.Format(ResUI.MsgStartUpdating, type));
|
||||
CheckUpdateAsync(downloadHandle, type, preRelease);
|
||||
var args = await CheckUpdateAsync(downloadHandle, type, preRelease);
|
||||
if (args.Success)
|
||||
{
|
||||
_updateFunc(false, string.Format(ResUI.MsgParsingSuccessfully, type));
|
||||
_updateFunc(false, args.Msg);
|
||||
|
||||
url = args.Url;
|
||||
fileName = Utils.GetTempPath(Utils.GetGUID());
|
||||
await downloadHandle.DownloadFileAsync(url, fileName, true, _timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!args.Msg.IsNullOrEmpty())
|
||||
{
|
||||
_updateFunc(false, args.Msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateSubscriptionProcess(Config config, string subId, bool blProxy, Action<bool, string> update)
|
||||
@@ -154,7 +143,7 @@ namespace ServiceLib.Handler
|
||||
string url = item.url.TrimEx();
|
||||
string userAgent = item.userAgent.TrimEx();
|
||||
string hashCode = $"{item.remarks}->";
|
||||
if (Utils.IsNullOrEmpty(id) || Utils.IsNullOrEmpty(url) || (!Utils.IsNullOrEmpty(subId) && item.id != subId))
|
||||
if (Utils.IsNullOrEmpty(id) || Utils.IsNullOrEmpty(url) || (Utils.IsNotEmpty(subId) && item.id != subId))
|
||||
{
|
||||
//_updateFunc(false, $"{hashCode}{ResUI.MsgNoValidSubscription}");
|
||||
continue;
|
||||
@@ -180,7 +169,7 @@ namespace ServiceLib.Handler
|
||||
//one url
|
||||
url = Utils.GetPunycode(url);
|
||||
//convert
|
||||
if (!Utils.IsNullOrEmpty(item.convertTarget))
|
||||
if (Utils.IsNotEmpty(item.convertTarget))
|
||||
{
|
||||
var subConvertUrl = Utils.IsNullOrEmpty(config.constItem.subConvertUrl) ? Global.SubConvertUrls.FirstOrDefault() : config.constItem.subConvertUrl;
|
||||
url = string.Format(subConvertUrl!, Utils.UrlEncode(url));
|
||||
@@ -200,9 +189,9 @@ namespace ServiceLib.Handler
|
||||
}
|
||||
|
||||
//more url
|
||||
if (Utils.IsNullOrEmpty(item.convertTarget) && !Utils.IsNullOrEmpty(item.moreUrl.TrimEx()))
|
||||
if (Utils.IsNullOrEmpty(item.convertTarget) && Utils.IsNotEmpty(item.moreUrl.TrimEx()))
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(result) && Utils.IsBase64String(result!))
|
||||
if (Utils.IsNotEmpty(result) && Utils.IsBase64String(result!))
|
||||
{
|
||||
result = Utils.Base64Decode(result);
|
||||
}
|
||||
@@ -221,7 +210,7 @@ namespace ServiceLib.Handler
|
||||
{
|
||||
result2 = await downloadHandle.TryDownloadString(url2, false, userAgent);
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(result2))
|
||||
if (Utils.IsNotEmpty(result2))
|
||||
{
|
||||
if (Utils.IsBase64String(result2!))
|
||||
{
|
||||
@@ -265,50 +254,43 @@ namespace ServiceLib.Handler
|
||||
});
|
||||
}
|
||||
|
||||
public void UpdateGeoFileAll(Config config, Action<bool, string> update)
|
||||
public async Task UpdateGeoFileAll(Config config, Action<bool, string> update)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await UpdateGeoFile("geosite", _config, update);
|
||||
await UpdateGeoFile("geoip", _config, update);
|
||||
_updateFunc(true, string.Format(ResUI.MsgDownloadGeoFileSuccessfully, "geo"));
|
||||
});
|
||||
await UpdateGeoFile("geosite", _config, update);
|
||||
await UpdateGeoFile("geoip", _config, update);
|
||||
_updateFunc(true, string.Format(ResUI.MsgDownloadGeoFileSuccessfully, "geo"));
|
||||
}
|
||||
|
||||
public void RunAvailabilityCheck(Action<bool, string> update)
|
||||
public async Task RunAvailabilityCheck(Action<bool, string> update)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
var time = await (new DownloadHandler()).RunAvailabilityCheck(null);
|
||||
|
||||
update(false, string.Format(ResUI.TestMeOutput, time));
|
||||
});
|
||||
var time = await (new DownloadHandler()).RunAvailabilityCheck(null);
|
||||
update(false, string.Format(ResUI.TestMeOutput, time));
|
||||
}
|
||||
|
||||
#region private
|
||||
|
||||
private async void CheckUpdateAsync(DownloadHandler downloadHandle, ECoreType type, bool preRelease)
|
||||
private async Task<ResultEventArgs> CheckUpdateAsync(DownloadHandler downloadHandle, ECoreType type, bool preRelease)
|
||||
{
|
||||
try
|
||||
{
|
||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(type);
|
||||
string url = coreInfo.coreReleaseApiUrl;
|
||||
var url = coreInfo?.coreReleaseApiUrl;
|
||||
|
||||
var result = await downloadHandle.DownloadStringAsync(url, true, Global.AppName);
|
||||
if (!Utils.IsNullOrEmpty(result))
|
||||
if (Utils.IsNotEmpty(result))
|
||||
{
|
||||
ResponseHandler(type, result, preRelease);
|
||||
return await ParseDownloadUrl(type, result, preRelease);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.SaveLog("StatusCode error: " + url);
|
||||
return;
|
||||
return new ResultEventArgs(false, "");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
_updateFunc(false, ex.Message);
|
||||
return new ResultEventArgs(false, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,7 +362,7 @@ namespace ServiceLib.Handler
|
||||
}
|
||||
}
|
||||
|
||||
private void ResponseHandler(ECoreType type, string gitHubReleaseApi, bool preRelease)
|
||||
private async Task<ResultEventArgs> ParseDownloadUrl(ECoreType type, string gitHubReleaseApi, bool preRelease)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -434,16 +416,16 @@ namespace ServiceLib.Handler
|
||||
|
||||
if (curVersion >= version && version != new SemanticVersion(0, 0, 0))
|
||||
{
|
||||
AbsoluteCompleted?.Invoke(this, new ResultEventArgs(false, message));
|
||||
return;
|
||||
return new ResultEventArgs(false, message);
|
||||
}
|
||||
|
||||
AbsoluteCompleted?.Invoke(this, new ResultEventArgs(true, body, url));
|
||||
return new ResultEventArgs(true, body, url);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(ex.Message, ex);
|
||||
_updateFunc(false, ex.Message);
|
||||
return new ResultEventArgs(false, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -451,6 +433,15 @@ namespace ServiceLib.Handler
|
||||
{
|
||||
if (Utils.IsWindows())
|
||||
{
|
||||
//Check for standalone windows .Net version
|
||||
if (coreInfo?.coreType == ECoreType.v2rayN
|
||||
&& File.Exists(Path.Combine(Utils.StartupPath(), "wpfgfx_cor3.dll"))
|
||||
&& File.Exists(Path.Combine(Utils.StartupPath(), "D3DCompiler_47_cor3.dll"))
|
||||
)
|
||||
{
|
||||
return coreInfo?.coreDownloadUrl64.Replace("v2rayN.zip", "zz_v2rayN-SelfContained.zip");
|
||||
}
|
||||
|
||||
return RuntimeInformation.ProcessArchitecture switch
|
||||
{
|
||||
Architecture.Arm64 => coreInfo?.coreDownloadUrlArm64,
|
||||
@@ -472,31 +463,12 @@ namespace ServiceLib.Handler
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task AskToDownload(DownloadHandler downloadHandle, string url, bool blAsk)
|
||||
{
|
||||
//bool blDownload = false;
|
||||
//if (blAsk)
|
||||
//{
|
||||
// if (UI.ShowYesNo(string.Format(ResUI.DownloadYesNo, url)) == MessageBoxResult.Yes)
|
||||
// {
|
||||
// blDownload = true;
|
||||
// }
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// blDownload = true;
|
||||
//}
|
||||
//if (blDownload)
|
||||
//{
|
||||
await downloadHandle.DownloadFileAsync(url, true, 60);
|
||||
//}
|
||||
}
|
||||
|
||||
private async Task UpdateGeoFile(string geoName, Config config, Action<bool, string> update)
|
||||
{
|
||||
_config = config;
|
||||
_updateFunc = update;
|
||||
var url = string.Format(Global.GeoUrl, geoName);
|
||||
var fileName = Utils.GetTempPath(Utils.GetGUID());
|
||||
|
||||
DownloadHandler downloadHandle = new();
|
||||
downloadHandle.UpdateCompleted += (sender2, args) =>
|
||||
@@ -507,7 +479,6 @@ namespace ServiceLib.Handler
|
||||
|
||||
try
|
||||
{
|
||||
string fileName = Utils.GetTempPath(Utils.GetDownloadFileName(url));
|
||||
if (File.Exists(fileName))
|
||||
{
|
||||
string targetPath = Utils.GetBinPath($"{geoName}.dat");
|
||||
@@ -531,7 +502,8 @@ namespace ServiceLib.Handler
|
||||
{
|
||||
_updateFunc(false, args.GetException().Message);
|
||||
};
|
||||
await AskToDownload(downloadHandle, url, false);
|
||||
|
||||
await downloadHandle.DownloadFileAsync(url, fileName, true, _timeout);
|
||||
}
|
||||
|
||||
#endregion private
|
||||
|
||||
171
v2rayN/ServiceLib/Handler/WebDavHandler.cs
Normal file
171
v2rayN/ServiceLib/Handler/WebDavHandler.cs
Normal file
@@ -0,0 +1,171 @@
|
||||
using System.Net;
|
||||
using WebDav;
|
||||
|
||||
namespace ServiceLib.Handler
|
||||
{
|
||||
public sealed class WebDavHandler
|
||||
{
|
||||
private static readonly Lazy<WebDavHandler> _instance = new(() => new());
|
||||
public static WebDavHandler Instance => _instance.Value;
|
||||
|
||||
private Config? _config;
|
||||
private WebDavClient? _client;
|
||||
private string? _lastDescription;
|
||||
private string _webDir = Global.AppName + "_backup";
|
||||
private string _webFileName = "backup.zip";
|
||||
private string _logTitle = "WebDav--";
|
||||
|
||||
public WebDavHandler()
|
||||
{
|
||||
_config = LazyConfig.Instance.Config;
|
||||
}
|
||||
|
||||
private async Task<bool> GetClient()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_config.webDavItem.url.IsNullOrEmpty()
|
||||
|| _config.webDavItem.userName.IsNullOrEmpty()
|
||||
|| _config.webDavItem.password.IsNullOrEmpty())
|
||||
{
|
||||
throw new ArgumentException("webdav parameter error or null");
|
||||
}
|
||||
if (_client != null)
|
||||
{
|
||||
_client?.Dispose();
|
||||
_client = null;
|
||||
}
|
||||
if (_config.webDavItem.dirName.IsNullOrEmpty())
|
||||
{
|
||||
_webDir = Global.AppName + "_backup";
|
||||
}
|
||||
else
|
||||
{
|
||||
_webDir = _config.webDavItem.dirName.TrimEx();
|
||||
}
|
||||
|
||||
var clientParams = new WebDavClientParams
|
||||
{
|
||||
BaseAddress = new Uri(_config.webDavItem.url),
|
||||
Credentials = new NetworkCredential(_config.webDavItem.userName, _config.webDavItem.password)
|
||||
};
|
||||
_client = new WebDavClient(clientParams);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SaveLog(ex);
|
||||
return false;
|
||||
}
|
||||
return await Task.FromResult(true);
|
||||
}
|
||||
|
||||
private async Task<bool> TryCreateDir()
|
||||
{
|
||||
if (_client is null) return false;
|
||||
try
|
||||
{
|
||||
var result2 = await _client.Mkcol(_webDir);
|
||||
if (result2.IsSuccessful)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
SaveLog(result2.Description);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SaveLog(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SaveLog(string desc)
|
||||
{
|
||||
_lastDescription = desc;
|
||||
Logging.SaveLog(_logTitle + desc);
|
||||
}
|
||||
|
||||
private void SaveLog(Exception ex)
|
||||
{
|
||||
_lastDescription = ex.Message;
|
||||
Logging.SaveLog(_logTitle, ex);
|
||||
}
|
||||
|
||||
public async Task<bool> CheckConnection()
|
||||
{
|
||||
if (await GetClient() == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
await TryCreateDir();
|
||||
|
||||
var testName = "readme_test";
|
||||
var myContent = new StringContent(testName);
|
||||
var result = await _client.PutFile($"{_webDir}/{testName}", myContent);
|
||||
if (result.IsSuccessful)
|
||||
{
|
||||
await _client.Delete($"{_webDir}/{testName}");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
SaveLog(result.Description);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> PutFile(string fileName)
|
||||
{
|
||||
if (await GetClient() == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
await TryCreateDir();
|
||||
|
||||
try
|
||||
{
|
||||
using var fs = File.OpenRead(fileName);
|
||||
var result = await _client.PutFile($"{_webDir}/{_webFileName}", fs); // upload a resource
|
||||
if (result.IsSuccessful)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
SaveLog(result.Description);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SaveLog(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task<bool> GetRawFile(string fileName)
|
||||
{
|
||||
if (await GetClient() == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
await TryCreateDir();
|
||||
|
||||
try
|
||||
{
|
||||
var response = await _client.GetRawFile($"{_webDir}/{_webFileName}");
|
||||
if (!response.IsSuccessful)
|
||||
{
|
||||
SaveLog(response.Description);
|
||||
return false;
|
||||
}
|
||||
using var outputFileStream = new FileStream(fileName, FileMode.Create);
|
||||
response.Stream.CopyTo(outputFileStream);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SaveLog(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public string GetLastError() => _lastDescription ?? string.Empty;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ServiceLib.Models
|
||||
namespace ServiceLib.Models
|
||||
{
|
||||
public class CheckUpdateItem
|
||||
{
|
||||
@@ -14,4 +8,4 @@ namespace ServiceLib.Models
|
||||
public string? fileName { get; set; }
|
||||
public bool? isFinished { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,7 @@
|
||||
public GrpcItem grpcItem { get; set; }
|
||||
public RoutingBasicItem routingBasicItem { get; set; }
|
||||
public GUIItem guiItem { get; set; }
|
||||
public MsgUIItem msgUIItem { get; set; }
|
||||
public UIItem uiItem { get; set; }
|
||||
public ConstItem constItem { get; set; }
|
||||
public SpeedTestItem speedTestItem { get; set; }
|
||||
@@ -46,6 +47,7 @@
|
||||
public HysteriaItem hysteriaItem { get; set; }
|
||||
public ClashUIItem clashUIItem { get; set; }
|
||||
public SystemProxyItem systemProxyItem { get; set; }
|
||||
public WebDavItem webDavItem { get; set; }
|
||||
public List<InItem> inbound { get; set; }
|
||||
public List<KeyEventItem> globalHotkeys { get; set; }
|
||||
public List<CoreTypeItem> coreTypeItem { get; set; }
|
||||
|
||||
@@ -107,6 +107,13 @@
|
||||
public bool enableLog { get; set; } = true;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class MsgUIItem
|
||||
{
|
||||
public string? mainMsgFilter { get; set; }
|
||||
public bool? autoRefresh { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class UIItem
|
||||
{
|
||||
@@ -126,7 +133,6 @@
|
||||
public bool enableDragDropSort { get; set; }
|
||||
public bool doubleClick2Activate { get; set; }
|
||||
public bool autoHideStartup { get; set; }
|
||||
public string mainMsgFilter { get; set; }
|
||||
public List<ColumnItem> mainColumnItem { get; set; }
|
||||
public bool showInTaskbar { get; set; }
|
||||
}
|
||||
@@ -242,4 +248,13 @@
|
||||
public bool notProxyLocalAddress { get; set; } = true;
|
||||
public string systemProxyAdvancedProtocol { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class WebDavItem
|
||||
{
|
||||
public string? url { get; set; }
|
||||
public string? userName { get; set; }
|
||||
public string? password { get; set; }
|
||||
public string? dirName { get; set; }
|
||||
}
|
||||
}
|
||||
126
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
126
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
@@ -105,6 +105,15 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Host filter 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string ConnectionsHostFilterTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionsHostFilterTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Note that custom configuration relies entirely on your own configuration and does not work with all settings. If you want to use the system proxy, please modify the listening port manually. 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -321,6 +330,15 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Invalid backup file 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string LocalRestoreInvalidZipTips {
|
||||
get {
|
||||
return ResourceManager.GetString("LocalRestoreInvalidZipTips", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Address 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -582,6 +600,51 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 WebDav Check 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string LvWebDavCheck {
|
||||
get {
|
||||
return ResourceManager.GetString("LvWebDavCheck", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Remote folder name (optional) 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string LvWebDavDirName {
|
||||
get {
|
||||
return ResourceManager.GetString("LvWebDavDirName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 WebDav Password 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string LvWebDavPassword {
|
||||
get {
|
||||
return ResourceManager.GetString("LvWebDavPassword", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 WebDav Url 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string LvWebDavUrl {
|
||||
get {
|
||||
return ResourceManager.GetString("LvWebDavUrl", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 WebDav User Name 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string LvWebDavUserName {
|
||||
get {
|
||||
return ResourceManager.GetString("LvWebDavUserName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Add a custom configuration server 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -690,6 +753,15 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Backup and Restore 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuBackupAndRestore {
|
||||
get {
|
||||
return ResourceManager.GetString("menuBackupAndRestore", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Check Update 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -861,6 +933,33 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Backup to local 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuLocalBackup {
|
||||
get {
|
||||
return ResourceManager.GetString("menuLocalBackup", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Local 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuLocalBackupAndRestore {
|
||||
get {
|
||||
return ResourceManager.GetString("menuLocalBackupAndRestore", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Restore from local 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuLocalRestore {
|
||||
get {
|
||||
return ResourceManager.GetString("menuLocalRestore", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 One-click multi test Latency and speed (Ctrl+E) 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -1095,6 +1194,33 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Backup to remote (WebDAV) 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuRemoteBackup {
|
||||
get {
|
||||
return ResourceManager.GetString("menuRemoteBackup", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Remote (WebDAV) 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuRemoteBackupAndRestore {
|
||||
get {
|
||||
return ResourceManager.GetString("menuRemoteBackupAndRestore", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Restore from remote (WebDAV) 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuRemoteRestore {
|
||||
get {
|
||||
return ResourceManager.GetString("menuRemoteRestore", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Remove duplicate servers 的本地化字符串。
|
||||
/// </summary>
|
||||
|
||||
@@ -1279,4 +1279,46 @@
|
||||
<data name="TbPreSocksPort4Sub" xml:space="preserve">
|
||||
<value>Custom config socks port</value>
|
||||
</data>
|
||||
<data name="menuBackupAndRestore" xml:space="preserve">
|
||||
<value>Backup and Restore</value>
|
||||
</data>
|
||||
<data name="menuLocalBackup" xml:space="preserve">
|
||||
<value>Backup to local</value>
|
||||
</data>
|
||||
<data name="menuLocalRestore" xml:space="preserve">
|
||||
<value>Restore from local</value>
|
||||
</data>
|
||||
<data name="menuRemoteBackup" xml:space="preserve">
|
||||
<value>Backup to remote (WebDAV)</value>
|
||||
</data>
|
||||
<data name="menuRemoteRestore" xml:space="preserve">
|
||||
<value>Restore from remote (WebDAV)</value>
|
||||
</data>
|
||||
<data name="menuLocalBackupAndRestore" xml:space="preserve">
|
||||
<value>Local</value>
|
||||
</data>
|
||||
<data name="menuRemoteBackupAndRestore" xml:space="preserve">
|
||||
<value>Remote (WebDAV)</value>
|
||||
</data>
|
||||
<data name="LvWebDavUrl" xml:space="preserve">
|
||||
<value>WebDav Url</value>
|
||||
</data>
|
||||
<data name="LvWebDavUserName" xml:space="preserve">
|
||||
<value>WebDav User Name</value>
|
||||
</data>
|
||||
<data name="LvWebDavPassword" xml:space="preserve">
|
||||
<value>WebDav Password</value>
|
||||
</data>
|
||||
<data name="LvWebDavCheck" xml:space="preserve">
|
||||
<value>WebDav Check</value>
|
||||
</data>
|
||||
<data name="LvWebDavDirName" xml:space="preserve">
|
||||
<value>Remote folder name (optional)</value>
|
||||
</data>
|
||||
<data name="LocalRestoreInvalidZipTips" xml:space="preserve">
|
||||
<value>Invalid backup file</value>
|
||||
</data>
|
||||
<data name="ConnectionsHostFilterTitle" xml:space="preserve">
|
||||
<value>Host filter</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1276,4 +1276,46 @@
|
||||
<data name="TbPreSocksPort4Sub" xml:space="preserve">
|
||||
<value>自定义配置的Socks端口</value>
|
||||
</data>
|
||||
<data name="menuBackupAndRestore" xml:space="preserve">
|
||||
<value>备份和还原</value>
|
||||
</data>
|
||||
<data name="menuLocalBackup" xml:space="preserve">
|
||||
<value>备份到本地</value>
|
||||
</data>
|
||||
<data name="menuLocalRestore" xml:space="preserve">
|
||||
<value>从本地恢复</value>
|
||||
</data>
|
||||
<data name="menuRemoteBackup" xml:space="preserve">
|
||||
<value>备份到远程 (WebDAV)</value>
|
||||
</data>
|
||||
<data name="menuRemoteRestore" xml:space="preserve">
|
||||
<value>从远程恢复 (WebDAV)</value>
|
||||
</data>
|
||||
<data name="menuLocalBackupAndRestore" xml:space="preserve">
|
||||
<value>本地</value>
|
||||
</data>
|
||||
<data name="menuRemoteBackupAndRestore" xml:space="preserve">
|
||||
<value>远程 (WebDAV)</value>
|
||||
</data>
|
||||
<data name="LvWebDavUserName" xml:space="preserve">
|
||||
<value>WebDav 账户</value>
|
||||
</data>
|
||||
<data name="LvWebDavCheck" xml:space="preserve">
|
||||
<value>WebDav 可用检查</value>
|
||||
</data>
|
||||
<data name="LvWebDavPassword" xml:space="preserve">
|
||||
<value>WebDav 密码</value>
|
||||
</data>
|
||||
<data name="LvWebDavUrl" xml:space="preserve">
|
||||
<value>WebDav 服务器地址</value>
|
||||
</data>
|
||||
<data name="LvWebDavDirName" xml:space="preserve">
|
||||
<value>远程文件夹名称(可选)</value>
|
||||
</data>
|
||||
<data name="LocalRestoreInvalidZipTips" xml:space="preserve">
|
||||
<value>无效备份文件</value>
|
||||
</data>
|
||||
<data name="ConnectionsHostFilterTitle" xml:space="preserve">
|
||||
<value>主机过滤器</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1156,4 +1156,46 @@
|
||||
<data name="TbPreSocksPort4Sub" xml:space="preserve">
|
||||
<value>自訂配置的Socks端口</value>
|
||||
</data>
|
||||
<data name="menuBackupAndRestore" xml:space="preserve">
|
||||
<value>備份和還原</value>
|
||||
</data>
|
||||
<data name="menuLocalBackup" xml:space="preserve">
|
||||
<value>備份到本地</value>
|
||||
</data>
|
||||
<data name="menuLocalRestore" xml:space="preserve">
|
||||
<value>從本地恢復</value>
|
||||
</data>
|
||||
<data name="menuRemoteBackup" xml:space="preserve">
|
||||
<value>備份到遠端 (WebDAV)</value>
|
||||
</data>
|
||||
<data name="menuRemoteRestore" xml:space="preserve">
|
||||
<value>從遠端恢復 (WebDAV)</value>
|
||||
</data>
|
||||
<data name="menuLocalBackupAndRestore" xml:space="preserve">
|
||||
<value>本地</value>
|
||||
</data>
|
||||
<data name="menuRemoteBackupAndRestore" xml:space="preserve">
|
||||
<value>遠端 (WebDAV)</value>
|
||||
</data>
|
||||
<data name="LvWebDavUserName" xml:space="preserve">
|
||||
<value>WebDav 賬戶</value>
|
||||
</data>
|
||||
<data name="LvWebDavCheck" xml:space="preserve">
|
||||
<value>WebDav 可用檢查</value>
|
||||
</data>
|
||||
<data name="LvWebDavPassword" xml:space="preserve">
|
||||
<value>WebDav 密碼</value>
|
||||
</data>
|
||||
<data name="LvWebDavUrl" xml:space="preserve">
|
||||
<value>WebDav 服務器地址</value>
|
||||
</data>
|
||||
<data name="LvWebDavDirName" xml:space="preserve">
|
||||
<value>遠端資料夾名稱(可選)</value>
|
||||
</data>
|
||||
<data name="LocalRestoreInvalidZipTips" xml:space="preserve">
|
||||
<value>無效備份文件</value>
|
||||
</data>
|
||||
<data name="ConnectionsHostFilterTitle" xml:space="preserve">
|
||||
<value>主機過濾器</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -4,6 +4,7 @@
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Version>6.59.0</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -12,7 +13,8 @@
|
||||
<PackageReference Include="ReactiveUI.Fody" Version="19.5.41" />
|
||||
<PackageReference Include="sqlite-net-pcl" Version="1.9.172" />
|
||||
<PackageReference Include="Splat.NLog" Version="15.1.1" />
|
||||
<PackageReference Include="YamlDotNet" Version="16.0.0" />
|
||||
<PackageReference Include="WebDav.Client" Version="2.8.0" />
|
||||
<PackageReference Include="YamlDotNet" Version="16.1.2" />
|
||||
<PackageReference Include="QRCoder" Version="1.6.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace ServiceLib.ViewModels
|
||||
{
|
||||
[Reactive]
|
||||
public ProfileItem SelectedSource { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string? CoreType { get; set; }
|
||||
|
||||
@@ -89,7 +90,7 @@ namespace ServiceLib.ViewModels
|
||||
if (ConfigHandler.AddCustomServer(_config, item, false) == 0)
|
||||
{
|
||||
_noticeHandler?.Enqueue(ResUI.SuccessfullyImportedCustomServer);
|
||||
if (!Utils.IsNullOrEmpty(item.indexId))
|
||||
if (Utils.IsNotEmpty(item.indexId))
|
||||
{
|
||||
SelectedSource = JsonUtils.DeepCopy(item);
|
||||
}
|
||||
|
||||
166
v2rayN/ServiceLib/ViewModels/BackupAndRestoreViewModel.cs
Normal file
166
v2rayN/ServiceLib/ViewModels/BackupAndRestoreViewModel.cs
Normal file
@@ -0,0 +1,166 @@
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using Splat;
|
||||
using System.Reactive;
|
||||
|
||||
namespace ServiceLib.ViewModels
|
||||
{
|
||||
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)
|
||||
{
|
||||
_config = LazyConfig.Instance.Config;
|
||||
_updateView = updateView;
|
||||
_noticeHandler = Locator.Current.GetService<NoticeHandler>();
|
||||
|
||||
WebDavCheckCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await WebDavCheck();
|
||||
});
|
||||
|
||||
RemoteBackupCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await RemoteBackup();
|
||||
});
|
||||
RemoteRestoreCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await RemoteRestore();
|
||||
});
|
||||
|
||||
SelectedSource = JsonUtils.DeepCopy(_config.webDavItem);
|
||||
}
|
||||
|
||||
private void DisplayOperationMsg(string msg = "")
|
||||
{
|
||||
OperationMsg = msg;
|
||||
}
|
||||
|
||||
private async Task WebDavCheck()
|
||||
{
|
||||
DisplayOperationMsg();
|
||||
_config.webDavItem = SelectedSource;
|
||||
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()
|
||||
{
|
||||
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 LocalRestore(string fileName)
|
||||
{
|
||||
DisplayOperationMsg();
|
||||
if (Utils.IsNullOrEmpty(fileName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
//exist
|
||||
if (!File.Exists(fileName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
//check
|
||||
var lstFiles = FileManager.GetFilesFromZip(fileName);
|
||||
if (lstFiles is null || !lstFiles.Where(t => t.Contains(_guiConfigs)).Any())
|
||||
{
|
||||
DisplayOperationMsg(ResUI.LocalRestoreInvalidZipTips);
|
||||
return;
|
||||
}
|
||||
|
||||
//backup first
|
||||
var fileBackup = Utils.GetBackupPath(BackupFileName);
|
||||
var result = await CreateZipFileFromDirectory(fileBackup);
|
||||
if (result)
|
||||
{
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.V2rayUpgrade(fileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
DisplayOperationMsg(WebDavHandler.Instance.GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> CreateZipFileFromDirectory(string fileName)
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(fileName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var configDir = Utils.GetConfigPath();
|
||||
var configDirZipTemp = Utils.GetTempPath($"v2rayN_{DateTime.Now:yyyyMMddHHmmss}");
|
||||
var configDirTemp = Path.Combine(configDirZipTemp, _guiConfigs);
|
||||
|
||||
await Task.Run(() => FileManager.CopyDirectory(configDir, configDirTemp, false, "cache.db"));
|
||||
var ret = await Task.Run(() => FileManager.CreateFromDirectory(configDirZipTemp, fileName));
|
||||
await Task.Run(() => Directory.Delete(configDirZipTemp, true));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ using DynamicData.Binding;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using Splat;
|
||||
using System.Diagnostics;
|
||||
using System.Reactive;
|
||||
|
||||
namespace ServiceLib.ViewModels
|
||||
@@ -11,6 +10,7 @@ namespace ServiceLib.ViewModels
|
||||
public class CheckUpdateViewModel : MyReactiveObject
|
||||
{
|
||||
private const string _geo = "GeoFiles";
|
||||
private string _v2rayN = ECoreType.v2rayN.ToString();
|
||||
private List<CheckUpdateItem> _lstUpdated = [];
|
||||
|
||||
private IObservableCollection<CheckUpdateItem> _checkUpdateItem = new ObservableCollectionExtended<CheckUpdateItem>();
|
||||
@@ -28,9 +28,13 @@ namespace ServiceLib.ViewModels
|
||||
|
||||
RefreshSubItems();
|
||||
|
||||
CheckUpdateCmd = ReactiveCommand.Create(() =>
|
||||
CheckUpdateCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
CheckUpdate();
|
||||
await CheckUpdate()
|
||||
.ContinueWith(t =>
|
||||
{
|
||||
UpdateFinished();
|
||||
});
|
||||
});
|
||||
EnableCheckPreReleaseUpdate = _config.guiItem.checkPreReleaseUpdate;
|
||||
IsCheckUpdate = true;
|
||||
@@ -48,7 +52,7 @@ namespace ServiceLib.ViewModels
|
||||
_checkUpdateItem.Add(new CheckUpdateItem()
|
||||
{
|
||||
isSelected = false,
|
||||
coreType = ECoreType.v2rayN.ToString(),
|
||||
coreType = _v2rayN,
|
||||
remarks = ResUI.menuCheckUpdate,
|
||||
});
|
||||
_checkUpdateItem.Add(new CheckUpdateItem()
|
||||
@@ -77,9 +81,11 @@ namespace ServiceLib.ViewModels
|
||||
});
|
||||
}
|
||||
|
||||
private void CheckUpdate()
|
||||
private async Task CheckUpdate()
|
||||
{
|
||||
_lstUpdated.Clear();
|
||||
_lstUpdated = _checkUpdateItem.Where(x => x.isSelected == true)
|
||||
.Select(x => new CheckUpdateItem() { coreType = x.coreType }).ToList();
|
||||
|
||||
for (int k = _checkUpdateItem.Count - 1; k >= 0; k--)
|
||||
{
|
||||
@@ -87,23 +93,22 @@ namespace ServiceLib.ViewModels
|
||||
if (item.isSelected == true)
|
||||
{
|
||||
IsCheckUpdate = false;
|
||||
_lstUpdated.Add(new CheckUpdateItem() { coreType = item.coreType });
|
||||
UpdateView(item.coreType, "...");
|
||||
if (item.coreType == _geo)
|
||||
{
|
||||
CheckUpdateGeo();
|
||||
await CheckUpdateGeo();
|
||||
}
|
||||
else if (item.coreType == ECoreType.v2rayN.ToString())
|
||||
else if (item.coreType == _v2rayN)
|
||||
{
|
||||
CheckUpdateN(EnableCheckPreReleaseUpdate);
|
||||
await CheckUpdateN(EnableCheckPreReleaseUpdate);
|
||||
}
|
||||
else if (item.coreType == ECoreType.mihomo.ToString())
|
||||
{
|
||||
CheckUpdateCore(item, false);
|
||||
await CheckUpdateCore(item, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
CheckUpdateCore(item, EnableCheckPreReleaseUpdate);
|
||||
await CheckUpdateCore(item, EnableCheckPreReleaseUpdate);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,7 +128,7 @@ namespace ServiceLib.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckUpdateGeo()
|
||||
private async Task CheckUpdateGeo()
|
||||
{
|
||||
void _updateUI(bool success, string msg)
|
||||
{
|
||||
@@ -131,74 +136,61 @@ namespace ServiceLib.ViewModels
|
||||
if (success)
|
||||
{
|
||||
UpdatedPlusPlus(_geo, "");
|
||||
UpdateFinished();
|
||||
}
|
||||
}
|
||||
(new UpdateHandler()).UpdateGeoFileAll(_config, _updateUI);
|
||||
await (new UpdateHandler()).UpdateGeoFileAll(_config, _updateUI)
|
||||
.ContinueWith(t =>
|
||||
{
|
||||
UpdatedPlusPlus(_geo, "");
|
||||
});
|
||||
}
|
||||
|
||||
private void CheckUpdateN(bool preRelease)
|
||||
private async Task CheckUpdateN(bool preRelease)
|
||||
{
|
||||
//Check for standalone windows .Net version
|
||||
if (Utils.IsWindows()
|
||||
&& File.Exists(Path.Combine(Utils.StartupPath(), "wpfgfx_cor3.dll"))
|
||||
&& File.Exists(Path.Combine(Utils.StartupPath(), "D3DCompiler_47_cor3.dll"))
|
||||
)
|
||||
{
|
||||
UpdateView(ResUI.UpdateStandalonePackageTip, ResUI.UpdateStandalonePackageTip);
|
||||
return;
|
||||
}
|
||||
////Check for standalone windows .Net version
|
||||
//if (Utils.IsWindows()
|
||||
// && File.Exists(Path.Combine(Utils.StartupPath(), "wpfgfx_cor3.dll"))
|
||||
// && File.Exists(Path.Combine(Utils.StartupPath(), "D3DCompiler_47_cor3.dll"))
|
||||
// )
|
||||
//{
|
||||
// UpdateView(_v2rayN, ResUI.UpdateStandalonePackageTip);
|
||||
// return;
|
||||
//}
|
||||
|
||||
void _updateUI(bool success, string msg)
|
||||
{
|
||||
UpdateView(_v2rayN, msg);
|
||||
if (success)
|
||||
{
|
||||
UpdateView(ECoreType.v2rayN.ToString(), ResUI.OperationSuccess);
|
||||
UpdatedPlusPlus(ECoreType.v2rayN.ToString(), msg);
|
||||
UpdateFinished();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (msg.IsNullOrEmpty())
|
||||
{
|
||||
UpdatedPlusPlus(ECoreType.v2rayN.ToString(), "");
|
||||
UpdateFinished();
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateView(ECoreType.v2rayN.ToString(), msg);
|
||||
}
|
||||
UpdateView(_v2rayN, ResUI.OperationSuccess);
|
||||
UpdatedPlusPlus(_v2rayN, msg);
|
||||
}
|
||||
}
|
||||
(new UpdateHandler()).CheckUpdateGuiN(_config, _updateUI, preRelease);
|
||||
await (new UpdateHandler()).CheckUpdateGuiN(_config, _updateUI, preRelease)
|
||||
.ContinueWith(t =>
|
||||
{
|
||||
UpdatedPlusPlus(_v2rayN, "");
|
||||
});
|
||||
}
|
||||
|
||||
private void CheckUpdateCore(CheckUpdateItem item, bool preRelease)
|
||||
private async Task CheckUpdateCore(CheckUpdateItem item, bool preRelease)
|
||||
{
|
||||
void _updateUI(bool success, string msg)
|
||||
{
|
||||
UpdateView(item.coreType, msg);
|
||||
if (success)
|
||||
{
|
||||
UpdateView(item.coreType, ResUI.MsgUpdateV2rayCoreSuccessfullyMore);
|
||||
|
||||
UpdatedPlusPlus(item.coreType, msg);
|
||||
UpdateFinished();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (msg.IsNullOrEmpty())
|
||||
{
|
||||
UpdatedPlusPlus(item.coreType, "");
|
||||
UpdateFinished();
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateView(item.coreType, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
var type = (ECoreType)Enum.Parse(typeof(ECoreType), item.coreType);
|
||||
(new UpdateHandler()).CheckUpdateCore(type, _config, _updateUI, preRelease);
|
||||
await (new UpdateHandler()).CheckUpdateCore(type, _config, _updateUI, preRelease)
|
||||
.ContinueWith(t =>
|
||||
{
|
||||
UpdatedPlusPlus(item.coreType, "");
|
||||
});
|
||||
}
|
||||
|
||||
private void UpdateFinished()
|
||||
@@ -206,13 +198,15 @@ namespace ServiceLib.ViewModels
|
||||
if (_lstUpdated.Count > 0 && _lstUpdated.Count(x => x.isFinished == true) == _lstUpdated.Count)
|
||||
{
|
||||
_updateView?.Invoke(EViewAction.DispatcherCheckUpdateFinished, false);
|
||||
|
||||
Task.Delay(1000);
|
||||
UpgradeCore();
|
||||
|
||||
if (_lstUpdated.Any(x => x.coreType == ECoreType.v2rayN.ToString() && x.isFinished == true))
|
||||
if (_lstUpdated.Any(x => x.coreType == _v2rayN && x.isFinished == true))
|
||||
{
|
||||
Task.Delay(1000);
|
||||
UpgradeN();
|
||||
}
|
||||
Task.Delay(1000);
|
||||
_updateView?.Invoke(EViewAction.DispatcherCheckUpdateFinished, true);
|
||||
}
|
||||
}
|
||||
@@ -234,30 +228,16 @@ namespace ServiceLib.ViewModels
|
||||
{
|
||||
try
|
||||
{
|
||||
var fileName = _lstUpdated.FirstOrDefault(x => x.coreType == ECoreType.v2rayN.ToString())?.fileName;
|
||||
var fileName = _lstUpdated.FirstOrDefault(x => x.coreType == _v2rayN)?.fileName;
|
||||
if (fileName.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Process process = new()
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "v2rayUpgrade",
|
||||
Arguments = fileName.AppendQuotes(),
|
||||
WorkingDirectory = Utils.StartupPath()
|
||||
}
|
||||
};
|
||||
process.Start();
|
||||
if (process.Id > 0)
|
||||
{
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.MyAppExitAsync(false);
|
||||
}
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.V2rayUpgrade(fileName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UpdateView(ECoreType.v2rayN.ToString(), ex.Message);
|
||||
UpdateView(_v2rayN, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,7 +250,7 @@ namespace ServiceLib.ViewModels
|
||||
continue;
|
||||
}
|
||||
|
||||
var fileName = Utils.GetTempPath(Utils.GetDownloadFileName(item.fileName));
|
||||
var fileName = item.fileName;
|
||||
if (!File.Exists(fileName))
|
||||
{
|
||||
continue;
|
||||
|
||||
@@ -19,6 +19,9 @@ namespace ServiceLib.ViewModels
|
||||
public ReactiveCommand<Unit, Unit> ConnectionCloseCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> ConnectionCloseAllCmd { get; }
|
||||
|
||||
[Reactive]
|
||||
public string HostFilter { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public int SortingSelected { get; set; }
|
||||
|
||||
@@ -34,7 +37,7 @@ namespace ServiceLib.ViewModels
|
||||
|
||||
var canEditRemove = this.WhenAnyValue(
|
||||
x => x.SelectedSource,
|
||||
selectedSource => selectedSource != null && !string.IsNullOrEmpty(selectedSource.id));
|
||||
selectedSource => selectedSource != null && Utils.IsNotEmpty(selectedSource.id));
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.SortingSelected,
|
||||
@@ -77,7 +80,7 @@ namespace ServiceLib.ViewModels
|
||||
{
|
||||
var lastTime = DateTime.Now;
|
||||
|
||||
Observable.Interval(TimeSpan.FromSeconds(10))
|
||||
Observable.Interval(TimeSpan.FromSeconds(5))
|
||||
.Subscribe(x =>
|
||||
{
|
||||
if (!(AutoRefresh && _config.uiItem.showInTaskbar && _config.IsRunningCore(ECoreType.clash)))
|
||||
@@ -118,12 +121,18 @@ namespace ServiceLib.ViewModels
|
||||
var lstModel = new List<ClashConnectionModel>();
|
||||
foreach (var item in connections ?? [])
|
||||
{
|
||||
var host = $"{(Utils.IsNullOrEmpty(item.metadata.host) ? item.metadata.destinationIP : item.metadata.host)}:{item.metadata.destinationPort}";
|
||||
if (HostFilter.IsNotEmpty() && !host.Contains(HostFilter))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ClashConnectionModel model = new();
|
||||
|
||||
model.id = item.id;
|
||||
model.network = item.metadata.network;
|
||||
model.type = item.metadata.type;
|
||||
model.host = $"{(string.IsNullOrEmpty(item.metadata.host) ? item.metadata.destinationIP : item.metadata.host)}:{item.metadata.destinationPort}";
|
||||
model.host = host;
|
||||
var sp = (dtNow - item.start);
|
||||
model.time = sp.TotalSeconds < 0 ? 1 : sp.TotalSeconds;
|
||||
model.upload = item.upload;
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace ServiceLib.ViewModels
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.SelectedGroup,
|
||||
y => y != null && !string.IsNullOrEmpty(y.name))
|
||||
y => y != null && Utils.IsNotEmpty(y.name))
|
||||
.Subscribe(c => RefreshProxyDetails(c));
|
||||
|
||||
this.WhenAnyValue(
|
||||
@@ -135,7 +135,7 @@ namespace ServiceLib.ViewModels
|
||||
|
||||
private void UpdateHandler(bool notify, string msg)
|
||||
{
|
||||
_noticeHandler?.SendMessage(msg, true);
|
||||
_noticeHandler?.SendMessageEx(msg);
|
||||
}
|
||||
|
||||
public void ProxiesReload()
|
||||
@@ -194,7 +194,7 @@ namespace ServiceLib.ViewModels
|
||||
{
|
||||
foreach (var it in proxyGroups)
|
||||
{
|
||||
if (string.IsNullOrEmpty(it.name) || !_proxies.ContainsKey(it.name))
|
||||
if (Utils.IsNullOrEmpty(it.name) || !_proxies.ContainsKey(it.name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -220,7 +220,7 @@ namespace ServiceLib.ViewModels
|
||||
continue;
|
||||
}
|
||||
var item = _proxyGroups.Where(t => t.name == kv.Key).FirstOrDefault();
|
||||
if (item != null && !string.IsNullOrEmpty(item.name))
|
||||
if (item != null && Utils.IsNotEmpty(item.name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -257,7 +257,7 @@ namespace ServiceLib.ViewModels
|
||||
return;
|
||||
}
|
||||
var name = SelectedGroup?.name;
|
||||
if (string.IsNullOrEmpty(name))
|
||||
if (Utils.IsNullOrEmpty(name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -342,21 +342,21 @@ namespace ServiceLib.ViewModels
|
||||
|
||||
public void SetActiveProxy()
|
||||
{
|
||||
if (SelectedGroup == null || string.IsNullOrEmpty(SelectedGroup.name))
|
||||
if (SelectedGroup == null || Utils.IsNullOrEmpty(SelectedGroup.name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (SelectedDetail == null || string.IsNullOrEmpty(SelectedDetail.name))
|
||||
if (SelectedDetail == null || Utils.IsNullOrEmpty(SelectedDetail.name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var name = SelectedGroup.name;
|
||||
if (string.IsNullOrEmpty(name))
|
||||
if (Utils.IsNullOrEmpty(name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var nameNode = SelectedDetail.name;
|
||||
if (string.IsNullOrEmpty(nameNode))
|
||||
if (Utils.IsNullOrEmpty(nameNode))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -393,7 +393,7 @@ namespace ServiceLib.ViewModels
|
||||
GetClashProxies(true);
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrEmpty(result))
|
||||
if (Utils.IsNullOrEmpty(result))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace ServiceLib.ViewModels
|
||||
|
||||
private async Task SaveSettingAsync()
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(normalDNS))
|
||||
if (Utils.IsNotEmpty(normalDNS))
|
||||
{
|
||||
var obj = JsonUtils.ParseJson(normalDNS);
|
||||
if (obj != null && obj["servers"] != null)
|
||||
@@ -73,7 +73,7 @@ namespace ServiceLib.ViewModels
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(normalDNS2))
|
||||
if (Utils.IsNotEmpty(normalDNS2))
|
||||
{
|
||||
var obj2 = JsonUtils.Deserialize<Dns4Sbox>(normalDNS2);
|
||||
if (obj2 == null)
|
||||
@@ -82,7 +82,7 @@ namespace ServiceLib.ViewModels
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(tunDNS2))
|
||||
if (Utils.IsNotEmpty(tunDNS2))
|
||||
{
|
||||
var obj2 = JsonUtils.Deserialize<Dns4Sbox>(tunDNS2);
|
||||
if (obj2 == null)
|
||||
|
||||
@@ -71,13 +71,6 @@ namespace ServiceLib.ViewModels
|
||||
public ReactiveCommand<Unit, Unit> ClearServerStatisticsCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> OpenTheFileLocationCmd { get; }
|
||||
|
||||
//CheckUpdate
|
||||
public ReactiveCommand<Unit, Unit> CheckUpdateNCmd { get; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> CheckUpdateXrayCoreCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> CheckUpdateClashMetaCoreCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> CheckUpdateSingBoxCoreCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> CheckUpdateGeoCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> ReloadCmd { get; }
|
||||
|
||||
[Reactive]
|
||||
@@ -295,28 +288,6 @@ namespace ServiceLib.ViewModels
|
||||
Utils.ProcessStart("Explorer", $"/select,{Utils.GetConfigPath()}");
|
||||
});
|
||||
|
||||
//CheckUpdate
|
||||
CheckUpdateNCmd = ReactiveCommand.Create(() =>
|
||||
{
|
||||
CheckUpdateN();
|
||||
});
|
||||
CheckUpdateXrayCoreCmd = ReactiveCommand.Create(() =>
|
||||
{
|
||||
CheckUpdateCore(ECoreType.Xray, null);
|
||||
});
|
||||
CheckUpdateClashMetaCoreCmd = ReactiveCommand.Create(() =>
|
||||
{
|
||||
CheckUpdateCore(ECoreType.mihomo, false);
|
||||
});
|
||||
CheckUpdateSingBoxCoreCmd = ReactiveCommand.Create(() =>
|
||||
{
|
||||
CheckUpdateCore(ECoreType.sing_box, null);
|
||||
});
|
||||
CheckUpdateGeoCmd = ReactiveCommand.Create(() =>
|
||||
{
|
||||
CheckUpdateGeo();
|
||||
});
|
||||
|
||||
ReloadCmd = ReactiveCommand.Create(() =>
|
||||
{
|
||||
Reload();
|
||||
@@ -378,10 +349,6 @@ namespace ServiceLib.ViewModels
|
||||
|
||||
private void UpdateHandler(bool notify, string msg)
|
||||
{
|
||||
if (!_config.uiItem.showInTaskbar)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_noticeHandler?.SendMessage(msg);
|
||||
if (notify)
|
||||
{
|
||||
@@ -391,7 +358,7 @@ namespace ServiceLib.ViewModels
|
||||
|
||||
private void UpdateTaskHandler(bool success, string msg)
|
||||
{
|
||||
_noticeHandler?.SendMessage(msg);
|
||||
_noticeHandler?.SendMessageEx(msg);
|
||||
if (success)
|
||||
{
|
||||
var indexIdOld = _config.indexId;
|
||||
@@ -457,6 +424,24 @@ namespace ServiceLib.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public async Task V2rayUpgrade(string fileName)
|
||||
{
|
||||
Process process = new()
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "v2rayUpgrade",
|
||||
Arguments = fileName.AppendQuotes(),
|
||||
WorkingDirectory = Utils.StartupPath()
|
||||
}
|
||||
};
|
||||
process.Start();
|
||||
if (process.Id > 0)
|
||||
{
|
||||
await MyAppExitAsync(false);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Actions
|
||||
|
||||
#region Servers && Groups
|
||||
@@ -622,21 +607,16 @@ namespace ServiceLib.ViewModels
|
||||
SetDefaultServer(SelectedServer.ID);
|
||||
}
|
||||
|
||||
public void TestServerAvailability()
|
||||
public async Task TestServerAvailability()
|
||||
{
|
||||
var item = ConfigHandler.GetDefaultServer(_config);
|
||||
if (item == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
(new UpdateHandler()).RunAvailabilityCheck(async (bool success, string msg) =>
|
||||
await (new UpdateHandler()).RunAvailabilityCheck(async (bool success, string msg) =>
|
||||
{
|
||||
_noticeHandler?.SendMessage(msg, true);
|
||||
|
||||
if (!_config.uiItem.showInTaskbar)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_noticeHandler?.SendMessageEx(msg);
|
||||
await _updateView?.Invoke(EViewAction.DispatcherServerAvailability, msg);
|
||||
});
|
||||
}
|
||||
@@ -718,99 +698,6 @@ namespace ServiceLib.ViewModels
|
||||
|
||||
#endregion Setting
|
||||
|
||||
#region CheckUpdate
|
||||
|
||||
private void CheckUpdateN()
|
||||
{
|
||||
//Check for standalone windows .Net version
|
||||
if (Utils.IsWindows()
|
||||
&& File.Exists(Path.Combine(Utils.StartupPath(), "wpfgfx_cor3.dll"))
|
||||
&& File.Exists(Path.Combine(Utils.StartupPath(), "D3DCompiler_47_cor3.dll"))
|
||||
)
|
||||
{
|
||||
_noticeHandler?.SendMessageAndEnqueue(ResUI.UpdateStandalonePackageTip);
|
||||
return;
|
||||
}
|
||||
|
||||
void _updateUI(bool success, string msg)
|
||||
{
|
||||
_noticeHandler?.SendMessage(msg);
|
||||
if (success)
|
||||
{
|
||||
try
|
||||
{
|
||||
var fileName = msg;
|
||||
Process process = new()
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "v2rayUpgrade",
|
||||
Arguments = fileName.AppendQuotes(),
|
||||
WorkingDirectory = Utils.StartupPath()
|
||||
}
|
||||
};
|
||||
process.Start();
|
||||
if (process.Id > 0)
|
||||
{
|
||||
MyAppExitAsync(false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_noticeHandler?.SendMessage(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
(new UpdateHandler()).CheckUpdateGuiN(_config, _updateUI, _config.guiItem.checkPreReleaseUpdate);
|
||||
}
|
||||
|
||||
private void CheckUpdateCore(ECoreType type, bool? preRelease)
|
||||
{
|
||||
void _updateUI(bool success, string msg)
|
||||
{
|
||||
_noticeHandler?.SendMessage(msg);
|
||||
if (success)
|
||||
{
|
||||
CloseCore();
|
||||
|
||||
string fileName = Utils.GetTempPath(Utils.GetDownloadFileName(msg));
|
||||
string toPath = Utils.GetBinPath("", type.ToString());
|
||||
|
||||
if (fileName.Contains(".tar.gz"))
|
||||
{
|
||||
//It's too complicated to unzip. TODO
|
||||
}
|
||||
else if (fileName.Contains(".gz"))
|
||||
{
|
||||
FileManager.UncompressedFile(fileName, toPath, type.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
FileManager.ZipExtractToFile(fileName, toPath, _config.guiItem.ignoreGeoUpdateCore ? "geo" : "");
|
||||
}
|
||||
|
||||
_noticeHandler?.SendMessage(ResUI.MsgUpdateV2rayCoreSuccessfullyMore);
|
||||
|
||||
Reload();
|
||||
|
||||
_noticeHandler?.SendMessage(ResUI.MsgUpdateV2rayCoreSuccessfully);
|
||||
|
||||
if (File.Exists(fileName))
|
||||
{
|
||||
File.Delete(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
(new UpdateHandler()).CheckUpdateCore(type, _config, _updateUI, preRelease ?? _config.guiItem.checkPreReleaseUpdate);
|
||||
}
|
||||
|
||||
private void CheckUpdateGeo()
|
||||
{
|
||||
(new UpdateHandler()).UpdateGeoFileAll(_config, UpdateTaskHandler);
|
||||
}
|
||||
|
||||
#endregion CheckUpdate
|
||||
|
||||
#region core job
|
||||
|
||||
public void Reload()
|
||||
@@ -819,7 +706,7 @@ namespace ServiceLib.ViewModels
|
||||
|
||||
LoadCore().ContinueWith(async task =>
|
||||
{
|
||||
TestServerAvailability();
|
||||
await TestServerAvailability();
|
||||
|
||||
await _updateView?.Invoke(EViewAction.DispatcherReload, null);
|
||||
});
|
||||
@@ -881,7 +768,7 @@ namespace ServiceLib.ViewModels
|
||||
private async Task ChangeSystemProxyStatusAsync(ESysProxyType type, bool blChange)
|
||||
{
|
||||
await _updateView?.Invoke(EViewAction.UpdateSysProxy, _config.tunModeItem.enableTun ? true : false);
|
||||
_noticeHandler?.SendMessage($"{ResUI.TipChangeSystemProxy} - {_config.systemProxyItem.sysProxyType.ToString()}", true);
|
||||
_noticeHandler?.SendMessageEx($"{ResUI.TipChangeSystemProxy} - {_config.systemProxyItem.sysProxyType.ToString()}");
|
||||
|
||||
BlSystemProxyClear = (type == ESysProxyType.ForcedClear);
|
||||
BlSystemProxySet = (type == ESysProxyType.ForcedChange);
|
||||
@@ -941,7 +828,7 @@ namespace ServiceLib.ViewModels
|
||||
|
||||
if (ConfigHandler.SetDefaultRouting(_config, item) == 0)
|
||||
{
|
||||
_noticeHandler?.SendMessage(ResUI.TipChangeRouting, true);
|
||||
_noticeHandler?.SendMessageEx(ResUI.TipChangeRouting);
|
||||
Reload();
|
||||
await _updateView?.Invoke(EViewAction.DispatcherRefreshIcon, null);
|
||||
}
|
||||
|
||||
115
v2rayN/ServiceLib/ViewModels/MsgViewModel.cs
Normal file
115
v2rayN/ServiceLib/ViewModels/MsgViewModel.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using Splat;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ServiceLib.ViewModels
|
||||
{
|
||||
public class MsgViewModel : MyReactiveObject
|
||||
{
|
||||
private ConcurrentQueue<string> _queueMsg = new();
|
||||
private int _numMaxMsg = 500;
|
||||
private string _lastMsgFilter = string.Empty;
|
||||
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)
|
||||
{
|
||||
_config = LazyConfig.Instance.Config;
|
||||
_updateView = updateView;
|
||||
_noticeHandler = Locator.Current.GetService<NoticeHandler>();
|
||||
|
||||
MessageBus.Current.Listen<string>(Global.CommandSendMsgView).Subscribe(async x => await AppendQueueMsg(x));
|
||||
|
||||
MsgFilter = _config.msgUIItem.mainMsgFilter ?? string.Empty;
|
||||
AutoRefresh = _config.msgUIItem.autoRefresh ?? true;
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.MsgFilter)
|
||||
.Subscribe(c => _config.msgUIItem.mainMsgFilter = MsgFilter);
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.AutoRefresh,
|
||||
y => y == true)
|
||||
.Subscribe(c => { _config.msgUIItem.autoRefresh = AutoRefresh; });
|
||||
}
|
||||
|
||||
private async Task AppendQueueMsg(string msg)
|
||||
{
|
||||
//if (msg == Global.CommandClearMsg)
|
||||
//{
|
||||
// ClearMsg();
|
||||
// return;
|
||||
//}
|
||||
if (AutoRefresh == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_ = EnqueueQueueMsg(msg);
|
||||
|
||||
if (_blLockShow)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_blLockShow = true;
|
||||
if (!_config.uiItem.showInTaskbar)
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
|
||||
await Task.Delay(100);
|
||||
var txt = string.Join("", _queueMsg.ToArray());
|
||||
await _updateView?.Invoke(EViewAction.DispatcherShowMsg, txt);
|
||||
|
||||
_blLockShow = false;
|
||||
}
|
||||
|
||||
private async Task EnqueueQueueMsg(string msg)
|
||||
{
|
||||
//filter msg
|
||||
if (MsgFilter != _lastMsgFilter) _lastMsgFilterNotAvailable = false;
|
||||
if (Utils.IsNotEmpty(MsgFilter) && !_lastMsgFilterNotAvailable)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Regex.IsMatch(msg, MsgFilter))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_lastMsgFilterNotAvailable = true;
|
||||
}
|
||||
}
|
||||
_lastMsgFilter = MsgFilter;
|
||||
|
||||
//Enqueue
|
||||
if (_queueMsg.Count > _numMaxMsg)
|
||||
{
|
||||
for (int k = 0; k < _queueMsg.Count - _numMaxMsg; k++)
|
||||
{
|
||||
_queueMsg.TryDequeue(out _);
|
||||
}
|
||||
}
|
||||
_queueMsg.Enqueue(msg);
|
||||
if (!msg.EndsWith(Environment.NewLine))
|
||||
{
|
||||
_queueMsg.Enqueue(Environment.NewLine);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearMsg()
|
||||
{
|
||||
_queueMsg.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -258,20 +258,20 @@ namespace ServiceLib.ViewModels
|
||||
{
|
||||
if (Utils.IsNullOrEmpty(result.IndexId))
|
||||
{
|
||||
_noticeHandler?.SendMessage(result.Delay, true);
|
||||
_noticeHandler?.SendMessageEx(result.Delay);
|
||||
_noticeHandler?.Enqueue(result.Delay);
|
||||
return;
|
||||
}
|
||||
var item = _profileItems.Where(it => it.indexId == result.IndexId).FirstOrDefault();
|
||||
if (item != null)
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(result.Delay))
|
||||
if (Utils.IsNotEmpty(result.Delay))
|
||||
{
|
||||
int.TryParse(result.Delay, out int temp);
|
||||
item.delay = temp;
|
||||
item.delayVal = $"{result.Delay} {Global.DelayUnit}";
|
||||
}
|
||||
if (!Utils.IsNullOrEmpty(result.Speed))
|
||||
if (Utils.IsNotEmpty(result.Speed))
|
||||
{
|
||||
item.speedVal = $"{result.Speed} {Global.SpeedUnit}";
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace ServiceLib.ViewModels
|
||||
|| SelectedSource.ip?.Count > 0
|
||||
|| SelectedSource.protocol?.Count > 0
|
||||
|| SelectedSource.process?.Count > 0
|
||||
|| !Utils.IsNullOrEmpty(SelectedSource.port);
|
||||
|| Utils.IsNotEmpty(SelectedSource.port);
|
||||
|
||||
if (!hasRule)
|
||||
{
|
||||
|
||||
@@ -13,7 +13,10 @@
|
||||
|
||||
<TrayIcon.Icons>
|
||||
<TrayIcons>
|
||||
<TrayIcon Clicked="TrayIcon_Clicked" Icon="/Assets/NotifyIcon1.ico">
|
||||
<TrayIcon
|
||||
Clicked="TrayIcon_Clicked"
|
||||
Icon="/Assets/NotifyIcon1.ico"
|
||||
ToolTipText="v2rayN Desktop">
|
||||
<TrayIcon.Menu>
|
||||
<NativeMenu>
|
||||
<NativeMenuItem Click="MenuAddServerViaClipboardClick" Header="{x:Static resx:ResUI.menuAddServerViaClipboard}" />
|
||||
|
||||
@@ -5,22 +5,6 @@
|
||||
</Border>
|
||||
</Design.PreviewWith>
|
||||
|
||||
<Style Selector="TextBlock.Margin4">
|
||||
<Setter Property="Margin" Value="8" />
|
||||
</Style>
|
||||
<Style Selector="StackPanel.Margin4">
|
||||
<Setter Property="Margin" Value="8" />
|
||||
</Style>
|
||||
<Style Selector="DockPanel.Margin4">
|
||||
<Setter Property="Margin" Value="8" />
|
||||
</Style>
|
||||
<Style Selector="WrapPanel.Margin4">
|
||||
<Setter Property="Margin" Value="8" />
|
||||
</Style>
|
||||
<Style Selector="Grid.Margin4">
|
||||
<Setter Property="Margin" Value="8" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="TextBlock.Margin8">
|
||||
<Setter Property="Margin" Value="8" />
|
||||
</Style>
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace v2rayN.Desktop.ViewModels
|
||||
y => y != null && !y.IsNullOrEmpty())
|
||||
.Subscribe(c =>
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(CurrentLanguage) && _config.uiItem.currentLanguage != CurrentLanguage)
|
||||
if (Utils.IsNotEmpty(CurrentLanguage) && _config.uiItem.currentLanguage != CurrentLanguage)
|
||||
{
|
||||
_config.uiItem.currentLanguage = CurrentLanguage;
|
||||
Thread.CurrentThread.CurrentUICulture = new(CurrentLanguage);
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<DockPanel Classes="Margin8">
|
||||
<StackPanel
|
||||
HorizontalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
DockPanel.Dock="Bottom"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
@@ -57,14 +57,14 @@
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.menuServers}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.TbRemarks}" />
|
||||
|
||||
<TextBox
|
||||
@@ -74,13 +74,13 @@
|
||||
Width="400"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4" />
|
||||
Classes="Margin8" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.TbAddress}" />
|
||||
<TextBox
|
||||
x:Name="txtAddress"
|
||||
@@ -89,7 +89,7 @@
|
||||
Width="400"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
IsReadOnly="True" />
|
||||
<StackPanel
|
||||
Grid.Row="2"
|
||||
@@ -110,7 +110,7 @@
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.TbCoreType}" />
|
||||
<ComboBox
|
||||
x:Name="cmbCoreType"
|
||||
@@ -118,24 +118,24 @@
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
HorizontalAlignment="Left"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
MaxDropDownHeight="1000" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.TbDisplayLog}" />
|
||||
<StackPanel
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Orientation="Horizontal">
|
||||
<ToggleSwitch
|
||||
x:Name="togDisplayLog"
|
||||
HorizontalAlignment="Left"
|
||||
Classes="Margin4" />
|
||||
Classes="Margin8" />
|
||||
<TextBlock
|
||||
Margin="8,0"
|
||||
VerticalAlignment="Center"
|
||||
@@ -146,7 +146,7 @@
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.TbPreSocksPort}" />
|
||||
<TextBox
|
||||
x:Name="txtPreSocksPort"
|
||||
@@ -154,12 +154,12 @@
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
HorizontalAlignment="Left"
|
||||
Classes="Margin4" />
|
||||
Classes="Margin8" />
|
||||
<StackPanel
|
||||
Grid.Row="6"
|
||||
Grid.Column="1"
|
||||
Grid.ColumnSpan="2"
|
||||
Classes="Margin4">
|
||||
Classes="Margin8">
|
||||
<TextBlock
|
||||
Width="500"
|
||||
VerticalAlignment="Center"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
96
v2rayN/v2rayN.Desktop/Views/CheckUpdateView.axaml
Normal file
96
v2rayN/v2rayN.Desktop/Views/CheckUpdateView.axaml
Normal file
@@ -0,0 +1,96 @@
|
||||
<UserControl
|
||||
x:Class="v2rayN.Desktop.Views.CheckUpdateView"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
x:DataType="vms:CheckUpdateViewModel"
|
||||
mc:Ignorable="d">
|
||||
<Button Classes="Tertiary">
|
||||
<Button.Content>
|
||||
<TextBlock Text="{x:Static resx:ResUI.menuCheckUpdate}" />
|
||||
</Button.Content>
|
||||
<Button.Flyout>
|
||||
<Flyout Placement="RightEdgeAlignedTop">
|
||||
<DockPanel Margin="16">
|
||||
<StackPanel
|
||||
HorizontalAlignment="Right"
|
||||
Classes="Margin8"
|
||||
DockPanel.Dock="Bottom"
|
||||
Orientation="Horizontal">
|
||||
|
||||
<TextBlock
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.TbSettingsEnableCheckPreReleaseUpdate}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togEnableCheckPreReleaseUpdate"
|
||||
HorizontalAlignment="Left"
|
||||
Classes="Margin8" />
|
||||
|
||||
<Button
|
||||
x:Name="btnCheckUpdate"
|
||||
Width="100"
|
||||
Classes="Margin8"
|
||||
Content="{x:Static resx:ResUI.menuCheckUpdate}" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel>
|
||||
<ListBox
|
||||
x:Name="lstCheckUpdates"
|
||||
BorderThickness="1"
|
||||
ItemsSource="{Binding CheckUpdateItems}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Vertical" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border
|
||||
Width="500"
|
||||
Height="80"
|
||||
Margin="0"
|
||||
VerticalAlignment="Center"
|
||||
Theme="{StaticResource CardBorder}">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*" />
|
||||
<ColumnDefinition Width="1*" />
|
||||
<ColumnDefinition Width="3*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ToggleSwitch
|
||||
x:Name="togAutoRefresh"
|
||||
Grid.Column="0"
|
||||
Margin="8"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
IsChecked="{Binding isSelected}" />
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding coreType}" />
|
||||
<TextBlock
|
||||
Grid.Column="2"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding remarks}"
|
||||
TextWrapping="WrapWithOverflow" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ListBox>
|
||||
</StackPanel>
|
||||
</DockPanel>
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
</UserControl>
|
||||
48
v2rayN/v2rayN.Desktop/Views/CheckUpdateView.axaml.cs
Normal file
48
v2rayN/v2rayN.Desktop/Views/CheckUpdateView.axaml.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using Avalonia.ReactiveUI;
|
||||
using Avalonia.Threading;
|
||||
using ReactiveUI;
|
||||
using System.Reactive.Disposables;
|
||||
|
||||
namespace v2rayN.Desktop.Views
|
||||
{
|
||||
public partial class CheckUpdateView : ReactiveUserControl<CheckUpdateViewModel>
|
||||
{
|
||||
public CheckUpdateView()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
ViewModel = new CheckUpdateViewModel(UpdateViewHandler);
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
this.OneWayBind(ViewModel, vm => vm.CheckUpdateItems, v => v.lstCheckUpdates.ItemsSource).DisposeWith(disposables);
|
||||
|
||||
this.Bind(ViewModel, vm => vm.EnableCheckPreReleaseUpdate, v => v.togEnableCheckPreReleaseUpdate.IsChecked).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.CheckUpdateCmd, v => v.btnCheckUpdate).DisposeWith(disposables);
|
||||
this.OneWayBind(ViewModel, vm => vm.IsCheckUpdate, v => v.btnCheckUpdate.IsEnabled).DisposeWith(disposables);
|
||||
});
|
||||
}
|
||||
|
||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case EViewAction.DispatcherCheckUpdate:
|
||||
if (obj is null) return false;
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
ViewModel?.UpdateViewResult((CheckUpdateItem)obj),
|
||||
DispatcherPriority.Default);
|
||||
break;
|
||||
|
||||
case EViewAction.DispatcherCheckUpdateFinished:
|
||||
if (obj is null) return false;
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
ViewModel?.UpdateFinishedResult((bool)obj),
|
||||
DispatcherPriority.Default);
|
||||
break;
|
||||
}
|
||||
|
||||
return await Task.FromResult(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,13 @@
|
||||
DockPanel.Dock="Top"
|
||||
Orientation="Horizontal">
|
||||
|
||||
<TextBox
|
||||
x:Name="txtHostFilter"
|
||||
Width="200"
|
||||
Margin="8,0"
|
||||
VerticalContentAlignment="Center"
|
||||
Watermark="{x:Static resx:ResUI.ConnectionsHostFilterTitle}" />
|
||||
|
||||
<TextBlock
|
||||
Margin="8,0"
|
||||
VerticalAlignment="Center"
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace v2rayN.Desktop.Views
|
||||
this.BindCommand(ViewModel, vm => vm.ConnectionCloseCmd, v => v.menuConnectionClose).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.menuConnectionCloseAll).DisposeWith(disposables);
|
||||
|
||||
this.Bind(ViewModel, vm => vm.HostFilter, v => v.txtHostFilter.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SortingSelected, v => v.cmbSorting.SelectedIndex).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.btnConnectionCloseAll).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables);
|
||||
|
||||
@@ -92,7 +92,6 @@
|
||||
<DockPanel>
|
||||
<ListBox
|
||||
x:Name="lstProxyGroups"
|
||||
Margin="0,0,5,0"
|
||||
DockPanel.Dock="Left"
|
||||
ItemsSource="{Binding ProxyGroups}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
@@ -104,20 +103,21 @@
|
||||
<DataTemplate>
|
||||
<Border
|
||||
Width="160"
|
||||
Margin="4,0"
|
||||
Margin="0"
|
||||
Padding="0"
|
||||
Theme="{StaticResource CardBorder}">
|
||||
<DockPanel>
|
||||
<Grid Grid.Column="0" Classes="Margin4">
|
||||
<Grid Grid.Column="0" Classes="Margin8">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="1*" />
|
||||
<RowDefinition Height="8" />
|
||||
<RowDefinition Height="1*" />
|
||||
</Grid.RowDefinitions>
|
||||
<DockPanel Grid.Row="0">
|
||||
<TextBlock DockPanel.Dock="Right" Text="{Binding type}" />
|
||||
<TextBlock Text="{Binding name}" />
|
||||
</DockPanel>
|
||||
<TextBlock Grid.Row="1" Text="{Binding now}" />
|
||||
<TextBlock Grid.Row="2" Text="{Binding now}" />
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
@@ -141,6 +141,7 @@
|
||||
<DataTemplate>
|
||||
<Border
|
||||
Width="160"
|
||||
Margin="0"
|
||||
Padding="0"
|
||||
Theme="{StaticResource CardBorder}">
|
||||
<DockPanel>
|
||||
@@ -152,16 +153,17 @@
|
||||
CornerRadius="4"
|
||||
DockPanel.Dock="Left"
|
||||
IsVisible="{Binding isActive}" />
|
||||
<Grid Classes="Margin4">
|
||||
<Grid Classes="Margin8">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="2*" />
|
||||
<RowDefinition Height="1*" />
|
||||
<RowDefinition Height="8" />
|
||||
<RowDefinition Height="1*" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Text="{Binding name}"
|
||||
TextWrapping="WrapWithOverflow" />
|
||||
<DockPanel Grid.Row="1">
|
||||
<DockPanel Grid.Row="2">
|
||||
<TextBlock
|
||||
DockPanel.Dock="Right"
|
||||
Foreground="{Binding Path=delay, Converter={StaticResource DelayColorConverter}}"
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<DockPanel Classes="Margin8">
|
||||
<StackPanel
|
||||
HorizontalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
DockPanel.Dock="Bottom"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
@@ -43,9 +43,9 @@
|
||||
Text="{x:Static resx:ResUI.TbSettingsRemoteDNS}" />
|
||||
|
||||
<TextBlock VerticalAlignment="Center" Classes="Margin8">
|
||||
<Button Click="linkDnsObjectDoc_Click">
|
||||
<HyperlinkButton Classes="WithIcon" Click="linkDnsObjectDoc_Click">
|
||||
<TextBlock Text="{x:Static resx:ResUI.TbDnsObjectDoc}" />
|
||||
</Button>
|
||||
</HyperlinkButton>
|
||||
</TextBlock>
|
||||
<Button
|
||||
x:Name="btnImportDefConfig4V2ray"
|
||||
@@ -95,7 +95,7 @@
|
||||
BorderThickness="1"
|
||||
Classes="TextArea Margin8"
|
||||
TextWrapping="Wrap"
|
||||
ToolTip.Tip="Http/Socks" />
|
||||
Watermark="Http/Socks" />
|
||||
</DockPanel>
|
||||
</TabItem>
|
||||
|
||||
@@ -103,9 +103,9 @@
|
||||
<DockPanel Classes="Margin8">
|
||||
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" Classes="Margin8">
|
||||
<Button Click="linkDnsSingboxObjectDoc_Click">
|
||||
<HyperlinkButton Classes="WithIcon" Click="linkDnsSingboxObjectDoc_Click">
|
||||
<TextBlock Text="{x:Static resx:ResUI.TbDnsSingboxObjectDoc}" />
|
||||
</Button>
|
||||
</HyperlinkButton>
|
||||
</TextBlock>
|
||||
<Button
|
||||
x:Name="btnImportDefConfig4Singbox"
|
||||
@@ -153,7 +153,7 @@
|
||||
BorderThickness="1"
|
||||
Classes="TextArea Margin8"
|
||||
TextWrapping="Wrap"
|
||||
ToolTip.Tip="Http/Socks" />
|
||||
Watermark="Http/Socks" />
|
||||
|
||||
<GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" />
|
||||
|
||||
@@ -165,7 +165,7 @@
|
||||
BorderThickness="1"
|
||||
Classes="TextArea Margin8"
|
||||
TextWrapping="Wrap"
|
||||
ToolTip.Tip="{x:Static resx:ResUI.TbSettingsTunMode}" />
|
||||
Watermark="{x:Static resx:ResUI.TbSettingsTunMode}" />
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</TabItem>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
mc:Ignorable="d">
|
||||
<DockPanel Classes="Margin8">
|
||||
<StackPanel
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
HorizontalAlignment="Center"
|
||||
DockPanel.Dock="Bottom"
|
||||
Orientation="Horizontal">
|
||||
@@ -60,13 +60,13 @@
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.TbGlobalHotkeySetting}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbDisplayGUI}" />
|
||||
|
||||
@@ -74,68 +74,68 @@
|
||||
x:Name="txtGlobalHotkey0"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
VerticalAlignment="Center"
|
||||
IsReadOnly="True" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbClearSystemProxy}" />
|
||||
<TextBox
|
||||
x:Name="txtGlobalHotkey1"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
VerticalAlignment="Center"
|
||||
IsReadOnly="True" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSetSystemProxy}" />
|
||||
<TextBox
|
||||
x:Name="txtGlobalHotkey2"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
VerticalAlignment="Center"
|
||||
IsReadOnly="True" />
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbNotChangeSystemProxy}" />
|
||||
<TextBox
|
||||
x:Name="txtGlobalHotkey3"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
VerticalAlignment="Center"
|
||||
IsReadOnly="True" />
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSystemProxyPac}" />
|
||||
<TextBox
|
||||
x:Name="txtGlobalHotkey4"
|
||||
Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
VerticalAlignment="Center"
|
||||
IsReadOnly="True" />
|
||||
</Grid>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbGlobalHotkeySettingTip}" />
|
||||
</Grid>
|
||||
|
||||
@@ -87,16 +87,8 @@
|
||||
|
||||
<MenuItem Padding="8,0">
|
||||
<MenuItem.Header>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="{x:Static resx:ResUI.menuCheckUpdate}" />
|
||||
</StackPanel>
|
||||
<ContentControl x:Name="conCheckUpdate" />
|
||||
</MenuItem.Header>
|
||||
<MenuItem x:Name="menuCheckUpdateN" Header="V2rayN" />
|
||||
<MenuItem x:Name="menuCheckUpdateXrayCore" Header="Xray Core" />
|
||||
<MenuItem x:Name="menuCheckUpdateMihomoCore" Header="Mihomo Core" />
|
||||
<MenuItem x:Name="menuCheckUpdateSingBoxCore" Header="Sing-box Core" />
|
||||
<Separator />
|
||||
<MenuItem x:Name="menuCheckUpdateGeo" Header="Geo files" />
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem x:Name="menuHelp" Padding="8,0">
|
||||
@@ -156,7 +148,8 @@
|
||||
<ToggleSwitch
|
||||
x:Name="togEnableTun"
|
||||
HorizontalAlignment="Left"
|
||||
Classes="Margin4" />
|
||||
Classes="Margin8"
|
||||
Theme="{StaticResource SimpleToggleSwitch}" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel
|
||||
|
||||
@@ -72,13 +72,7 @@ namespace v2rayN.Desktop.Views
|
||||
this.BindCommand(ViewModel, vm => vm.ClearServerStatisticsCmd, v => v.menuClearServerStatistics).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.OpenTheFileLocationCmd, v => v.menuOpenTheFileLocation).DisposeWith(disposables);
|
||||
|
||||
//check update
|
||||
this.BindCommand(ViewModel, vm => vm.CheckUpdateNCmd, v => v.menuCheckUpdateN).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.CheckUpdateXrayCoreCmd, v => v.menuCheckUpdateXrayCore).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.CheckUpdateClashMetaCoreCmd, v => v.menuCheckUpdateMihomoCore).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.CheckUpdateSingBoxCoreCmd, v => v.menuCheckUpdateSingBoxCore).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.CheckUpdateGeoCmd, v => v.menuCheckUpdateGeo).DisposeWith(disposables);
|
||||
|
||||
|
||||
this.BindCommand(ViewModel, vm => vm.ReloadCmd, v => v.menuReload).DisposeWith(disposables);
|
||||
this.OneWayBind(ViewModel, vm => vm.BlReloadEnabled, v => v.menuReload.IsEnabled).DisposeWith(disposables);
|
||||
|
||||
@@ -172,6 +166,7 @@ namespace v2rayN.Desktop.Views
|
||||
tabClashConnections2.Content ??= new ClashConnectionsView();
|
||||
}
|
||||
conTheme.Content ??= new ThemeSettingView();
|
||||
conCheckUpdate.Content ??= new CheckUpdateView();
|
||||
|
||||
RestoreUI();
|
||||
AddHelpMenuItem();
|
||||
@@ -279,6 +274,7 @@ namespace v2rayN.Desktop.Views
|
||||
var clipboardData = await AvaUtils.GetClipboardData(this);
|
||||
ViewModel?.AddServerViaClipboardAsync(clipboardData);
|
||||
break;
|
||||
|
||||
case EViewAction.AdjustMainLvColWidth:
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
Locator.Current.GetService<ProfilesViewModel>()?.AutofitColumnWidthAsync(),
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
Width="200"
|
||||
Margin="8,0"
|
||||
VerticalContentAlignment="Center"
|
||||
ToolTip.Tip="{x:Static resx:ResUI.MsgFilterTitle}" />
|
||||
Watermark="{x:Static resx:ResUI.MsgFilterTitle}" />
|
||||
|
||||
<Button
|
||||
x:Name="btnCopy"
|
||||
@@ -62,18 +62,18 @@
|
||||
x:Name="togAutoRefresh"
|
||||
Margin="8,0"
|
||||
HorizontalAlignment="Left"
|
||||
IsChecked="True" />
|
||||
IsChecked="True"
|
||||
Theme="{StaticResource SimpleToggleSwitch}" />
|
||||
<TextBlock
|
||||
Margin="8,0"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="False"
|
||||
Text="{x:Static resx:ResUI.TbAutoScrollToEnd}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togScrollToEnd"
|
||||
Margin="8,0"
|
||||
HorizontalAlignment="Left"
|
||||
IsChecked="True"
|
||||
IsVisible="False" />
|
||||
Theme="{StaticResource SimpleToggleSwitch}" />
|
||||
</WrapPanel>
|
||||
<TextBox
|
||||
Name="txtMsg"
|
||||
|
||||
@@ -1,103 +1,54 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.ReactiveUI;
|
||||
using Avalonia.Threading;
|
||||
using ReactiveUI;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Reactive.Disposables;
|
||||
using v2rayN.Desktop.Common;
|
||||
|
||||
namespace v2rayN.Desktop.Views
|
||||
{
|
||||
public partial class MsgView : UserControl
|
||||
public partial class MsgView : ReactiveUserControl<MsgViewModel>
|
||||
{
|
||||
private static Config? _config;
|
||||
|
||||
private string lastMsgFilter = string.Empty;
|
||||
private bool lastMsgFilterNotAvailable;
|
||||
private ConcurrentBag<string> _lstMsg = [];
|
||||
|
||||
public MsgView()
|
||||
{
|
||||
InitializeComponent();
|
||||
_config = LazyConfig.Instance.Config;
|
||||
MessageBus.Current.Listen<string>(Global.CommandSendMsgView).Subscribe(x => DelegateAppendText(x));
|
||||
//Global.PresetMsgFilters.ForEach(it =>
|
||||
//{
|
||||
// cmbMsgFilter.Items.Add(it);
|
||||
//});
|
||||
if (!_config.uiItem.mainMsgFilter.IsNullOrEmpty())
|
||||
|
||||
ViewModel = new MsgViewModel(UpdateViewHandler);
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
cmbMsgFilter.Text = _config.uiItem.mainMsgFilter;
|
||||
}
|
||||
cmbMsgFilter.TextChanged += (s, e) =>
|
||||
{
|
||||
_config.uiItem.mainMsgFilter = cmbMsgFilter.Text?.ToString();
|
||||
};
|
||||
this.Bind(ViewModel, vm => vm.MsgFilter, v => v.cmbMsgFilter.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables);
|
||||
});
|
||||
}
|
||||
|
||||
private void DelegateAppendText(string msg)
|
||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
||||
{
|
||||
Dispatcher.UIThread.Post(() => AppendText(msg), DispatcherPriority.ApplicationIdle);
|
||||
switch (action)
|
||||
{
|
||||
case EViewAction.DispatcherShowMsg:
|
||||
if (obj is null) return false;
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
ShowMsg(obj),
|
||||
DispatcherPriority.ApplicationIdle);
|
||||
break;
|
||||
}
|
||||
return await Task.FromResult(true);
|
||||
}
|
||||
|
||||
public void AppendText(string msg)
|
||||
private void ShowMsg(object msg)
|
||||
{
|
||||
if (msg == Global.CommandClearMsg)
|
||||
{
|
||||
ClearMsg();
|
||||
return;
|
||||
}
|
||||
if (togAutoRefresh.IsChecked == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var MsgFilter = cmbMsgFilter.Text?.ToString();
|
||||
if (MsgFilter != lastMsgFilter) lastMsgFilterNotAvailable = false;
|
||||
if (!Utils.IsNullOrEmpty(MsgFilter) && !lastMsgFilterNotAvailable)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Regex.IsMatch(msg, MsgFilter))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
lastMsgFilterNotAvailable = true;
|
||||
}
|
||||
}
|
||||
lastMsgFilter = MsgFilter;
|
||||
|
||||
ShowMsg(msg);
|
||||
|
||||
txtMsg.Text = msg.ToString();
|
||||
if (togScrollToEnd.IsChecked ?? true)
|
||||
{
|
||||
txtMsg.CaretIndex = int.MaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowMsg(string msg)
|
||||
{
|
||||
if (_lstMsg.Count > 999)
|
||||
{
|
||||
ClearMsg();
|
||||
}
|
||||
if (!msg.EndsWith(Environment.NewLine))
|
||||
{
|
||||
_lstMsg.Add(Environment.NewLine);
|
||||
}
|
||||
_lstMsg.Add(msg);
|
||||
// if (!msg.EndsWith(Environment.NewLine))
|
||||
// {
|
||||
// _lstMsg.Add(Environment.NewLine);
|
||||
// }
|
||||
this.txtMsg.Text = string.Join("", _lstMsg);
|
||||
}
|
||||
|
||||
public void ClearMsg()
|
||||
{
|
||||
_lstMsg.Clear();
|
||||
ViewModel?.ClearMsg();
|
||||
txtMsg.Clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<DockPanel Classes="Margin8">
|
||||
<StackPanel
|
||||
HorizontalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
DockPanel.Dock="Bottom"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
@@ -113,7 +113,7 @@
|
||||
<ListBox
|
||||
x:Name="clbdestOverride"
|
||||
HorizontalAlignment="Left"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
SelectionMode="Multiple"
|
||||
Theme="{DynamicResource PureCardRadioGroupListBox}" />
|
||||
</StackPanel>
|
||||
@@ -309,12 +309,12 @@
|
||||
x:Name="txtUpMbps"
|
||||
Width="90"
|
||||
Classes="Margin8"
|
||||
ToolTip.Tip="Up" />
|
||||
Watermark="Up" />
|
||||
<TextBox
|
||||
x:Name="txtDownMbps"
|
||||
Width="90"
|
||||
Classes="Margin8"
|
||||
ToolTip.Tip="Down" />
|
||||
Watermark="Down" />
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
Width="200"
|
||||
Margin="4,0"
|
||||
VerticalContentAlignment="Center"
|
||||
ToolTip.Tip="{x:Static resx:ResUI.MsgServerTitle}" />
|
||||
Watermark="{x:Static resx:ResUI.MsgServerTitle}" />
|
||||
</WrapPanel>
|
||||
<DataGrid
|
||||
x:Name="lstProfiles"
|
||||
|
||||
@@ -32,27 +32,27 @@
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="outboundTag" />
|
||||
<ComboBox
|
||||
x:Name="cmbOutboundTag"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
MaxDropDownHeight="1000" />
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Left"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.TbRuleMatchingTips}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="port" />
|
||||
<TextBox
|
||||
x:Name="txtPort"
|
||||
@@ -60,29 +60,29 @@
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
HorizontalAlignment="Left"
|
||||
Classes="Margin4" />
|
||||
Classes="Margin8" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4">
|
||||
<Button Click="linkRuleobjectDoc_Click">
|
||||
Classes="Margin8">
|
||||
<HyperlinkButton Classes="WithIcon" Click="linkRuleobjectDoc_Click">
|
||||
<TextBlock Text="{x:Static resx:ResUI.TbRuleobjectDoc}" />
|
||||
</Button>
|
||||
</HyperlinkButton>
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="protocol" />
|
||||
<ListBox
|
||||
x:Name="clbProtocol"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Left"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
SelectionMode="Multiple"
|
||||
Theme="{DynamicResource PureCardRadioGroupListBox}" />
|
||||
|
||||
@@ -90,13 +90,13 @@
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="inboundTag" />
|
||||
<ListBox
|
||||
x:Name="clbInboundTag"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
SelectionMode="Multiple"
|
||||
Theme="{DynamicResource PureCardRadioGroupListBox}" />
|
||||
|
||||
@@ -104,34 +104,34 @@
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="network" />
|
||||
<ComboBox
|
||||
x:Name="cmbNetwork"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
MaxDropDownHeight="1000" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="enabled" />
|
||||
<ToggleSwitch
|
||||
x:Name="togEnabled"
|
||||
Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Left"
|
||||
Classes="Margin4" />
|
||||
Classes="Margin8" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Left"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.TbRoutingTips}" />
|
||||
</Grid>
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.LvRemarks}" />
|
||||
<StackPanel
|
||||
Grid.Row="0"
|
||||
@@ -76,25 +76,25 @@
|
||||
Width="300"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.LvSort}" />
|
||||
<TextBox
|
||||
x:Name="txtSort"
|
||||
Width="100"
|
||||
HorizontalAlignment="Left"
|
||||
Classes="Margin4" />
|
||||
Classes="Margin8" />
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.TbdomainStrategy}" />
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
@@ -103,22 +103,22 @@
|
||||
<ComboBox
|
||||
x:Name="cmbdomainStrategy"
|
||||
Width="200"
|
||||
Classes="Margin4" />
|
||||
Classes="Margin8" />
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.TbdomainStrategy4Singbox}" />
|
||||
<ComboBox
|
||||
x:Name="cmbdomainStrategy4Singbox"
|
||||
Width="200"
|
||||
Classes="Margin4" />
|
||||
Classes="Margin8" />
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.LvUrl}" />
|
||||
<TextBox
|
||||
x:Name="txtUrl"
|
||||
@@ -127,14 +127,14 @@
|
||||
Width="600"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.LvCustomIcon}" />
|
||||
<TextBox
|
||||
x:Name="txtCustomIcon"
|
||||
@@ -143,23 +143,23 @@
|
||||
Width="600"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
TextWrapping="Wrap" />
|
||||
<Button
|
||||
x:Name="btnBrowseCustomIcon"
|
||||
Grid.Row="3"
|
||||
Grid.Column="2"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Content="{x:Static resx:ResUI.TbBrowse}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4">
|
||||
<Button Click="linkCustomRulesetPath4Singbox">
|
||||
Classes="Margin8">
|
||||
<HyperlinkButton Classes="WithIcon" Click="linkCustomRulesetPath4Singbox">
|
||||
<TextBlock Text="{x:Static resx:ResUI.LvCustomRulesetPath4Singbox}" />
|
||||
</Button>
|
||||
</HyperlinkButton>
|
||||
</TextBlock>
|
||||
<TextBox
|
||||
x:Name="txtCustomRulesetPath4Singbox"
|
||||
@@ -168,13 +168,13 @@
|
||||
Width="600"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
TextWrapping="Wrap" />
|
||||
<Button
|
||||
x:Name="btnBrowseCustomRulesetPath4Singbox"
|
||||
Grid.Row="4"
|
||||
Grid.Column="2"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Content="{x:Static resx:ResUI.TbBrowse}" />
|
||||
</Grid>
|
||||
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
</Menu>
|
||||
|
||||
<TextBlock Margin="8,0,0,0" VerticalAlignment="Center">
|
||||
<Button Click="linkdomainStrategy_Click">
|
||||
<HyperlinkButton Classes="WithIcon" Click="linkdomainStrategy_Click">
|
||||
<TextBlock Text="{x:Static resx:ResUI.TbdomainStrategy}" />
|
||||
</Button>
|
||||
</HyperlinkButton>
|
||||
</TextBlock>
|
||||
<ComboBox
|
||||
x:Name="cmbdomainStrategy"
|
||||
@@ -45,9 +45,9 @@
|
||||
Margin="8,0,0,0" />
|
||||
<Separator />
|
||||
<TextBlock Margin="8,0,0,0" VerticalAlignment="Center">
|
||||
<Button Click="linkdomainStrategy4Singbox_Click">
|
||||
<HyperlinkButton Classes="WithIcon" Click="linkdomainStrategy4Singbox_Click">
|
||||
<TextBlock Text="{x:Static resx:ResUI.TbdomainStrategy4Singbox}" />
|
||||
</Button>
|
||||
</HyperlinkButton>
|
||||
</TextBlock>
|
||||
<ComboBox
|
||||
x:Name="cmbdomainStrategy4Singbox"
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<DockPanel Classes="Margin8">
|
||||
<StackPanel
|
||||
HorizontalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
DockPanel.Dock="Bottom"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
@@ -60,14 +60,14 @@
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.menuSubscription}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.LvRemarks}" />
|
||||
|
||||
<TextBox
|
||||
@@ -75,53 +75,53 @@
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.LvUrl}" />
|
||||
<TextBox
|
||||
x:Name="txtUrl"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
TextWrapping="Wrap"
|
||||
ToolTip.Tip="{x:Static resx:ResUI.SubUrlTips}" />
|
||||
Watermark="{x:Static resx:ResUI.SubUrlTips}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.LvEnabled}" />
|
||||
|
||||
<DockPanel
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Classes="Margin4">
|
||||
Classes="Margin8">
|
||||
<ToggleSwitch
|
||||
x:Name="togEnable"
|
||||
HorizontalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
DockPanel.Dock="Left" />
|
||||
|
||||
<TextBox
|
||||
x:Name="txtAutoUpdateInterval"
|
||||
Width="200"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
DockPanel.Dock="Right"
|
||||
ToolTip.Tip="{x:Static resx:ResUI.SubUrlTips}" />
|
||||
Watermark="{x:Static resx:ResUI.SubUrlTips}" />
|
||||
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.LvAutoUpdateInterval}" />
|
||||
</DockPanel>
|
||||
|
||||
@@ -129,21 +129,21 @@
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.LvFilter}" />
|
||||
<TextBox
|
||||
x:Name="txtFilter"
|
||||
Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
ToolTip.Tip="{x:Static resx:ResUI.SubUrlTips}" />
|
||||
Classes="Margin8"
|
||||
Watermark="{x:Static resx:ResUI.SubUrlTips}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="6"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.LvConvertTarget}" />
|
||||
<ComboBox
|
||||
x:Name="cmbConvertTarget"
|
||||
@@ -151,29 +151,29 @@
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
VerticalAlignment=" "
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
ToolTip.Tip="{x:Static resx:ResUI.LvConvertTargetTip}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="7"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.LvUserAgent}" />
|
||||
<TextBox
|
||||
x:Name="txtUserAgent"
|
||||
Grid.Row="7"
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
TextWrapping="Wrap"
|
||||
ToolTip.Tip="{x:Static resx:ResUI.SubUrlTips}" />
|
||||
Watermark="{x:Static resx:ResUI.SubUrlTips}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="8"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.LvSort}" />
|
||||
<TextBox
|
||||
x:Name="txtSort"
|
||||
@@ -181,42 +181,42 @@
|
||||
Grid.Column="1"
|
||||
Width="100"
|
||||
HorizontalAlignment="Left"
|
||||
Classes="Margin4" />
|
||||
Classes="Margin8" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="9"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.LvPrevProfile}" />
|
||||
<TextBox
|
||||
x:Name="txtPrevProfile"
|
||||
Grid.Row="9"
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
ToolTip.Tip="{x:Static resx:ResUI.LvPrevProfileTip}" />
|
||||
Classes="Margin8"
|
||||
Watermark="{x:Static resx:ResUI.LvPrevProfileTip}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="10"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.LvNextProfile}" />
|
||||
<TextBox
|
||||
x:Name="txtNextProfile"
|
||||
Grid.Row="10"
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
ToolTip.Tip="{x:Static resx:ResUI.LvPrevProfileTip}" />
|
||||
Classes="Margin8"
|
||||
Watermark="{x:Static resx:ResUI.LvPrevProfileTip}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="11"
|
||||
Grid.Column="0"
|
||||
Margin="4"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.TbPreSocksPort4Sub}" />
|
||||
<TextBox
|
||||
x:Name="txtPreSocksPort"
|
||||
@@ -225,15 +225,16 @@
|
||||
Width="200"
|
||||
Margin="4"
|
||||
HorizontalAlignment="Left"
|
||||
Classes="Margin4"
|
||||
ToolTip.Tip="{x:Static resx:ResUI.TipPreSocksPort}" />
|
||||
Classes="Margin8"
|
||||
ToolTip.Tip="{x:Static resx:ResUI.TipPreSocksPort}"
|
||||
Watermark="{x:Static resx:ResUI.TipPreSocksPort}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="12"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Margin4"
|
||||
Classes="Margin8"
|
||||
Text="{x:Static resx:ResUI.LvMoreUrl}" />
|
||||
<TextBox
|
||||
x:Name="txtMoreUrl"
|
||||
@@ -242,10 +243,10 @@
|
||||
MinHeight="100"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Classes="TextArea Margin4"
|
||||
Classes="TextArea Margin8"
|
||||
MinLines="4"
|
||||
TextWrapping="Wrap"
|
||||
ToolTip.Tip="{x:Static resx:ResUI.SubUrlTips}" />
|
||||
Watermark="{x:Static resx:ResUI.SubUrlTips}" />
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</DockPanel>
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
<ApplicationIcon>v2rayN.ico</ApplicationIcon>
|
||||
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
||||
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
|
||||
<FileVersion>6.55</FileVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -26,8 +25,8 @@
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.1.3" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="11.1.3" />
|
||||
<PackageReference Include="MessageBox.Avalonia" Version="3.1.6" />
|
||||
<PackageReference Include="Semi.Avalonia" Version="11.1.0.2" />
|
||||
<PackageReference Include="Semi.Avalonia.DataGrid" Version="11.1.0.2" />
|
||||
<PackageReference Include="Semi.Avalonia" Version="11.1.0.4" />
|
||||
<PackageReference Include="Semi.Avalonia.DataGrid" Version="11.1.0.4" />
|
||||
<PackageReference Include="ReactiveUI" Version="20.1.1" />
|
||||
<PackageReference Include="ReactiveUI.Fody" Version="19.5.41" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<Application
|
||||
x:Class="v2rayN.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:conv="clr-namespace:v2rayN.Converters"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
ShutdownMode="OnExplicitShutdown"
|
||||
StartupUri="Views/MainWindow.xaml">
|
||||
<Application.Resources>
|
||||
|
||||
@@ -35,5 +35,25 @@ namespace v2rayN
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool? SaveFileDialog(out string fileName, string filter)
|
||||
{
|
||||
fileName = string.Empty;
|
||||
|
||||
SaveFileDialog fileDialog = new()
|
||||
{
|
||||
Filter = filter,
|
||||
FilterIndex = 2,
|
||||
RestoreDirectory = true
|
||||
};
|
||||
if (fileDialog.ShowDialog() != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
fileName = fileDialog.FileName;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ namespace v2rayN.Converters
|
||||
try
|
||||
{
|
||||
var fontFamily = LazyConfig.Instance.Config.uiItem.currentFontFamily;
|
||||
if (!Utils.IsNullOrEmpty(fontFamily))
|
||||
if (Utils.IsNotEmpty(fontFamily))
|
||||
{
|
||||
var fontPath = Utils.GetFontsPath();
|
||||
MyFont = new FontFamily(new Uri(@$"file:///{fontPath}\"), $"./#{fontFamily}");
|
||||
|
||||
@@ -156,7 +156,7 @@ namespace v2rayN.ViewModels
|
||||
y => y != null && !y.IsNullOrEmpty())
|
||||
.Subscribe(c =>
|
||||
{
|
||||
if (!Utils.IsNullOrEmpty(CurrentLanguage) && _config.uiItem.currentLanguage != CurrentLanguage)
|
||||
if (Utils.IsNotEmpty(CurrentLanguage) && _config.uiItem.currentLanguage != CurrentLanguage)
|
||||
{
|
||||
_config.uiItem.currentLanguage = CurrentLanguage;
|
||||
Thread.CurrentThread.CurrentUICulture = new(CurrentLanguage);
|
||||
|
||||
256
v2rayN/v2rayN/Views/BackupAndRestoreView.xaml
Normal file
256
v2rayN/v2rayN/Views/BackupAndRestoreView.xaml
Normal file
@@ -0,0 +1,256 @@
|
||||
<reactiveui:ReactiveUserControl
|
||||
x:Class="v2rayN.Views.BackupAndRestoreView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converters="clr-namespace:v2rayN.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:reactiveui="http://reactiveui.net"
|
||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||
d:DesignHeight="600"
|
||||
d:DesignWidth="800"
|
||||
x:TypeArguments="vms:BackupAndRestoreViewModel"
|
||||
mc:Ignorable="d">
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Popupbox.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<DockPanel Margin="16">
|
||||
<DockPanel Margin="8" DockPanel.Dock="Bottom">
|
||||
<Button
|
||||
Width="100"
|
||||
Margin="8"
|
||||
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
|
||||
Content="{x:Static resx:ResUI.menuClose}"
|
||||
DockPanel.Dock="Right"
|
||||
IsCancel="True"
|
||||
IsDefault="True"
|
||||
Style="{StaticResource MaterialDesignFlatButton}" />
|
||||
|
||||
<TextBlock
|
||||
x:Name="txtMsg"
|
||||
Margin="8"
|
||||
HorizontalAlignment="Left"
|
||||
Style="{StaticResource ToolbarTextBlock}" />
|
||||
</DockPanel>
|
||||
|
||||
<StackPanel>
|
||||
<materialDesign:Card Width="Auto" Margin="8">
|
||||
<Grid Margin="8">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="300" />
|
||||
<ColumnDefinition Width="200" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Margin="8"
|
||||
Style="{StaticResource ModuleTitle}"
|
||||
Text="{x:Static resx:ResUI.menuLocalBackupAndRestore}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.menuLocalBackup}" />
|
||||
<Button
|
||||
x:Name="menuLocalBackup"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Content="{x:Static resx:ResUI.menuLocalBackup}"
|
||||
Style="{StaticResource DefButton}" />
|
||||
|
||||
<Separator
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="2"
|
||||
Style="{StaticResource MaterialDesignLightSeparator}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.menuLocalRestore}" />
|
||||
<Button
|
||||
x:Name="menuLocalRestore"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Content="{x:Static resx:ResUI.menuLocalRestore}"
|
||||
Style="{StaticResource DefButton}" />
|
||||
</Grid>
|
||||
</materialDesign:Card>
|
||||
<materialDesign:Card Width="Auto" Margin="8">
|
||||
<Grid Margin="8">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="300" />
|
||||
<ColumnDefinition Width="200" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Margin="8"
|
||||
Style="{StaticResource ModuleTitle}"
|
||||
Text="{x:Static resx:ResUI.menuRemoteBackupAndRestore}" />
|
||||
|
||||
<materialDesign:PopupBox
|
||||
Padding="8,0"
|
||||
HorizontalAlignment="Right"
|
||||
StaysOpen="True"
|
||||
Style="{StaticResource MaterialDesignToolForegroundPopupBox}">
|
||||
<StackPanel Margin="16">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="300" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.LvWebDavUrl}" />
|
||||
|
||||
<TextBox
|
||||
x:Name="txtWebDavUrl"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource DefTextBox}"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.LvWebDavUserName}" />
|
||||
|
||||
<TextBox
|
||||
x:Name="txtWebDavUserName"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource DefTextBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.LvWebDavPassword}" />
|
||||
|
||||
<TextBox
|
||||
x:Name="txtWebDavPassword"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource DefTextBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.LvWebDavDirName}" />
|
||||
|
||||
<TextBox
|
||||
x:Name="txtWebDavDirName"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource DefTextBox}" />
|
||||
|
||||
<Button
|
||||
x:Name="menuWebDavCheck"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Content="{x:Static resx:ResUI.LvWebDavCheck}"
|
||||
Style="{StaticResource DefButton}" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</materialDesign:PopupBox>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.menuRemoteBackup}" />
|
||||
<Button
|
||||
x:Name="menuRemoteBackup"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Content="{x:Static resx:ResUI.menuRemoteBackup}"
|
||||
Style="{StaticResource DefButton}" />
|
||||
|
||||
<Separator
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="3"
|
||||
Style="{StaticResource MaterialDesignLightSeparator}" />
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.menuRemoteRestore}" />
|
||||
<Button
|
||||
x:Name="menuRemoteRestore"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Margin="8"
|
||||
VerticalAlignment="Center"
|
||||
Content="{x:Static resx:ResUI.menuRemoteRestore}"
|
||||
Style="{StaticResource DefButton}" />
|
||||
</Grid>
|
||||
</materialDesign:Card>
|
||||
</StackPanel>
|
||||
</DockPanel>
|
||||
</reactiveui:ReactiveUserControl>
|
||||
65
v2rayN/v2rayN/Views/BackupAndRestoreView.xaml.cs
Normal file
65
v2rayN/v2rayN/Views/BackupAndRestoreView.xaml.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using ReactiveUI;
|
||||
using Splat;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Windows;
|
||||
|
||||
namespace v2rayN.Views
|
||||
{
|
||||
public partial class BackupAndRestoreView
|
||||
{
|
||||
private NoticeHandler? _noticeHandler;
|
||||
|
||||
public BackupAndRestoreView()
|
||||
{
|
||||
InitializeComponent();
|
||||
menuLocalBackup.Click += MenuLocalBackup_Click;
|
||||
menuLocalRestore.Click += MenuLocalRestore_Click;
|
||||
|
||||
ViewModel = new BackupAndRestoreViewModel(UpdateViewHandler);
|
||||
|
||||
_noticeHandler = Locator.Current.GetService<NoticeHandler>();
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
this.Bind(ViewModel, vm => vm.OperationMsg, v => v.txtMsg.Text).DisposeWith(disposables);
|
||||
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.url, v => v.txtWebDavUrl.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.userName, v => v.txtWebDavUserName.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.password, v => v.txtWebDavPassword.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.dirName, v => v.txtWebDavDirName.Text).DisposeWith(disposables);
|
||||
|
||||
this.BindCommand(ViewModel, vm => vm.WebDavCheckCmd, v => v.menuWebDavCheck).DisposeWith(disposables);
|
||||
|
||||
this.BindCommand(ViewModel, vm => vm.RemoteBackupCmd, v => v.menuRemoteBackup).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.RemoteRestoreCmd, v => v.menuRemoteRestore).DisposeWith(disposables);
|
||||
});
|
||||
}
|
||||
|
||||
private void MenuRemoteRestore_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private void MenuLocalBackup_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (UI.SaveFileDialog(out string fileName, "Zip|*.zip") != true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
ViewModel?.LocalBackup(fileName);
|
||||
}
|
||||
|
||||
private void MenuLocalRestore_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (UI.OpenFileDialog(out string fileName, "Zip|*.zip|All|*.*") != true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
ViewModel?.LocalRestore(fileName);
|
||||
}
|
||||
|
||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
||||
{
|
||||
return await Task.FromResult(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,32 +22,25 @@
|
||||
Orientation="Horizontal">
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="9"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource SettingItemMargin}"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource ToolbarTextBlock}"
|
||||
Text="{x:Static resx:ResUI.TbSettingsEnableCheckPreReleaseUpdate}" />
|
||||
<ToggleButton
|
||||
x:Name="togEnableCheckPreReleaseUpdate"
|
||||
Grid.Row="9"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource SettingItemMargin}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<Button
|
||||
x:Name="btnCheckUpdate"
|
||||
Grid.Row="2"
|
||||
Width="100"
|
||||
Margin="8"
|
||||
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
|
||||
Content="{x:Static resx:ResUI.menuCheckUpdate}"
|
||||
IsCancel="True"
|
||||
IsDefault="True"
|
||||
Style="{StaticResource MaterialDesignFlatButton}" />
|
||||
|
||||
<Button
|
||||
Grid.Row="2"
|
||||
Width="100"
|
||||
Margin="8"
|
||||
HorizontalAlignment="Right"
|
||||
@@ -56,7 +49,6 @@
|
||||
IsCancel="True"
|
||||
IsDefault="True"
|
||||
Style="{StaticResource MaterialDesignFlatButton}" />
|
||||
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel>
|
||||
@@ -106,7 +98,6 @@
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
|
||||
</StackPanel>
|
||||
</DockPanel>
|
||||
</reactiveui:ReactiveUserControl>
|
||||
@@ -34,6 +34,7 @@ namespace v2rayN.Views
|
||||
ViewModel?.UpdateViewResult((CheckUpdateItem)obj);
|
||||
}), DispatcherPriority.Normal);
|
||||
break;
|
||||
|
||||
case EViewAction.DispatcherCheckUpdateFinished:
|
||||
if (obj is null) return false;
|
||||
Application.Current?.Dispatcher.Invoke((() =>
|
||||
@@ -41,7 +42,6 @@ namespace v2rayN.Views
|
||||
ViewModel?.UpdateFinishedResult((bool)obj);
|
||||
}), DispatcherPriority.Normal);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return await Task.FromResult(true);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<reactiveui:ReactiveUserControl
|
||||
x:Class="v2rayN.Views.ClashConnectionsView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:reactiveui="http://reactiveui.net"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:reactiveui="http://reactiveui.net"
|
||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||
d:DesignHeight="450"
|
||||
@@ -20,6 +20,15 @@
|
||||
DockPanel.Dock="Top"
|
||||
Orientation="Horizontal">
|
||||
|
||||
<TextBox
|
||||
x:Name="txtHostFilter"
|
||||
Width="200"
|
||||
Margin="8,0"
|
||||
VerticalContentAlignment="Center"
|
||||
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.ConnectionsHostFilterTitle}"
|
||||
materialDesign:TextFieldAssist.HasClearButton="True"
|
||||
Style="{StaticResource DefTextBox}" />
|
||||
|
||||
<TextBlock
|
||||
Margin="8,0"
|
||||
VerticalAlignment="Center"
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace v2rayN.Views
|
||||
this.BindCommand(ViewModel, vm => vm.ConnectionCloseCmd, v => v.menuConnectionClose).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.menuConnectionCloseAll).DisposeWith(disposables);
|
||||
|
||||
this.Bind(ViewModel, vm => vm.HostFilter, v => v.txtHostFilter.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SortingSelected, v => v.cmbSorting.SelectedIndex).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.btnConnectionCloseAll).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables);
|
||||
|
||||
@@ -15,7 +15,6 @@ namespace v2rayN.Views
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
|
||||
this.Owner = Application.Current.MainWindow;
|
||||
_config = LazyConfig.Instance.Config;
|
||||
_config.globalHotkeys ??= new List<KeyEventItem>();
|
||||
|
||||
@@ -186,14 +186,14 @@
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuClearServerStatistics}" />
|
||||
<Separator Margin="-40,5" />
|
||||
<MenuItem
|
||||
x:Name="menuBackupAndRestore"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuBackupAndRestore}" />
|
||||
<MenuItem
|
||||
x:Name="menuOpenTheFileLocation"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuOpenTheFileLocation}" />
|
||||
<!--<MenuItem
|
||||
x:Name="menuImportOldGuiConfig"
|
||||
Height="{StaticResource MenuItemHeight}"
|
||||
Header="{x:Static resx:ResUI.menuImportOldGuiConfig}" />-->
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
<Separator />
|
||||
|
||||
@@ -15,8 +15,9 @@ namespace v2rayN.Views
|
||||
{
|
||||
public partial class MainWindow
|
||||
{
|
||||
private static Config _config;
|
||||
private static Config _config;
|
||||
private CheckUpdateView? _checkUpdateView;
|
||||
private BackupAndRestoreView? _backupAndRestoreView;
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
@@ -33,6 +34,7 @@ namespace v2rayN.Views
|
||||
menuClose.Click += menuClose_Click;
|
||||
menuExit.Click += menuExit_Click;
|
||||
menuCheckUpdate.Click += MenuCheckUpdate_Click;
|
||||
menuBackupAndRestore.Click += MenuBackupAndRestore_Click;
|
||||
|
||||
MessageBus.Current.Listen<string>(Global.CommandSendSnackMsg).Subscribe(x => DelegateSnackMsg(x));
|
||||
ViewModel = new MainWindowViewModel(UpdateViewHandler);
|
||||
@@ -71,14 +73,6 @@ namespace v2rayN.Views
|
||||
this.BindCommand(ViewModel, vm => vm.RebootAsAdminCmd, v => v.menuRebootAsAdmin).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.ClearServerStatisticsCmd, v => v.menuClearServerStatistics).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.OpenTheFileLocationCmd, v => v.menuOpenTheFileLocation).DisposeWith(disposables);
|
||||
//this.BindCommand(ViewModel, vm => vm.ImportOldGuiConfigCmd, v => v.menuImportOldGuiConfig).DisposeWith(disposables);
|
||||
|
||||
//check update
|
||||
//this.BindCommand(ViewModel, vm => vm.CheckUpdateNCmd, v => v.menuCheckUpdateN).DisposeWith(disposables);
|
||||
//this.BindCommand(ViewModel, vm => vm.CheckUpdateXrayCoreCmd, v => v.menuCheckUpdateXrayCore).DisposeWith(disposables);
|
||||
//this.BindCommand(ViewModel, vm => vm.CheckUpdateClashMetaCoreCmd, v => v.menuCheckUpdateMihomoCore).DisposeWith(disposables);
|
||||
//this.BindCommand(ViewModel, vm => vm.CheckUpdateSingBoxCoreCmd, v => v.menuCheckUpdateSingBoxCore).DisposeWith(disposables);
|
||||
//this.BindCommand(ViewModel, vm => vm.CheckUpdateGeoCmd, v => v.menuCheckUpdateGeo).DisposeWith(disposables);
|
||||
|
||||
this.BindCommand(ViewModel, vm => vm.ReloadCmd, v => v.menuReload).DisposeWith(disposables);
|
||||
this.OneWayBind(ViewModel, vm => vm.BlReloadEnabled, v => v.menuReload.IsEnabled).DisposeWith(disposables);
|
||||
@@ -196,17 +190,14 @@ namespace v2rayN.Views
|
||||
AddHelpMenuItem();
|
||||
}
|
||||
|
||||
private void MenuCheckUpdate_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_checkUpdateView ??= new CheckUpdateView();
|
||||
DialogHost.Show(_checkUpdateView, "RootDialog");
|
||||
}
|
||||
|
||||
#region Event
|
||||
|
||||
private void OnProgramStarted(object state, bool timeout)
|
||||
{
|
||||
ShowHideWindow(true);
|
||||
Application.Current?.Dispatcher.Invoke((Action)(() =>
|
||||
{
|
||||
ShowHideWindow(true);
|
||||
}));
|
||||
}
|
||||
|
||||
private void DelegateSnackMsg(string content)
|
||||
@@ -290,7 +281,10 @@ namespace v2rayN.Views
|
||||
break;
|
||||
|
||||
case EViewAction.Shutdown:
|
||||
Application.Current.Shutdown();
|
||||
Application.Current?.Dispatcher.Invoke((() =>
|
||||
{
|
||||
Application.Current.Shutdown();
|
||||
}), DispatcherPriority.Normal);
|
||||
break;
|
||||
|
||||
case EViewAction.ScanScreenTask:
|
||||
@@ -427,6 +421,18 @@ namespace v2rayN.Views
|
||||
ViewModel?.ScanScreenTaskAsync(result);
|
||||
}
|
||||
|
||||
private void MenuCheckUpdate_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_checkUpdateView ??= new CheckUpdateView();
|
||||
DialogHost.Show(_checkUpdateView, "RootDialog");
|
||||
}
|
||||
|
||||
private void MenuBackupAndRestore_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_backupAndRestoreView ??= new BackupAndRestoreView();
|
||||
DialogHost.Show(_backupAndRestoreView, "RootDialog");
|
||||
}
|
||||
|
||||
#endregion Event
|
||||
|
||||
#region UI
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
<UserControl
|
||||
<reactiveui:ReactiveUserControl
|
||||
x:Class="v2rayN.Views.MsgView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:reactiveui="http://reactiveui.net"
|
||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
x:TypeArguments="vms:MsgViewModel"
|
||||
mc:Ignorable="d">
|
||||
<DockPanel Margin="2">
|
||||
<WrapPanel
|
||||
@@ -23,8 +26,7 @@
|
||||
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.MsgFilterTitle}"
|
||||
materialDesign:TextFieldAssist.HasClearButton="True"
|
||||
IsEditable="True"
|
||||
Style="{StaticResource DefComboBox}"
|
||||
TextBoxBase.TextChanged="cmbMsgFilter_TextChanged" />
|
||||
Style="{StaticResource DefComboBox}" />
|
||||
<Button
|
||||
x:Name="btnCopy"
|
||||
Width="24"
|
||||
@@ -96,4 +98,4 @@
|
||||
</TextBox.ContextMenu>
|
||||
</TextBox>
|
||||
</DockPanel>
|
||||
</UserControl>
|
||||
</reactiveui:ReactiveUserControl>
|
||||
@@ -1,22 +1,23 @@
|
||||
using ReactiveUI;
|
||||
using System.Reactive.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace v2rayN.Views
|
||||
{
|
||||
public partial class MsgView
|
||||
{
|
||||
private static Config? _config;
|
||||
|
||||
private string lastMsgFilter = string.Empty;
|
||||
private bool lastMsgFilterNotAvailable;
|
||||
|
||||
public MsgView()
|
||||
{
|
||||
InitializeComponent();
|
||||
_config = LazyConfig.Instance.Config;
|
||||
MessageBus.Current.Listen<string>(Global.CommandSendMsgView).Subscribe(x => DelegateAppendText(x));
|
||||
|
||||
ViewModel = new MsgViewModel(UpdateViewHandler);
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
this.Bind(ViewModel, vm => vm.MsgFilter, v => v.cmbMsgFilter.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables);
|
||||
});
|
||||
|
||||
btnCopy.Click += menuMsgViewCopyAll_Click;
|
||||
btnClear.Click += menuMsgViewClear_Click;
|
||||
@@ -29,70 +30,37 @@ namespace v2rayN.Views
|
||||
{
|
||||
cmbMsgFilter.Items.Add(it);
|
||||
});
|
||||
if (!_config.uiItem.mainMsgFilter.IsNullOrEmpty())
|
||||
{
|
||||
cmbMsgFilter.Text = _config.uiItem.mainMsgFilter;
|
||||
}
|
||||
}
|
||||
|
||||
private void DelegateAppendText(string msg)
|
||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
||||
{
|
||||
Dispatcher.BeginInvoke(AppendText, DispatcherPriority.ApplicationIdle, msg);
|
||||
}
|
||||
|
||||
public void AppendText(string msg)
|
||||
{
|
||||
if (msg == Global.CommandClearMsg)
|
||||
switch (action)
|
||||
{
|
||||
ClearMsg();
|
||||
return;
|
||||
}
|
||||
if (togAutoRefresh.IsChecked == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var MsgFilter = cmbMsgFilter.Text.TrimEx();
|
||||
if (MsgFilter != lastMsgFilter) lastMsgFilterNotAvailable = false;
|
||||
if (!Utils.IsNullOrEmpty(MsgFilter) && !lastMsgFilterNotAvailable)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Regex.IsMatch(msg, MsgFilter)) // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʽ<EFBFBD><CABD><EFBFBD>쳣
|
||||
case EViewAction.DispatcherShowMsg:
|
||||
if (obj is null) return false;
|
||||
Application.Current?.Dispatcher.Invoke((() =>
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
lastMsgFilterNotAvailable = true;
|
||||
}
|
||||
ShowMsg(obj);
|
||||
}), DispatcherPriority.ApplicationIdle);
|
||||
break;
|
||||
}
|
||||
lastMsgFilter = MsgFilter;
|
||||
|
||||
ShowMsg(msg);
|
||||
return await Task.FromResult(true);
|
||||
}
|
||||
|
||||
private void ShowMsg(object msg)
|
||||
{
|
||||
txtMsg.BeginChange();
|
||||
txtMsg.Text = msg.ToString();
|
||||
if (togScrollToEnd.IsChecked ?? true)
|
||||
{
|
||||
txtMsg.ScrollToEnd();
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowMsg(string msg)
|
||||
{
|
||||
if (txtMsg.LineCount > 999)
|
||||
{
|
||||
ClearMsg();
|
||||
}
|
||||
this.txtMsg.AppendText(msg);
|
||||
if (!msg.EndsWith(Environment.NewLine))
|
||||
{
|
||||
this.txtMsg.AppendText(Environment.NewLine);
|
||||
}
|
||||
txtMsg.EndChange();
|
||||
}
|
||||
|
||||
public void ClearMsg()
|
||||
{
|
||||
ViewModel?.ClearMsg();
|
||||
txtMsg.Clear();
|
||||
}
|
||||
|
||||
@@ -118,10 +86,5 @@ namespace v2rayN.Views
|
||||
{
|
||||
ClearMsg();
|
||||
}
|
||||
|
||||
private void cmbMsgFilter_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
|
||||
{
|
||||
_config.uiItem.mainMsgFilter = cmbMsgFilter.Text.TrimEx();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using MaterialDesignThemes.Wpf;
|
||||
using Microsoft.Win32;
|
||||
using ReactiveUI;
|
||||
using Splat;
|
||||
using System.Reactive.Disposables;
|
||||
@@ -129,17 +128,11 @@ namespace v2rayN.Views
|
||||
|
||||
case EViewAction.SaveFileDialog:
|
||||
if (obj is null) return false;
|
||||
SaveFileDialog fileDialog = new()
|
||||
{
|
||||
Filter = "Config|*.json",
|
||||
FilterIndex = 2,
|
||||
RestoreDirectory = true
|
||||
};
|
||||
if (fileDialog.ShowDialog() != true)
|
||||
if (UI.SaveFileDialog(out string fileName, "Config|*.json") != true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
ViewModel?.Export2ClientConfigResult(fileDialog.FileName, (ProfileItem)obj);
|
||||
ViewModel?.Export2ClientConfigResult(fileName, (ProfileItem)obj);
|
||||
break;
|
||||
|
||||
case EViewAction.AddServerWindow:
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<reactiveui:ReactiveWindow
|
||||
x:Class="v2rayN.Views.RoutingRuleSettingWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:conv="clr-namespace:v2rayN.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:reactiveui="http://reactiveui.net"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||
xmlns:conv="clr-namespace:v2rayN.Converters"
|
||||
Title="{x:Static resx:ResUI.menuRoutingRuleSetting}"
|
||||
Width="960"
|
||||
Height="700"
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<reactiveui:ReactiveWindow
|
||||
x:Class="v2rayN.Views.SubEditWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:conv="clr-namespace:v2rayN.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:reactiveui="http://reactiveui.net"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||
xmlns:conv="clr-namespace:v2rayN.Converters"
|
||||
Title="{x:Static resx:ResUI.menuSubSetting}"
|
||||
Width="700"
|
||||
Height="600"
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
<reactiveui:ReactiveUserControl
|
||||
x:Class="v2rayN.Views.ThemeSettingView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converters="clr-namespace:v2rayN.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:reactiveui="http://reactiveui.net"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||
xmlns:vms="clr-namespace:v2rayN.ViewModels"
|
||||
d:DesignHeight="450"
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<ApplicationIcon>v2rayN.ico</ApplicationIcon>
|
||||
<Copyright>Copyright © 2017-2024 (GPLv3)</Copyright>
|
||||
<FileVersion>6.56</FileVersion>
|
||||
<SupportedOSPlatformVersion>7.0</SupportedOSPlatformVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace v2rayUpgrade
|
||||
{
|
||||
string thisAppOldFile = $"{GetExePath()}.tmp";
|
||||
File.Delete(thisAppOldFile);
|
||||
string startKey = "v2rayN/";
|
||||
string splitKey = "/";
|
||||
|
||||
using ZipArchive archive = ZipFile.OpenRead(fileName);
|
||||
foreach (ZipArchiveEntry entry in archive.Entries)
|
||||
@@ -77,11 +77,11 @@ namespace v2rayUpgrade
|
||||
{
|
||||
continue;
|
||||
}
|
||||
string fullName = entry.FullName;
|
||||
if (fullName.StartsWith(startKey))
|
||||
{
|
||||
fullName = fullName[startKey.Length..];
|
||||
}
|
||||
|
||||
var lst = entry.FullName.Split(splitKey);
|
||||
if (lst.Length == 1) continue;
|
||||
string fullName = string.Join(splitKey, lst[1..lst.Length]);
|
||||
|
||||
if (string.Equals(GetExePath(), GetPath(fullName), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
File.Move(GetExePath(), thisAppOldFile);
|
||||
@@ -90,6 +90,8 @@ namespace v2rayUpgrade
|
||||
string entryOutputPath = GetPath(fullName);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(entryOutputPath)!);
|
||||
entry.ExtractToFile(entryOutputPath, true);
|
||||
|
||||
Console.WriteLine(entryOutputPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -104,11 +106,12 @@ namespace v2rayUpgrade
|
||||
}
|
||||
if (sb.Length > 0)
|
||||
{
|
||||
Console.WriteLine("Upgrade Failed,Hold ctrl + c to copy to clipboard.\n" +
|
||||
"(升级失败,按住ctrl+c可以复制到剪贴板)." + sb.ToString());
|
||||
Console.WriteLine("Upgrade Failed.\n" +
|
||||
"(升级失败)." + sb.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine("Start v2rayN, please wait...(正在重启,请等待)");
|
||||
Process.Start("v2rayN");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user