v2rayN/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs

849 lines
26 KiB
C#
Raw Normal View History

2025-01-30 09:10:05 +00:00
using System.Reactive;
2025-08-30 10:01:01 +00:00
using System.Reactive.Disposables;
2025-01-30 09:10:05 +00:00
using System.Reactive.Linq;
using System.Text;
2024-07-18 09:39:11 +00:00
using DynamicData;
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using Splat;
namespace ServiceLib.ViewModels;
public class ProfilesViewModel : MyReactiveObject
2024-07-18 09:39:11 +00:00
{
#region private prop
2024-07-18 09:39:11 +00:00
private List<ProfileItem> _lstProfile;
private string _serverFilter = string.Empty;
private Dictionary<string, bool> _dicHeaderSort = new();
private SpeedtestService? _speedtestService;
2024-07-18 09:39:11 +00:00
#endregion private prop
2024-07-18 09:39:11 +00:00
#region ObservableCollection
2024-07-18 09:39:11 +00:00
2025-08-31 07:41:25 +00:00
public IObservableCollection<ProfileItemModel> ProfileItems { get; } = new ObservableCollectionExtended<ProfileItemModel>();
2024-07-18 09:39:11 +00:00
2025-08-31 07:41:25 +00:00
public IObservableCollection<SubItem> SubItems { get; } = new ObservableCollectionExtended<SubItem>();
2024-07-18 09:39:11 +00:00
[Reactive]
public ProfileItemModel SelectedProfile { get; set; }
2024-07-18 09:39:11 +00:00
public IList<ProfileItemModel> SelectedProfiles { get; set; }
2024-07-18 09:39:11 +00:00
[Reactive]
public SubItem SelectedSub { get; set; }
2024-07-18 09:39:11 +00:00
[Reactive]
public SubItem SelectedMoveToGroup { get; set; }
2024-07-18 09:39:11 +00:00
[Reactive]
public string ServerFilter { get; set; }
2024-07-18 09:39:11 +00:00
#endregion ObservableCollection
2024-07-18 09:39:11 +00:00
#region Menu
2024-07-18 09:39:11 +00:00
//servers delete
public ReactiveCommand<Unit, Unit> EditServerCmd { get; }
2024-07-18 09:39:11 +00:00
public ReactiveCommand<Unit, Unit> RemoveServerCmd { get; }
public ReactiveCommand<Unit, Unit> RemoveDuplicateServerCmd { get; }
public ReactiveCommand<Unit, Unit> CopyServerCmd { get; }
public ReactiveCommand<Unit, Unit> SetDefaultServerCmd { get; }
public ReactiveCommand<Unit, Unit> ShareServerCmd { get; }
2025-09-11 04:44:18 +00:00
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerXrayRandomCmd { get; }
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerXrayRoundRobinCmd { get; }
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerXrayLeastPingCmd { get; }
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerXrayLeastLoadCmd { get; }
public ReactiveCommand<Unit, Unit> GenGroupMultipleServerSingBoxLeastPingCmd { get; }
2024-10-12 11:46:06 +00:00
//servers move
public ReactiveCommand<Unit, Unit> MoveTopCmd { get; }
2024-07-18 09:39:11 +00:00
public ReactiveCommand<Unit, Unit> MoveUpCmd { get; }
public ReactiveCommand<Unit, Unit> MoveDownCmd { get; }
public ReactiveCommand<Unit, Unit> MoveBottomCmd { get; }
2024-10-12 11:46:06 +00:00
//servers ping
public ReactiveCommand<Unit, Unit> MixedTestServerCmd { get; }
2024-07-18 09:39:11 +00:00
public ReactiveCommand<Unit, Unit> TcpingServerCmd { get; }
public ReactiveCommand<Unit, Unit> RealPingServerCmd { get; }
public ReactiveCommand<Unit, Unit> SpeedServerCmd { get; }
public ReactiveCommand<Unit, Unit> SortServerResultCmd { get; }
public ReactiveCommand<Unit, Unit> RemoveInvalidServerResultCmd { get; }
//servers export
public ReactiveCommand<Unit, Unit> Export2ClientConfigCmd { get; }
2024-10-12 11:46:06 +00:00
public ReactiveCommand<Unit, Unit> Export2ClientConfigClipboardCmd { get; }
public ReactiveCommand<Unit, Unit> Export2ShareUrlCmd { get; }
public ReactiveCommand<Unit, Unit> Export2ShareUrlBase64Cmd { get; }
2024-07-18 09:39:11 +00:00
public ReactiveCommand<Unit, Unit> AddSubCmd { get; }
public ReactiveCommand<Unit, Unit> EditSubCmd { get; }
2024-07-18 09:39:11 +00:00
#endregion Menu
2024-07-18 09:39:11 +00:00
#region Init
2024-07-18 09:39:11 +00:00
public ProfilesViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
{
2025-08-17 09:31:55 +00:00
_config = AppManager.Instance.Config;
_updateView = updateView;
#region WhenAnyValue && ReactiveCommand
var canEditRemove = this.WhenAnyValue(
x => x.SelectedProfile,
selectedSource => selectedSource != null && !selectedSource.IndexId.IsNullOrEmpty());
this.WhenAnyValue(
x => x.SelectedSub,
y => y != null && !y.Remarks.IsNullOrEmpty() && _config.SubIndexId != y.Id)
.Subscribe(async c => await SubSelectedChangedAsync(c));
this.WhenAnyValue(
x => x.SelectedMoveToGroup,
y => y != null && !y.Remarks.IsNullOrEmpty())
.Subscribe(async c => await MoveToGroup(c));
this.WhenAnyValue(
x => x.ServerFilter,
y => y != null && _serverFilter != y)
.Subscribe(async c => await ServerFilterChanged(c));
//servers delete
EditServerCmd = ReactiveCommand.CreateFromTask(async () =>
{
await EditServerAsync(EConfigType.Custom);
}, canEditRemove);
RemoveServerCmd = ReactiveCommand.CreateFromTask(async () =>
{
await RemoveServerAsync();
}, canEditRemove);
RemoveDuplicateServerCmd = ReactiveCommand.CreateFromTask(async () =>
{
await RemoveDuplicateServer();
});
CopyServerCmd = ReactiveCommand.CreateFromTask(async () =>
{
await CopyServer();
}, canEditRemove);
SetDefaultServerCmd = ReactiveCommand.CreateFromTask(async () =>
2024-07-18 09:39:11 +00:00
{
await SetDefaultServer();
}, canEditRemove);
ShareServerCmd = ReactiveCommand.CreateFromTask(async () =>
{
await ShareServerAsync();
}, canEditRemove);
2025-09-11 04:44:18 +00:00
GenGroupMultipleServerXrayRandomCmd = ReactiveCommand.CreateFromTask(async () =>
{
2025-09-11 04:44:18 +00:00
await GenGroupMultipleServer(ECoreType.Xray, EMultipleLoad.Random);
}, canEditRemove);
2025-09-11 04:44:18 +00:00
GenGroupMultipleServerXrayRoundRobinCmd = ReactiveCommand.CreateFromTask(async () =>
{
2025-09-11 04:44:18 +00:00
await GenGroupMultipleServer(ECoreType.Xray, EMultipleLoad.RoundRobin);
}, canEditRemove);
2025-09-11 04:44:18 +00:00
GenGroupMultipleServerXrayLeastPingCmd = ReactiveCommand.CreateFromTask(async () =>
{
2025-09-11 04:44:18 +00:00
await GenGroupMultipleServer(ECoreType.Xray, EMultipleLoad.LeastPing);
}, canEditRemove);
2025-09-11 04:44:18 +00:00
GenGroupMultipleServerXrayLeastLoadCmd = ReactiveCommand.CreateFromTask(async () =>
{
2025-09-11 04:44:18 +00:00
await GenGroupMultipleServer(ECoreType.Xray, EMultipleLoad.LeastLoad);
}, canEditRemove);
2025-09-11 04:44:18 +00:00
GenGroupMultipleServerSingBoxLeastPingCmd = ReactiveCommand.CreateFromTask(async () =>
{
2025-09-11 04:44:18 +00:00
await GenGroupMultipleServer(ECoreType.sing_box, EMultipleLoad.LeastPing);
}, canEditRemove);
2024-07-18 09:39:11 +00:00
//servers move
MoveTopCmd = ReactiveCommand.CreateFromTask(async () =>
{
await MoveServer(EMove.Top);
}, canEditRemove);
MoveUpCmd = ReactiveCommand.CreateFromTask(async () =>
{
await MoveServer(EMove.Up);
}, canEditRemove);
MoveDownCmd = ReactiveCommand.CreateFromTask(async () =>
{
await MoveServer(EMove.Down);
}, canEditRemove);
MoveBottomCmd = ReactiveCommand.CreateFromTask(async () =>
{
await MoveServer(EMove.Bottom);
}, canEditRemove);
2024-07-18 09:39:11 +00:00
//servers ping
MixedTestServerCmd = ReactiveCommand.CreateFromTask(async () =>
{
await ServerSpeedtest(ESpeedActionType.Mixedtest);
});
TcpingServerCmd = ReactiveCommand.CreateFromTask(async () =>
{
await ServerSpeedtest(ESpeedActionType.Tcping);
}, canEditRemove);
RealPingServerCmd = ReactiveCommand.CreateFromTask(async () =>
{
await ServerSpeedtest(ESpeedActionType.Realping);
}, canEditRemove);
SpeedServerCmd = ReactiveCommand.CreateFromTask(async () =>
{
await ServerSpeedtest(ESpeedActionType.Speedtest);
}, canEditRemove);
SortServerResultCmd = ReactiveCommand.CreateFromTask(async () =>
{
await SortServer(EServerColName.DelayVal.ToString());
});
RemoveInvalidServerResultCmd = ReactiveCommand.CreateFromTask(async () =>
{
await RemoveInvalidServerResult();
});
//servers export
Export2ClientConfigCmd = ReactiveCommand.CreateFromTask(async () =>
{
await Export2ClientConfigAsync(false);
}, canEditRemove);
Export2ClientConfigClipboardCmd = ReactiveCommand.CreateFromTask(async () =>
{
await Export2ClientConfigAsync(true);
}, canEditRemove);
Export2ShareUrlCmd = ReactiveCommand.CreateFromTask(async () =>
{
await Export2ShareUrlAsync(false);
}, canEditRemove);
Export2ShareUrlBase64Cmd = ReactiveCommand.CreateFromTask(async () =>
{
await Export2ShareUrlAsync(true);
}, canEditRemove);
2024-07-18 09:39:11 +00:00
//Subscription
AddSubCmd = ReactiveCommand.CreateFromTask(async () =>
{
await EditSubAsync(true);
});
EditSubCmd = ReactiveCommand.CreateFromTask(async () =>
{
await EditSubAsync(false);
});
2024-07-18 09:39:11 +00:00
#endregion WhenAnyValue && ReactiveCommand
2024-07-18 09:39:11 +00:00
2025-08-30 10:01:01 +00:00
#region AppEvents
AppEvents.ProfilesRefreshRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
2025-08-30 10:01:01 +00:00
.Subscribe(async _ => await RefreshServersBiz());
AppEvents.DispatcherStatisticsRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async result => await UpdateStatistics(result));
#endregion AppEvents
2024-07-18 09:39:11 +00:00
_ = Init();
}
2024-07-21 03:26:10 +00:00
private async Task Init()
{
SelectedProfile = new();
SelectedSub = new();
SelectedMoveToGroup = new();
2024-07-18 09:39:11 +00:00
await RefreshSubscriptions();
//await RefreshServers();
}
2024-07-18 09:39:11 +00:00
#endregion Init
2024-07-18 09:39:11 +00:00
#region Actions
2024-10-21 05:46:13 +00:00
private void Reload()
{
Locator.Current.GetService<MainWindowViewModel>()?.Reload();
}
2024-10-21 05:46:13 +00:00
2025-08-30 10:01:01 +00:00
public async Task SetSpeedTestResult(SpeedTestResult result)
{
if (result.IndexId.IsNullOrEmpty())
2024-10-21 05:46:13 +00:00
{
2025-08-17 09:31:55 +00:00
NoticeManager.Instance.SendMessageEx(result.Delay);
NoticeManager.Instance.Enqueue(result.Delay);
return;
2024-07-18 09:39:11 +00:00
}
2025-08-31 07:41:25 +00:00
var item = ProfileItems.FirstOrDefault(it => it.IndexId == result.IndexId);
if (item == null)
2024-10-21 05:46:13 +00:00
{
return;
2024-10-21 05:46:13 +00:00
}
if (result.Delay.IsNotEmpty())
2024-07-18 09:39:11 +00:00
{
int.TryParse(result.Delay, out var temp);
item.Delay = temp;
item.DelayVal = result.Delay ?? string.Empty;
2024-07-18 09:39:11 +00:00
}
if (result.Speed.IsNotEmpty())
2024-07-18 09:39:11 +00:00
{
item.SpeedVal = result.Speed ?? string.Empty;
2024-07-18 09:39:11 +00:00
}
}
2024-07-18 09:39:11 +00:00
2025-08-30 10:01:01 +00:00
public async Task UpdateStatistics(ServerSpeedItem update)
{
2025-08-30 10:01:01 +00:00
if (!_config.GuiItem.EnableStatistics
|| (update.ProxyUp + update.ProxyDown) <= 0
|| DateTime.Now.Second % 3 != 0)
{
return;
}
try
2024-07-18 09:39:11 +00:00
{
2025-08-31 07:41:25 +00:00
var item = ProfileItems.FirstOrDefault(it => it.IndexId == update.IndexId);
if (item != null)
2024-07-18 09:39:11 +00:00
{
item.TodayDown = Utils.HumanFy(update.TodayDown);
item.TodayUp = Utils.HumanFy(update.TodayUp);
item.TotalDown = Utils.HumanFy(update.TotalDown);
item.TotalUp = Utils.HumanFy(update.TotalUp);
2024-07-18 09:39:11 +00:00
}
}
catch
{
}
}
#endregion Actions
2024-07-18 09:39:11 +00:00
#region Servers && Groups
private async Task SubSelectedChangedAsync(bool c)
{
if (!c)
2024-07-18 09:39:11 +00:00
{
return;
}
_config.SubIndexId = SelectedSub?.Id;
2024-07-18 09:39:11 +00:00
await RefreshServers();
2024-07-18 09:39:11 +00:00
await _updateView?.Invoke(EViewAction.ProfilesFocus, null);
}
2024-07-18 09:39:11 +00:00
private async Task ServerFilterChanged(bool c)
{
if (!c)
2024-07-18 09:39:11 +00:00
{
return;
2024-07-18 09:39:11 +00:00
}
_serverFilter = ServerFilter;
if (_serverFilter.IsNullOrEmpty())
2024-07-18 09:39:11 +00:00
{
await RefreshServers();
2024-07-18 09:39:11 +00:00
}
}
2024-07-18 09:39:11 +00:00
public async Task RefreshServers()
{
AppEvents.ProfilesRefreshRequested.OnNext(Unit.Default);
await Task.Delay(200);
}
2024-07-18 09:39:11 +00:00
private async Task RefreshServersBiz()
{
var lstModel = await GetProfileItemsEx(_config.SubIndexId, _serverFilter);
_lstProfile = JsonUtils.Deserialize<List<ProfileItem>>(JsonUtils.Serialize(lstModel)) ?? [];
2024-07-18 09:39:11 +00:00
2025-08-31 07:41:25 +00:00
ProfileItems.Clear();
ProfileItems.AddRange(lstModel);
if (lstModel.Count > 0)
2024-07-18 09:39:11 +00:00
{
var selected = lstModel.FirstOrDefault(t => t.IndexId == _config.IndexId);
if (selected != null)
2024-07-18 09:39:11 +00:00
{
SelectedProfile = selected;
2024-07-18 09:39:11 +00:00
}
else
{
SelectedProfile = lstModel.First();
2024-07-18 09:39:11 +00:00
}
}
await _updateView?.Invoke(EViewAction.DispatcherRefreshServersBiz, null);
}
2024-07-18 09:39:11 +00:00
public async Task RefreshSubscriptions()
{
2025-08-31 07:41:25 +00:00
SubItems.Clear();
2024-07-18 09:39:11 +00:00
2025-08-31 07:41:25 +00:00
SubItems.Add(new SubItem { Remarks = ResUI.AllGroupServers });
2024-07-18 09:39:11 +00:00
2025-08-17 09:31:55 +00:00
foreach (var item in await AppManager.Instance.SubItems())
{
2025-08-31 07:41:25 +00:00
SubItems.Add(item);
}
2025-08-31 07:41:25 +00:00
if (_config.SubIndexId != null && SubItems.FirstOrDefault(t => t.Id == _config.SubIndexId) != null)
{
2025-08-31 07:41:25 +00:00
SelectedSub = SubItems.FirstOrDefault(t => t.Id == _config.SubIndexId);
}
else
{
2025-08-31 07:41:25 +00:00
SelectedSub = SubItems.First();
2024-07-18 09:39:11 +00:00
}
}
private async Task<List<ProfileItemModel>?> GetProfileItemsEx(string subid, string filter)
{
2025-08-17 09:31:55 +00:00
var lstModel = await AppManager.Instance.ProfileItems(_config.SubIndexId, filter);
await ConfigHandler.SetDefaultServer(_config, lstModel);
2025-08-17 09:31:55 +00:00
var lstServerStat = (_config.GuiItem.EnableStatistics ? StatisticsManager.Instance.ServerStat : null) ?? [];
var lstProfileExs = await ProfileExManager.Instance.GetProfileExs();
lstModel = (from t in lstModel
join t2 in lstServerStat on t.IndexId equals t2.IndexId into t2b
from t22 in t2b.DefaultIfEmpty()
join t3 in lstProfileExs on t.IndexId equals t3.IndexId into t3b
from t33 in t3b.DefaultIfEmpty()
select new ProfileItemModel
{
IndexId = t.IndexId,
ConfigType = t.ConfigType,
Remarks = t.Remarks,
Address = t.Address,
Port = t.Port,
Security = t.Security,
Network = t.Network,
StreamSecurity = t.StreamSecurity,
Subid = t.Subid,
SubRemarks = t.SubRemarks,
IsActive = t.IndexId == _config.IndexId,
Sort = t33?.Sort ?? 0,
Delay = t33?.Delay ?? 0,
Speed = t33?.Speed ?? 0,
DelayVal = t33?.Delay != 0 ? $"{t33?.Delay}" : string.Empty,
SpeedVal = t33?.Speed > 0 ? $"{t33?.Speed}" : t33?.Message ?? string.Empty,
TodayDown = t22 == null ? "" : Utils.HumanFy(t22.TodayDown),
TodayUp = t22 == null ? "" : Utils.HumanFy(t22.TodayUp),
TotalDown = t22 == null ? "" : Utils.HumanFy(t22.TotalDown),
TotalUp = t22 == null ? "" : Utils.HumanFy(t22.TotalUp)
}).OrderBy(t => t.Sort).ToList();
return lstModel;
}
#endregion Servers && Groups
#region Add Servers
2024-07-18 09:39:11 +00:00
private async Task<List<ProfileItem>?> GetProfileItems(bool latest)
{
var lstSelected = new List<ProfileItem>();
if (SelectedProfiles == null || SelectedProfiles.Count <= 0)
2024-07-18 09:39:11 +00:00
{
return null;
}
var orderProfiles = SelectedProfiles?.OrderBy(t => t.Sort);
if (latest)
{
foreach (var profile in orderProfiles)
2024-07-18 09:39:11 +00:00
{
2025-08-17 09:31:55 +00:00
var item = await AppManager.Instance.GetProfileItem(profile.IndexId);
if (item is not null)
2024-07-18 09:39:11 +00:00
{
lstSelected.Add(item);
2024-07-18 09:39:11 +00:00
}
}
}
else
{
lstSelected = JsonUtils.Deserialize<List<ProfileItem>>(JsonUtils.Serialize(orderProfiles));
}
2024-07-18 09:39:11 +00:00
return lstSelected;
}
public async Task EditServerAsync(EConfigType eConfigType)
{
if (string.IsNullOrEmpty(SelectedProfile?.IndexId))
2024-07-18 09:39:11 +00:00
{
return;
}
2025-08-17 09:31:55 +00:00
var item = await AppManager.Instance.GetProfileItem(SelectedProfile.IndexId);
if (item is null)
{
2025-08-17 09:31:55 +00:00
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectServer);
return;
}
eConfigType = item.ConfigType;
2024-07-18 09:39:11 +00:00
bool? ret = false;
if (eConfigType == EConfigType.Custom)
{
ret = await _updateView?.Invoke(EViewAction.AddServer2Window, item);
}
2025-09-10 15:44:42 +00:00
else if (eConfigType is EConfigType.PolicyGroup or EConfigType.ProxyChain)
{
ret = await _updateView?.Invoke(EViewAction.AddGroupServerWindow, item);
}
else
{
ret = await _updateView?.Invoke(EViewAction.AddServerWindow, item);
}
if (ret == true)
{
await RefreshServers();
if (item.IndexId == _config.IndexId)
2024-07-18 09:39:11 +00:00
{
Reload();
}
}
}
2024-07-18 09:39:11 +00:00
public async Task RemoveServerAsync()
{
var lstSelected = await GetProfileItems(true);
if (lstSelected == null)
2024-07-18 09:39:11 +00:00
{
return;
2024-07-18 09:39:11 +00:00
}
if (await _updateView?.Invoke(EViewAction.ShowYesNo, null) == false)
2024-07-18 09:39:11 +00:00
{
return;
2024-07-18 09:39:11 +00:00
}
var exists = lstSelected.Exists(t => t.IndexId == _config.IndexId);
2024-07-18 09:39:11 +00:00
await ConfigHandler.RemoveServers(_config, lstSelected);
2025-08-17 09:31:55 +00:00
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
2025-08-31 07:41:25 +00:00
if (lstSelected.Count == ProfileItems.Count)
2024-07-18 09:39:11 +00:00
{
2025-08-31 07:41:25 +00:00
ProfileItems.Clear();
2024-07-18 09:39:11 +00:00
}
await RefreshServers();
if (exists)
2024-07-18 09:39:11 +00:00
{
Reload();
2024-07-18 09:39:11 +00:00
}
}
2024-07-18 09:39:11 +00:00
private async Task RemoveDuplicateServer()
{
var tuple = await ConfigHandler.DedupServerList(_config, _config.SubIndexId);
if (tuple.Item1 > 0 || tuple.Item2 > 0)
2024-07-18 09:39:11 +00:00
{
await RefreshServers();
Reload();
2024-07-18 09:39:11 +00:00
}
2025-08-17 09:31:55 +00:00
NoticeManager.Instance.Enqueue(string.Format(ResUI.RemoveDuplicateServerResult, tuple.Item1, tuple.Item2));
}
2024-07-18 09:39:11 +00:00
private async Task CopyServer()
{
var lstSelected = await GetProfileItems(false);
if (lstSelected == null)
2024-07-18 09:39:11 +00:00
{
return;
2024-07-18 09:39:11 +00:00
}
if (await ConfigHandler.CopyServer(_config, lstSelected) == 0)
2024-07-18 09:39:11 +00:00
{
await RefreshServers();
2025-08-17 09:31:55 +00:00
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
}
}
2024-07-18 09:39:11 +00:00
public async Task SetDefaultServer()
{
if (string.IsNullOrEmpty(SelectedProfile?.IndexId))
{
return;
2024-07-18 09:39:11 +00:00
}
await SetDefaultServer(SelectedProfile.IndexId);
}
2024-07-18 09:39:11 +00:00
public async Task SetDefaultServer(string? indexId)
{
if (indexId.IsNullOrEmpty())
2024-07-18 09:39:11 +00:00
{
return;
}
if (indexId == _config.IndexId)
{
return;
}
2025-08-17 09:31:55 +00:00
var item = await AppManager.Instance.GetProfileItem(indexId);
if (item is null)
{
2025-08-17 09:31:55 +00:00
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectServer);
return;
}
2024-07-18 09:39:11 +00:00
if (await ConfigHandler.SetDefaultServerIndex(_config, indexId) == 0)
{
await RefreshServers();
Reload();
2024-07-18 09:39:11 +00:00
}
}
2024-07-18 09:39:11 +00:00
public async Task ShareServerAsync()
{
2025-08-17 09:31:55 +00:00
var item = await AppManager.Instance.GetProfileItem(SelectedProfile.IndexId);
if (item is null)
2024-07-18 09:39:11 +00:00
{
2025-08-17 09:31:55 +00:00
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectServer);
return;
}
var url = FmtHandler.GetShareUri(item);
if (url.IsNullOrEmpty())
{
return;
}
2024-07-18 09:39:11 +00:00
await _updateView?.Invoke(EViewAction.ShareServer, url);
}
2024-07-18 09:39:11 +00:00
2025-09-11 04:44:18 +00:00
private async Task GenGroupMultipleServer(ECoreType coreType, EMultipleLoad multipleLoad)
{
var lstSelected = await GetProfileItems(true);
if (lstSelected == null)
{
return;
}
2024-07-18 09:39:11 +00:00
2025-09-11 05:23:38 +00:00
var ret = await ConfigHandler.AddGroupServer4Multiple(_config, lstSelected, coreType, multipleLoad, SelectedSub?.Id);
if (ret.Success != true)
{
2025-08-17 09:31:55 +00:00
NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
return;
}
if (ret?.Data?.ToString() == _config.IndexId)
{
await RefreshServers();
Reload();
2024-07-18 09:39:11 +00:00
}
else
{
await SetDefaultServer(ret?.Data?.ToString());
}
}
2024-07-18 09:39:11 +00:00
public async Task SortServer(string colName)
{
if (colName.IsNullOrEmpty())
2024-07-18 09:39:11 +00:00
{
return;
}
2024-07-18 09:39:11 +00:00
_dicHeaderSort.TryAdd(colName, true);
_dicHeaderSort.TryGetValue(colName, out bool asc);
if (await ConfigHandler.SortServers(_config, _config.SubIndexId, colName, asc) != 0)
{
return;
2024-07-18 09:39:11 +00:00
}
_dicHeaderSort[colName] = !asc;
await RefreshServers();
}
public async Task RemoveInvalidServerResult()
{
var count = await ConfigHandler.RemoveInvalidServerResult(_config, _config.SubIndexId);
await RefreshServers();
2025-08-17 09:31:55 +00:00
NoticeManager.Instance.Enqueue(string.Format(ResUI.RemoveInvalidServerResultTip, count));
}
2024-07-18 09:39:11 +00:00
//move server
private async Task MoveToGroup(bool c)
{
if (!c)
2024-07-18 09:39:11 +00:00
{
return;
2024-07-18 09:39:11 +00:00
}
var lstSelected = await GetProfileItems(true);
if (lstSelected == null)
2024-07-18 09:39:11 +00:00
{
return;
}
await ConfigHandler.MoveToGroup(_config, lstSelected, SelectedMoveToGroup.Id);
2025-08-17 09:31:55 +00:00
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
await RefreshServers();
SelectedMoveToGroup = null;
SelectedMoveToGroup = new();
}
2024-10-07 03:18:24 +00:00
public async Task MoveServer(EMove eMove)
{
var item = _lstProfile.FirstOrDefault(t => t.IndexId == SelectedProfile.IndexId);
if (item is null)
{
2025-08-17 09:31:55 +00:00
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectServer);
return;
2024-07-18 09:39:11 +00:00
}
var index = _lstProfile.IndexOf(item);
if (index < 0)
2024-08-19 10:15:54 +00:00
{
return;
2024-08-19 10:15:54 +00:00
}
if (await ConfigHandler.MoveServer(_config, _lstProfile, index, eMove) == 0)
{
await RefreshServers();
}
}
2024-08-19 10:15:54 +00:00
public async Task MoveServerTo(int startIndex, ProfileItemModel targetItem)
{
2025-08-31 07:41:25 +00:00
var targetIndex = ProfileItems.IndexOf(targetItem);
if (startIndex >= 0 && targetIndex >= 0 && startIndex != targetIndex)
2024-07-18 09:39:11 +00:00
{
if (await ConfigHandler.MoveServer(_config, _lstProfile, startIndex, EMove.Position, targetIndex) == 0)
{
await RefreshServers();
}
}
}
public async Task ServerSpeedtest(ESpeedActionType actionType)
{
if (actionType == ESpeedActionType.Mixedtest)
{
2025-08-31 07:41:25 +00:00
SelectedProfiles = ProfileItems;
}
var lstSelected = await GetProfileItems(false);
if (lstSelected == null)
{
return;
}
2025-08-30 10:01:01 +00:00
_speedtestService ??= new SpeedtestService(_config, async (SpeedTestResult result) =>
{
RxApp.MainThreadScheduler.Schedule(result, (scheduler, result) =>
{
_ = SetSpeedTestResult(result);
return Disposable.Empty;
});
});
_speedtestService?.RunLoop(actionType, lstSelected);
}
public void ServerSpeedtestStop()
{
_speedtestService?.ExitLoop();
}
private async Task Export2ClientConfigAsync(bool blClipboard)
{
2025-08-17 09:31:55 +00:00
var item = await AppManager.Instance.GetProfileItem(SelectedProfile.IndexId);
if (item is null)
{
2025-08-17 09:31:55 +00:00
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectServer);
return;
}
if (blClipboard)
{
var result = await CoreConfigHandler.GenerateClientConfig(item, null);
2024-10-21 10:18:41 +00:00
if (result.Success != true)
{
2025-08-17 09:31:55 +00:00
NoticeManager.Instance.Enqueue(result.Msg);
}
else
{
await _updateView?.Invoke(EViewAction.SetClipboardData, result.Data);
2025-08-17 09:31:55 +00:00
NoticeManager.Instance.SendMessage(ResUI.OperationSuccess);
}
2024-07-18 09:39:11 +00:00
}
else
{
await _updateView?.Invoke(EViewAction.SaveFileDialog, item);
}
}
public async Task Export2ClientConfigResult(string fileName, ProfileItem item)
{
if (fileName.IsNullOrEmpty())
{
return;
}
var result = await CoreConfigHandler.GenerateClientConfig(item, fileName);
if (result.Success != true)
{
2025-08-17 09:31:55 +00:00
NoticeManager.Instance.Enqueue(result.Msg);
}
else
{
2025-08-17 09:31:55 +00:00
NoticeManager.Instance.SendMessageAndEnqueue(string.Format(ResUI.SaveClientConfigurationIn, fileName));
}
}
public async Task Export2ShareUrlAsync(bool blEncode)
{
var lstSelected = await GetProfileItems(true);
if (lstSelected == null)
{
return;
}
2024-07-18 09:39:11 +00:00
StringBuilder sb = new();
foreach (var it in lstSelected)
2024-07-18 09:39:11 +00:00
{
var url = FmtHandler.GetShareUri(it);
if (url.IsNullOrEmpty())
2024-07-18 09:39:11 +00:00
{
continue;
2024-07-18 09:39:11 +00:00
}
sb.Append(url);
sb.AppendLine();
}
if (sb.Length > 0)
{
if (blEncode)
2024-07-18 09:39:11 +00:00
{
await _updateView?.Invoke(EViewAction.SetClipboardData, Utils.Base64Encode(sb.ToString()));
2024-07-18 09:39:11 +00:00
}
else
2024-07-18 09:39:11 +00:00
{
await _updateView?.Invoke(EViewAction.SetClipboardData, sb.ToString());
2024-07-18 09:39:11 +00:00
}
2025-08-17 09:31:55 +00:00
NoticeManager.Instance.SendMessage(ResUI.BatchExportURLSuccessfully);
2024-07-18 09:39:11 +00:00
}
}
2024-07-18 09:39:11 +00:00
#endregion Add Servers
2024-07-18 09:39:11 +00:00
#region Subscription
2024-07-18 09:39:11 +00:00
private async Task EditSubAsync(bool blNew)
{
SubItem item;
if (blNew)
2024-07-18 09:39:11 +00:00
{
item = new();
}
else
{
2025-08-17 09:31:55 +00:00
item = await AppManager.Instance.GetSubItem(_config.SubIndexId);
if (item is null)
2024-07-18 09:39:11 +00:00
{
return;
2024-07-18 09:39:11 +00:00
}
}
if (await _updateView?.Invoke(EViewAction.SubEditWindow, item) == true)
{
await RefreshSubscriptions();
await SubSelectedChangedAsync(true);
}
2024-07-18 09:39:11 +00:00
}
#endregion Subscription
2025-01-30 09:10:05 +00:00
}