mirror of
https://github.com/2dust/v2rayN.git
synced 2026-02-28 21:23:04 +00:00
Compare commits
4 commits
677e81f9a7
...
0f3a3eac02
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f3a3eac02 | ||
|
|
54608ab2b9 | ||
|
|
6167624443 | ||
|
|
7a58e78381 |
24 changed files with 449 additions and 140 deletions
|
|
@ -844,7 +844,7 @@ public static class ConfigHandler
|
||||||
/// <returns>0 if successful, -1 if failed</returns>
|
/// <returns>0 if successful, -1 if failed</returns>
|
||||||
public static async Task<int> SortServers(Config config, string subId, string colName, bool asc)
|
public static async Task<int> SortServers(Config config, string subId, string colName, bool asc)
|
||||||
{
|
{
|
||||||
var lstModel = await AppManager.Instance.ProfileItems(subId, "");
|
var lstModel = await AppManager.Instance.ProfileModels(subId, "");
|
||||||
if (lstModel.Count <= 0)
|
if (lstModel.Count <= 0)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -1213,7 +1213,8 @@ public static class ConfigHandler
|
||||||
}
|
}
|
||||||
var extraItem = new ProtocolExtraItem
|
var extraItem = new ProtocolExtraItem
|
||||||
{
|
{
|
||||||
ChildItems = childProfileIndexId, MultipleLoad = multipleLoad,
|
ChildItems = childProfileIndexId,
|
||||||
|
MultipleLoad = multipleLoad,
|
||||||
};
|
};
|
||||||
profile.SetProtocolExtra(extraItem);
|
profile.SetProtocolExtra(extraItem);
|
||||||
var ret = await AddServerCommon(config, profile, true);
|
var ret = await AddServerCommon(config, profile, true);
|
||||||
|
|
@ -1277,7 +1278,7 @@ public static class ConfigHandler
|
||||||
/// <returns>Number of removed servers or -1 if failed</returns>
|
/// <returns>Number of removed servers or -1 if failed</returns>
|
||||||
public static async Task<int> RemoveInvalidServerResult(Config config, string subid)
|
public static async Task<int> RemoveInvalidServerResult(Config config, string subid)
|
||||||
{
|
{
|
||||||
var lstModel = await AppManager.Instance.ProfileItems(subid, "");
|
var lstModel = await AppManager.Instance.ProfileModels(subid, "");
|
||||||
if (lstModel is { Count: <= 0 })
|
if (lstModel is { Count: <= 0 })
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
||||||
|
|
@ -191,10 +191,17 @@ public sealed class AppManager
|
||||||
return (await ProfileItems(subid))?.Select(t => t.IndexId)?.ToList();
|
return (await ProfileItems(subid))?.Select(t => t.IndexId)?.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<ProfileItemModel>?> ProfileItems(string subid, string filter)
|
public async Task<List<ProfileItemModel>?> ProfileModels(string subid, string filter)
|
||||||
{
|
{
|
||||||
var sql = @$"select a.*
|
var sql = @$"select a.IndexId
|
||||||
,b.remarks subRemarks
|
,a.ConfigType
|
||||||
|
,a.Remarks
|
||||||
|
,a.Address
|
||||||
|
,a.Port
|
||||||
|
,a.Network
|
||||||
|
,a.StreamSecurity
|
||||||
|
,a.Subid
|
||||||
|
,b.remarks as subRemarks
|
||||||
from ProfileItem a
|
from ProfileItem a
|
||||||
left join SubItem b on a.subid = b.id
|
left join SubItem b on a.subid = b.id
|
||||||
where 1=1 ";
|
where 1=1 ";
|
||||||
|
|
@ -264,50 +271,51 @@ public sealed class AppManager
|
||||||
|
|
||||||
public async Task MigrateProfileExtra()
|
public async Task MigrateProfileExtra()
|
||||||
{
|
{
|
||||||
#pragma warning disable CS0618
|
await MigrateProfileExtraGroup();
|
||||||
var list = await SQLiteHelper.Instance.TableAsync<ProfileGroupItem>().ToListAsync();
|
|
||||||
var groupItems = new ConcurrentDictionary<string, ProfileGroupItem>(list.Where(t => !string.IsNullOrEmpty(t.IndexId)).ToDictionary(t => t.IndexId!));
|
|
||||||
|
|
||||||
const int pageSize = 500;
|
#pragma warning disable CS0618
|
||||||
|
|
||||||
|
const int pageSize = 100;
|
||||||
var offset = 0;
|
var offset = 0;
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
var sql = $"SELECT * FROM ProfileItem WHERE ConfigVersion < 3 LIMIT {pageSize} OFFSET {offset}";
|
var sql = $"SELECT * FROM ProfileItem " +
|
||||||
|
$"WHERE ConfigVersion < 3 " +
|
||||||
|
$"AND ConfigType NOT IN ({(int)EConfigType.PolicyGroup}, {(int)EConfigType.ProxyChain}) " +
|
||||||
|
$"LIMIT {pageSize} OFFSET {offset}";
|
||||||
var batch = await SQLiteHelper.Instance.QueryAsync<ProfileItem>(sql);
|
var batch = await SQLiteHelper.Instance.QueryAsync<ProfileItem>(sql);
|
||||||
if (batch is null || batch.Count == 0)
|
if (batch is null || batch.Count == 0)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var batchSuccessCount = 0;
|
var batchSuccessCount = await MigrateProfileExtraSub(batch);
|
||||||
|
|
||||||
|
// Only increment offset by the number of failed items that remain in the result set
|
||||||
|
// Successfully updated items are automatically excluded from future queries due to ConfigVersion = 3
|
||||||
|
offset += batch.Count - batchSuccessCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
//await ProfileGroupItemManager.Instance.ClearAll();
|
||||||
|
#pragma warning restore CS0618
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<int> MigrateProfileExtraSub(List<ProfileItem> batch)
|
||||||
|
{
|
||||||
|
var updateProfileItems = new List<ProfileItem>();
|
||||||
|
|
||||||
foreach (var item in batch)
|
foreach (var item in batch)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var extra = item.GetProtocolExtra();
|
var extra = item.GetProtocolExtra();
|
||||||
|
|
||||||
if (item.ConfigType is EConfigType.PolicyGroup or EConfigType.ProxyChain)
|
|
||||||
{
|
|
||||||
extra = extra with { GroupType = nameof(item.ConfigType) };
|
|
||||||
groupItems.TryGetValue(item.IndexId, out var groupItem);
|
|
||||||
if (groupItem != null && !groupItem.NotHasChild())
|
|
||||||
{
|
|
||||||
extra = extra with
|
|
||||||
{
|
|
||||||
ChildItems = groupItem.ChildItems,
|
|
||||||
SubChildItems = groupItem.SubChildItems,
|
|
||||||
Filter = groupItem.Filter,
|
|
||||||
MultipleLoad = groupItem.MultipleLoad,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (item.ConfigType)
|
switch (item.ConfigType)
|
||||||
{
|
{
|
||||||
case EConfigType.Shadowsocks:
|
case EConfigType.Shadowsocks:
|
||||||
extra = extra with { SsMethod = item.Security.NullIfEmpty() };
|
extra = extra with { SsMethod = item.Security.NullIfEmpty() };
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.VMess:
|
case EConfigType.VMess:
|
||||||
extra = extra with
|
extra = extra with
|
||||||
{
|
{
|
||||||
|
|
@ -315,6 +323,7 @@ public sealed class AppManager
|
||||||
VmessSecurity = item.Security.NullIfEmpty(),
|
VmessSecurity = item.Security.NullIfEmpty(),
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.VLESS:
|
case EConfigType.VLESS:
|
||||||
extra = extra with
|
extra = extra with
|
||||||
{
|
{
|
||||||
|
|
@ -322,6 +331,7 @@ public sealed class AppManager
|
||||||
VlessEncryption = item.Security,
|
VlessEncryption = item.Security,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.Hysteria2:
|
case EConfigType.Hysteria2:
|
||||||
extra = extra with
|
extra = extra with
|
||||||
{
|
{
|
||||||
|
|
@ -332,15 +342,18 @@ public sealed class AppManager
|
||||||
HopInterval = _config.HysteriaItem.HopInterval.ToString(),
|
HopInterval = _config.HysteriaItem.HopInterval.ToString(),
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.TUIC:
|
case EConfigType.TUIC:
|
||||||
item.Username = item.Id;
|
item.Username = item.Id;
|
||||||
item.Id = item.Security;
|
item.Id = item.Security;
|
||||||
item.Password = item.Security;
|
item.Password = item.Security;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.HTTP:
|
case EConfigType.HTTP:
|
||||||
case EConfigType.SOCKS:
|
case EConfigType.SOCKS:
|
||||||
item.Username = item.Security;
|
item.Username = item.Security;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EConfigType.WireGuard:
|
case EConfigType.WireGuard:
|
||||||
extra = extra with
|
extra = extra with
|
||||||
{
|
{
|
||||||
|
|
@ -357,8 +370,8 @@ public sealed class AppManager
|
||||||
item.Password = item.Id;
|
item.Password = item.Id;
|
||||||
|
|
||||||
item.ConfigVersion = 3;
|
item.ConfigVersion = 3;
|
||||||
await SQLiteHelper.Instance.UpdateAsync(item);
|
|
||||||
batchSuccessCount++;
|
updateProfileItems.Add(item);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
@ -366,10 +379,90 @@ public sealed class AppManager
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only increment offset by the number of failed items that remain in the result set
|
if (updateProfileItems.Count > 0)
|
||||||
// Successfully updated items are automatically excluded from future queries due to ConfigVersion = 3
|
{
|
||||||
offset += batch.Count - batchSuccessCount;
|
try
|
||||||
|
{
|
||||||
|
var count = await SQLiteHelper.Instance.UpdateAllAsync(updateProfileItems);
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.SaveLog($"MigrateProfileExtraGroup update error: {ex}");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> MigrateProfileExtraGroup()
|
||||||
|
{
|
||||||
|
#pragma warning disable CS0618
|
||||||
|
var list = await SQLiteHelper.Instance.TableAsync<ProfileGroupItem>().ToListAsync();
|
||||||
|
var groupItems = new ConcurrentDictionary<string, ProfileGroupItem>(list.Where(t => !string.IsNullOrEmpty(t.IndexId)).ToDictionary(t => t.IndexId!));
|
||||||
|
|
||||||
|
var sql = $"SELECT * FROM ProfileItem WHERE ConfigVersion < 3 AND ConfigType IN ({(int)EConfigType.PolicyGroup}, {(int)EConfigType.ProxyChain})";
|
||||||
|
var items = await SQLiteHelper.Instance.QueryAsync<ProfileItem>(sql);
|
||||||
|
|
||||||
|
if (items is null || items.Count == 0)
|
||||||
|
{
|
||||||
|
Logging.SaveLog("MigrateProfileExtraGroup: No items to migrate.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logging.SaveLog($"MigrateProfileExtraGroup: Found {items.Count} group items to migrate.");
|
||||||
|
|
||||||
|
var updateProfileItems = new List<ProfileItem>();
|
||||||
|
|
||||||
|
foreach (var item in items)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var extra = item.GetProtocolExtra();
|
||||||
|
|
||||||
|
extra = extra with { GroupType = nameof(item.ConfigType) };
|
||||||
|
groupItems.TryGetValue(item.IndexId, out var groupItem);
|
||||||
|
if (groupItem != null && !groupItem.NotHasChild())
|
||||||
|
{
|
||||||
|
extra = extra with
|
||||||
|
{
|
||||||
|
ChildItems = groupItem.ChildItems,
|
||||||
|
SubChildItems = groupItem.SubChildItems,
|
||||||
|
Filter = groupItem.Filter,
|
||||||
|
MultipleLoad = groupItem.MultipleLoad,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
item.SetProtocolExtra(extra);
|
||||||
|
|
||||||
|
item.ConfigVersion = 3;
|
||||||
|
updateProfileItems.Add(item);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.SaveLog($"MigrateProfileExtraGroup item error [{item.IndexId}]: {ex}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateProfileItems.Count > 0)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var count = await SQLiteHelper.Instance.UpdateAllAsync(updateProfileItems);
|
||||||
|
Logging.SaveLog($"MigrateProfileExtraGroup: Successfully updated {updateProfileItems.Count} items.");
|
||||||
|
return updateProfileItems.Count == count;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.SaveLog($"MigrateProfileExtraGroup update error: {ex}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
//await ProfileGroupItemManager.Instance.ClearAll();
|
//await ProfileGroupItemManager.Instance.ClearAll();
|
||||||
#pragma warning restore CS0618
|
#pragma warning restore CS0618
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ public class GroupProfileManager
|
||||||
return await HasCycle(indexId, extraInfo, new HashSet<string>(), new HashSet<string>());
|
return await HasCycle(indexId, extraInfo, new HashSet<string>(), new HashSet<string>());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<bool> HasCycle(string? indexId, ProtocolExtraItem? extraInfo, HashSet<string> visited, HashSet<string> stack)
|
private static async Task<bool> HasCycle(string? indexId, ProtocolExtraItem? extraInfo, HashSet<string> visited, HashSet<string> stack)
|
||||||
{
|
{
|
||||||
if (indexId.IsNullOrEmpty() || extraInfo == null)
|
if (indexId.IsNullOrEmpty() || extraInfo == null)
|
||||||
{
|
{
|
||||||
|
|
@ -81,14 +81,15 @@ public class GroupProfileManager
|
||||||
{
|
{
|
||||||
return new();
|
return new();
|
||||||
}
|
}
|
||||||
var items = await GetSelectedChildProfileItems(protocolExtra);
|
|
||||||
var subItems = await GetSubChildProfileItems(protocolExtra);
|
var items = new List<ProfileItem>();
|
||||||
items.AddRange(subItems);
|
items.AddRange(await GetSubChildProfileItems(protocolExtra));
|
||||||
|
items.AddRange(await GetSelectedChildProfileItems(protocolExtra));
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<List<ProfileItem>> GetSelectedChildProfileItems(ProtocolExtraItem? extra)
|
private static async Task<List<ProfileItem>> GetSelectedChildProfileItems(ProtocolExtraItem? extra)
|
||||||
{
|
{
|
||||||
if (extra == null || extra.ChildItems.IsNullOrEmpty())
|
if (extra == null || extra.ChildItems.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
|
|
@ -105,10 +106,10 @@ public class GroupProfileManager
|
||||||
p.ConfigType != EConfigType.Custom
|
p.ConfigType != EConfigType.Custom
|
||||||
)
|
)
|
||||||
.ToList();
|
.ToList();
|
||||||
return childProfiles;
|
return childProfiles ?? new();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<List<ProfileItem>> GetSubChildProfileItems(ProtocolExtraItem? extra)
|
private static async Task<List<ProfileItem>> GetSubChildProfileItems(ProtocolExtraItem? extra)
|
||||||
{
|
{
|
||||||
if (extra == null || extra.SubChildItems.IsNullOrEmpty())
|
if (extra == null || extra.SubChildItems.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
namespace ServiceLib.Models;
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class ProfileItem : ReactiveObject
|
public class ProfileItem
|
||||||
{
|
{
|
||||||
private ProtocolExtraItem? _protocolExtraCache;
|
private ProtocolExtraItem? _protocolExtraCache;
|
||||||
|
|
||||||
|
|
@ -10,6 +10,7 @@ public class ProfileItem : ReactiveObject
|
||||||
IndexId = string.Empty;
|
IndexId = string.Empty;
|
||||||
ConfigType = EConfigType.VMess;
|
ConfigType = EConfigType.VMess;
|
||||||
ConfigVersion = 3;
|
ConfigVersion = 3;
|
||||||
|
Subid = string.Empty;
|
||||||
Address = string.Empty;
|
Address = string.Empty;
|
||||||
Port = 0;
|
Port = 0;
|
||||||
Password = string.Empty;
|
Password = string.Empty;
|
||||||
|
|
@ -21,7 +22,6 @@ public class ProfileItem : ReactiveObject
|
||||||
Path = string.Empty;
|
Path = string.Empty;
|
||||||
StreamSecurity = string.Empty;
|
StreamSecurity = string.Empty;
|
||||||
AllowInsecure = string.Empty;
|
AllowInsecure = string.Empty;
|
||||||
Subid = string.Empty;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#region function
|
#region function
|
||||||
|
|
@ -148,26 +148,26 @@ public class ProfileItem : ReactiveObject
|
||||||
public string IndexId { get; set; }
|
public string IndexId { get; set; }
|
||||||
|
|
||||||
public EConfigType ConfigType { get; set; }
|
public EConfigType ConfigType { get; set; }
|
||||||
|
public ECoreType? CoreType { get; set; }
|
||||||
public int ConfigVersion { get; set; }
|
public int ConfigVersion { get; set; }
|
||||||
|
public string Subid { get; set; }
|
||||||
|
public bool IsSub { get; set; } = true;
|
||||||
|
public int? PreSocksPort { get; set; }
|
||||||
|
public bool DisplayLog { get; set; } = true;
|
||||||
|
public string Remarks { get; set; }
|
||||||
public string Address { get; set; }
|
public string Address { get; set; }
|
||||||
public int Port { get; set; }
|
public int Port { get; set; }
|
||||||
public string Password { get; set; }
|
public string Password { get; set; }
|
||||||
public string Username { get; set; }
|
public string Username { get; set; }
|
||||||
public string Network { get; set; }
|
public string Network { get; set; }
|
||||||
public string Remarks { get; set; }
|
|
||||||
public string HeaderType { get; set; }
|
public string HeaderType { get; set; }
|
||||||
public string RequestHost { get; set; }
|
public string RequestHost { get; set; }
|
||||||
public string Path { get; set; }
|
public string Path { get; set; }
|
||||||
public string StreamSecurity { get; set; }
|
public string StreamSecurity { get; set; }
|
||||||
public string AllowInsecure { get; set; }
|
public string AllowInsecure { get; set; }
|
||||||
public string Subid { get; set; }
|
|
||||||
public bool IsSub { get; set; } = true;
|
|
||||||
public string Sni { get; set; }
|
public string Sni { get; set; }
|
||||||
public string Alpn { get; set; } = string.Empty;
|
public string Alpn { get; set; } = string.Empty;
|
||||||
public ECoreType? CoreType { get; set; }
|
|
||||||
public int? PreSocksPort { get; set; }
|
|
||||||
public string Fingerprint { get; set; }
|
public string Fingerprint { get; set; }
|
||||||
public bool DisplayLog { get; set; } = true;
|
|
||||||
public string PublicKey { get; set; }
|
public string PublicKey { get; set; }
|
||||||
public string ShortId { get; set; }
|
public string ShortId { get; set; }
|
||||||
public string SpiderX { get; set; }
|
public string SpiderX { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,24 @@
|
||||||
namespace ServiceLib.Models;
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class ProfileItemModel : ProfileItem
|
public class ProfileItemModel : ReactiveObject
|
||||||
{
|
{
|
||||||
public bool IsActive { get; set; }
|
public bool IsActive { get; set; }
|
||||||
|
public string IndexId { get; set; }
|
||||||
|
public EConfigType ConfigType { get; set; }
|
||||||
|
public string Remarks { get; set; }
|
||||||
|
public string Address { get; set; }
|
||||||
|
public int Port { get; set; }
|
||||||
|
public string Network { get; set; }
|
||||||
|
public string StreamSecurity { get; set; }
|
||||||
|
public string Subid { get; set; }
|
||||||
public string SubRemarks { get; set; }
|
public string SubRemarks { get; set; }
|
||||||
|
public int Sort { get; set; }
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public int Delay { get; set; }
|
public int Delay { get; set; }
|
||||||
|
|
||||||
public decimal Speed { get; set; }
|
public decimal Speed { get; set; }
|
||||||
public int Sort { get; set; }
|
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public string DelayVal { get; set; }
|
public string DelayVal { get; set; }
|
||||||
|
|
@ -29,4 +37,15 @@ public class ProfileItemModel : ProfileItem
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public string TotalDown { get; set; }
|
public string TotalDown { get; set; }
|
||||||
|
|
||||||
|
public string GetSummary()
|
||||||
|
{
|
||||||
|
var summary = $"[{ConfigType}] {Remarks}";
|
||||||
|
if (!ConfigType.IsComplexType())
|
||||||
|
{
|
||||||
|
summary += $"({Address}:{Port})";
|
||||||
|
}
|
||||||
|
|
||||||
|
return summary;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
9
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
9
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
|
|
@ -1680,6 +1680,15 @@ namespace ServiceLib.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Configuration item preview 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string menuServerListPreview {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("menuServerListPreview", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Configuration 的本地化字符串。
|
/// 查找类似 Configuration 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -1668,4 +1668,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||||
<data name="TbHopInt7" xml:space="preserve">
|
<data name="TbHopInt7" xml:space="preserve">
|
||||||
<value>Port hopping interval</value>
|
<value>Port hopping interval</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="menuServerListPreview" xml:space="preserve">
|
||||||
|
<value>Configuration item preview</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1665,4 +1665,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||||
<data name="TbHopInt7" xml:space="preserve">
|
<data name="TbHopInt7" xml:space="preserve">
|
||||||
<value>Port hopping interval</value>
|
<value>Port hopping interval</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="menuServerListPreview" xml:space="preserve">
|
||||||
|
<value>Configuration item preview</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1668,4 +1668,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||||
<data name="TbHopInt7" xml:space="preserve">
|
<data name="TbHopInt7" xml:space="preserve">
|
||||||
<value>Port hopping interval</value>
|
<value>Port hopping interval</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="menuServerListPreview" xml:space="preserve">
|
||||||
|
<value>Configuration item preview</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1668,4 +1668,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||||
<data name="TbHopInt7" xml:space="preserve">
|
<data name="TbHopInt7" xml:space="preserve">
|
||||||
<value>Port hopping interval</value>
|
<value>Port hopping interval</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="menuServerListPreview" xml:space="preserve">
|
||||||
|
<value>Configuration item preview</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1668,4 +1668,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||||
<data name="TbHopInt7" xml:space="preserve">
|
<data name="TbHopInt7" xml:space="preserve">
|
||||||
<value>Port hopping interval</value>
|
<value>Port hopping interval</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="menuServerListPreview" xml:space="preserve">
|
||||||
|
<value>Configuration item preview</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1665,4 +1665,7 @@
|
||||||
<data name="TbHopInt7" xml:space="preserve">
|
<data name="TbHopInt7" xml:space="preserve">
|
||||||
<value>端口跳跃间隔</value>
|
<value>端口跳跃间隔</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="menuServerListPreview" xml:space="preserve">
|
||||||
|
<value>子配置项预览</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1665,4 +1665,7 @@
|
||||||
<data name="TbHopInt7" xml:space="preserve">
|
<data name="TbHopInt7" xml:space="preserve">
|
||||||
<value>Port hopping interval</value>
|
<value>Port hopping interval</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="menuServerListPreview" xml:space="preserve">
|
||||||
|
<value>Configuration item preview</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -27,6 +27,8 @@ public class AddGroupServerViewModel : MyReactiveObject
|
||||||
|
|
||||||
public IObservableCollection<ProfileItem> ChildItemsObs { get; } = new ObservableCollectionExtended<ProfileItem>();
|
public IObservableCollection<ProfileItem> ChildItemsObs { get; } = new ObservableCollectionExtended<ProfileItem>();
|
||||||
|
|
||||||
|
public IObservableCollection<ProfileItem> AllProfilePreviewItemsObs { get; } = new ObservableCollectionExtended<ProfileItem>();
|
||||||
|
|
||||||
//public ReactiveCommand<Unit, Unit> AddCmd { get; }
|
//public ReactiveCommand<Unit, Unit> AddCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> RemoveCmd { get; }
|
public ReactiveCommand<Unit, Unit> RemoveCmd { get; }
|
||||||
|
|
||||||
|
|
@ -182,6 +184,32 @@ public class AddGroupServerViewModel : MyReactiveObject
|
||||||
await Task.CompletedTask;
|
await Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ProtocolExtraItem GetUpdatedProtocolExtra()
|
||||||
|
{
|
||||||
|
return SelectedSource.GetProtocolExtra() with
|
||||||
|
{
|
||||||
|
ChildItems =
|
||||||
|
Utils.List2String(ChildItemsObs.Where(s => !s.IndexId.IsNullOrEmpty()).Select(s => s.IndexId).ToList()),
|
||||||
|
MultipleLoad = PolicyGroupType switch
|
||||||
|
{
|
||||||
|
var s when s == ResUI.TbLeastPing => EMultipleLoad.LeastPing,
|
||||||
|
var s when s == ResUI.TbFallback => EMultipleLoad.Fallback,
|
||||||
|
var s when s == ResUI.TbRandom => EMultipleLoad.Random,
|
||||||
|
var s when s == ResUI.TbRoundRobin => EMultipleLoad.RoundRobin,
|
||||||
|
var s when s == ResUI.TbLeastLoad => EMultipleLoad.LeastLoad,
|
||||||
|
_ => EMultipleLoad.LeastPing,
|
||||||
|
},
|
||||||
|
SubChildItems = SelectedSubItem?.Id,
|
||||||
|
Filter = Filter,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdatePreviewList()
|
||||||
|
{
|
||||||
|
AllProfilePreviewItemsObs.Clear();
|
||||||
|
AllProfilePreviewItemsObs.AddRange(await GroupProfileManager.GetChildProfileItemsByProtocolExtra(GetUpdatedProtocolExtra()));
|
||||||
|
}
|
||||||
|
|
||||||
private async Task SaveServerAsync()
|
private async Task SaveServerAsync()
|
||||||
{
|
{
|
||||||
var remarks = SelectedSource.Remarks;
|
var remarks = SelectedSource.Remarks;
|
||||||
|
|
@ -202,24 +230,11 @@ public class AddGroupServerViewModel : MyReactiveObject
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectedSource.SetProtocolExtra(SelectedSource.GetProtocolExtra() with
|
var protocolExtra = GetUpdatedProtocolExtra();
|
||||||
{
|
|
||||||
ChildItems =
|
|
||||||
Utils.List2String(ChildItemsObs.Where(s => !s.IndexId.IsNullOrEmpty()).Select(s => s.IndexId).ToList()),
|
|
||||||
MultipleLoad = PolicyGroupType switch
|
|
||||||
{
|
|
||||||
var s when s == ResUI.TbLeastPing => EMultipleLoad.LeastPing,
|
|
||||||
var s when s == ResUI.TbFallback => EMultipleLoad.Fallback,
|
|
||||||
var s when s == ResUI.TbRandom => EMultipleLoad.Random,
|
|
||||||
var s when s == ResUI.TbRoundRobin => EMultipleLoad.RoundRobin,
|
|
||||||
var s when s == ResUI.TbLeastLoad => EMultipleLoad.LeastLoad,
|
|
||||||
_ => EMultipleLoad.LeastPing,
|
|
||||||
},
|
|
||||||
SubChildItems = SelectedSubItem?.Id,
|
|
||||||
Filter = Filter,
|
|
||||||
});
|
|
||||||
|
|
||||||
var hasCycle = await GroupProfileManager.HasCycle(SelectedSource.IndexId, SelectedSource.GetProtocolExtra());
|
SelectedSource.SetProtocolExtra(protocolExtra);
|
||||||
|
|
||||||
|
var hasCycle = await GroupProfileManager.HasCycle(SelectedSource.IndexId, protocolExtra);
|
||||||
if (hasCycle)
|
if (hasCycle)
|
||||||
{
|
{
|
||||||
NoticeManager.Instance.Enqueue(string.Format(ResUI.GroupSelfReference, remarks));
|
NoticeManager.Instance.Enqueue(string.Format(ResUI.GroupSelfReference, remarks));
|
||||||
|
|
|
||||||
|
|
@ -49,12 +49,15 @@ public class AddServerViewModel : MyReactiveObject
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public string WgPublicKey { get; set; }
|
public string WgPublicKey { get; set; }
|
||||||
|
|
||||||
//[Reactive]
|
//[Reactive]
|
||||||
//public string WgPresharedKey { get; set; }
|
//public string WgPresharedKey { get; set; }
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public string WgInterfaceAddress { get; set; }
|
public string WgInterfaceAddress { get; set; }
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public string WgReserved { get; set; }
|
public string WgReserved { get; set; }
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public int WgMtu { get; set; }
|
public int WgMtu { get; set; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -200,7 +200,7 @@ public class ProfilesSelectViewModel : MyReactiveObject
|
||||||
|
|
||||||
private async Task<List<ProfileItemModel>?> GetProfileItemsEx(string subid, string filter)
|
private async Task<List<ProfileItemModel>?> GetProfileItemsEx(string subid, string filter)
|
||||||
{
|
{
|
||||||
var lstModel = await AppManager.Instance.ProfileItems(_subIndexId, filter);
|
var lstModel = await AppManager.Instance.ProfileModels(_subIndexId, filter);
|
||||||
lstModel = (from t in lstModel
|
lstModel = (from t in lstModel
|
||||||
select new ProfileItemModel
|
select new ProfileItemModel
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -428,7 +428,7 @@ public class ProfilesViewModel : MyReactiveObject
|
||||||
|
|
||||||
private async Task<List<ProfileItemModel>?> GetProfileItemsEx(string subid, string filter)
|
private async Task<List<ProfileItemModel>?> GetProfileItemsEx(string subid, string filter)
|
||||||
{
|
{
|
||||||
var lstModel = await AppManager.Instance.ProfileItems(_config.SubIndexId, filter);
|
var lstModel = await AppManager.Instance.ProfileModels(_config.SubIndexId, filter);
|
||||||
|
|
||||||
await ConfigHandler.SetDefaultServer(_config, lstModel);
|
await ConfigHandler.SetDefaultServer(_config, lstModel);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -303,7 +303,7 @@ public class StatusBarViewModel : MyReactiveObject
|
||||||
|
|
||||||
private async Task RefreshServersMenu()
|
private async Task RefreshServersMenu()
|
||||||
{
|
{
|
||||||
var lstModel = await AppManager.Instance.ProfileItems(_config.SubIndexId, "");
|
var lstModel = await AppManager.Instance.ProfileModels(_config.SubIndexId, "");
|
||||||
|
|
||||||
Servers.Clear();
|
Servers.Clear();
|
||||||
if (lstModel.Count > _config.GuiItem.TrayMenuServersLimit)
|
if (lstModel.Count > _config.GuiItem.TrayMenuServersLimit)
|
||||||
|
|
@ -315,7 +315,7 @@ public class StatusBarViewModel : MyReactiveObject
|
||||||
BlServers = true;
|
BlServers = true;
|
||||||
for (var k = 0; k < lstModel.Count; k++)
|
for (var k = 0; k < lstModel.Count; k++)
|
||||||
{
|
{
|
||||||
ProfileItem it = lstModel[k];
|
var it = lstModel[k];
|
||||||
var name = it.GetSummary();
|
var name = it.GetSummary();
|
||||||
|
|
||||||
var item = new ComboItem() { ID = it.IndexId, Text = name };
|
var item = new ComboItem() { ID = it.IndexId, Text = name };
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,10 @@
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<TabControl HorizontalContentAlignment="Stretch" DockPanel.Dock="Top">
|
<TabControl
|
||||||
|
x:Name="tabControl"
|
||||||
|
HorizontalContentAlignment="Stretch"
|
||||||
|
DockPanel.Dock="Top">
|
||||||
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.menuServerList}">
|
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.menuServerList}">
|
||||||
<Grid
|
<Grid
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
|
|
@ -134,7 +137,6 @@
|
||||||
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.menuServerList2}">
|
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.menuServerList2}">
|
||||||
<DataGrid
|
<DataGrid
|
||||||
x:Name="lstChild"
|
x:Name="lstChild"
|
||||||
Grid.Row="1"
|
|
||||||
AutoGenerateColumns="False"
|
AutoGenerateColumns="False"
|
||||||
Background="Transparent"
|
Background="Transparent"
|
||||||
BorderThickness="1"
|
BorderThickness="1"
|
||||||
|
|
@ -204,6 +206,48 @@
|
||||||
</DataGrid.Columns>
|
</DataGrid.Columns>
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.menuServerListPreview}">
|
||||||
|
<DataGrid
|
||||||
|
x:Name="lstPreviewChild"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
Background="Transparent"
|
||||||
|
BorderThickness="1"
|
||||||
|
CanUserReorderColumns="False"
|
||||||
|
CanUserResizeColumns="True"
|
||||||
|
CanUserSortColumns="False"
|
||||||
|
GridLinesVisibility="All"
|
||||||
|
HeadersVisibility="Column"
|
||||||
|
IsReadOnly="True"
|
||||||
|
ItemsSource="{Binding AllProfilePreviewItemsObs}"
|
||||||
|
SelectionMode="Extended">
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="150"
|
||||||
|
Binding="{Binding ConfigType}"
|
||||||
|
Header="{x:Static resx:ResUI.LvServiceType}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="150"
|
||||||
|
Binding="{Binding Remarks}"
|
||||||
|
Header="{x:Static resx:ResUI.LvRemarks}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="120"
|
||||||
|
Binding="{Binding Address}"
|
||||||
|
Header="{x:Static resx:ResUI.LvAddress}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="100"
|
||||||
|
Binding="{Binding Port}"
|
||||||
|
Header="{x:Static resx:ResUI.LvPort}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="100"
|
||||||
|
Binding="{Binding Network}"
|
||||||
|
Header="{x:Static resx:ResUI.LvTransportProtocol}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="100"
|
||||||
|
Binding="{Binding StreamSecurity}"
|
||||||
|
Header="{x:Static resx:ResUI.LvTLS}" />
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
</TabItem>
|
||||||
</TabControl>
|
</TabControl>
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
</Window>
|
</Window>
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
|
||||||
Loaded += Window_Loaded;
|
Loaded += Window_Loaded;
|
||||||
btnCancel.Click += (s, e) => Close();
|
btnCancel.Click += (s, e) => Close();
|
||||||
lstChild.SelectionChanged += LstChild_SelectionChanged;
|
lstChild.SelectionChanged += LstChild_SelectionChanged;
|
||||||
|
tabControl.SelectionChanged += TabControl_SelectionChanged;
|
||||||
|
|
||||||
ViewModel = new AddGroupServerViewModel(profileItem, UpdateViewHandler);
|
ViewModel = new AddGroupServerViewModel(profileItem, UpdateViewHandler);
|
||||||
|
|
||||||
|
|
@ -38,6 +39,10 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
|
||||||
case EConfigType.ProxyChain:
|
case EConfigType.ProxyChain:
|
||||||
Title = ResUI.TbConfigTypeProxyChain;
|
Title = ResUI.TbConfigTypeProxyChain;
|
||||||
gridPolicyGroup.IsVisible = false;
|
gridPolicyGroup.IsVisible = false;
|
||||||
|
if (tabControl.Items.Count > 0)
|
||||||
|
{
|
||||||
|
tabControl.Items.RemoveAt(0);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,7 +55,6 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSubItem, v => v.cmbSubChildItems.SelectedItem).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSubItem, v => v.cmbSubChildItems.SelectedItem).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.Filter, v => v.txtFilter.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.Filter, v => v.txtFilter.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
this.OneWayBind(ViewModel, vm => vm.ChildItemsObs, v => v.lstChild.ItemsSource).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedChild, v => v.lstChild.SelectedItem).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedChild, v => v.lstChild.SelectedItem).DisposeWith(disposables);
|
||||||
|
|
||||||
this.BindCommand(ViewModel, vm => vm.RemoveCmd, v => v.menuRemoveChildServer).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.RemoveCmd, v => v.menuRemoveChildServer).DisposeWith(disposables);
|
||||||
|
|
@ -167,4 +171,29 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
|
||||||
ViewModel.SelectedChildren = lstChild.SelectedItems.Cast<ProfileItem>().ToList();
|
ViewModel.SelectedChildren = lstChild.SelectedItems.Cast<ProfileItem>().ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void TabControl_SelectionChanged(object? sender, SelectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (e.Source is not TabControl tc)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!(tc.SelectedIndex == tc.Items.Count - 1 && tc.Items.Count > 0))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ViewModel == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ViewModel.UpdatePreviewList();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,7 @@
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<TabControl
|
<TabControl
|
||||||
|
x:Name="tabControl"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
HorizontalContentAlignment="Left"
|
HorizontalContentAlignment="Left"
|
||||||
DockPanel.Dock="Top">
|
DockPanel.Dock="Top">
|
||||||
|
|
@ -272,6 +273,47 @@
|
||||||
</DataGrid.Columns>
|
</DataGrid.Columns>
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.menuServerListPreview}">
|
||||||
|
<DataGrid
|
||||||
|
x:Name="lstPreviewChild"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
BorderThickness="1"
|
||||||
|
CanUserAddRows="False"
|
||||||
|
CanUserResizeRows="False"
|
||||||
|
CanUserSortColumns="False"
|
||||||
|
EnableRowVirtualization="True"
|
||||||
|
GridLinesVisibility="All"
|
||||||
|
HeadersVisibility="Column"
|
||||||
|
IsReadOnly="True"
|
||||||
|
Style="{StaticResource DefDataGrid}">
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="150"
|
||||||
|
Binding="{Binding ConfigType}"
|
||||||
|
Header="{x:Static resx:ResUI.LvServiceType}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="150"
|
||||||
|
Binding="{Binding Remarks}"
|
||||||
|
Header="{x:Static resx:ResUI.LvRemarks}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="120"
|
||||||
|
Binding="{Binding Address}"
|
||||||
|
Header="{x:Static resx:ResUI.LvAddress}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="100"
|
||||||
|
Binding="{Binding Port}"
|
||||||
|
Header="{x:Static resx:ResUI.LvPort}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="100"
|
||||||
|
Binding="{Binding Network}"
|
||||||
|
Header="{x:Static resx:ResUI.LvTransportProtocol}" />
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="100"
|
||||||
|
Binding="{Binding StreamSecurity}"
|
||||||
|
Header="{x:Static resx:ResUI.LvTLS}" />
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
</TabItem>
|
||||||
</TabControl>
|
</TabControl>
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
</base:WindowBase>
|
</base:WindowBase>
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ public partial class AddGroupServerWindow
|
||||||
PreviewKeyDown += AddGroupServerWindow_PreviewKeyDown;
|
PreviewKeyDown += AddGroupServerWindow_PreviewKeyDown;
|
||||||
lstChild.SelectionChanged += LstChild_SelectionChanged;
|
lstChild.SelectionChanged += LstChild_SelectionChanged;
|
||||||
menuSelectAllChild.Click += MenuSelectAllChild_Click;
|
menuSelectAllChild.Click += MenuSelectAllChild_Click;
|
||||||
|
tabControl.SelectionChanged += TabControl_SelectionChanged;
|
||||||
|
|
||||||
ViewModel = new AddGroupServerViewModel(profileItem, UpdateViewHandler);
|
ViewModel = new AddGroupServerViewModel(profileItem, UpdateViewHandler);
|
||||||
|
|
||||||
|
|
@ -33,6 +34,10 @@ public partial class AddGroupServerWindow
|
||||||
case EConfigType.ProxyChain:
|
case EConfigType.ProxyChain:
|
||||||
Title = ResUI.TbConfigTypeProxyChain;
|
Title = ResUI.TbConfigTypeProxyChain;
|
||||||
gridPolicyGroup.Visibility = Visibility.Collapsed;
|
gridPolicyGroup.Visibility = Visibility.Collapsed;
|
||||||
|
if (tabControl.Items.Count > 0)
|
||||||
|
{
|
||||||
|
tabControl.Items.RemoveAt(0);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -48,6 +53,8 @@ public partial class AddGroupServerWindow
|
||||||
this.OneWayBind(ViewModel, vm => vm.ChildItemsObs, v => v.lstChild.ItemsSource).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.ChildItemsObs, v => v.lstChild.ItemsSource).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedChild, v => v.lstChild.SelectedItem).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedChild, v => v.lstChild.SelectedItem).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.OneWayBind(ViewModel, vm => vm.AllProfilePreviewItemsObs, v => v.lstPreviewChild.ItemsSource).DisposeWith(disposables);
|
||||||
|
|
||||||
this.BindCommand(ViewModel, vm => vm.RemoveCmd, v => v.menuRemoveChildServer).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.RemoveCmd, v => v.menuRemoveChildServer).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.MoveTopCmd, v => v.menuMoveTop).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.MoveTopCmd, v => v.menuMoveTop).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.MoveUpCmd, v => v.menuMoveUp).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.MoveUpCmd, v => v.menuMoveUp).DisposeWith(disposables);
|
||||||
|
|
@ -148,4 +155,29 @@ public partial class AddGroupServerWindow
|
||||||
{
|
{
|
||||||
lstChild.SelectAll();
|
lstChild.SelectAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void TabControl_SelectionChanged(object? sender, System.Windows.Controls.SelectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (e.Source is not System.Windows.Controls.TabControl tc)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!(tc.SelectedIndex == tc.Items.Count - 1 && tc.Items.Count > 0))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ViewModel == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ViewModel.UpdatePreviewList();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue