Refactors ProfilesViewModel for code reuse

This commit is contained in:
DHR60 2025-09-06 17:12:52 +08:00
parent 34a93a307f
commit 4d23911bfb
3 changed files with 329 additions and 543 deletions

View file

@ -0,0 +1,232 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using System.Threading.Tasks;
using DynamicData;
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using ServiceLib.Base;
namespace ServiceLib.ViewModels;
public abstract class ProfilesBaseViewModel : MyReactiveObject
{
#region protected fields
protected List<ProfileItem> _lstProfile = new();
protected string _serverFilter = string.Empty;
protected Dictionary<string, bool> _dicHeaderSort = new();
#endregion
#region Observable properties
public IObservableCollection<ProfileItemModel> ProfileItems { get; } = new ObservableCollectionExtended<ProfileItemModel>();
public IObservableCollection<SubItem> SubItems { get; } = new ObservableCollectionExtended<SubItem>();
[Reactive]
public ProfileItemModel SelectedProfile { get; set; }
public IList<ProfileItemModel> SelectedProfiles { get; set; }
[Reactive]
public SubItem SelectedSub { get; set; }
[Reactive]
public string ServerFilter { get; set; }
#endregion
protected ProfilesBaseViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
{
_config = AppManager.Instance.Config;
_updateView = updateView;
this.WhenAnyValue(
x => x.SelectedSub,
y => y != null && !y.Remarks.IsNullOrEmpty() && GetCurrentSubIndexId() != y.Id)
.Subscribe(async c => await SubSelectedChangedAsync(c));
this.WhenAnyValue(
x => x.ServerFilter,
y => y != null && _serverFilter != y)
.Subscribe(async c => await ServerFilterChanged(c));
AppEvents.ProfilesRefreshRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async _ => await RefreshServersBiz());
AppEvents.DispatcherStatisticsRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(UpdateStatistics);
_ = Initialize();
}
protected virtual async Task Initialize()
{
SelectedProfile = new();
SelectedSub = new();
await RefreshSubscriptions();
await RefreshServers();
}
#region Hook points
protected abstract string GetCurrentSubIndexId();
protected abstract void SetCurrentSubIndexId(string? id);
protected virtual bool ShouldSetDefaultServer => false;
public virtual async Task RefreshServers()
{
await RefreshServersBiz();
}
#endregion
#region Shared actions
protected virtual async Task SubSelectedChangedAsync(bool changed)
{
if (!changed)
{
return;
}
SetCurrentSubIndexId(SelectedSub?.Id);
await RefreshServers();
await _updateView?.Invoke(EViewAction.ProfilesFocus, null);
}
protected virtual async Task ServerFilterChanged(bool changed)
{
if (!changed)
{
return;
}
_serverFilter = ServerFilter;
if (_serverFilter.IsNullOrEmpty())
{
await RefreshServers();
}
}
protected async Task RefreshServersBiz()
{
var lstModel = await GetProfileItemsEx(GetCurrentSubIndexId(), _serverFilter) ?? new List<ProfileItemModel>();
_lstProfile = JsonUtils.Deserialize<List<ProfileItem>>(JsonUtils.Serialize(lstModel)) ?? [];
ProfileItems.Clear();
ProfileItems.AddRange(lstModel);
if (lstModel.Count > 0)
{
var selected = lstModel.FirstOrDefault(t => t.IndexId == _config.IndexId);
SelectedProfile = selected ?? lstModel.First();
}
await _updateView?.Invoke(EViewAction.DispatcherRefreshServersBiz, null);
}
public virtual async Task RefreshSubscriptions()
{
SubItems.Clear();
SubItems.Add(new SubItem { Remarks = ResUI.AllGroupServers });
foreach (var item in await AppManager.Instance.SubItems())
{
SubItems.Add(item);
}
var curId = GetCurrentSubIndexId();
if (!curId.IsNullOrEmpty() && SubItems.FirstOrDefault(t => t.Id == curId) != null)
{
SelectedSub = SubItems.FirstOrDefault(t => t.Id == curId);
}
else
{
SelectedSub = SubItems.First();
}
}
protected virtual async Task<List<ProfileItemModel>?> GetProfileItemsEx(string subid, string filter)
{
var lstModel = await AppManager.Instance.ProfileItems(subid, filter);
if (ShouldSetDefaultServer)
{
await ConfigHandler.SetDefaultServer(_config, lstModel);
}
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;
}
public virtual void UpdateStatistics(ServerSpeedItem update)
{
if (!_config.GuiItem.EnableStatistics
|| (update.ProxyUp + update.ProxyDown) <= 0
|| DateTime.Now.Second % 3 != 0)
{
return;
}
try
{
var item = ProfileItems.FirstOrDefault(it => it.IndexId == update.IndexId);
if (item != null)
{
item.TodayDown = Utils.HumanFy(update.TodayDown);
item.TodayUp = Utils.HumanFy(update.TodayUp);
item.TotalDown = Utils.HumanFy(update.TotalDown);
item.TotalUp = Utils.HumanFy(update.TotalUp);
}
}
catch
{
}
}
#endregion
}

View file

@ -10,125 +10,27 @@ using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using Splat;
using ServiceLib.Base;
namespace ServiceLib.ViewModels;
public class ProfilesSelectViewModel : MyReactiveObject
public class ProfilesSelectViewModel(Func<EViewAction, object?, Task<bool>>? updateView) : ProfilesBaseViewModel(updateView)
{
#region private prop
private List<ProfileItem> _lstProfile;
private string _serverFilter = string.Empty;
private Dictionary<string, bool> _dicHeaderSort = new();
private string _subIndexId = string.Empty;
#endregion private prop
#region ObservableCollection
public IObservableCollection<ProfileItemModel> ProfileItems { get; } = new ObservableCollectionExtended<ProfileItemModel>();
public IObservableCollection<SubItem> SubItems { get; } = new ObservableCollectionExtended<SubItem>();
[Reactive]
public ProfileItemModel SelectedProfile { get; set; }
public IList<ProfileItemModel> SelectedProfiles { get; set; }
[Reactive]
public SubItem SelectedSub { get; set; }
[Reactive]
public string ServerFilter { get; set; }
#endregion ObservableCollection
#region Init
public ProfilesSelectViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
protected override async Task Initialize()
{
_config = AppManager.Instance.Config;
_updateView = updateView;
_subIndexId = _config.SubIndexId ?? string.Empty;
#region WhenAnyValue && ReactiveCommand
this.WhenAnyValue(
x => x.SelectedSub,
y => y != null && !y.Remarks.IsNullOrEmpty() && _subIndexId != y.Id)
.Subscribe(async c => await SubSelectedChangedAsync(c));
this.WhenAnyValue(
x => x.ServerFilter,
y => y != null && _serverFilter != y)
.Subscribe(async c => await ServerFilterChanged(c));
#endregion WhenAnyValue && ReactiveCommand
#region AppEvents
AppEvents.ProfilesRefreshRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async _ => await RefreshServersBiz());
AppEvents.DispatcherStatisticsRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(UpdateStatistics);
#endregion AppEvents
_ = Init();
await base.Initialize();
}
private async Task Init()
{
SelectedProfile = new();
SelectedSub = new();
await RefreshSubscriptions();
await RefreshServers();
}
#endregion Init
#region Actions
public void UpdateStatistics(ServerSpeedItem update)
{
if (!_config.GuiItem.EnableStatistics
|| (update.ProxyUp + update.ProxyDown) <= 0
|| DateTime.Now.Second % 3 != 0)
{
return;
}
try
{
var item = ProfileItems.FirstOrDefault(it => it.IndexId == update.IndexId);
if (item != null)
{
item.TodayDown = Utils.HumanFy(update.TodayDown);
item.TodayUp = Utils.HumanFy(update.TodayUp);
item.TotalDown = Utils.HumanFy(update.TotalDown);
item.TotalUp = Utils.HumanFy(update.TotalUp);
//if (SelectedProfile?.IndexId == item.IndexId)
//{
// var temp = JsonUtils.DeepCopy(item);
// _profileItems.Replace(item, temp);
// SelectedProfile = temp;
//}
//else
//{
// _profileItems.Replace(item, JsonUtils.DeepCopy(item));
//}
}
}
catch
{
}
}
public bool CanOk()
{
return SelectedProfile != null && !SelectedProfile.IndexId.IsNullOrEmpty();
@ -147,120 +49,6 @@ public class ProfilesSelectViewModel : MyReactiveObject
#region Servers && Groups
private async Task SubSelectedChangedAsync(bool c)
{
if (!c)
{
return;
}
_subIndexId = SelectedSub?.Id;
await RefreshServers();
await _updateView?.Invoke(EViewAction.ProfilesFocus, null);
}
private async Task ServerFilterChanged(bool c)
{
if (!c)
{
return;
}
_serverFilter = ServerFilter;
if (_serverFilter.IsNullOrEmpty())
{
await RefreshServers();
}
}
public async Task RefreshServers()
{
await RefreshServersBiz();
}
private async Task RefreshServersBiz()
{
var lstModel = await GetProfileItemsEx(_subIndexId, _serverFilter);
_lstProfile = JsonUtils.Deserialize<List<ProfileItem>>(JsonUtils.Serialize(lstModel)) ?? [];
ProfileItems.Clear();
ProfileItems.AddRange(lstModel);
if (lstModel.Count > 0)
{
var selected = lstModel.FirstOrDefault(t => t.IndexId == _config.IndexId);
if (selected != null)
{
SelectedProfile = selected;
}
else
{
SelectedProfile = lstModel.First();
}
}
await _updateView?.Invoke(EViewAction.DispatcherRefreshServersBiz, null);
}
public async Task RefreshSubscriptions()
{
SubItems.Clear();
SubItems.Add(new SubItem { Remarks = ResUI.AllGroupServers });
foreach (var item in await AppManager.Instance.SubItems())
{
SubItems.Add(item);
}
if (_subIndexId != null && SubItems.FirstOrDefault(t => t.Id == _subIndexId) != null)
{
SelectedSub = SubItems.FirstOrDefault(t => t.Id == _subIndexId);
}
else
{
SelectedSub = SubItems.First();
}
}
private async Task<List<ProfileItemModel>?> GetProfileItemsEx(string subid, string filter)
{
var lstModel = await AppManager.Instance.ProfileItems(_subIndexId, filter);
//await ConfigHandler.SetDefaultServer(_config, lstModel);
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;
}
public async Task<ProfileItem?> GetProfileItem()
{
if (string.IsNullOrEmpty(SelectedProfile?.IndexId))
@ -277,8 +65,9 @@ public class ProfilesSelectViewModel : MyReactiveObject
return item;
}
public async Task SortServer(string colName)
public Task SortServer(string colName)
{
_ = colName;
//if (colName.IsNullOrEmpty())
//{
// return;
@ -292,7 +81,23 @@ public class ProfilesSelectViewModel : MyReactiveObject
//}
//_dicHeaderSort[colName] = !asc;
//await RefreshServers();
return Task.CompletedTask;
}
#endregion Servers && Groups
#region overrides
protected override string GetCurrentSubIndexId()
{
return _subIndexId;
}
protected override void SetCurrentSubIndexId(string? id)
{
_subIndexId = id ?? string.Empty;
}
protected override bool ShouldSetDefaultServer => false;
#endregion overrides
}

View file

@ -7,40 +7,23 @@ using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using Splat;
using ServiceLib.Base;
namespace ServiceLib.ViewModels;
public class ProfilesViewModel : MyReactiveObject
public class ProfilesViewModel : ProfilesBaseViewModel
{
#region private prop
private List<ProfileItem> _lstProfile;
private string _serverFilter = string.Empty;
private Dictionary<string, bool> _dicHeaderSort = new();
private SpeedtestService? _speedtestService;
#endregion private prop
#region ObservableCollection
public IObservableCollection<ProfileItemModel> ProfileItems { get; } = new ObservableCollectionExtended<ProfileItemModel>();
public IObservableCollection<SubItem> SubItems { get; } = new ObservableCollectionExtended<SubItem>();
[Reactive]
public ProfileItemModel SelectedProfile { get; set; }
public IList<ProfileItemModel> SelectedProfiles { get; set; }
[Reactive]
public SubItem SelectedSub { get; set; }
[Reactive]
public SubItem SelectedMoveToGroup { get; set; }
[Reactive]
public string ServerFilter { get; set; }
#endregion ObservableCollection
#region Menu
@ -89,175 +72,61 @@ public class ProfilesViewModel : MyReactiveObject
#region Init
public ProfilesViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
public ProfilesViewModel(Func<EViewAction, object?, Task<bool>>? updateView) : base(updateView)
{
_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 () =>
{
await SetDefaultServer();
}, canEditRemove);
ShareServerCmd = ReactiveCommand.CreateFromTask(async () =>
{
await ShareServerAsync();
}, canEditRemove);
SetDefaultMultipleServerXrayRandomCmd = ReactiveCommand.CreateFromTask(async () =>
{
await SetDefaultMultipleServer(ECoreType.Xray, EMultipleLoad.Random);
}, canEditRemove);
SetDefaultMultipleServerXrayRoundRobinCmd = ReactiveCommand.CreateFromTask(async () =>
{
await SetDefaultMultipleServer(ECoreType.Xray, EMultipleLoad.RoundRobin);
}, canEditRemove);
SetDefaultMultipleServerXrayLeastPingCmd = ReactiveCommand.CreateFromTask(async () =>
{
await SetDefaultMultipleServer(ECoreType.Xray, EMultipleLoad.LeastPing);
}, canEditRemove);
SetDefaultMultipleServerXrayLeastLoadCmd = ReactiveCommand.CreateFromTask(async () =>
{
await SetDefaultMultipleServer(ECoreType.Xray, EMultipleLoad.LeastLoad);
}, canEditRemove);
SetDefaultMultipleServerSingBoxLeastPingCmd = ReactiveCommand.CreateFromTask(async () =>
{
await SetDefaultMultipleServer(ECoreType.sing_box, EMultipleLoad.LeastPing);
}, canEditRemove);
EditServerCmd = ReactiveCommand.CreateFromTask(EditServerAsync, canEditRemove);
RemoveServerCmd = ReactiveCommand.CreateFromTask(RemoveServerAsync, canEditRemove);
RemoveDuplicateServerCmd = ReactiveCommand.CreateFromTask(RemoveDuplicateServer);
CopyServerCmd = ReactiveCommand.CreateFromTask(CopyServer, canEditRemove);
SetDefaultServerCmd = ReactiveCommand.CreateFromTask(SetDefaultServer, canEditRemove);
ShareServerCmd = ReactiveCommand.CreateFromTask(ShareServerAsync, canEditRemove);
SetDefaultMultipleServerXrayRandomCmd = ReactiveCommand.CreateFromTask(() =>
SetDefaultMultipleServer(ECoreType.Xray, EMultipleLoad.Random), canEditRemove);
SetDefaultMultipleServerXrayRoundRobinCmd = ReactiveCommand.CreateFromTask(() =>
SetDefaultMultipleServer(ECoreType.Xray, EMultipleLoad.RoundRobin), canEditRemove);
SetDefaultMultipleServerXrayLeastPingCmd = ReactiveCommand.CreateFromTask(() =>
SetDefaultMultipleServer(ECoreType.Xray, EMultipleLoad.LeastPing), canEditRemove);
SetDefaultMultipleServerXrayLeastLoadCmd = ReactiveCommand.CreateFromTask(() =>
SetDefaultMultipleServer(ECoreType.Xray, EMultipleLoad.LeastLoad), canEditRemove);
SetDefaultMultipleServerSingBoxLeastPingCmd = ReactiveCommand.CreateFromTask(() =>
SetDefaultMultipleServer(ECoreType.sing_box, EMultipleLoad.LeastPing), canEditRemove);
//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);
MoveTopCmd = ReactiveCommand.CreateFromTask(() => MoveServer(EMove.Top), canEditRemove);
MoveUpCmd = ReactiveCommand.CreateFromTask(() => MoveServer(EMove.Up), canEditRemove);
MoveDownCmd = ReactiveCommand.CreateFromTask(() => MoveServer(EMove.Down), canEditRemove);
MoveBottomCmd = ReactiveCommand.CreateFromTask(() => MoveServer(EMove.Bottom), canEditRemove);
//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();
});
MixedTestServerCmd = ReactiveCommand.CreateFromTask(() => ServerSpeedtest(ESpeedActionType.Mixedtest));
TcpingServerCmd = ReactiveCommand.CreateFromTask(() => ServerSpeedtest(ESpeedActionType.Tcping), canEditRemove);
RealPingServerCmd = ReactiveCommand.CreateFromTask(() => ServerSpeedtest(ESpeedActionType.Realping), canEditRemove);
SpeedServerCmd = ReactiveCommand.CreateFromTask(() => ServerSpeedtest(ESpeedActionType.Speedtest), canEditRemove);
SortServerResultCmd = ReactiveCommand.CreateFromTask(() => SortServer(EServerColName.DelayVal.ToString()));
RemoveInvalidServerResultCmd = ReactiveCommand.CreateFromTask(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);
Export2ClientConfigCmd = ReactiveCommand.CreateFromTask(() => Export2ClientConfigAsync(false), canEditRemove);
Export2ClientConfigClipboardCmd = ReactiveCommand.CreateFromTask(() => Export2ClientConfigAsync(true), canEditRemove);
Export2ShareUrlCmd = ReactiveCommand.CreateFromTask(() => Export2ShareUrlAsync(false), canEditRemove);
Export2ShareUrlBase64Cmd = ReactiveCommand.CreateFromTask(() => Export2ShareUrlAsync(true), canEditRemove);
//Subscription
AddSubCmd = ReactiveCommand.CreateFromTask(async () =>
{
await EditSubAsync(true);
});
EditSubCmd = ReactiveCommand.CreateFromTask(async () =>
{
await EditSubAsync(false);
});
AddSubCmd = ReactiveCommand.CreateFromTask(() => EditSubAsync(true));
EditSubCmd = ReactiveCommand.CreateFromTask(() => EditSubAsync(false));
#endregion WhenAnyValue && ReactiveCommand
#region AppEvents
AppEvents.ProfilesRefreshRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async _ => await RefreshServersBiz());
AppEvents.DispatcherStatisticsRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async result => await UpdateStatistics(result));
#endregion AppEvents
_ = Init();
}
private async Task Init()
{
SelectedProfile = new();
SelectedSub = new();
SelectedMoveToGroup = new();
await RefreshSubscriptions();
await RefreshServers();
}
#endregion Init
@ -269,18 +138,18 @@ public class ProfilesViewModel : MyReactiveObject
Locator.Current.GetService<MainWindowViewModel>()?.Reload();
}
public async Task SetSpeedTestResult(SpeedTestResult result)
public Task SetSpeedTestResult(SpeedTestResult result)
{
if (result.IndexId.IsNullOrEmpty())
{
NoticeManager.Instance.SendMessageEx(result.Delay);
NoticeManager.Instance.Enqueue(result.Delay);
return;
return Task.CompletedTask;
}
var item = ProfileItems.FirstOrDefault(it => it.IndexId == result.IndexId);
if (item == null)
{
return;
return Task.CompletedTask;
}
if (result.Delay.IsNotEmpty())
@ -294,165 +163,16 @@ public class ProfilesViewModel : MyReactiveObject
item.SpeedVal = result.Speed ?? string.Empty;
}
//_profileItems.Replace(item, JsonUtils.DeepCopy(item));
}
public async Task UpdateStatistics(ServerSpeedItem update)
{
if (!_config.GuiItem.EnableStatistics
|| (update.ProxyUp + update.ProxyDown) <= 0
|| DateTime.Now.Second % 3 != 0)
{
return;
}
try
{
var item = ProfileItems.FirstOrDefault(it => it.IndexId == update.IndexId);
if (item != null)
{
item.TodayDown = Utils.HumanFy(update.TodayDown);
item.TodayUp = Utils.HumanFy(update.TodayUp);
item.TotalDown = Utils.HumanFy(update.TotalDown);
item.TotalUp = Utils.HumanFy(update.TotalUp);
//if (SelectedProfile?.IndexId == item.IndexId)
//{
// var temp = JsonUtils.DeepCopy(item);
// _profileItems.Replace(item, temp);
// SelectedProfile = temp;
//}
//else
//{
// _profileItems.Replace(item, JsonUtils.DeepCopy(item));
//}
}
}
catch
{
}
return Task.CompletedTask;
}
#endregion Actions
#region Servers && Groups
private async Task SubSelectedChangedAsync(bool c)
protected override async Task Initialize()
{
if (!c)
{
return;
await base.Initialize();
SelectedMoveToGroup = new();
}
_config.SubIndexId = SelectedSub?.Id;
await RefreshServers();
await _updateView?.Invoke(EViewAction.ProfilesFocus, null);
}
private async Task ServerFilterChanged(bool c)
{
if (!c)
{
return;
}
_serverFilter = ServerFilter;
if (_serverFilter.IsNullOrEmpty())
{
await RefreshServers();
}
}
public async Task RefreshServers()
{
AppEvents.ProfilesRefreshRequested.OnNext(Unit.Default);
await Task.Delay(200);
}
private async Task RefreshServersBiz()
{
var lstModel = await GetProfileItemsEx(_config.SubIndexId, _serverFilter);
_lstProfile = JsonUtils.Deserialize<List<ProfileItem>>(JsonUtils.Serialize(lstModel)) ?? [];
ProfileItems.Clear();
ProfileItems.AddRange(lstModel);
if (lstModel.Count > 0)
{
var selected = lstModel.FirstOrDefault(t => t.IndexId == _config.IndexId);
if (selected != null)
{
SelectedProfile = selected;
}
else
{
SelectedProfile = lstModel.First();
}
}
await _updateView?.Invoke(EViewAction.DispatcherRefreshServersBiz, null);
}
public async Task RefreshSubscriptions()
{
SubItems.Clear();
SubItems.Add(new SubItem { Remarks = ResUI.AllGroupServers });
foreach (var item in await AppManager.Instance.SubItems())
{
SubItems.Add(item);
}
if (_config.SubIndexId != null && SubItems.FirstOrDefault(t => t.Id == _config.SubIndexId) != null)
{
SelectedSub = SubItems.FirstOrDefault(t => t.Id == _config.SubIndexId);
}
else
{
SelectedSub = SubItems.First();
}
}
private async Task<List<ProfileItemModel>?> GetProfileItemsEx(string subid, string filter)
{
var lstModel = await AppManager.Instance.ProfileItems(_config.SubIndexId, filter);
await ConfigHandler.SetDefaultServer(_config, lstModel);
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
@ -484,7 +204,7 @@ public class ProfilesViewModel : MyReactiveObject
return lstSelected;
}
public async Task EditServerAsync(EConfigType eConfigType)
public async Task EditServerAsync()
{
if (string.IsNullOrEmpty(SelectedProfile?.IndexId))
{
@ -496,7 +216,7 @@ public class ProfilesViewModel : MyReactiveObject
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectServer);
return;
}
eConfigType = item.ConfigType;
var eConfigType = item.ConfigType;
bool? ret = false;
if (eConfigType == EConfigType.Custom)
@ -517,6 +237,11 @@ public class ProfilesViewModel : MyReactiveObject
}
}
public Task EditServerAsync(EConfigType _)
{
return EditServerAsync();
}
public async Task RemoveServerAsync()
{
var lstSelected = await GetProfileItems(true);
@ -651,7 +376,7 @@ public class ProfilesViewModel : MyReactiveObject
}
_dicHeaderSort.TryAdd(colName, true);
_dicHeaderSort.TryGetValue(colName, out bool asc);
_dicHeaderSort.TryGetValue(colName, out var asc);
if (await ConfigHandler.SortServers(_config, _config.SubIndexId, colName, asc) != 0)
{
return;
@ -733,13 +458,14 @@ public class ProfilesViewModel : MyReactiveObject
return;
}
_speedtestService ??= new SpeedtestService(_config, async (SpeedTestResult result) =>
_speedtestService ??= new SpeedtestService(_config, (SpeedTestResult result) =>
{
RxApp.MainThreadScheduler.Schedule(result, (scheduler, result) =>
{
_ = SetSpeedTestResult(result);
return Disposable.Empty;
});
return Task.CompletedTask;
});
_speedtestService?.RunLoop(actionType, lstSelected);
}
@ -853,4 +579,27 @@ public class ProfilesViewModel : MyReactiveObject
}
#endregion Subscription
#region overrides
protected override string GetCurrentSubIndexId()
{
return _config.SubIndexId;
}
protected override void SetCurrentSubIndexId(string? id)
{
_config.SubIndexId = id;
}
public override async Task RefreshServers()
{
AppEvents.ProfilesRefreshRequested.OnNext(Unit.Default);
await Task.Delay(200);
await base.RefreshServers();
}
protected override bool ShouldSetDefaultServer => true;
#endregion overrides
}