Compare commits

..

16 Commits

Author SHA1 Message Date
2dust
d094370209 up 7.14.6 2025-09-03 19:05:45 +08:00
2dust
1a6fbf782d Using RxApp replace ViewAction 2025-09-02 17:12:38 +08:00
2dust
3f67a23f8b up 7.14.5 2025-08-31 19:55:17 +08:00
2dust
b8eb7e7b29 Optimization and Improvement. 2025-08-31 15:41:25 +08:00
2dust
1d69916410 Update GlobalHotKeys 2025-08-31 14:21:22 +08:00
2dust
49fa103077 Optimize UI 2025-08-31 14:08:05 +08:00
2dust
e3a63db966 Using RxApp replace ViewAction 2025-08-30 20:36:16 +08:00
DHR60
ef4a1903ec Update mihomo download url (#7852) 2025-08-30 19:44:54 +08:00
2dust
5a3286dad1 Using RxApp replace ViewAction 2025-08-30 19:32:07 +08:00
2dust
058c6e4a85 Use Rx event subscription instead of MessageBus to send information 2025-08-29 15:46:09 +08:00
2dust
ea1d438e40 Use Rx event subscription to replace MessageBus refresh configuration file function 2025-08-29 14:46:08 +08:00
2dust
a108eaf34b Optimization and Improvement.
Changed callback from synchronous Action<bool, string> to asynchronous Func<bool, string, Task>
2025-08-29 11:53:30 +08:00
2dust
da28c639b3 Optimization and Improvement.
Changed callback from synchronous Action<bool, string> to asynchronous Func<bool, string, Task>
2025-08-29 11:40:08 +08:00
2dust
8ef68127d4 Optimization and Improvement.
Changed callback from synchronous Action<bool, string> to asynchronous Func<bool, string, Task>
2025-08-29 10:53:57 +08:00
2dust
f39d966a33 Optimization and Improvement.
Changed callback from synchronous Action<bool, string> to asynchronous Func<bool, string, Task>
2025-08-29 10:31:09 +08:00
2dust
f83e83de13 Optimization and Improvement
Changed callback from synchronous Action<bool, string> to asynchronous Func<bool, string, Task>
2025-08-29 09:49:30 +08:00
52 changed files with 590 additions and 719 deletions

View File

@@ -1,7 +1,7 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<Version>7.14.4</Version> <Version>7.14.6</Version>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>

View File

@@ -1,10 +0,0 @@
namespace ServiceLib.Enums;
public enum EMsgCommand
{
ClearMsg,
SendMsgView,
SendSnackMsg,
RefreshProfiles,
AppExit
}

View File

@@ -6,7 +6,6 @@ public enum EViewAction
ShowYesNo, ShowYesNo,
SaveFileDialog, SaveFileDialog,
AddBatchRoutingRulesYesNo, AddBatchRoutingRulesYesNo,
AdjustMainLvColWidth,
SetClipboardData, SetClipboardData,
AddServerViaClipboard, AddServerViaClipboard,
ImportRulesFromClipboard, ImportRulesFromClipboard,
@@ -16,7 +15,6 @@ public enum EViewAction
ShowHideWindow, ShowHideWindow,
ScanScreenTask, ScanScreenTask,
ScanImageTask, ScanImageTask,
Shutdown,
BrowseServer, BrowseServer,
ImportRulesFromFile, ImportRulesFromFile,
InitSettingFont, InitSettingFont,
@@ -32,16 +30,7 @@ public enum EViewAction
FullConfigTemplateWindow, FullConfigTemplateWindow,
GlobalHotkeySettingWindow, GlobalHotkeySettingWindow,
SubSettingWindow, SubSettingWindow,
DispatcherSpeedTest,
DispatcherRefreshConnections,
DispatcherRefreshProxyGroups,
DispatcherProxiesDelayTest,
DispatcherStatistics,
DispatcherServerAvailability,
DispatcherReload,
DispatcherRefreshServersBiz, DispatcherRefreshServersBiz,
DispatcherRefreshIcon, DispatcherRefreshIcon,
DispatcherCheckUpdate,
DispatcherCheckUpdateFinished,
DispatcherShowMsg, DispatcherShowMsg,
} }

View File

@@ -0,0 +1,21 @@
using System.Reactive;
using System.Reactive.Subjects;
namespace ServiceLib.Handler;
public static class AppEvents
{
public static readonly Subject<Unit> ProfilesRefreshRequested = new();
public static readonly Subject<string> SendSnackMsgRequested = new();
public static readonly Subject<string> SendMsgViewRequested = new();
public static readonly Subject<Unit> AppExitRequested = new();
public static readonly Subject<bool> ShutdownRequested = new();
public static readonly Subject<Unit> AdjustMainLvColWidthRequested = new();
public static readonly Subject<ServerSpeedItem> DispatcherStatisticsRequested = new();
}

View File

@@ -2,17 +2,18 @@ namespace ServiceLib.Handler;
public static class SubscriptionHandler public static class SubscriptionHandler
{ {
public static async Task UpdateProcess(Config config, string subId, bool blProxy, Action<bool, string> updateFunc) public static async Task UpdateProcess(Config config, string subId, bool blProxy, Func<bool, string, Task> updateFunc)
{ {
updateFunc?.Invoke(false, ResUI.MsgUpdateSubscriptionStart); await updateFunc?.Invoke(false, ResUI.MsgUpdateSubscriptionStart);
var subItem = await AppManager.Instance.SubItems(); var subItem = await AppManager.Instance.SubItems();
if (subItem is not { Count: > 0 }) if (subItem is not { Count: > 0 })
{ {
updateFunc?.Invoke(false, ResUI.MsgNoValidSubscription); await updateFunc?.Invoke(false, ResUI.MsgNoValidSubscription);
return; return;
} }
var successCount = 0;
foreach (var item in subItem) foreach (var item in subItem)
{ {
try try
@@ -25,32 +26,35 @@ public static class SubscriptionHandler
var hashCode = $"{item.Remarks}->"; var hashCode = $"{item.Remarks}->";
if (item.Enabled == false) if (item.Enabled == false)
{ {
updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgSkipSubscriptionUpdate}"); await updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgSkipSubscriptionUpdate}");
continue; continue;
} }
// Create download handler // Create download handler
var downloadHandle = CreateDownloadHandler(hashCode, updateFunc); var downloadHandle = CreateDownloadHandler(hashCode, updateFunc);
updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgStartGettingSubscriptions}"); await updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgStartGettingSubscriptions}");
// Get all subscription content (main subscription + additional subscriptions) // Get all subscription content (main subscription + additional subscriptions)
var result = await DownloadAllSubscriptions(config, item, blProxy, downloadHandle); var result = await DownloadAllSubscriptions(config, item, blProxy, downloadHandle);
// Process download result // Process download result
await ProcessDownloadResult(config, item.Id, result, hashCode, updateFunc); if (await ProcessDownloadResult(config, item.Id, result, hashCode, updateFunc))
{
successCount++;
}
updateFunc?.Invoke(false, "-------------------------------------------------------"); await updateFunc?.Invoke(false, "-------------------------------------------------------");
} }
catch (Exception ex) catch (Exception ex)
{ {
var hashCode = $"{item.Remarks}->"; var hashCode = $"{item.Remarks}->";
Logging.SaveLog("UpdateSubscription", ex); Logging.SaveLog("UpdateSubscription", ex);
updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgFailedImportSubscription}: {ex.Message}"); await updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgFailedImportSubscription}: {ex.Message}");
updateFunc?.Invoke(false, "-------------------------------------------------------"); await updateFunc?.Invoke(false, "-------------------------------------------------------");
} }
} }
updateFunc?.Invoke(true, $"{ResUI.MsgUpdateSubscriptionEnd}"); await updateFunc?.Invoke(successCount > 0, $"{ResUI.MsgUpdateSubscriptionEnd}");
} }
private static bool IsValidSubscription(SubItem item, string subId) private static bool IsValidSubscription(SubItem item, string subId)
@@ -76,7 +80,7 @@ public static class SubscriptionHandler
return true; return true;
} }
private static DownloadService CreateDownloadHandler(string hashCode, Action<bool, string> updateFunc) private static DownloadService CreateDownloadHandler(string hashCode, Func<bool, string, Task> updateFunc)
{ {
var downloadHandle = new DownloadService(); var downloadHandle = new DownloadService();
downloadHandle.Error += (sender2, args) => downloadHandle.Error += (sender2, args) =>
@@ -181,23 +185,23 @@ public static class SubscriptionHandler
return result; return result;
} }
private static async Task ProcessDownloadResult(Config config, string id, string result, string hashCode, Action<bool, string> updateFunc) private static async Task<bool> ProcessDownloadResult(Config config, string id, string result, string hashCode, Func<bool, string, Task> updateFunc)
{ {
if (result.IsNullOrEmpty()) if (result.IsNullOrEmpty())
{ {
updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgSubscriptionDecodingFailed}"); await updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgSubscriptionDecodingFailed}");
return; return false;
} }
updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgGetSubscriptionSuccessfully}"); await updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgGetSubscriptionSuccessfully}");
// If result is too short, display content directly // If result is too short, display content directly
if (result.Length < 99) if (result.Length < 99)
{ {
updateFunc?.Invoke(false, $"{hashCode}{result}"); await updateFunc?.Invoke(false, $"{hashCode}{result}");
} }
updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgStartParsingSubscription}"); await updateFunc?.Invoke(false, $"{hashCode}{ResUI.MsgStartParsingSubscription}");
// Add servers to configuration // Add servers to configuration
var ret = await ConfigHandler.AddBatchServers(config, result, id, true); var ret = await ConfigHandler.AddBatchServers(config, result, id, true);
@@ -208,9 +212,10 @@ public static class SubscriptionHandler
} }
// Update completion message // Update completion message
updateFunc?.Invoke(false, await updateFunc?.Invoke(false, ret > 0
ret > 0
? $"{hashCode}{ResUI.MsgUpdateSubscriptionEnd}" ? $"{hashCode}{ResUI.MsgUpdateSubscriptionEnd}"
: $"{hashCode}{ResUI.MsgFailedImportSubscription}"); : $"{hashCode}{ResUI.MsgFailedImportSubscription}");
return ret > 0;
} }
} }

View File

@@ -1,3 +1,5 @@
using System.Reactive;
namespace ServiceLib.Manager; namespace ServiceLib.Manager;
public sealed class AppManager public sealed class AppManager
@@ -34,7 +36,7 @@ public sealed class AppManager
#endregion Property #endregion Property
#region Init #region App
public bool InitApp() public bool InitApp()
{ {
@@ -87,7 +89,40 @@ public sealed class AppManager
return true; return true;
} }
#endregion Init public async Task AppExitAsync(bool needShutdown)
{
try
{
Logging.SaveLog("AppExitAsync Begin");
await SysProxyHandler.UpdateSysProxy(_config, true);
AppEvents.AppExitRequested.OnNext(Unit.Default);
await Task.Delay(50); //Wait for AppExitRequested to be processed
await ConfigHandler.SaveConfig(_config);
await ProfileExManager.Instance.SaveTo();
await StatisticsManager.Instance.SaveTo();
await CoreManager.Instance.CoreStop();
StatisticsManager.Instance.Close();
Logging.SaveLog("AppExitAsync End");
}
catch { }
finally
{
if (needShutdown)
{
Shutdown(false);
}
}
}
public void Shutdown(bool byUser)
{
AppEvents.ShutdownRequested.OnNext(byUser);
}
#endregion App
#region Config #region Config

View File

@@ -35,7 +35,7 @@ public sealed class ClashApiManager
return null; return null;
} }
public void ClashProxiesDelayTest(bool blAll, List<ClashProxyModel> lstProxy, Action<ClashProxyModel?, string> updateFunc) public void ClashProxiesDelayTest(bool blAll, List<ClashProxyModel> lstProxy, Func<ClashProxyModel?, string, Task> updateFunc)
{ {
Task.Run(async () => Task.Run(async () =>
{ {
@@ -79,12 +79,12 @@ public sealed class ClashApiManager
tasks.Add(Task.Run(async () => tasks.Add(Task.Run(async () =>
{ {
var result = await HttpClientHelper.Instance.TryGetAsync(url); var result = await HttpClientHelper.Instance.TryGetAsync(url);
updateFunc?.Invoke(it, result); await updateFunc?.Invoke(it, result);
})); }));
} }
await Task.WhenAll(tasks); await Task.WhenAll(tasks);
await Task.Delay(1000); await Task.Delay(1000);
updateFunc?.Invoke(null, ""); await updateFunc?.Invoke(null, "");
}); });
} }

View File

@@ -10,11 +10,11 @@ public class CoreAdminManager
private static readonly Lazy<CoreAdminManager> _instance = new(() => new()); private static readonly Lazy<CoreAdminManager> _instance = new(() => new());
public static CoreAdminManager Instance => _instance.Value; public static CoreAdminManager Instance => _instance.Value;
private Config _config; private Config _config;
private Action<bool, string>? _updateFunc; private Func<bool, string, Task>? _updateFunc;
private int _linuxSudoPid = -1; private int _linuxSudoPid = -1;
private const string _tag = "CoreAdminHandler"; private const string _tag = "CoreAdminHandler";
public async Task Init(Config config, Action<bool, string> updateFunc) public async Task Init(Config config, Func<bool, string, Task> updateFunc)
{ {
if (_config != null) if (_config != null)
{ {
@@ -26,9 +26,9 @@ public class CoreAdminManager
await Task.CompletedTask; await Task.CompletedTask;
} }
private void UpdateFunc(bool notify, string msg) private async Task UpdateFunc(bool notify, string msg)
{ {
_updateFunc?.Invoke(notify, msg); await _updateFunc?.Invoke(notify, msg);
} }
public async Task<Process?> RunProcessAsLinuxSudo(string fileName, CoreInfo coreInfo, string configPath) public async Task<Process?> RunProcessAsLinuxSudo(string fileName, CoreInfo coreInfo, string configPath)
@@ -60,7 +60,7 @@ public class CoreAdminManager
{ {
if (e.Data.IsNotEmpty()) if (e.Data.IsNotEmpty())
{ {
UpdateFunc(false, e.Data + Environment.NewLine); _ = UpdateFunc(false, e.Data + Environment.NewLine);
} }
} }
@@ -106,7 +106,7 @@ public class CoreAdminManager
.WithStandardInputPipe(PipeSource.FromString(AppManager.Instance.LinuxSudoPwd)) .WithStandardInputPipe(PipeSource.FromString(AppManager.Instance.LinuxSudoPwd))
.ExecuteBufferedAsync(); .ExecuteBufferedAsync();
UpdateFunc(false, result.StandardOutput.ToString()); await UpdateFunc(false, result.StandardOutput.ToString());
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -125,15 +125,15 @@ public sealed class CoreInfoManager
new CoreInfo new CoreInfo
{ {
CoreType = ECoreType.mihomo, CoreType = ECoreType.mihomo,
CoreExes = ["mihomo-windows-amd64-compatible", "mihomo-windows-amd64", "mihomo-linux-amd64", "clash", "mihomo"], CoreExes = ["mihomo-windows-amd64-v1", "mihomo-windows-amd64-compatible", "mihomo-windows-amd64", "mihomo-linux-amd64", "clash", "mihomo"],
Arguments = "-f {0}" + PortableMode(), Arguments = "-f {0}" + PortableMode(),
Url = GetCoreUrl(ECoreType.mihomo), Url = GetCoreUrl(ECoreType.mihomo),
ReleaseApiUrl = urlMihomo.Replace(Global.GithubUrl, Global.GithubApiUrl), ReleaseApiUrl = urlMihomo.Replace(Global.GithubUrl, Global.GithubApiUrl),
DownloadUrlWin64 = urlMihomo + "/download/{0}/mihomo-windows-amd64-compatible-{0}.zip", DownloadUrlWin64 = urlMihomo + "/download/{0}/mihomo-windows-amd64-v1-{0}.zip",
DownloadUrlWinArm64 = urlMihomo + "/download/{0}/mihomo-windows-arm64-{0}.zip", DownloadUrlWinArm64 = urlMihomo + "/download/{0}/mihomo-windows-arm64-{0}.zip",
DownloadUrlLinux64 = urlMihomo + "/download/{0}/mihomo-linux-amd64-compatible-{0}.gz", DownloadUrlLinux64 = urlMihomo + "/download/{0}/mihomo-linux-amd64-v1-{0}.gz",
DownloadUrlLinuxArm64 = urlMihomo + "/download/{0}/mihomo-linux-arm64-{0}.gz", DownloadUrlLinuxArm64 = urlMihomo + "/download/{0}/mihomo-linux-arm64-{0}.gz",
DownloadUrlOSX64 = urlMihomo + "/download/{0}/mihomo-darwin-amd64-compatible-{0}.gz", DownloadUrlOSX64 = urlMihomo + "/download/{0}/mihomo-darwin-amd64-v1-{0}.gz",
DownloadUrlOSXArm64 = urlMihomo + "/download/{0}/mihomo-darwin-arm64-{0}.gz", DownloadUrlOSXArm64 = urlMihomo + "/download/{0}/mihomo-darwin-arm64-{0}.gz",
Match = "Mihomo", Match = "Mihomo",
VersionArg = "-v", VersionArg = "-v",

View File

@@ -14,10 +14,10 @@ public class CoreManager
private Process? _process; private Process? _process;
private Process? _processPre; private Process? _processPre;
private bool _linuxSudo = false; private bool _linuxSudo = false;
private Action<bool, string>? _updateFunc; private Func<bool, string, Task>? _updateFunc;
private const string _tag = "CoreHandler"; private const string _tag = "CoreHandler";
public async Task Init(Config config, Action<bool, string> updateFunc) public async Task Init(Config config, Func<bool, string, Task> updateFunc)
{ {
_config = config; _config = config;
_updateFunc = updateFunc; _updateFunc = updateFunc;
@@ -63,7 +63,7 @@ public class CoreManager
{ {
if (node == null) if (node == null)
{ {
UpdateFunc(false, ResUI.CheckServerSettings); await UpdateFunc(false, ResUI.CheckServerSettings);
return; return;
} }
@@ -71,13 +71,13 @@ public class CoreManager
var result = await CoreConfigHandler.GenerateClientConfig(node, fileName); var result = await CoreConfigHandler.GenerateClientConfig(node, fileName);
if (result.Success != true) if (result.Success != true)
{ {
UpdateFunc(true, result.Msg); await UpdateFunc(true, result.Msg);
return; return;
} }
UpdateFunc(false, $"{node.GetSummary()}"); await UpdateFunc(false, $"{node.GetSummary()}");
UpdateFunc(false, $"{Utils.GetRuntimeInfo()}"); await UpdateFunc(false, $"{Utils.GetRuntimeInfo()}");
UpdateFunc(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"))); await UpdateFunc(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
await CoreStop(); await CoreStop();
await Task.Delay(100); await Task.Delay(100);
@@ -91,7 +91,7 @@ public class CoreManager
await CoreStartPreService(node); await CoreStartPreService(node);
if (_process != null) if (_process != null)
{ {
UpdateFunc(true, $"{node.GetSummary()}"); await UpdateFunc(true, $"{node.GetSummary()}");
} }
} }
@@ -101,14 +101,14 @@ public class CoreManager
var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false)); var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false));
var configPath = Utils.GetBinConfigPath(fileName); var configPath = Utils.GetBinConfigPath(fileName);
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType); var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType);
UpdateFunc(false, result.Msg); await UpdateFunc(false, result.Msg);
if (result.Success != true) if (result.Success != true)
{ {
return -1; return -1;
} }
UpdateFunc(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"))); await UpdateFunc(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
UpdateFunc(false, configPath); await UpdateFunc(false, configPath);
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(coreType); var coreInfo = CoreInfoManager.Instance.GetCoreInfo(coreType);
var proc = await RunProcess(coreInfo, fileName, true, false); var proc = await RunProcess(coreInfo, fileName, true, false);
@@ -216,9 +216,9 @@ public class CoreManager
} }
} }
private void UpdateFunc(bool notify, string msg) private async Task UpdateFunc(bool notify, string msg)
{ {
_updateFunc?.Invoke(notify, msg); await _updateFunc?.Invoke(notify, msg);
} }
#endregion Private #endregion Private
@@ -230,7 +230,7 @@ public class CoreManager
var fileName = CoreInfoManager.Instance.GetCoreExecFile(coreInfo, out var msg); var fileName = CoreInfoManager.Instance.GetCoreExecFile(coreInfo, out var msg);
if (fileName.IsNullOrEmpty()) if (fileName.IsNullOrEmpty())
{ {
UpdateFunc(false, msg); await UpdateFunc(false, msg);
return null; return null;
} }
@@ -251,7 +251,7 @@ public class CoreManager
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(_tag, ex); Logging.SaveLog(_tag, ex);
UpdateFunc(mayNeedSudo, ex.Message); await UpdateFunc(mayNeedSudo, ex.Message);
return null; return null;
} }
} }
@@ -284,7 +284,7 @@ public class CoreManager
{ {
if (e.Data.IsNotEmpty()) if (e.Data.IsNotEmpty())
{ {
UpdateFunc(false, e.Data + Environment.NewLine); _ = UpdateFunc(false, e.Data + Environment.NewLine);
} }
} }
proc.OutputDataReceived += dataHandler; proc.OutputDataReceived += dataHandler;

View File

@@ -1,5 +1,3 @@
using ReactiveUI;
namespace ServiceLib.Manager; namespace ServiceLib.Manager;
public class NoticeManager public class NoticeManager
@@ -13,7 +11,7 @@ public class NoticeManager
{ {
return; return;
} }
MessageBus.Current.SendMessage(content, EMsgCommand.SendSnackMsg.ToString()); AppEvents.SendSnackMsgRequested.OnNext(content);
} }
public void SendMessage(string? content) public void SendMessage(string? content)
@@ -22,7 +20,7 @@ public class NoticeManager
{ {
return; return;
} }
MessageBus.Current.SendMessage(content, EMsgCommand.SendMsgView.ToString()); AppEvents.SendMsgViewRequested.OnNext(content);
} }
public void SendMessageEx(string? content) public void SendMessageEx(string? content)

View File

@@ -8,14 +8,14 @@ public class StatisticsManager
private Config _config; private Config _config;
private ServerStatItem? _serverStatItem; private ServerStatItem? _serverStatItem;
private List<ServerStatItem> _lstServerStat; private List<ServerStatItem> _lstServerStat;
private Action<ServerSpeedItem>? _updateFunc; private Func<ServerSpeedItem, Task>? _updateFunc;
private StatisticsXrayService? _statisticsXray; private StatisticsXrayService? _statisticsXray;
private StatisticsSingboxService? _statisticsSingbox; private StatisticsSingboxService? _statisticsSingbox;
private static readonly string _tag = "StatisticsHandler"; private static readonly string _tag = "StatisticsHandler";
public List<ServerStatItem> ServerStat => _lstServerStat; public List<ServerStatItem> ServerStat => _lstServerStat;
public async Task Init(Config config, Action<ServerSpeedItem> updateFunc) public async Task Init(Config config, Func<ServerSpeedItem, Task> updateFunc)
{ {
_config = config; _config = config;
_updateFunc = updateFunc; _updateFunc = updateFunc;
@@ -97,9 +97,9 @@ public class StatisticsManager
_lstServerStat = await SQLiteHelper.Instance.TableAsync<ServerStatItem>().ToListAsync(); _lstServerStat = await SQLiteHelper.Instance.TableAsync<ServerStatItem>().ToListAsync();
} }
private void UpdateServerStatHandler(ServerSpeedItem server) private async Task UpdateServerStatHandler(ServerSpeedItem server)
{ {
_ = UpdateServerStat(server); await UpdateServerStat(server);
} }
private async Task UpdateServerStat(ServerSpeedItem server) private async Task UpdateServerStat(ServerSpeedItem server)
@@ -123,7 +123,7 @@ public class StatisticsManager
server.TodayDown = _serverStatItem.TodayDown; server.TodayDown = _serverStatItem.TodayDown;
server.TotalUp = _serverStatItem.TotalUp; server.TotalUp = _serverStatItem.TotalUp;
server.TotalDown = _serverStatItem.TotalDown; server.TotalDown = _serverStatItem.TotalDown;
_updateFunc?.Invoke(server); await _updateFunc?.Invoke(server);
} }
private async Task GetServerStatItem(string indexId) private async Task GetServerStatItem(string indexId)

View File

@@ -4,13 +4,18 @@ public class TaskManager
{ {
private static readonly Lazy<TaskManager> _instance = new(() => new()); private static readonly Lazy<TaskManager> _instance = new(() => new());
public static TaskManager Instance => _instance.Value; public static TaskManager Instance => _instance.Value;
private Config _config;
private Func<bool, string, Task>? _updateFunc;
public void RegUpdateTask(Config config, Action<bool, string> updateFunc) public void RegUpdateTask(Config config, Func<bool, string, Task> updateFunc)
{ {
Task.Run(() => ScheduledTasks(config, updateFunc)); _config = config;
_updateFunc = updateFunc;
Task.Run(ScheduledTasks);
} }
private async Task ScheduledTasks(Config config, Action<bool, string> updateFunc) private async Task ScheduledTasks()
{ {
Logging.SaveLog("Setup Scheduled Tasks"); Logging.SaveLog("Setup Scheduled Tasks");
@@ -21,14 +26,14 @@ public class TaskManager
await Task.Delay(1000 * 60); await Task.Delay(1000 * 60);
//Execute once 1 minute //Execute once 1 minute
await UpdateTaskRunSubscription(config, updateFunc); await UpdateTaskRunSubscription();
//Execute once 20 minute //Execute once 20 minute
if (numOfExecuted % 20 == 0) if (numOfExecuted % 20 == 0)
{ {
//Logging.SaveLog("Execute save config"); //Logging.SaveLog("Execute save config");
await ConfigHandler.SaveConfig(config); await ConfigHandler.SaveConfig(_config);
await ProfileExManager.Instance.SaveTo(); await ProfileExManager.Instance.SaveTo();
} }
@@ -42,14 +47,14 @@ public class TaskManager
FileManager.DeleteExpiredFiles(Utils.GetTempPath(), DateTime.Now.AddMonths(-1)); FileManager.DeleteExpiredFiles(Utils.GetTempPath(), DateTime.Now.AddMonths(-1));
//Check once 1 hour //Check once 1 hour
await UpdateTaskRunGeo(config, numOfExecuted / 60, updateFunc); await UpdateTaskRunGeo(numOfExecuted / 60);
} }
numOfExecuted++; numOfExecuted++;
} }
} }
private async Task UpdateTaskRunSubscription(Config config, Action<bool, string> updateFunc) private async Task UpdateTaskRunSubscription()
{ {
var updateTime = ((DateTimeOffset)DateTime.Now).ToUnixTimeSeconds(); var updateTime = ((DateTimeOffset)DateTime.Now).ToUnixTimeSeconds();
var lstSubs = (await AppManager.Instance.SubItems())? var lstSubs = (await AppManager.Instance.SubItems())?
@@ -66,30 +71,30 @@ public class TaskManager
foreach (var item in lstSubs) foreach (var item in lstSubs)
{ {
await SubscriptionHandler.UpdateProcess(config, item.Id, true, (success, msg) => await SubscriptionHandler.UpdateProcess(_config, item.Id, true, async (success, msg) =>
{ {
updateFunc?.Invoke(success, msg); await _updateFunc?.Invoke(success, msg);
if (success) if (success)
{ {
Logging.SaveLog($"Update subscription end. {msg}"); Logging.SaveLog($"Update subscription end. {msg}");
} }
}); });
item.UpdateTime = updateTime; item.UpdateTime = updateTime;
await ConfigHandler.AddSubItem(config, item); await ConfigHandler.AddSubItem(_config, item);
await Task.Delay(1000); await Task.Delay(1000);
} }
} }
private async Task UpdateTaskRunGeo(Config config, int hours, Action<bool, string> updateFunc) private async Task UpdateTaskRunGeo(int hours)
{ {
if (config.GuiItem.AutoUpdateInterval > 0 && hours > 0 && hours % config.GuiItem.AutoUpdateInterval == 0) if (_config.GuiItem.AutoUpdateInterval > 0 && hours > 0 && hours % _config.GuiItem.AutoUpdateInterval == 0)
{ {
Logging.SaveLog("Execute update geo files"); Logging.SaveLog("Execute update geo files");
var updateHandle = new UpdateService(); var updateHandle = new UpdateService();
await updateHandle.UpdateGeoFileAll(config, (success, msg) => await updateHandle.UpdateGeoFileAll(_config, async (success, msg) =>
{ {
updateFunc?.Invoke(false, msg); await _updateFunc?.Invoke(false, msg);
}); });
} }
} }

View File

@@ -15,7 +15,7 @@ public class DownloadService
private static readonly string _tag = "DownloadService"; private static readonly string _tag = "DownloadService";
public async Task<int> DownloadDataAsync(string url, WebProxy webProxy, int downloadTimeout, Action<bool, string> updateFunc) public async Task<int> DownloadDataAsync(string url, WebProxy webProxy, int downloadTimeout, Func<bool, string, Task> updateFunc)
{ {
try try
{ {
@@ -31,10 +31,10 @@ public class DownloadService
} }
catch (Exception ex) catch (Exception ex)
{ {
updateFunc?.Invoke(false, ex.Message); await updateFunc?.Invoke(false, ex.Message);
if (ex.InnerException != null) if (ex.InnerException != null)
{ {
updateFunc?.Invoke(false, ex.InnerException.Message); await updateFunc?.Invoke(false, ex.InnerException.Message);
} }
} }
return 0; return 0;

View File

@@ -5,26 +5,20 @@ using System.Net.Sockets;
namespace ServiceLib.Services; namespace ServiceLib.Services;
public class SpeedtestService public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateFunc)
{ {
private static readonly string _tag = "SpeedtestService"; private static readonly string _tag = "SpeedtestService";
private Config? _config; private readonly Config? _config = config;
private Action<SpeedTestResult>? _updateFunc; private readonly Func<SpeedTestResult, Task>? _updateFunc = updateFunc;
private static readonly ConcurrentBag<string> _lstExitLoop = new(); private static readonly ConcurrentBag<string> _lstExitLoop = new();
public SpeedtestService(Config config, Action<SpeedTestResult> updateFunc)
{
_config = config;
_updateFunc = updateFunc;
}
public void RunLoop(ESpeedActionType actionType, List<ProfileItem> selecteds) public void RunLoop(ESpeedActionType actionType, List<ProfileItem> selecteds)
{ {
Task.Run(async () => Task.Run(async () =>
{ {
await RunAsync(actionType, selecteds); await RunAsync(actionType, selecteds);
await ProfileExManager.Instance.SaveTo(); await ProfileExManager.Instance.SaveTo();
UpdateFunc("", ResUI.SpeedtestingCompleted); await UpdateFunc("", ResUI.SpeedtestingCompleted);
}); });
} }
@@ -43,7 +37,7 @@ public class SpeedtestService
var exitLoopKey = Utils.GetGuid(false); var exitLoopKey = Utils.GetGuid(false);
_lstExitLoop.Add(exitLoopKey); _lstExitLoop.Add(exitLoopKey);
var lstSelected = GetClearItem(actionType, selecteds); var lstSelected = await GetClearItem(actionType, selecteds);
switch (actionType) switch (actionType)
{ {
@@ -65,7 +59,7 @@ public class SpeedtestService
} }
} }
private List<ServerTestItem> GetClearItem(ESpeedActionType actionType, List<ProfileItem> selecteds) private async Task<List<ServerTestItem>> GetClearItem(ESpeedActionType actionType, List<ProfileItem> selecteds)
{ {
var lstSelected = new List<ServerTestItem>(); var lstSelected = new List<ServerTestItem>();
foreach (var it in selecteds) foreach (var it in selecteds)
@@ -97,17 +91,17 @@ public class SpeedtestService
{ {
case ESpeedActionType.Tcping: case ESpeedActionType.Tcping:
case ESpeedActionType.Realping: case ESpeedActionType.Realping:
UpdateFunc(it.IndexId, ResUI.Speedtesting, ""); await UpdateFunc(it.IndexId, ResUI.Speedtesting, "");
ProfileExManager.Instance.SetTestDelay(it.IndexId, 0); ProfileExManager.Instance.SetTestDelay(it.IndexId, 0);
break; break;
case ESpeedActionType.Speedtest: case ESpeedActionType.Speedtest:
UpdateFunc(it.IndexId, "", ResUI.SpeedtestingWait); await UpdateFunc(it.IndexId, "", ResUI.SpeedtestingWait);
ProfileExManager.Instance.SetTestSpeed(it.IndexId, 0); ProfileExManager.Instance.SetTestSpeed(it.IndexId, 0);
break; break;
case ESpeedActionType.Mixedtest: case ESpeedActionType.Mixedtest:
UpdateFunc(it.IndexId, ResUI.Speedtesting, ResUI.SpeedtestingWait); await UpdateFunc(it.IndexId, ResUI.Speedtesting, ResUI.SpeedtestingWait);
ProfileExManager.Instance.SetTestDelay(it.IndexId, 0); ProfileExManager.Instance.SetTestDelay(it.IndexId, 0);
ProfileExManager.Instance.SetTestSpeed(it.IndexId, 0); ProfileExManager.Instance.SetTestSpeed(it.IndexId, 0);
break; break;
@@ -133,7 +127,7 @@ public class SpeedtestService
var responseTime = await GetTcpingTime(it.Address, it.Port); var responseTime = await GetTcpingTime(it.Address, it.Port);
ProfileExManager.Instance.SetTestDelay(it.IndexId, responseTime); ProfileExManager.Instance.SetTestDelay(it.IndexId, responseTime);
UpdateFunc(it.IndexId, responseTime.ToString()); await UpdateFunc(it.IndexId, responseTime.ToString());
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -169,11 +163,11 @@ public class SpeedtestService
{ {
if (_lstExitLoop.Any(p => p == exitLoopKey) == false) if (_lstExitLoop.Any(p => p == exitLoopKey) == false)
{ {
UpdateFunc("", ResUI.SpeedtestingSkip); await UpdateFunc("", ResUI.SpeedtestingSkip);
return; return;
} }
UpdateFunc("", string.Format(ResUI.SpeedtestingTestFailedPart, lstFailed.Count)); await UpdateFunc("", string.Format(ResUI.SpeedtestingTestFailedPart, lstFailed.Count));
if (pageSizeNext > _config.SpeedTestItem.MixedConcurrencyCount) if (pageSizeNext > _config.SpeedTestItem.MixedConcurrencyCount)
{ {
@@ -239,7 +233,7 @@ public class SpeedtestService
{ {
if (_lstExitLoop.Any(p => p == exitLoopKey) == false) if (_lstExitLoop.Any(p => p == exitLoopKey) == false)
{ {
UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip); await UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip);
continue; continue;
} }
if (it.ConfigType == EConfigType.Custom) if (it.ConfigType == EConfigType.Custom)
@@ -256,7 +250,7 @@ public class SpeedtestService
pid = await CoreManager.Instance.LoadCoreConfigSpeedtest(it); pid = await CoreManager.Instance.LoadCoreConfigSpeedtest(it);
if (pid < 0) if (pid < 0)
{ {
UpdateFunc(it.IndexId, "", ResUI.FailedToRunCore); await UpdateFunc(it.IndexId, "", ResUI.FailedToRunCore);
} }
else else
{ {
@@ -270,7 +264,7 @@ public class SpeedtestService
} }
else else
{ {
UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip); await UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip);
} }
} }
} }
@@ -298,25 +292,25 @@ public class SpeedtestService
var responseTime = await HttpClientHelper.Instance.GetRealPingTime(_config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10); var responseTime = await HttpClientHelper.Instance.GetRealPingTime(_config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10);
ProfileExManager.Instance.SetTestDelay(it.IndexId, responseTime); ProfileExManager.Instance.SetTestDelay(it.IndexId, responseTime);
UpdateFunc(it.IndexId, responseTime.ToString()); await UpdateFunc(it.IndexId, responseTime.ToString());
return responseTime; return responseTime;
} }
private async Task DoSpeedTest(DownloadService downloadHandle, ServerTestItem it) private async Task DoSpeedTest(DownloadService downloadHandle, ServerTestItem it)
{ {
UpdateFunc(it.IndexId, "", ResUI.Speedtesting); await UpdateFunc(it.IndexId, "", ResUI.Speedtesting);
var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}"); var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}");
var url = _config.SpeedTestItem.SpeedTestUrl; var url = _config.SpeedTestItem.SpeedTestUrl;
var timeout = _config.SpeedTestItem.SpeedTestTimeout; var timeout = _config.SpeedTestItem.SpeedTestTimeout;
await downloadHandle.DownloadDataAsync(url, webProxy, timeout, (success, msg) => await downloadHandle.DownloadDataAsync(url, webProxy, timeout, async (success, msg) =>
{ {
decimal.TryParse(msg, out var dec); decimal.TryParse(msg, out var dec);
if (dec > 0) if (dec > 0)
{ {
ProfileExManager.Instance.SetTestSpeed(it.IndexId, dec); ProfileExManager.Instance.SetTestSpeed(it.IndexId, dec);
} }
UpdateFunc(it.IndexId, "", msg); await UpdateFunc(it.IndexId, "", msg);
}); });
} }
@@ -371,9 +365,9 @@ public class SpeedtestService
return lstTest; return lstTest;
} }
private void UpdateFunc(string indexId, string delay, string speed = "") private async Task UpdateFunc(string indexId, string delay, string speed = "")
{ {
_updateFunc?.Invoke(new() { IndexId = indexId, Delay = delay, Speed = speed }); await _updateFunc?.Invoke(new() { IndexId = indexId, Delay = delay, Speed = speed });
if (indexId.IsNotEmpty() && speed.IsNotEmpty()) if (indexId.IsNotEmpty() && speed.IsNotEmpty())
{ {
ProfileExManager.Instance.SetTestMessage(indexId, speed); ProfileExManager.Instance.SetTestMessage(indexId, speed);

View File

@@ -8,11 +8,11 @@ public class StatisticsSingboxService
private readonly Config _config; private readonly Config _config;
private bool _exitFlag; private bool _exitFlag;
private ClientWebSocket? webSocket; private ClientWebSocket? webSocket;
private Action<ServerSpeedItem>? _updateFunc; private readonly Func<ServerSpeedItem, Task>? _updateFunc;
private string Url => $"ws://{Global.Loopback}:{AppManager.Instance.StatePort2}/traffic"; private string Url => $"ws://{Global.Loopback}:{AppManager.Instance.StatePort2}/traffic";
private static readonly string _tag = "StatisticsSingboxService"; private static readonly string _tag = "StatisticsSingboxService";
public StatisticsSingboxService(Config config, Action<ServerSpeedItem> updateFunc) public StatisticsSingboxService(Config config, Func<ServerSpeedItem, Task> updateFunc)
{ {
_config = config; _config = config;
_updateFunc = updateFunc; _updateFunc = updateFunc;
@@ -90,7 +90,7 @@ public class StatisticsSingboxService
{ {
ParseOutput(result, out var up, out var down); ParseOutput(result, out var up, out var down);
_updateFunc?.Invoke(new ServerSpeedItem() await _updateFunc?.Invoke(new ServerSpeedItem()
{ {
ProxyUp = (long)(up / 1000), ProxyUp = (long)(up / 1000),
ProxyDown = (long)(down / 1000) ProxyDown = (long)(down / 1000)

View File

@@ -6,10 +6,10 @@ public class StatisticsXrayService
private ServerSpeedItem _serverSpeedItem = new(); private ServerSpeedItem _serverSpeedItem = new();
private readonly Config _config; private readonly Config _config;
private bool _exitFlag; private bool _exitFlag;
private Action<ServerSpeedItem>? _updateFunc; private readonly Func<ServerSpeedItem, Task>? _updateFunc;
private string Url => $"{Global.HttpProtocol}{Global.Loopback}:{AppManager.Instance.StatePort}/debug/vars"; private string Url => $"{Global.HttpProtocol}{Global.Loopback}:{AppManager.Instance.StatePort}/debug/vars";
public StatisticsXrayService(Config config, Action<ServerSpeedItem> updateFunc) public StatisticsXrayService(Config config, Func<ServerSpeedItem, Task> updateFunc)
{ {
_config = config; _config = config;
_updateFunc = updateFunc; _updateFunc = updateFunc;
@@ -39,7 +39,7 @@ public class StatisticsXrayService
if (result != null) if (result != null)
{ {
var server = ParseOutput(result) ?? new ServerSpeedItem(); var server = ParseOutput(result) ?? new ServerSpeedItem();
_updateFunc?.Invoke(server); await _updateFunc?.Invoke(server);
} }
} }
catch catch

View File

@@ -5,11 +5,11 @@ namespace ServiceLib.Services;
public class UpdateService public class UpdateService
{ {
private Action<bool, string>? _updateFunc; private Func<bool, string, Task>? _updateFunc;
private readonly int _timeout = 30; private readonly int _timeout = 30;
private static readonly string _tag = "UpdateService"; private static readonly string _tag = "UpdateService";
public async Task CheckUpdateGuiN(Config config, Action<bool, string> updateFunc, bool preRelease) public async Task CheckUpdateGuiN(Config config, Func<bool, string, Task> updateFunc, bool preRelease)
{ {
_updateFunc = updateFunc; _updateFunc = updateFunc;
var url = string.Empty; var url = string.Empty;
@@ -20,25 +20,25 @@ public class UpdateService
{ {
if (args.Success) if (args.Success)
{ {
_updateFunc?.Invoke(false, ResUI.MsgDownloadV2rayCoreSuccessfully); UpdateFunc(false, ResUI.MsgDownloadV2rayCoreSuccessfully);
_updateFunc?.Invoke(true, Utils.UrlEncode(fileName)); UpdateFunc(true, Utils.UrlEncode(fileName));
} }
else else
{ {
_updateFunc?.Invoke(false, args.Msg); UpdateFunc(false, args.Msg);
} }
}; };
downloadHandle.Error += (sender2, args) => downloadHandle.Error += (sender2, args) =>
{ {
_updateFunc?.Invoke(false, args.GetException().Message); UpdateFunc(false, args.GetException().Message);
}; };
_updateFunc?.Invoke(false, string.Format(ResUI.MsgStartUpdating, ECoreType.v2rayN)); await UpdateFunc(false, string.Format(ResUI.MsgStartUpdating, ECoreType.v2rayN));
var result = await CheckUpdateAsync(downloadHandle, ECoreType.v2rayN, preRelease); var result = await CheckUpdateAsync(downloadHandle, ECoreType.v2rayN, preRelease);
if (result.Success) if (result.Success)
{ {
_updateFunc?.Invoke(false, string.Format(ResUI.MsgParsingSuccessfully, ECoreType.v2rayN)); await UpdateFunc(false, string.Format(ResUI.MsgParsingSuccessfully, ECoreType.v2rayN));
_updateFunc?.Invoke(false, result.Msg); await UpdateFunc(false, result.Msg);
url = result.Data?.ToString(); url = result.Data?.ToString();
fileName = Utils.GetTempPath(Utils.GetGuid()); fileName = Utils.GetTempPath(Utils.GetGuid());
@@ -46,11 +46,11 @@ public class UpdateService
} }
else else
{ {
_updateFunc?.Invoke(false, result.Msg); await UpdateFunc(false, result.Msg);
} }
} }
public async Task CheckUpdateCore(ECoreType type, Config config, Action<bool, string> updateFunc, bool preRelease) public async Task CheckUpdateCore(ECoreType type, Config config, Func<bool, string, Task> updateFunc, bool preRelease)
{ {
_updateFunc = updateFunc; _updateFunc = updateFunc;
var url = string.Empty; var url = string.Empty;
@@ -61,34 +61,34 @@ public class UpdateService
{ {
if (args.Success) if (args.Success)
{ {
_updateFunc?.Invoke(false, ResUI.MsgDownloadV2rayCoreSuccessfully); UpdateFunc(false, ResUI.MsgDownloadV2rayCoreSuccessfully);
_updateFunc?.Invoke(false, ResUI.MsgUnpacking); UpdateFunc(false, ResUI.MsgUnpacking);
try try
{ {
_updateFunc?.Invoke(true, fileName); UpdateFunc(true, fileName);
} }
catch (Exception ex) catch (Exception ex)
{ {
_updateFunc?.Invoke(false, ex.Message); UpdateFunc(false, ex.Message);
} }
} }
else else
{ {
_updateFunc?.Invoke(false, args.Msg); UpdateFunc(false, args.Msg);
} }
}; };
downloadHandle.Error += (sender2, args) => downloadHandle.Error += (sender2, args) =>
{ {
_updateFunc?.Invoke(false, args.GetException().Message); UpdateFunc(false, args.GetException().Message);
}; };
_updateFunc?.Invoke(false, string.Format(ResUI.MsgStartUpdating, type)); await UpdateFunc(false, string.Format(ResUI.MsgStartUpdating, type));
var result = await CheckUpdateAsync(downloadHandle, type, preRelease); var result = await CheckUpdateAsync(downloadHandle, type, preRelease);
if (result.Success) if (result.Success)
{ {
_updateFunc?.Invoke(false, string.Format(ResUI.MsgParsingSuccessfully, type)); await UpdateFunc(false, string.Format(ResUI.MsgParsingSuccessfully, type));
_updateFunc?.Invoke(false, result.Msg); await UpdateFunc(false, result.Msg);
url = result.Data?.ToString(); url = result.Data?.ToString();
var ext = url.Contains(".tar.gz") ? ".tar.gz" : Path.GetExtension(url); var ext = url.Contains(".tar.gz") ? ".tar.gz" : Path.GetExtension(url);
@@ -99,17 +99,17 @@ public class UpdateService
{ {
if (!result.Msg.IsNullOrEmpty()) if (!result.Msg.IsNullOrEmpty())
{ {
_updateFunc?.Invoke(false, result.Msg); await UpdateFunc(false, result.Msg);
} }
} }
} }
public async Task UpdateGeoFileAll(Config config, Action<bool, string> updateFunc) public async Task UpdateGeoFileAll(Config config, Func<bool, string, Task> updateFunc)
{ {
await UpdateGeoFiles(config, updateFunc); await UpdateGeoFiles(config, updateFunc);
await UpdateOtherFiles(config, updateFunc); await UpdateOtherFiles(config, updateFunc);
await UpdateSrsFileAll(config, updateFunc); await UpdateSrsFileAll(config, updateFunc);
_updateFunc?.Invoke(true, string.Format(ResUI.MsgDownloadGeoFileSuccessfully, "geo")); await UpdateFunc(true, string.Format(ResUI.MsgDownloadGeoFileSuccessfully, "geo"));
} }
#region CheckUpdate private #region CheckUpdate private
@@ -128,7 +128,7 @@ public class UpdateService
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(_tag, ex); Logging.SaveLog(_tag, ex);
_updateFunc?.Invoke(false, ex.Message); await UpdateFunc(false, ex.Message);
return new RetResult(false, ex.Message); return new RetResult(false, ex.Message);
} }
} }
@@ -212,7 +212,7 @@ public class UpdateService
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(_tag, ex); Logging.SaveLog(_tag, ex);
_updateFunc?.Invoke(false, ex.Message); await UpdateFunc(false, ex.Message);
return new SemanticVersion(""); return new SemanticVersion("");
} }
} }
@@ -272,7 +272,7 @@ public class UpdateService
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog(_tag, ex); Logging.SaveLog(_tag, ex);
_updateFunc?.Invoke(false, ex.Message); await UpdateFunc(false, ex.Message);
return new RetResult(false, ex.Message); return new RetResult(false, ex.Message);
} }
} }
@@ -333,7 +333,7 @@ public class UpdateService
#region Geo private #region Geo private
private async Task UpdateGeoFiles(Config config, Action<bool, string> updateFunc) private async Task UpdateGeoFiles(Config config, Func<bool, string, Task> updateFunc)
{ {
_updateFunc = updateFunc; _updateFunc = updateFunc;
@@ -352,7 +352,7 @@ public class UpdateService
} }
} }
private async Task UpdateOtherFiles(Config config, Action<bool, string> updateFunc) private async Task UpdateOtherFiles(Config config, Func<bool, string, Task> updateFunc)
{ {
//If it is not in China area, no update is required //If it is not in China area, no update is required
if (config.ConstItem.GeoSourceUrl.IsNotEmpty()) if (config.ConstItem.GeoSourceUrl.IsNotEmpty())
@@ -371,7 +371,7 @@ public class UpdateService
} }
} }
private async Task UpdateSrsFileAll(Config config, Action<bool, string> updateFunc) private async Task UpdateSrsFileAll(Config config, Func<bool, string, Task> updateFunc)
{ {
_updateFunc = updateFunc; _updateFunc = updateFunc;
@@ -426,7 +426,7 @@ public class UpdateService
} }
} }
private async Task UpdateSrsFile(string type, string srsName, Config config, Action<bool, string> updateFunc) private async Task UpdateSrsFile(string type, string srsName, Config config, Func<bool, string, Task> updateFunc)
{ {
var srsUrl = string.IsNullOrEmpty(config.ConstItem.SrsSourceUrl) var srsUrl = string.IsNullOrEmpty(config.ConstItem.SrsSourceUrl)
? Global.SingboxRulesetUrl ? Global.SingboxRulesetUrl
@@ -439,7 +439,7 @@ public class UpdateService
await DownloadGeoFile(url, fileName, targetPath, updateFunc); await DownloadGeoFile(url, fileName, targetPath, updateFunc);
} }
private async Task DownloadGeoFile(string url, string fileName, string targetPath, Action<bool, string> updateFunc) private async Task DownloadGeoFile(string url, string fileName, string targetPath, Func<bool, string, Task> updateFunc)
{ {
var tmpFileName = Utils.GetTempPath(Utils.GetGuid()); var tmpFileName = Utils.GetTempPath(Utils.GetGuid());
@@ -448,7 +448,7 @@ public class UpdateService
{ {
if (args.Success) if (args.Success)
{ {
_updateFunc?.Invoke(false, string.Format(ResUI.MsgDownloadGeoFileSuccessfully, fileName)); UpdateFunc(false, string.Format(ResUI.MsgDownloadGeoFileSuccessfully, fileName));
try try
{ {
@@ -457,26 +457,31 @@ public class UpdateService
File.Copy(tmpFileName, targetPath, true); File.Copy(tmpFileName, targetPath, true);
File.Delete(tmpFileName); File.Delete(tmpFileName);
//_updateFunc?.Invoke(true, ""); //await UpdateFunc(true, "");
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
_updateFunc?.Invoke(false, ex.Message); UpdateFunc(false, ex.Message);
} }
} }
else else
{ {
_updateFunc?.Invoke(false, args.Msg); UpdateFunc(false, args.Msg);
} }
}; };
downloadHandle.Error += (sender2, args) => downloadHandle.Error += (sender2, args) =>
{ {
_updateFunc?.Invoke(false, args.GetException().Message); UpdateFunc(false, args.GetException().Message);
}; };
await downloadHandle.DownloadFileAsync(url, tmpFileName, true, _timeout); await downloadHandle.DownloadFileAsync(url, tmpFileName, true, _timeout);
} }
#endregion Geo private #endregion Geo private
private async Task UpdateFunc(bool notify, string msg)
{
await _updateFunc?.Invoke(notify, msg);
}
} }

View File

@@ -136,8 +136,7 @@ public class BackupAndRestoreViewModel : MyReactiveObject
var result = await CreateZipFileFromDirectory(fileBackup); var result = await CreateZipFileFromDirectory(fileBackup);
if (result) if (result)
{ {
var service = Locator.Current.GetService<MainWindowViewModel>(); await AppManager.Instance.AppExitAsync(false);
await service?.MyAppExitAsync(true);
await SQLiteHelper.Instance.DisposeDbConnectionAsync(); await SQLiteHelper.Instance.DisposeDbConnectionAsync();
var toPath = Utils.GetConfigPath(); var toPath = Utils.GetConfigPath();
@@ -154,7 +153,7 @@ public class BackupAndRestoreViewModel : MyReactiveObject
_ = ProcUtils.ProcessStart(upgradeFileName, Global.RebootAs, Utils.StartupPath()); _ = ProcUtils.ProcessStart(upgradeFileName, Global.RebootAs, Utils.StartupPath());
} }
} }
service?.Shutdown(true); AppManager.Instance.Shutdown(true);
} }
else else
{ {

View File

@@ -1,4 +1,6 @@
using System.Reactive; using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using DynamicData; using DynamicData;
using DynamicData.Binding; using DynamicData.Binding;
@@ -11,11 +13,11 @@ namespace ServiceLib.ViewModels;
public class CheckUpdateViewModel : MyReactiveObject public class CheckUpdateViewModel : MyReactiveObject
{ {
private const string _geo = "GeoFiles"; private const string _geo = "GeoFiles";
private string _v2rayN = ECoreType.v2rayN.ToString(); private readonly string _v2rayN = ECoreType.v2rayN.ToString();
private List<CheckUpdateModel> _lstUpdated = []; private List<CheckUpdateModel> _lstUpdated = [];
private static readonly string _tag = "CheckUpdateViewModel";
private IObservableCollection<CheckUpdateModel> _checkUpdateModel = new ObservableCollectionExtended<CheckUpdateModel>(); public IObservableCollection<CheckUpdateModel> CheckUpdateModels { get; } = new ObservableCollectionExtended<CheckUpdateModel>();
public IObservableCollection<CheckUpdateModel> CheckUpdateModels => _checkUpdateModel;
public ReactiveCommand<Unit, Unit> CheckUpdateCmd { get; } public ReactiveCommand<Unit, Unit> CheckUpdateCmd { get; }
[Reactive] public bool EnableCheckPreReleaseUpdate { get; set; } [Reactive] public bool EnableCheckPreReleaseUpdate { get; set; }
@@ -24,9 +26,11 @@ public class CheckUpdateViewModel : MyReactiveObject
_config = AppManager.Instance.Config; _config = AppManager.Instance.Config;
_updateView = updateView; _updateView = updateView;
CheckUpdateCmd = ReactiveCommand.CreateFromTask(async () => CheckUpdateCmd = ReactiveCommand.CreateFromTask(CheckUpdate);
CheckUpdateCmd.ThrownExceptions.Subscribe(ex =>
{ {
await CheckUpdate(); Logging.SaveLog(_tag, ex);
_ = UpdateView(_v2rayN, ex.Message);
}); });
EnableCheckPreReleaseUpdate = _config.CheckUpdateItem.CheckPreReleaseUpdate; EnableCheckPreReleaseUpdate = _config.CheckUpdateItem.CheckPreReleaseUpdate;
@@ -41,20 +45,20 @@ public class CheckUpdateViewModel : MyReactiveObject
private void RefreshCheckUpdateItems() private void RefreshCheckUpdateItems()
{ {
_checkUpdateModel.Clear(); CheckUpdateModels.Clear();
if (RuntimeInformation.ProcessArchitecture != Architecture.X86) if (RuntimeInformation.ProcessArchitecture != Architecture.X86)
{ {
_checkUpdateModel.Add(GetCheckUpdateModel(_v2rayN)); CheckUpdateModels.Add(GetCheckUpdateModel(_v2rayN));
//Not Windows and under Win10 //Not Windows and under Win10
if (!(Utils.IsWindows() && Environment.OSVersion.Version.Major < 10)) if (!(Utils.IsWindows() && Environment.OSVersion.Version.Major < 10))
{ {
_checkUpdateModel.Add(GetCheckUpdateModel(ECoreType.Xray.ToString())); CheckUpdateModels.Add(GetCheckUpdateModel(ECoreType.Xray.ToString()));
_checkUpdateModel.Add(GetCheckUpdateModel(ECoreType.mihomo.ToString())); CheckUpdateModels.Add(GetCheckUpdateModel(ECoreType.mihomo.ToString()));
_checkUpdateModel.Add(GetCheckUpdateModel(ECoreType.sing_box.ToString())); CheckUpdateModels.Add(GetCheckUpdateModel(ECoreType.sing_box.ToString()));
} }
} }
_checkUpdateModel.Add(GetCheckUpdateModel(_geo)); CheckUpdateModels.Add(GetCheckUpdateModel(_geo));
} }
private CheckUpdateModel GetCheckUpdateModel(string coreType) private CheckUpdateModel GetCheckUpdateModel(string coreType)
@@ -69,7 +73,7 @@ public class CheckUpdateViewModel : MyReactiveObject
private async Task SaveSelectedCoreTypes() private async Task SaveSelectedCoreTypes()
{ {
_config.CheckUpdateItem.SelectedCoreTypes = _checkUpdateModel.Where(t => t.IsSelected == true).Select(t => t.CoreType ?? "").ToList(); _config.CheckUpdateItem.SelectedCoreTypes = CheckUpdateModels.Where(t => t.IsSelected == true).Select(t => t.CoreType ?? "").ToList();
await ConfigHandler.SaveConfig(_config); await ConfigHandler.SaveConfig(_config);
} }
@@ -81,17 +85,19 @@ public class CheckUpdateViewModel : MyReactiveObject
private async Task CheckUpdateTask() private async Task CheckUpdateTask()
{ {
_lstUpdated.Clear(); _lstUpdated.Clear();
_lstUpdated = _checkUpdateModel.Where(x => x.IsSelected == true) _lstUpdated = CheckUpdateModels.Where(x => x.IsSelected == true)
.Select(x => new CheckUpdateModel() { CoreType = x.CoreType }).ToList(); .Select(x => new CheckUpdateModel() { CoreType = x.CoreType }).ToList();
await SaveSelectedCoreTypes(); await SaveSelectedCoreTypes();
for (var k = _checkUpdateModel.Count - 1; k >= 0; k--) for (var k = CheckUpdateModels.Count - 1; k >= 0; k--)
{ {
var item = _checkUpdateModel[k]; var item = CheckUpdateModels[k];
if (item.IsSelected != true) if (item.IsSelected != true)
{
continue; continue;
}
UpdateView(item.CoreType, "..."); await UpdateView(item.CoreType, "...");
if (item.CoreType == _geo) if (item.CoreType == _geo)
{ {
await CheckUpdateGeo(); await CheckUpdateGeo();
@@ -129,9 +135,9 @@ public class CheckUpdateViewModel : MyReactiveObject
private async Task CheckUpdateGeo() private async Task CheckUpdateGeo()
{ {
void _updateUI(bool success, string msg) async Task _updateUI(bool success, string msg)
{ {
UpdateView(_geo, msg); await UpdateView(_geo, msg);
if (success) if (success)
{ {
UpdatedPlusPlus(_geo, ""); UpdatedPlusPlus(_geo, "");
@@ -146,12 +152,12 @@ public class CheckUpdateViewModel : MyReactiveObject
private async Task CheckUpdateN(bool preRelease) private async Task CheckUpdateN(bool preRelease)
{ {
void _updateUI(bool success, string msg) async Task _updateUI(bool success, string msg)
{ {
UpdateView(_v2rayN, msg); await UpdateView(_v2rayN, msg);
if (success) if (success)
{ {
UpdateView(_v2rayN, ResUI.OperationSuccess); await UpdateView(_v2rayN, ResUI.OperationSuccess);
UpdatedPlusPlus(_v2rayN, msg); UpdatedPlusPlus(_v2rayN, msg);
} }
} }
@@ -164,12 +170,12 @@ public class CheckUpdateViewModel : MyReactiveObject
private async Task CheckUpdateCore(CheckUpdateModel model, bool preRelease) private async Task CheckUpdateCore(CheckUpdateModel model, bool preRelease)
{ {
void _updateUI(bool success, string msg) async Task _updateUI(bool success, string msg)
{ {
UpdateView(model.CoreType, msg); await UpdateView(model.CoreType, msg);
if (success) if (success)
{ {
UpdateView(model.CoreType, ResUI.MsgUpdateV2rayCoreSuccessfullyMore); await UpdateView(model.CoreType, ResUI.MsgUpdateV2rayCoreSuccessfullyMore);
UpdatedPlusPlus(model.CoreType, msg); UpdatedPlusPlus(model.CoreType, msg);
} }
@@ -186,21 +192,30 @@ public class CheckUpdateViewModel : MyReactiveObject
{ {
if (_lstUpdated.Count > 0 && _lstUpdated.Count(x => x.IsFinished == true) == _lstUpdated.Count) if (_lstUpdated.Count > 0 && _lstUpdated.Count(x => x.IsFinished == true) == _lstUpdated.Count)
{ {
_updateView?.Invoke(EViewAction.DispatcherCheckUpdateFinished, false); await UpdateFinishedSub(false);
await Task.Delay(2000); await Task.Delay(2000);
await UpgradeCore(); await UpgradeCore();
if (_lstUpdated.Any(x => x.CoreType == _v2rayN && x.IsFinished == true)) if (_lstUpdated.Any(x => x.CoreType == _v2rayN && x.IsFinished == true))
{ {
await Task.Delay(1000); await Task.Delay(1000);
UpgradeN(); await UpgradeN();
} }
await Task.Delay(1000); await Task.Delay(1000);
_updateView?.Invoke(EViewAction.DispatcherCheckUpdateFinished, true); await UpdateFinishedSub(true);
} }
} }
public void UpdateFinishedResult(bool blReload) private async Task UpdateFinishedSub(bool blReload)
{
RxApp.MainThreadScheduler.Schedule(blReload, (scheduler, blReload) =>
{
_ = UpdateFinishedResult(blReload);
return Disposable.Empty;
});
}
public async Task UpdateFinishedResult(bool blReload)
{ {
if (blReload) if (blReload)
{ {
@@ -212,7 +227,7 @@ public class CheckUpdateViewModel : MyReactiveObject
} }
} }
private void UpgradeN() private async Task UpgradeN()
{ {
try try
{ {
@@ -221,16 +236,23 @@ public class CheckUpdateViewModel : MyReactiveObject
{ {
return; return;
} }
if (!Utils.UpgradeAppExists(out _)) if (!Utils.UpgradeAppExists(out var upgradeFileName))
{ {
UpdateView(_v2rayN, ResUI.UpgradeAppNotExistTip); await UpdateView(_v2rayN, ResUI.UpgradeAppNotExistTip);
NoticeManager.Instance.SendMessageAndEnqueue(ResUI.UpgradeAppNotExistTip);
Logging.SaveLog("UpgradeApp does not exist");
return; return;
} }
Locator.Current.GetService<MainWindowViewModel>()?.UpgradeApp(fileName);
var id = ProcUtils.ProcessStart(upgradeFileName, fileName, Utils.StartupPath());
if (id > 0)
{
await AppManager.Instance.AppExitAsync(true);
}
} }
catch (Exception ex) catch (Exception ex)
{ {
UpdateView(_v2rayN, ex.Message); await UpdateView(_v2rayN, ex.Message);
} }
} }
@@ -281,7 +303,7 @@ public class CheckUpdateViewModel : MyReactiveObject
} }
} }
UpdateView(item.CoreType, ResUI.MsgUpdateV2rayCoreSuccessfully); await UpdateView(item.CoreType, ResUI.MsgUpdateV2rayCoreSuccessfully);
if (File.Exists(fileName)) if (File.Exists(fileName))
{ {
@@ -290,23 +312,31 @@ public class CheckUpdateViewModel : MyReactiveObject
} }
} }
private void UpdateView(string coreType, string msg) private async Task UpdateView(string coreType, string msg)
{ {
var item = new CheckUpdateModel() var item = new CheckUpdateModel()
{ {
CoreType = coreType, CoreType = coreType,
Remarks = msg, Remarks = msg,
}; };
_updateView?.Invoke(EViewAction.DispatcherCheckUpdate, item);
RxApp.MainThreadScheduler.Schedule(item, (scheduler, model) =>
{
_ = UpdateViewResult(model);
return Disposable.Empty;
});
} }
public void UpdateViewResult(CheckUpdateModel model) public async Task UpdateViewResult(CheckUpdateModel model)
{ {
var found = _checkUpdateModel.FirstOrDefault(t => t.CoreType == model.CoreType); var found = CheckUpdateModels.FirstOrDefault(t => t.CoreType == model.CoreType);
if (found == null) if (found == null)
{
return; return;
}
var itemCopy = JsonUtils.DeepCopy(found); var itemCopy = JsonUtils.DeepCopy(found);
itemCopy.Remarks = model.Remarks; itemCopy.Remarks = model.Remarks;
_checkUpdateModel.Replace(found, itemCopy); CheckUpdateModels.Replace(found, itemCopy);
} }
} }

View File

@@ -1,4 +1,5 @@
using System.Reactive; using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq; using System.Reactive.Linq;
using DynamicData; using DynamicData;
using DynamicData.Binding; using DynamicData.Binding;
@@ -9,8 +10,7 @@ namespace ServiceLib.ViewModels;
public class ClashConnectionsViewModel : MyReactiveObject public class ClashConnectionsViewModel : MyReactiveObject
{ {
private IObservableCollection<ClashConnectionModel> _connectionItems = new ObservableCollectionExtended<ClashConnectionModel>(); public IObservableCollection<ClashConnectionModel> ConnectionItems { get; } = new ObservableCollectionExtended<ClashConnectionModel>();
public IObservableCollection<ClashConnectionModel> ConnectionItems => _connectionItems;
[Reactive] [Reactive]
public ClashConnectionModel SelectedSource { get; set; } public ClashConnectionModel SelectedSource { get; set; }
@@ -64,12 +64,16 @@ public class ClashConnectionsViewModel : MyReactiveObject
return; return;
} }
_ = _updateView?.Invoke(EViewAction.DispatcherRefreshConnections, ret?.connections); RxApp.MainThreadScheduler.Schedule(ret?.connections, (scheduler, model) =>
{
_ = RefreshConnections(model);
return Disposable.Empty;
});
} }
public void RefreshConnections(List<ConnectionItem>? connections) public async Task RefreshConnections(List<ConnectionItem>? connections)
{ {
_connectionItems.Clear(); ConnectionItems.Clear();
var dtNow = DateTime.Now; var dtNow = DateTime.Now;
var lstModel = new List<ClashConnectionModel>(); var lstModel = new List<ClashConnectionModel>();
@@ -99,7 +103,7 @@ public class ClashConnectionsViewModel : MyReactiveObject
return; return;
} }
_connectionItems.AddRange(lstModel); ConnectionItems.AddRange(lstModel);
} }
public async Task ClashConnectionClose(bool all) public async Task ClashConnectionClose(bool all)
@@ -116,7 +120,7 @@ public class ClashConnectionsViewModel : MyReactiveObject
} }
else else
{ {
_connectionItems.Clear(); ConnectionItems.Clear();
} }
await ClashApiManager.Instance.ClashConnectionClose(id); await ClashApiManager.Instance.ClashConnectionClose(id);
await GetClashConnections(); await GetClashConnections();

View File

@@ -1,4 +1,6 @@
using System.Reactive; using System.Reactive;
using System.Reactive.Concurrency;
using System.Reactive.Disposables;
using System.Reactive.Linq; using System.Reactive.Linq;
using DynamicData; using DynamicData;
using DynamicData.Binding; using DynamicData.Binding;
@@ -15,11 +17,8 @@ public class ClashProxiesViewModel : MyReactiveObject
private Dictionary<string, ProvidersItem>? _providers; private Dictionary<string, ProvidersItem>? _providers;
private readonly int _delayTimeout = 99999999; private readonly int _delayTimeout = 99999999;
private IObservableCollection<ClashProxyModel> _proxyGroups = new ObservableCollectionExtended<ClashProxyModel>(); public IObservableCollection<ClashProxyModel> ProxyGroups { get; } = new ObservableCollectionExtended<ClashProxyModel>();
private IObservableCollection<ClashProxyModel> _proxyDetails = new ObservableCollectionExtended<ClashProxyModel>(); public IObservableCollection<ClashProxyModel> ProxyDetails { get; } = new ObservableCollectionExtended<ClashProxyModel>();
public IObservableCollection<ClashProxyModel> ProxyGroups => _proxyGroups;
public IObservableCollection<ClashProxyModel> ProxyDetails => _proxyDetails;
[Reactive] [Reactive]
public ClashProxyModel SelectedGroup { get; set; } public ClashProxyModel SelectedGroup { get; set; }
@@ -168,11 +167,11 @@ public class ClashProxiesViewModel : MyReactiveObject
if (refreshUI) if (refreshUI)
{ {
_updateView?.Invoke(EViewAction.DispatcherRefreshProxyGroups, null); RxApp.MainThreadScheduler.Schedule(() => _ = RefreshProxyGroups());
} }
} }
public void RefreshProxyGroups() public async Task RefreshProxyGroups()
{ {
if (_proxies == null) if (_proxies == null)
{ {
@@ -180,7 +179,7 @@ public class ClashProxiesViewModel : MyReactiveObject
} }
var selectedName = SelectedGroup?.Name; var selectedName = SelectedGroup?.Name;
_proxyGroups.Clear(); ProxyGroups.Clear();
var proxyGroups = ClashApiManager.Instance.GetClashProxyGroups(); var proxyGroups = ClashApiManager.Instance.GetClashProxyGroups();
if (proxyGroups != null && proxyGroups.Count > 0) if (proxyGroups != null && proxyGroups.Count > 0)
@@ -196,7 +195,7 @@ public class ClashProxiesViewModel : MyReactiveObject
{ {
continue; continue;
} }
_proxyGroups.Add(new ClashProxyModel() ProxyGroups.Add(new ClashProxyModel()
{ {
Now = item.now, Now = item.now,
Name = item.name, Name = item.name,
@@ -212,12 +211,12 @@ public class ClashProxiesViewModel : MyReactiveObject
{ {
continue; continue;
} }
var item = _proxyGroups.FirstOrDefault(t => t.Name == kv.Key); var item = ProxyGroups.FirstOrDefault(t => t.Name == kv.Key);
if (item != null && item.Name.IsNotEmpty()) if (item != null && item.Name.IsNotEmpty())
{ {
continue; continue;
} }
_proxyGroups.Add(new ClashProxyModel() ProxyGroups.Add(new ClashProxyModel()
{ {
Now = kv.Value.now, Now = kv.Value.now,
Name = kv.Key, Name = kv.Key,
@@ -225,15 +224,15 @@ public class ClashProxiesViewModel : MyReactiveObject
}); });
} }
if (_proxyGroups != null && _proxyGroups.Count > 0) if (ProxyGroups != null && ProxyGroups.Count > 0)
{ {
if (selectedName != null && _proxyGroups.Any(t => t.Name == selectedName)) if (selectedName != null && ProxyGroups.Any(t => t.Name == selectedName))
{ {
SelectedGroup = _proxyGroups.FirstOrDefault(t => t.Name == selectedName); SelectedGroup = ProxyGroups.FirstOrDefault(t => t.Name == selectedName);
} }
else else
{ {
SelectedGroup = _proxyGroups.First(); SelectedGroup = ProxyGroups.First();
} }
} }
else else
@@ -244,7 +243,7 @@ public class ClashProxiesViewModel : MyReactiveObject
private void RefreshProxyDetails(bool c) private void RefreshProxyDetails(bool c)
{ {
_proxyDetails.Clear(); ProxyDetails.Clear();
if (!c) if (!c)
{ {
return; return;
@@ -297,7 +296,7 @@ public class ClashProxiesViewModel : MyReactiveObject
default: default:
break; break;
} }
_proxyDetails.AddRange(lstDetails); ProxyDetails.AddRange(lstDetails);
} }
private ProxiesItem? TryGetProxy(string name) private ProxiesItem? TryGetProxy(string name)
@@ -359,12 +358,12 @@ public class ClashProxiesViewModel : MyReactiveObject
await ClashApiManager.Instance.ClashSetActiveProxy(name, nameNode); await ClashApiManager.Instance.ClashSetActiveProxy(name, nameNode);
selectedProxy.now = nameNode; selectedProxy.now = nameNode;
var group = _proxyGroups.FirstOrDefault(it => it.Name == SelectedGroup.Name); var group = ProxyGroups.FirstOrDefault(it => it.Name == SelectedGroup.Name);
if (group != null) if (group != null)
{ {
group.Now = nameNode; group.Now = nameNode;
var group2 = JsonUtils.DeepCopy(group); var group2 = JsonUtils.DeepCopy(group);
_proxyGroups.Replace(group, group2); ProxyGroups.Replace(group, group2);
SelectedGroup = group2; SelectedGroup = group2;
} }
@@ -373,22 +372,27 @@ public class ClashProxiesViewModel : MyReactiveObject
private async Task ProxiesDelayTest(bool blAll = true) private async Task ProxiesDelayTest(bool blAll = true)
{ {
ClashApiManager.Instance.ClashProxiesDelayTest(blAll, _proxyDetails.ToList(), (item, result) => ClashApiManager.Instance.ClashProxiesDelayTest(blAll, ProxyDetails.ToList(), async (item, result) =>
{ {
if (item == null || result.IsNullOrEmpty()) if (item == null || result.IsNullOrEmpty())
{ {
return; return;
} }
_updateView?.Invoke(EViewAction.DispatcherProxiesDelayTest, new SpeedTestResult() { IndexId = item.Name, Delay = result }); var model = new SpeedTestResult() { IndexId = item.Name, Delay = result };
RxApp.MainThreadScheduler.Schedule(model, (scheduler, model) =>
{
_ = ProxiesDelayTestResult(model);
return Disposable.Empty;
});
}); });
await Task.CompletedTask; await Task.CompletedTask;
} }
public void ProxiesDelayTestResult(SpeedTestResult result) public async Task ProxiesDelayTestResult(SpeedTestResult result)
{ {
//UpdateHandler(false, $"{item.name}={result}"); //UpdateHandler(false, $"{item.name}={result}");
var detail = _proxyDetails.FirstOrDefault(it => it.Name == result.IndexId); var detail = ProxyDetails.FirstOrDefault(it => it.Name == result.IndexId);
if (detail == null) if (detail == null)
{ {
return; return;
@@ -410,7 +414,7 @@ public class ClashProxiesViewModel : MyReactiveObject
detail.Delay = _delayTimeout; detail.Delay = _delayTimeout;
detail.DelayName = string.Empty; detail.DelayName = string.Empty;
} }
_proxyDetails.Replace(detail, JsonUtils.DeepCopy(detail)); ProxyDetails.Replace(detail, JsonUtils.DeepCopy(detail));
} }
#endregion proxy function #endregion proxy function

View File

@@ -1,4 +1,5 @@
using System.Reactive; using System.Reactive;
using System.Reactive.Concurrency;
using ReactiveUI; using ReactiveUI;
using ReactiveUI.Fody.Helpers; using ReactiveUI.Fody.Helpers;
using Splat; using Splat;
@@ -245,7 +246,7 @@ public class MainWindowViewModel : MyReactiveObject
#region Actions #region Actions
private void UpdateHandler(bool notify, string msg) private async Task UpdateHandler(bool notify, string msg)
{ {
NoticeManager.Instance.SendMessage(msg); NoticeManager.Instance.SendMessage(msg);
if (notify) if (notify)
@@ -254,86 +255,31 @@ public class MainWindowViewModel : MyReactiveObject
} }
} }
private void UpdateTaskHandler(bool success, string msg) private async Task UpdateTaskHandler(bool success, string msg)
{ {
NoticeManager.Instance.SendMessageEx(msg); NoticeManager.Instance.SendMessageEx(msg);
if (success) if (success)
{ {
var indexIdOld = _config.IndexId; var indexIdOld = _config.IndexId;
RefreshServers(); await RefreshServers();
if (indexIdOld != _config.IndexId) if (indexIdOld != _config.IndexId)
{ {
_ = Reload(); await Reload();
} }
if (_config.UiItem.EnableAutoAdjustMainLvColWidth) if (_config.UiItem.EnableAutoAdjustMainLvColWidth)
{ {
_updateView?.Invoke(EViewAction.AdjustMainLvColWidth, null); AppEvents.AdjustMainLvColWidthRequested.OnNext(Unit.Default);
} }
} }
} }
private void UpdateStatisticsHandler(ServerSpeedItem update) private async Task UpdateStatisticsHandler(ServerSpeedItem update)
{ {
if (!_config.UiItem.ShowInTaskbar) if (!_config.UiItem.ShowInTaskbar)
{ {
return; return;
} }
_updateView?.Invoke(EViewAction.DispatcherStatistics, update); AppEvents.DispatcherStatisticsRequested.OnNext(update);
}
public void SetStatisticsResult(ServerSpeedItem update)
{
if (_config.GuiItem.DisplayRealTimeSpeed)
{
Locator.Current.GetService<StatusBarViewModel>()?.UpdateStatistics(update);
}
if (_config.GuiItem.EnableStatistics && (update.ProxyUp + update.ProxyDown) > 0 && DateTime.Now.Second % 9 == 0)
{
Locator.Current.GetService<ProfilesViewModel>()?.UpdateStatistics(update);
}
}
public async Task MyAppExitAsync(bool blWindowsShutDown)
{
try
{
Logging.SaveLog("MyAppExitAsync Begin");
await SysProxyHandler.UpdateSysProxy(_config, true);
MessageBus.Current.SendMessage("", EMsgCommand.AppExit.ToString());
await ConfigHandler.SaveConfig(_config);
await ProfileExManager.Instance.SaveTo();
await StatisticsManager.Instance.SaveTo();
await CoreManager.Instance.CoreStop();
StatisticsManager.Instance.Close();
Logging.SaveLog("MyAppExitAsync End");
}
catch { }
finally
{
if (!blWindowsShutDown)
{
_updateView?.Invoke(EViewAction.Shutdown, false);
}
}
}
public async Task UpgradeApp(string arg)
{
if (!Utils.UpgradeAppExists(out var upgradeFileName))
{
NoticeManager.Instance.SendMessageAndEnqueue(ResUI.UpgradeAppNotExistTip);
Logging.SaveLog("UpgradeApp does not exist");
return;
}
var id = ProcUtils.ProcessStart(upgradeFileName, arg, Utils.StartupPath());
if (id > 0)
{
await MyAppExitAsync(false);
}
} }
public void ShowHideWindow(bool? blShow) public void ShowHideWindow(bool? blShow)
@@ -341,18 +287,15 @@ public class MainWindowViewModel : MyReactiveObject
_updateView?.Invoke(EViewAction.ShowHideWindow, blShow); _updateView?.Invoke(EViewAction.ShowHideWindow, blShow);
} }
public void Shutdown(bool byUser)
{
_updateView?.Invoke(EViewAction.Shutdown, byUser);
}
#endregion Actions #endregion Actions
#region Servers && Groups #region Servers && Groups
private void RefreshServers() private async Task RefreshServers()
{ {
MessageBus.Current.SendMessage("", EMsgCommand.RefreshProfiles.ToString()); AppEvents.ProfilesRefreshRequested.OnNext(Unit.Default);
await Task.Delay(200);
} }
private void RefreshSubscriptions() private void RefreshSubscriptions()
@@ -384,7 +327,7 @@ public class MainWindowViewModel : MyReactiveObject
} }
if (ret == true) if (ret == true)
{ {
RefreshServers(); await RefreshServers();
if (item.IndexId == _config.IndexId) if (item.IndexId == _config.IndexId)
{ {
await Reload(); await Reload();
@@ -399,11 +342,11 @@ public class MainWindowViewModel : MyReactiveObject
await _updateView?.Invoke(EViewAction.AddServerViaClipboard, null); await _updateView?.Invoke(EViewAction.AddServerViaClipboard, null);
return; return;
} }
int ret = await ConfigHandler.AddBatchServers(_config, clipboardData, _config.SubIndexId, false); var ret = await ConfigHandler.AddBatchServers(_config, clipboardData, _config.SubIndexId, false);
if (ret > 0) if (ret > 0)
{ {
RefreshSubscriptions(); RefreshSubscriptions();
RefreshServers(); await RefreshServers();
NoticeManager.Instance.Enqueue(string.Format(ResUI.SuccessfullyImportedServerViaClipboard, ret)); NoticeManager.Instance.Enqueue(string.Format(ResUI.SuccessfullyImportedServerViaClipboard, ret));
} }
else else
@@ -449,11 +392,11 @@ public class MainWindowViewModel : MyReactiveObject
} }
else else
{ {
int ret = await ConfigHandler.AddBatchServers(_config, result, _config.SubIndexId, false); var ret = await ConfigHandler.AddBatchServers(_config, result, _config.SubIndexId, false);
if (ret > 0) if (ret > 0)
{ {
RefreshSubscriptions(); RefreshSubscriptions();
RefreshServers(); await RefreshServers();
NoticeManager.Instance.Enqueue(ResUI.SuccessfullyImportedServerViaScan); NoticeManager.Instance.Enqueue(ResUI.SuccessfullyImportedServerViaScan);
} }
else else
@@ -526,13 +469,13 @@ public class MainWindowViewModel : MyReactiveObject
public async Task RebootAsAdmin() public async Task RebootAsAdmin()
{ {
ProcUtils.RebootAsAdmin(); ProcUtils.RebootAsAdmin();
await MyAppExitAsync(false); await AppManager.Instance.AppExitAsync(true);
} }
private async Task ClearServerStatistics() private async Task ClearServerStatistics()
{ {
await StatisticsManager.Instance.ClearAllServerStatistics(); await StatisticsManager.Instance.ClearAllServerStatistics();
RefreshServers(); await RefreshServers();
} }
private async Task OpenTheFileLocation() private async Task OpenTheFileLocation()
@@ -576,7 +519,7 @@ public class MainWindowViewModel : MyReactiveObject
}); });
Locator.Current.GetService<StatusBarViewModel>()?.TestServerAvailability(); Locator.Current.GetService<StatusBarViewModel>()?.TestServerAvailability();
_updateView?.Invoke(EViewAction.DispatcherReload, null); RxApp.MainThreadScheduler.Schedule(() => _ = ReloadResult());
BlReloadEnabled = true; BlReloadEnabled = true;
if (_hasNextReloadJob) if (_hasNextReloadJob)
@@ -586,7 +529,7 @@ public class MainWindowViewModel : MyReactiveObject
} }
} }
public void ReloadResult() public async Task ReloadResult()
{ {
// BlReloadEnabled = true; // BlReloadEnabled = true;
//Locator.Current.GetService<StatusBarViewModel>()?.ChangeSystemProxyAsync(_config.systemProxyItem.sysProxyType, false); //Locator.Current.GetService<StatusBarViewModel>()?.ChangeSystemProxyAsync(_config.systemProxyItem.sysProxyType, false);
@@ -596,7 +539,9 @@ public class MainWindowViewModel : MyReactiveObject
Locator.Current.GetService<ClashProxiesViewModel>()?.ProxiesReload(); Locator.Current.GetService<ClashProxiesViewModel>()?.ProxiesReload();
} }
else else
{ TabMainSelectedIndex = 0; } {
TabMainSelectedIndex = 0;
}
} }
private async Task LoadCore() private async Task LoadCore()
@@ -631,7 +576,7 @@ public class MainWindowViewModel : MyReactiveObject
Locator.Current.GetService<StatusBarViewModel>()?.RefreshRoutingsMenu(); Locator.Current.GetService<StatusBarViewModel>()?.RefreshRoutingsMenu();
await ConfigHandler.SaveConfig(_config); await ConfigHandler.SaveConfig(_config);
await new UpdateService().UpdateGeoFileAll(_config, UpdateHandler); await new UpdateService().UpdateGeoFileAll(_config, UpdateTaskHandler);
await Reload(); await Reload();
} }

View File

@@ -1,4 +1,5 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Reactive.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using ReactiveUI; using ReactiveUI;
using ReactiveUI.Fody.Helpers; using ReactiveUI.Fody.Helpers;
@@ -7,8 +8,8 @@ namespace ServiceLib.ViewModels;
public class MsgViewModel : MyReactiveObject public class MsgViewModel : MyReactiveObject
{ {
private ConcurrentQueue<string> _queueMsg = new(); private readonly ConcurrentQueue<string> _queueMsg = new();
private int _numMaxMsg = 500; private readonly int _numMaxMsg = 500;
private bool _lastMsgFilterNotAvailable; private bool _lastMsgFilterNotAvailable;
private bool _blLockShow = false; private bool _blLockShow = false;
@@ -34,12 +35,10 @@ public class MsgViewModel : MyReactiveObject
y => y == true) y => y == true)
.Subscribe(c => { _config.MsgUIItem.AutoRefresh = AutoRefresh; }); .Subscribe(c => { _config.MsgUIItem.AutoRefresh = AutoRefresh; });
MessageBus.Current.Listen<string>(EMsgCommand.SendMsgView.ToString()).Subscribe(OnNext); AppEvents.SendMsgViewRequested
} .AsObservable()
//.ObserveOn(RxApp.MainThreadScheduler)
private async void OnNext(string x) .Subscribe(async content => await AppendQueueMsg(content));
{
await AppendQueueMsg(x);
} }
private async Task AppendQueueMsg(string msg) private async Task AppendQueueMsg(string msg)

View File

@@ -1,4 +1,5 @@
using System.Reactive; using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Text; using System.Text;
using DynamicData; using DynamicData;
@@ -22,13 +23,9 @@ public class ProfilesViewModel : MyReactiveObject
#region ObservableCollection #region ObservableCollection
private IObservableCollection<ProfileItemModel> _profileItems = new ObservableCollectionExtended<ProfileItemModel>(); public IObservableCollection<ProfileItemModel> ProfileItems { get; } = new ObservableCollectionExtended<ProfileItemModel>();
public IObservableCollection<ProfileItemModel> ProfileItems => _profileItems;
private IObservableCollection<SubItem> _subItems = new ObservableCollectionExtended<SubItem>(); public IObservableCollection<SubItem> SubItems { get; } = new ObservableCollectionExtended<SubItem>();
public IObservableCollection<SubItem> SubItems => _subItems;
private IObservableCollection<ComboItem> _servers = new ObservableCollectionExtended<ComboItem>();
[Reactive] [Reactive]
public ProfileItemModel SelectedProfile { get; set; } public ProfileItemModel SelectedProfile { get; set; }
@@ -126,7 +123,7 @@ public class ProfilesViewModel : MyReactiveObject
this.WhenAnyValue( this.WhenAnyValue(
x => x.ServerFilter, x => x.ServerFilter,
y => y != null && _serverFilter != y) y => y != null && _serverFilter != y)
.Subscribe(c => ServerFilterChanged(c)); .Subscribe(async c => await ServerFilterChanged(c));
//servers delete //servers delete
EditServerCmd = ReactiveCommand.CreateFromTask(async () => EditServerCmd = ReactiveCommand.CreateFromTask(async () =>
@@ -247,10 +244,19 @@ public class ProfilesViewModel : MyReactiveObject
#endregion WhenAnyValue && ReactiveCommand #endregion WhenAnyValue && ReactiveCommand
if (_updateView != null) #region AppEvents
{
MessageBus.Current.Listen<string>(EMsgCommand.RefreshProfiles.ToString()).Subscribe(OnNext); 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(); _ = Init();
} }
@@ -263,24 +269,19 @@ public class ProfilesViewModel : MyReactiveObject
SelectedServer = new(); SelectedServer = new();
await RefreshSubscriptions(); await RefreshSubscriptions();
RefreshServers(); await RefreshServers();
} }
#endregion Init #endregion Init
#region Actions #region Actions
private async void OnNext(string x)
{
await _updateView?.Invoke(EViewAction.DispatcherRefreshServersBiz, null);
}
private void Reload() private void Reload()
{ {
Locator.Current.GetService<MainWindowViewModel>()?.Reload(); Locator.Current.GetService<MainWindowViewModel>()?.Reload();
} }
public void SetSpeedTestResult(SpeedTestResult result) public async Task SetSpeedTestResult(SpeedTestResult result)
{ {
if (result.IndexId.IsNullOrEmpty()) if (result.IndexId.IsNullOrEmpty())
{ {
@@ -288,7 +289,7 @@ public class ProfilesViewModel : MyReactiveObject
NoticeManager.Instance.Enqueue(result.Delay); NoticeManager.Instance.Enqueue(result.Delay);
return; return;
} }
var item = _profileItems.FirstOrDefault(it => it.IndexId == result.IndexId); var item = ProfileItems.FirstOrDefault(it => it.IndexId == result.IndexId);
if (item == null) if (item == null)
{ {
return; return;
@@ -307,11 +308,18 @@ public class ProfilesViewModel : MyReactiveObject
//_profileItems.Replace(item, JsonUtils.DeepCopy(item)); //_profileItems.Replace(item, JsonUtils.DeepCopy(item));
} }
public void UpdateStatistics(ServerSpeedItem update) public async Task UpdateStatistics(ServerSpeedItem update)
{ {
if (!_config.GuiItem.EnableStatistics
|| (update.ProxyUp + update.ProxyDown) <= 0
|| DateTime.Now.Second % 3 != 0)
{
return;
}
try try
{ {
var item = _profileItems.FirstOrDefault(it => it.IndexId == update.IndexId); var item = ProfileItems.FirstOrDefault(it => it.IndexId == update.IndexId);
if (item != null) if (item != null)
{ {
item.TodayDown = Utils.HumanFy(update.TodayDown); item.TodayDown = Utils.HumanFy(update.TodayDown);
@@ -336,11 +344,6 @@ public class ProfilesViewModel : MyReactiveObject
} }
} }
public async Task AutofitColumnWidthAsync()
{
await _updateView?.Invoke(EViewAction.AdjustMainLvColWidth, null);
}
#endregion Actions #endregion Actions
#region Servers && Groups #region Servers && Groups
@@ -353,12 +356,12 @@ public class ProfilesViewModel : MyReactiveObject
} }
_config.SubIndexId = SelectedSub?.Id; _config.SubIndexId = SelectedSub?.Id;
RefreshServers(); await RefreshServers();
await _updateView?.Invoke(EViewAction.ProfilesFocus, null); await _updateView?.Invoke(EViewAction.ProfilesFocus, null);
} }
private void ServerFilterChanged(bool c) private async Task ServerFilterChanged(bool c)
{ {
if (!c) if (!c)
{ {
@@ -367,22 +370,24 @@ public class ProfilesViewModel : MyReactiveObject
_serverFilter = ServerFilter; _serverFilter = ServerFilter;
if (_serverFilter.IsNullOrEmpty()) if (_serverFilter.IsNullOrEmpty())
{ {
RefreshServers(); await RefreshServers();
} }
} }
public void RefreshServers() public async Task RefreshServers()
{ {
MessageBus.Current.SendMessage("", EMsgCommand.RefreshProfiles.ToString()); AppEvents.ProfilesRefreshRequested.OnNext(Unit.Default);
await Task.Delay(200);
} }
public async Task RefreshServersBiz() private async Task RefreshServersBiz()
{ {
var lstModel = await GetProfileItemsEx(_config.SubIndexId, _serverFilter); var lstModel = await GetProfileItemsEx(_config.SubIndexId, _serverFilter);
_lstProfile = JsonUtils.Deserialize<List<ProfileItem>>(JsonUtils.Serialize(lstModel)) ?? []; _lstProfile = JsonUtils.Deserialize<List<ProfileItem>>(JsonUtils.Serialize(lstModel)) ?? [];
_profileItems.Clear(); ProfileItems.Clear();
_profileItems.AddRange(lstModel); ProfileItems.AddRange(lstModel);
if (lstModel.Count > 0) if (lstModel.Count > 0)
{ {
var selected = lstModel.FirstOrDefault(t => t.IndexId == _config.IndexId); var selected = lstModel.FirstOrDefault(t => t.IndexId == _config.IndexId);
@@ -395,25 +400,27 @@ public class ProfilesViewModel : MyReactiveObject
SelectedProfile = lstModel.First(); SelectedProfile = lstModel.First();
} }
} }
await _updateView?.Invoke(EViewAction.DispatcherRefreshServersBiz, null);
} }
public async Task RefreshSubscriptions() public async Task RefreshSubscriptions()
{ {
_subItems.Clear(); SubItems.Clear();
_subItems.Add(new SubItem { Remarks = ResUI.AllGroupServers }); SubItems.Add(new SubItem { Remarks = ResUI.AllGroupServers });
foreach (var item in await AppManager.Instance.SubItems()) foreach (var item in await AppManager.Instance.SubItems())
{ {
_subItems.Add(item); SubItems.Add(item);
} }
if (_config.SubIndexId != null && _subItems.FirstOrDefault(t => t.Id == _config.SubIndexId) != null) if (_config.SubIndexId != null && SubItems.FirstOrDefault(t => t.Id == _config.SubIndexId) != null)
{ {
SelectedSub = _subItems.FirstOrDefault(t => t.Id == _config.SubIndexId); SelectedSub = SubItems.FirstOrDefault(t => t.Id == _config.SubIndexId);
} }
else else
{ {
SelectedSub = _subItems.First(); SelectedSub = SubItems.First();
} }
} }
@@ -514,7 +521,7 @@ public class ProfilesViewModel : MyReactiveObject
} }
if (ret == true) if (ret == true)
{ {
RefreshServers(); await RefreshServers();
if (item.IndexId == _config.IndexId) if (item.IndexId == _config.IndexId)
{ {
Reload(); Reload();
@@ -537,11 +544,11 @@ public class ProfilesViewModel : MyReactiveObject
await ConfigHandler.RemoveServers(_config, lstSelected); await ConfigHandler.RemoveServers(_config, lstSelected);
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess); NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
if (lstSelected.Count == _profileItems.Count) if (lstSelected.Count == ProfileItems.Count)
{ {
_profileItems.Clear(); ProfileItems.Clear();
} }
RefreshServers(); await RefreshServers();
if (exists) if (exists)
{ {
Reload(); Reload();
@@ -553,7 +560,7 @@ public class ProfilesViewModel : MyReactiveObject
var tuple = await ConfigHandler.DedupServerList(_config, _config.SubIndexId); var tuple = await ConfigHandler.DedupServerList(_config, _config.SubIndexId);
if (tuple.Item1 > 0 || tuple.Item2 > 0) if (tuple.Item1 > 0 || tuple.Item2 > 0)
{ {
RefreshServers(); await RefreshServers();
Reload(); Reload();
} }
NoticeManager.Instance.Enqueue(string.Format(ResUI.RemoveDuplicateServerResult, tuple.Item1, tuple.Item2)); NoticeManager.Instance.Enqueue(string.Format(ResUI.RemoveDuplicateServerResult, tuple.Item1, tuple.Item2));
@@ -568,7 +575,7 @@ public class ProfilesViewModel : MyReactiveObject
} }
if (await ConfigHandler.CopyServer(_config, lstSelected) == 0) if (await ConfigHandler.CopyServer(_config, lstSelected) == 0)
{ {
RefreshServers(); await RefreshServers();
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess); NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
} }
} }
@@ -601,7 +608,7 @@ public class ProfilesViewModel : MyReactiveObject
if (await ConfigHandler.SetDefaultServerIndex(_config, indexId) == 0) if (await ConfigHandler.SetDefaultServerIndex(_config, indexId) == 0)
{ {
RefreshServers(); await RefreshServers();
Reload(); Reload();
} }
} }
@@ -652,7 +659,7 @@ public class ProfilesViewModel : MyReactiveObject
} }
if (ret?.Data?.ToString() == _config.IndexId) if (ret?.Data?.ToString() == _config.IndexId)
{ {
RefreshServers(); await RefreshServers();
Reload(); Reload();
} }
else else
@@ -675,13 +682,13 @@ public class ProfilesViewModel : MyReactiveObject
return; return;
} }
_dicHeaderSort[colName] = !asc; _dicHeaderSort[colName] = !asc;
RefreshServers(); await RefreshServers();
} }
public async Task RemoveInvalidServerResult() public async Task RemoveInvalidServerResult()
{ {
var count = await ConfigHandler.RemoveInvalidServerResult(_config, _config.SubIndexId); var count = await ConfigHandler.RemoveInvalidServerResult(_config, _config.SubIndexId);
RefreshServers(); await RefreshServers();
NoticeManager.Instance.Enqueue(string.Format(ResUI.RemoveInvalidServerResultTip, count)); NoticeManager.Instance.Enqueue(string.Format(ResUI.RemoveInvalidServerResultTip, count));
} }
@@ -702,7 +709,7 @@ public class ProfilesViewModel : MyReactiveObject
await ConfigHandler.MoveToGroup(_config, lstSelected, SelectedMoveToGroup.Id); await ConfigHandler.MoveToGroup(_config, lstSelected, SelectedMoveToGroup.Id);
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess); NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
RefreshServers(); await RefreshServers();
SelectedMoveToGroup = null; SelectedMoveToGroup = null;
SelectedMoveToGroup = new(); SelectedMoveToGroup = new();
} }
@@ -723,18 +730,18 @@ public class ProfilesViewModel : MyReactiveObject
} }
if (await ConfigHandler.MoveServer(_config, _lstProfile, index, eMove) == 0) if (await ConfigHandler.MoveServer(_config, _lstProfile, index, eMove) == 0)
{ {
RefreshServers(); await RefreshServers();
} }
} }
public async Task MoveServerTo(int startIndex, ProfileItemModel targetItem) public async Task MoveServerTo(int startIndex, ProfileItemModel targetItem)
{ {
var targetIndex = _profileItems.IndexOf(targetItem); var targetIndex = ProfileItems.IndexOf(targetItem);
if (startIndex >= 0 && targetIndex >= 0 && startIndex != targetIndex) if (startIndex >= 0 && targetIndex >= 0 && startIndex != targetIndex)
{ {
if (await ConfigHandler.MoveServer(_config, _lstProfile, startIndex, EMove.Position, targetIndex) == 0) if (await ConfigHandler.MoveServer(_config, _lstProfile, startIndex, EMove.Position, targetIndex) == 0)
{ {
RefreshServers(); await RefreshServers();
} }
} }
} }
@@ -743,7 +750,7 @@ public class ProfilesViewModel : MyReactiveObject
{ {
if (actionType == ESpeedActionType.Mixedtest) if (actionType == ESpeedActionType.Mixedtest)
{ {
SelectedProfiles = _profileItems; SelectedProfiles = ProfileItems;
} }
var lstSelected = await GetProfileItems(false); var lstSelected = await GetProfileItems(false);
if (lstSelected == null) if (lstSelected == null)
@@ -751,7 +758,14 @@ public class ProfilesViewModel : MyReactiveObject
return; return;
} }
_speedtestService ??= new SpeedtestService(_config, (SpeedTestResult result) => _updateView?.Invoke(EViewAction.DispatcherSpeedTest, result)); _speedtestService ??= new SpeedtestService(_config, async (SpeedTestResult result) =>
{
RxApp.MainThreadScheduler.Schedule(result, (scheduler, result) =>
{
_ = SetSpeedTestResult(result);
return Disposable.Empty;
});
});
_speedtestService?.RunLoop(actionType, lstSelected); _speedtestService?.RunLoop(actionType, lstSelected);
} }

View File

@@ -13,9 +13,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
[Reactive] [Reactive]
public RoutingItem SelectedRouting { get; set; } public RoutingItem SelectedRouting { get; set; }
public IObservableCollection<RulesItemModel> RulesItems { get; } = new ObservableCollectionExtended<RulesItemModel>();
private IObservableCollection<RulesItemModel> _rulesItems = new ObservableCollectionExtended<RulesItemModel>();
public IObservableCollection<RulesItemModel> RulesItems => _rulesItems;
[Reactive] [Reactive]
public RulesItemModel SelectedSource { get; set; } public RulesItemModel SelectedSource { get; set; }
@@ -101,7 +99,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
public void RefreshRulesItems() public void RefreshRulesItems()
{ {
_rulesItems.Clear(); RulesItems.Clear();
foreach (var item in _rules) foreach (var item in _rules)
{ {
@@ -118,7 +116,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
Enabled = item.Enabled, Enabled = item.Enabled,
Remarks = item.Remarks, Remarks = item.Remarks,
}; };
_rulesItems.Add(it); RulesItems.Add(it);
} }
} }

View File

@@ -9,8 +9,7 @@ public class RoutingSettingViewModel : MyReactiveObject
{ {
#region Reactive #region Reactive
private IObservableCollection<RoutingItemModel> _routingItems = new ObservableCollectionExtended<RoutingItemModel>(); public IObservableCollection<RoutingItemModel> RoutingItems { get; } = new ObservableCollectionExtended<RoutingItemModel>();
public IObservableCollection<RoutingItemModel> RoutingItems => _routingItems;
[Reactive] [Reactive]
public RoutingItemModel SelectedSource { get; set; } public RoutingItemModel SelectedSource { get; set; }
@@ -82,7 +81,7 @@ public class RoutingSettingViewModel : MyReactiveObject
public async Task RefreshRoutingItems() public async Task RefreshRoutingItems()
{ {
_routingItems.Clear(); RoutingItems.Clear();
var routings = await AppManager.Instance.RoutingItems(); var routings = await AppManager.Instance.RoutingItems();
foreach (var item in routings) foreach (var item in routings)
@@ -98,7 +97,7 @@ public class RoutingSettingViewModel : MyReactiveObject
CustomRulesetPath4Singbox = item.CustomRulesetPath4Singbox, CustomRulesetPath4Singbox = item.CustomRulesetPath4Singbox,
Sort = item.Sort, Sort = item.Sort,
}; };
_routingItems.Add(it); RoutingItems.Add(it);
} }
} }

View File

@@ -1,4 +1,6 @@
using System.Reactive; using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Text; using System.Text;
using DynamicData.Binding; using DynamicData.Binding;
using ReactiveUI; using ReactiveUI;
@@ -11,11 +13,9 @@ public class StatusBarViewModel : MyReactiveObject
{ {
#region ObservableCollection #region ObservableCollection
private IObservableCollection<RoutingItem> _routingItems = new ObservableCollectionExtended<RoutingItem>(); public IObservableCollection<RoutingItem> RoutingItems { get; } = new ObservableCollectionExtended<RoutingItem>();
public IObservableCollection<RoutingItem> RoutingItems => _routingItems;
private IObservableCollection<ComboItem> _servers = new ObservableCollectionExtended<ComboItem>(); public IObservableCollection<ComboItem> Servers { get; } = new ObservableCollectionExtended<ComboItem>();
public IObservableCollection<ComboItem> Servers => _servers;
[Reactive] [Reactive]
public RoutingItem SelectedRouting { get; set; } public RoutingItem SelectedRouting { get; set; }
@@ -197,10 +197,20 @@ public class StatusBarViewModel : MyReactiveObject
#endregion WhenAnyValue && ReactiveCommand #endregion WhenAnyValue && ReactiveCommand
#region AppEvents
if (updateView != null) if (updateView != null)
{ {
InitUpdateView(updateView); InitUpdateView(updateView);
} }
AppEvents.DispatcherStatisticsRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async result => await UpdateStatistics(result));
#endregion AppEvents
_ = Init(); _ = Init();
} }
@@ -216,15 +226,13 @@ public class StatusBarViewModel : MyReactiveObject
_updateView = updateView; _updateView = updateView;
if (_updateView != null) if (_updateView != null)
{ {
MessageBus.Current.Listen<string>(EMsgCommand.RefreshProfiles.ToString()).Subscribe(OnNext); AppEvents.ProfilesRefreshRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async _ => await RefreshServersBiz()); //.DisposeWith(_disposables);
} }
} }
private async void OnNext(string x)
{
await _updateView?.Invoke(EViewAction.DispatcherRefreshServersBiz, null);
}
private async Task CopyProxyCmdToClipboard() private async Task CopyProxyCmdToClipboard()
{ {
var cmd = Utils.IsWindows() ? "set" : "export"; var cmd = Utils.IsWindows() ? "set" : "export";
@@ -263,7 +271,7 @@ public class StatusBarViewModel : MyReactiveObject
await service.UpdateSubscriptionProcess("", blProxy); await service.UpdateSubscriptionProcess("", blProxy);
} }
public async Task RefreshServersBiz() private async Task RefreshServersBiz()
{ {
await RefreshServersMenu(); await RefreshServersMenu();
@@ -285,7 +293,7 @@ public class StatusBarViewModel : MyReactiveObject
{ {
var lstModel = await AppManager.Instance.ProfileItems(_config.SubIndexId, ""); var lstModel = await AppManager.Instance.ProfileItems(_config.SubIndexId, "");
_servers.Clear(); Servers.Clear();
if (lstModel.Count > _config.GuiItem.TrayMenuServersLimit) if (lstModel.Count > _config.GuiItem.TrayMenuServersLimit)
{ {
BlServers = false; BlServers = false;
@@ -299,7 +307,7 @@ public class StatusBarViewModel : MyReactiveObject
string name = it.GetSummary(); string name = it.GetSummary();
var item = new ComboItem() { ID = it.IndexId, Text = name }; var item = new ComboItem() { ID = it.IndexId, Text = name };
_servers.Add(item); Servers.Add(item);
if (_config.IndexId == it.IndexId) if (_config.IndexId == it.IndexId)
{ {
SelectedServer = item; SelectedServer = item;
@@ -332,15 +340,24 @@ public class StatusBarViewModel : MyReactiveObject
return; return;
} }
_updateView?.Invoke(EViewAction.DispatcherServerAvailability, ResUI.Speedtesting); await TestServerAvailabilitySub(ResUI.Speedtesting);
var msg = await Task.Run(ConnectionHandler.RunAvailabilityCheck); var msg = await Task.Run(ConnectionHandler.RunAvailabilityCheck);
NoticeManager.Instance.SendMessageEx(msg); NoticeManager.Instance.SendMessageEx(msg);
_updateView?.Invoke(EViewAction.DispatcherServerAvailability, msg); await TestServerAvailabilitySub(msg);
} }
public void TestServerAvailabilityResult(string msg) private async Task TestServerAvailabilitySub(string msg)
{
RxApp.MainThreadScheduler.Schedule(msg, (scheduler, msg) =>
{
_ = TestServerAvailabilityResult(msg);
return Disposable.Empty;
});
}
public async Task TestServerAvailabilityResult(string msg)
{ {
RunningInfoDisplay = msg; RunningInfoDisplay = msg;
} }
@@ -378,13 +395,13 @@ public class StatusBarViewModel : MyReactiveObject
public async Task RefreshRoutingsMenu() public async Task RefreshRoutingsMenu()
{ {
_routingItems.Clear(); RoutingItems.Clear();
BlRouting = true; BlRouting = true;
var routings = await AppManager.Instance.RoutingItems(); var routings = await AppManager.Instance.RoutingItems();
foreach (var item in routings) foreach (var item in routings)
{ {
_routingItems.Add(item); RoutingItems.Add(item);
if (item.IsActive) if (item.IsActive)
{ {
SelectedRouting = item; SelectedRouting = item;
@@ -509,8 +526,13 @@ public class StatusBarViewModel : MyReactiveObject
await Task.CompletedTask; await Task.CompletedTask;
} }
public void UpdateStatistics(ServerSpeedItem update) public async Task UpdateStatistics(ServerSpeedItem update)
{ {
if (!_config.GuiItem.DisplayRealTimeSpeed)
{
return;
}
try try
{ {
if (_config.IsRunningCore(ECoreType.sing_box)) if (_config.IsRunningCore(ECoreType.sing_box))

View File

@@ -8,8 +8,7 @@ namespace ServiceLib.ViewModels;
public class SubSettingViewModel : MyReactiveObject public class SubSettingViewModel : MyReactiveObject
{ {
private IObservableCollection<SubItem> _subItems = new ObservableCollectionExtended<SubItem>(); public IObservableCollection<SubItem> SubItems { get; } = new ObservableCollectionExtended<SubItem>();
public IObservableCollection<SubItem> SubItems => _subItems;
[Reactive] [Reactive]
public SubItem SelectedSource { get; set; } public SubItem SelectedSource { get; set; }
@@ -60,8 +59,8 @@ public class SubSettingViewModel : MyReactiveObject
public async Task RefreshSubItems() public async Task RefreshSubItems()
{ {
_subItems.Clear(); SubItems.Clear();
_subItems.AddRange(await AppManager.Instance.SubItems()); SubItems.AddRange(await AppManager.Instance.SubItems());
} }
public async Task EditSubAsync(bool blNew) public async Task EditSubAsync(bool blNew)

View File

@@ -74,11 +74,7 @@ public partial class App : Application
private async void MenuExit_Click(object? sender, EventArgs e) private async void MenuExit_Click(object? sender, EventArgs e)
{ {
var service = Locator.Current.GetService<MainWindowViewModel>(); await AppManager.Instance.AppExitAsync(false);
if (service != null) AppManager.Instance.Shutdown(true);
{
await service.MyAppExitAsync(true);
}
service?.Shutdown(true);
} }
} }

View File

@@ -22,4 +22,8 @@
<Style Selector="ScrollViewer"> <Style Selector="ScrollViewer">
<Setter Property="AllowAutoHide" Value="False" /> <Setter Property="AllowAutoHide" Value="False" />
</Style> </Style>
<Style Selector="TabControl">
<Setter Property="Theme" Value="{StaticResource LineTabControl}" />
</Style>
</Styles> </Styles>

View File

@@ -1,6 +1,5 @@
using System.Reactive.Disposables; using System.Reactive.Disposables;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using Avalonia.Threading;
using ReactiveUI; using ReactiveUI;
namespace v2rayN.Desktop.Views; namespace v2rayN.Desktop.Views;
@@ -24,25 +23,6 @@ public partial class CheckUpdateView : ReactiveUserControl<CheckUpdateViewModel>
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj) private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{ {
switch (action)
{
case EViewAction.DispatcherCheckUpdate:
if (obj is null)
return false;
Dispatcher.UIThread.Post(() =>
ViewModel?.UpdateViewResult((CheckUpdateModel)obj),
DispatcherPriority.Default);
break;
case EViewAction.DispatcherCheckUpdateFinished:
if (obj is null)
return false;
Dispatcher.UIThread.Post(() =>
ViewModel?.UpdateFinishedResult((bool)obj),
DispatcherPriority.Default);
break;
}
return await Task.FromResult(true); return await Task.FromResult(true);
} }
} }

View File

@@ -2,7 +2,6 @@ using System.Reactive.Disposables;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using Avalonia.Threading;
using ReactiveUI; using ReactiveUI;
namespace v2rayN.Desktop.Views; namespace v2rayN.Desktop.Views;
@@ -31,17 +30,6 @@ public partial class ClashConnectionsView : ReactiveUserControl<ClashConnections
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj) private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{ {
switch (action)
{
case EViewAction.DispatcherRefreshConnections:
if (obj is null)
return false;
Dispatcher.UIThread.Post(() =>
ViewModel?.RefreshConnections((List<ConnectionItem>?)obj),
DispatcherPriority.Default);
break;
}
return await Task.FromResult(true); return await Task.FromResult(true);
} }

View File

@@ -1,7 +1,6 @@
using System.Reactive.Disposables; using System.Reactive.Disposables;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using Avalonia.Threading;
using DynamicData; using DynamicData;
using ReactiveUI; using ReactiveUI;
using Splat; using Splat;
@@ -40,23 +39,6 @@ public partial class ClashProxiesView : ReactiveUserControl<ClashProxiesViewMode
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj) private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{ {
switch (action)
{
case EViewAction.DispatcherRefreshProxyGroups:
Dispatcher.UIThread.Post(() =>
ViewModel?.RefreshProxyGroups(),
DispatcherPriority.Default);
break;
case EViewAction.DispatcherProxiesDelayTest:
if (obj is null)
return false;
Dispatcher.UIThread.Post(() =>
ViewModel?.ProxiesDelayTestResult((SpeedTestResult)obj),
DispatcherPriority.Default);
break;
}
return await Task.FromResult(true); return await Task.FromResult(true);
} }

View File

@@ -59,7 +59,7 @@
x:Name="cmbDirectDNS" x:Name="cmbDirectDNS"
Grid.Row="1" Grid.Row="1"
Grid.Column="1" Grid.Column="1"
Width="200" Width="300"
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
Text="{Binding DirectDNS, Mode=TwoWay}" /> Text="{Binding DirectDNS, Mode=TwoWay}" />
@@ -73,7 +73,7 @@
x:Name="cmbRemoteDNS" x:Name="cmbRemoteDNS"
Grid.Row="2" Grid.Row="2"
Grid.Column="1" Grid.Column="1"
Width="200" Width="300"
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
Text="{Binding RemoteDNS, Mode=TwoWay}" /> Text="{Binding RemoteDNS, Mode=TwoWay}" />
@@ -87,7 +87,7 @@
x:Name="cmbSBResolverDNS" x:Name="cmbSBResolverDNS"
Grid.Row="3" Grid.Row="3"
Grid.Column="1" Grid.Column="1"
Width="200" Width="300"
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
Text="{Binding SingboxOutboundsResolveDNS, Mode=TwoWay}" /> Text="{Binding SingboxOutboundsResolveDNS, Mode=TwoWay}" />
<TextBlock <TextBlock
@@ -108,7 +108,7 @@
x:Name="cmbSBFinalResolverDNS" x:Name="cmbSBFinalResolverDNS"
Grid.Row="4" Grid.Row="4"
Grid.Column="1" Grid.Column="1"
Width="200" Width="300"
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
Text="{Binding SingboxFinalResolveDNS, Mode=TwoWay}" /> Text="{Binding SingboxFinalResolveDNS, Mode=TwoWay}" />
<TextBlock <TextBlock

View File

@@ -50,6 +50,7 @@ public partial class FullConfigTemplateWindow : WindowBase<FullConfigTemplateVie
{ {
ProcUtils.ProcessStart("https://github.com/2dust/v2rayN/wiki/Description-of-some-ui#%E5%AE%8C%E6%95%B4%E9%85%8D%E7%BD%AE%E6%A8%A1%E6%9D%BF%E8%AE%BE%E7%BD%AE"); ProcUtils.ProcessStart("https://github.com/2dust/v2rayN/wiki/Description-of-some-ui#%E5%AE%8C%E6%95%B4%E9%85%8D%E7%BD%AE%E6%A8%A1%E6%9D%BF%E8%AE%BE%E7%BD%AE");
} }
private void Window_Loaded(object? sender, RoutedEventArgs e) private void Window_Loaded(object? sender, RoutedEventArgs e)
{ {
btnCancel.Focus(); btnCancel.Focus();

View File

@@ -135,6 +135,7 @@ public partial class GlobalHotkeySettingWindow : WindowBase<GlobalHotkeySettingV
return res.ToString(); return res.ToString();
} }
private void Window_Loaded(object? sender, RoutedEventArgs e) private void Window_Loaded(object? sender, RoutedEventArgs e)
{ {
btnCancel.Focus(); btnCancel.Focus();

View File

@@ -1,4 +1,5 @@
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Reactive.Linq;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
@@ -39,7 +40,6 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
menuBackupAndRestore.Click += MenuBackupAndRestore_Click; menuBackupAndRestore.Click += MenuBackupAndRestore_Click;
menuClose.Click += MenuClose_Click; menuClose.Click += MenuClose_Click;
MessageBus.Current.Listen<string>(EMsgCommand.SendSnackMsg.ToString()).Subscribe(DelegateSnackMsg);
ViewModel = new MainWindowViewModel(UpdateViewHandler); ViewModel = new MainWindowViewModel(UpdateViewHandler);
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(MainWindowViewModel)); Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(MainWindowViewModel));
@@ -136,6 +136,24 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
this.Bind(ViewModel, vm => vm.TabMainSelectedIndex, v => v.tabMain2.SelectedIndex).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.TabMainSelectedIndex, v => v.tabMain2.SelectedIndex).DisposeWith(disposables);
break; break;
} }
AppEvents.SendSnackMsgRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async content => await DelegateSnackMsg(content))
.DisposeWith(disposables);
AppEvents.AppExitRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(_ => StorageUI())
.DisposeWith(disposables);
AppEvents.ShutdownRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(content => Shutdown(content))
.DisposeWith(disposables);
}); });
if (Utils.IsWindows()) if (Utils.IsWindows())
@@ -156,7 +174,6 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
menuAddServerViaScan.IsVisible = false; menuAddServerViaScan.IsVisible = false;
AddHelpMenuItem(); AddHelpMenuItem();
MessageBus.Current.Listen<string>(EMsgCommand.AppExit.ToString()).Subscribe(StorageUI);
} }
#region Event #region Event
@@ -168,11 +185,9 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
DispatcherPriority.Default); DispatcherPriority.Default);
} }
private void DelegateSnackMsg(string content) private async Task DelegateSnackMsg(string content)
{ {
Dispatcher.UIThread.Post(() => _manager?.Show(new Notification(null, content, NotificationType.Information));
_manager?.Show(new Notification(null, content, NotificationType.Information)),
DispatcherPriority.Normal);
} }
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj) private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
@@ -213,33 +228,6 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
DispatcherPriority.Default); DispatcherPriority.Default);
break; break;
case EViewAction.DispatcherStatistics:
if (obj is null)
return false;
Dispatcher.UIThread.Post(() =>
ViewModel?.SetStatisticsResult((ServerSpeedItem)obj),
DispatcherPriority.Default);
break;
case EViewAction.DispatcherReload:
Dispatcher.UIThread.Post(() =>
ViewModel?.ReloadResult(),
DispatcherPriority.Default);
break;
case EViewAction.Shutdown:
if (obj != null && _blCloseByUser == false)
{
_blCloseByUser = (bool)obj;
}
StorageUI();
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
HotkeyManager.Instance.Dispose();
desktop.Shutdown();
}
break;
case EViewAction.ScanScreenTask: case EViewAction.ScanScreenTask:
await ScanScreenTaskAsync(); await ScanScreenTaskAsync();
break; break;
@@ -255,12 +243,6 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
await ViewModel.AddServerViaClipboardAsync(clipboardData); await ViewModel.AddServerViaClipboardAsync(clipboardData);
} }
break; break;
case EViewAction.AdjustMainLvColWidth:
Dispatcher.UIThread.Post(() =>
Locator.Current.GetService<ProfilesViewModel>()?.AutofitColumnWidthAsync(),
DispatcherPriority.Default);
break;
} }
return await Task.FromResult(true); return await Task.FromResult(true);
@@ -300,10 +282,7 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
break; break;
case WindowCloseReason.ApplicationShutdown or WindowCloseReason.OSShutdown: case WindowCloseReason.ApplicationShutdown or WindowCloseReason.OSShutdown:
if (ViewModel != null) await AppManager.Instance.AppExitAsync(false);
{
await ViewModel.MyAppExitAsync(true);
}
break; break;
} }
@@ -398,9 +377,21 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
_blCloseByUser = true; _blCloseByUser = true;
StorageUI(); StorageUI();
if (ViewModel != null)
await AppManager.Instance.AppExitAsync(true);
}
private void Shutdown(bool obj)
{
if (obj is bool b && _blCloseByUser == false)
{ {
await ViewModel.MyAppExitAsync(false); _blCloseByUser = b;
}
StorageUI();
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
HotkeyManager.Instance.Dispose();
desktop.Shutdown();
} }
} }
@@ -462,7 +453,7 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
} }
} }
private void StorageUI(string? n = null) private void StorageUI()
{ {
ConfigHandler.SaveWindowSizeItem(_config, GetType().Name, Width, Height); ConfigHandler.SaveWindowSizeItem(_config, GetType().Name, Width, Height);

View File

@@ -211,6 +211,7 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
ViewModel.destOverride = clbdestOverride.SelectedItems.Cast<string>().ToList(); ViewModel.destOverride = clbdestOverride.SelectedItems.Cast<string>().ToList();
} }
} }
private void Window_Loaded(object? sender, RoutedEventArgs e) private void Window_Loaded(object? sender, RoutedEventArgs e)
{ {
btnCancel.Focus(); btnCancel.Focus();

View File

@@ -1,4 +1,5 @@
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Reactive.Linq;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
@@ -96,11 +97,22 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
this.BindCommand(ViewModel, vm => vm.Export2ClientConfigClipboardCmd, v => v.menuExport2ClientConfigClipboard).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.Export2ClientConfigClipboardCmd, v => v.menuExport2ClientConfigClipboard).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.Export2ShareUrlCmd, v => v.menuExport2ShareUrl).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.Export2ShareUrlCmd, v => v.menuExport2ShareUrl).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.Export2ShareUrlBase64Cmd, v => v.menuExport2ShareUrlBase64).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.Export2ShareUrlBase64Cmd, v => v.menuExport2ShareUrlBase64).DisposeWith(disposables);
AppEvents.AppExitRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(_ => StorageUI())
.DisposeWith(disposables);
AppEvents.AdjustMainLvColWidthRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(_ => AutofitColumnWidth())
.DisposeWith(disposables);
}); });
RestoreUI(); RestoreUI();
ViewModel?.RefreshServers(); ViewModel?.RefreshServers();
MessageBus.Current.Listen<string>(EMsgCommand.AppExit.ToString()).Subscribe(StorageUI);
} }
private async void LstProfiles_Sorting(object? sender, DataGridColumnEventArgs e) private async void LstProfiles_Sorting(object? sender, DataGridColumnEventArgs e)
@@ -127,13 +139,6 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
await AvaUtils.SetClipboardData(this, (string)obj); await AvaUtils.SetClipboardData(this, (string)obj);
break; break;
case EViewAction.AdjustMainLvColWidth:
Dispatcher.UIThread.Post(() =>
AutofitColumnWidth(),
DispatcherPriority.Default);
break;
case EViewAction.ProfilesFocus: case EViewAction.ProfilesFocus:
lstProfiles.Focus(); lstProfiles.Focus();
break; break;
@@ -177,21 +182,8 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
return false; return false;
return await new SubEditWindow((SubItem)obj).ShowDialog<bool>(_window); return await new SubEditWindow((SubItem)obj).ShowDialog<bool>(_window);
case EViewAction.DispatcherSpeedTest:
if (obj is null)
return false;
Dispatcher.UIThread.Post(() =>
ViewModel?.SetSpeedTestResult((SpeedTestResult)obj),
DispatcherPriority.Default);
break;
case EViewAction.DispatcherRefreshServersBiz: case EViewAction.DispatcherRefreshServersBiz:
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(RefreshServersBiz, DispatcherPriority.Default);
{
_ = RefreshServersBiz();
},
DispatcherPriority.Default);
break; break;
} }
@@ -209,13 +201,8 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
await DialogHost.Show(dialog); await DialogHost.Show(dialog);
} }
public async Task RefreshServersBiz() public void RefreshServersBiz()
{ {
if (ViewModel != null)
{
await ViewModel.RefreshServersBiz();
}
if (lstProfiles.SelectedIndex >= 0) if (lstProfiles.SelectedIndex >= 0)
{ {
lstProfiles.ScrollIntoView(lstProfiles.SelectedItem, null); lstProfiles.ScrollIntoView(lstProfiles.SelectedItem, null);
@@ -421,7 +408,7 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
} }
} }
private void StorageUI(string? n = null) private void StorageUI()
{ {
List<ColumnItem> lvColumnItem = new(); List<ColumnItem> lvColumnItem = new();
foreach (var item2 in lstProfiles.Columns) foreach (var item2 in lstProfiles.Columns)

View File

@@ -130,10 +130,6 @@
Width="*" Width="*"
Binding="{Binding Url}" Binding="{Binding Url}"
Header="{x:Static resx:ResUI.LvUrl}" /> Header="{x:Static resx:ResUI.LvUrl}" />
<DataGridTextColumn
Width="300"
Binding="{Binding CustomIcon}"
Header="{x:Static resx:ResUI.LvCustomIcon}" />
</DataGrid.Columns> </DataGrid.Columns>
</DataGrid> </DataGrid>
</TabItem> </TabItem>

View File

@@ -135,6 +135,7 @@ public partial class RoutingSettingWindow : WindowBase<RoutingSettingViewModel>
} }
} }
} }
private void Window_Loaded(object? sender, RoutedEventArgs e) private void Window_Loaded(object? sender, RoutedEventArgs e)
{ {
btnCancel.Focus(); btnCancel.Focus();

View File

@@ -56,20 +56,6 @@ public partial class StatusBarView : ReactiveUserControl<StatusBarViewModel>
{ {
switch (action) switch (action)
{ {
case EViewAction.DispatcherServerAvailability:
if (obj is null)
return false;
Dispatcher.UIThread.Post(() =>
ViewModel?.TestServerAvailabilityResult((string)obj),
DispatcherPriority.Default);
break;
case EViewAction.DispatcherRefreshServersBiz:
Dispatcher.UIThread.Post(() =>
ViewModel?.RefreshServersBiz(),
DispatcherPriority.Default);
break;
case EViewAction.DispatcherRefreshIcon: case EViewAction.DispatcherRefreshIcon:
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
{ {

View File

@@ -117,6 +117,7 @@ public partial class SubSettingWindow : WindowBase<SubSettingViewModel>
menuClose_Click(null, null); menuClose_Click(null, null);
} }
} }
private void Window_Loaded(object? sender, RoutedEventArgs e) private void Window_Loaded(object? sender, RoutedEventArgs e)
{ {
lstSubscription.Focus(); lstSubscription.Focus();

View File

@@ -1,6 +1,4 @@
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Windows;
using System.Windows.Threading;
using ReactiveUI; using ReactiveUI;
namespace v2rayN.Views; namespace v2rayN.Views;
@@ -24,27 +22,6 @@ public partial class CheckUpdateView
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj) private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{ {
switch (action)
{
case EViewAction.DispatcherCheckUpdate:
if (obj is null)
return false;
Application.Current?.Dispatcher.Invoke((() =>
{
ViewModel?.UpdateViewResult((CheckUpdateModel)obj);
}), DispatcherPriority.Normal);
break;
case EViewAction.DispatcherCheckUpdateFinished:
if (obj is null)
return false;
Application.Current?.Dispatcher.Invoke((() =>
{
ViewModel?.UpdateFinishedResult((bool)obj);
}), DispatcherPriority.Normal);
break;
}
return await Task.FromResult(true); return await Task.FromResult(true);
} }
} }

View File

@@ -1,7 +1,6 @@
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Threading;
using ReactiveUI; using ReactiveUI;
namespace v2rayN.Views; namespace v2rayN.Views;
@@ -33,18 +32,6 @@ public partial class ClashConnectionsView
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj) private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{ {
switch (action)
{
case EViewAction.DispatcherRefreshConnections:
if (obj is null)
return false;
Application.Current?.Dispatcher.Invoke((() =>
{
ViewModel?.RefreshConnections((List<ConnectionItem>?)obj);
}), DispatcherPriority.Normal);
break;
}
return await Task.FromResult(true); return await Task.FromResult(true);
} }

View File

@@ -1,7 +1,5 @@
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Windows;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Threading;
using ReactiveUI; using ReactiveUI;
using Splat; using Splat;
@@ -41,26 +39,6 @@ public partial class ClashProxiesView
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj) private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{ {
switch (action)
{
case EViewAction.DispatcherRefreshProxyGroups:
Application.Current?.Dispatcher.Invoke((() =>
{
ViewModel?.RefreshProxyGroups();
}), DispatcherPriority.Normal);
break;
case EViewAction.DispatcherProxiesDelayTest:
if (obj is null)
return false;
Application.Current?.Dispatcher.Invoke((() =>
{
ViewModel?.ProxiesDelayTestResult((SpeedTestResult)obj);
}), DispatcherPriority.Normal);
break;
}
return await Task.FromResult(true); return await Task.FromResult(true);
} }

View File

@@ -80,7 +80,7 @@
x:Name="cmbDirectDNS" x:Name="cmbDirectDNS"
Grid.Row="1" Grid.Row="1"
Grid.Column="1" Grid.Column="1"
Width="200" Width="300"
Margin="{StaticResource Margin8}" Margin="{StaticResource Margin8}"
IsEditable="True" IsEditable="True"
Style="{StaticResource DefComboBox}" /> Style="{StaticResource DefComboBox}" />
@@ -96,7 +96,7 @@
x:Name="cmbRemoteDNS" x:Name="cmbRemoteDNS"
Grid.Row="2" Grid.Row="2"
Grid.Column="1" Grid.Column="1"
Width="200" Width="300"
Margin="{StaticResource Margin8}" Margin="{StaticResource Margin8}"
IsEditable="True" IsEditable="True"
Style="{StaticResource DefComboBox}" /> Style="{StaticResource DefComboBox}" />
@@ -112,7 +112,7 @@
x:Name="cmbSBResolverDNS" x:Name="cmbSBResolverDNS"
Grid.Row="3" Grid.Row="3"
Grid.Column="1" Grid.Column="1"
Width="200" Width="300"
Margin="{StaticResource Margin8}" Margin="{StaticResource Margin8}"
IsEditable="True" IsEditable="True"
Style="{StaticResource DefComboBox}" /> Style="{StaticResource DefComboBox}" />
@@ -136,7 +136,7 @@
x:Name="cmbSBFinalResolverDNS" x:Name="cmbSBFinalResolverDNS"
Grid.Row="4" Grid.Row="4"
Grid.Column="1" Grid.Column="1"
Width="200" Width="300"
Margin="{StaticResource Margin8}" Margin="{StaticResource Margin8}"
IsEditable="True" IsEditable="True"
Style="{StaticResource DefComboBox}" /> Style="{StaticResource DefComboBox}" />

View File

@@ -1,5 +1,6 @@
using System.ComponentModel; using System.ComponentModel;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
@@ -36,7 +37,6 @@ public partial class MainWindow
menuCheckUpdate.Click += MenuCheckUpdate_Click; menuCheckUpdate.Click += MenuCheckUpdate_Click;
menuBackupAndRestore.Click += MenuBackupAndRestore_Click; menuBackupAndRestore.Click += MenuBackupAndRestore_Click;
MessageBus.Current.Listen<string>(EMsgCommand.SendSnackMsg.ToString()).Subscribe(DelegateSnackMsg);
ViewModel = new MainWindowViewModel(UpdateViewHandler); ViewModel = new MainWindowViewModel(UpdateViewHandler);
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(MainWindowViewModel)); Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(MainWindowViewModel));
@@ -133,6 +133,24 @@ public partial class MainWindow
this.Bind(ViewModel, vm => vm.TabMainSelectedIndex, v => v.tabMain2.SelectedIndex).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.TabMainSelectedIndex, v => v.tabMain2.SelectedIndex).DisposeWith(disposables);
break; break;
} }
AppEvents.SendSnackMsgRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async content => await DelegateSnackMsg(content))
.DisposeWith(disposables);
AppEvents.AppExitRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(_ => StorageUI())
.DisposeWith(disposables);
AppEvents.ShutdownRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(content => Shutdown(content))
.DisposeWith(disposables);
}); });
this.Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}"; this.Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
@@ -144,7 +162,6 @@ public partial class MainWindow
AddHelpMenuItem(); AddHelpMenuItem();
WindowsManager.Instance.RegisterGlobalHotkey(_config, OnHotkeyHandler, null); WindowsManager.Instance.RegisterGlobalHotkey(_config, OnHotkeyHandler, null);
MessageBus.Current.Listen<string>(EMsgCommand.AppExit.ToString()).Subscribe(StorageUI);
} }
#region Event #region Event
@@ -157,12 +174,9 @@ public partial class MainWindow
})); }));
} }
private void DelegateSnackMsg(string content) private async Task DelegateSnackMsg(string content)
{ {
Application.Current?.Dispatcher.Invoke((() => MainSnackbar.MessageQueue?.Enqueue(content);
{
MainSnackbar.MessageQueue?.Enqueue(content);
}), DispatcherPriority.Normal);
} }
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj) private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
@@ -204,29 +218,6 @@ public partial class MainWindow
}), DispatcherPriority.Normal); }), DispatcherPriority.Normal);
break; break;
case EViewAction.DispatcherStatistics:
if (obj is null)
return false;
Application.Current?.Dispatcher.Invoke((() =>
{
ViewModel?.SetStatisticsResult((ServerSpeedItem)obj);
}), DispatcherPriority.Normal);
break;
case EViewAction.DispatcherReload:
Application.Current?.Dispatcher.Invoke((() =>
{
ViewModel?.ReloadResult();
}), DispatcherPriority.Normal);
break;
case EViewAction.Shutdown:
Application.Current?.Dispatcher.Invoke((() =>
{
Application.Current.Shutdown();
}), DispatcherPriority.Normal);
break;
case EViewAction.ScanScreenTask: case EViewAction.ScanScreenTask:
await ScanScreenTaskAsync(); await ScanScreenTaskAsync();
break; break;
@@ -242,13 +233,6 @@ public partial class MainWindow
ViewModel?.AddServerViaClipboardAsync(clipboardData); ViewModel?.AddServerViaClipboardAsync(clipboardData);
} }
break; break;
case EViewAction.AdjustMainLvColWidth:
Application.Current?.Dispatcher.Invoke((() =>
{
Locator.Current.GetService<ProfilesViewModel>()?.AutofitColumnWidthAsync();
}), DispatcherPriority.Normal);
break;
} }
return await Task.FromResult(true); return await Task.FromResult(true);
@@ -281,7 +265,12 @@ public partial class MainWindow
{ {
Logging.SaveLog("Current_SessionEnding"); Logging.SaveLog("Current_SessionEnding");
StorageUI(); StorageUI();
await ViewModel?.MyAppExitAsync(true); await AppManager.Instance.AppExitAsync(false);
}
private void Shutdown(bool obj)
{
Application.Current.Shutdown();
} }
private void MainWindow_PreviewKeyDown(object sender, KeyEventArgs e) private void MainWindow_PreviewKeyDown(object sender, KeyEventArgs e)
@@ -423,7 +412,7 @@ public partial class MainWindow
} }
} }
private void StorageUI(string? n = null) private void StorageUI()
{ {
ConfigHandler.SaveWindowSizeItem(_config, GetType().Name, Width, Height); ConfigHandler.SaveWindowSizeItem(_config, GetType().Name, Width, Height);

View File

@@ -1,4 +1,5 @@
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Controls.Primitives; using System.Windows.Controls.Primitives;
@@ -90,11 +91,22 @@ public partial class ProfilesView
this.BindCommand(ViewModel, vm => vm.Export2ClientConfigClipboardCmd, v => v.menuExport2ClientConfigClipboard).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.Export2ClientConfigClipboardCmd, v => v.menuExport2ClientConfigClipboard).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.Export2ShareUrlCmd, v => v.menuExport2ShareUrl).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.Export2ShareUrlCmd, v => v.menuExport2ShareUrl).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.Export2ShareUrlBase64Cmd, v => v.menuExport2ShareUrlBase64).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.Export2ShareUrlBase64Cmd, v => v.menuExport2ShareUrlBase64).DisposeWith(disposables);
AppEvents.AppExitRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(_ => StorageUI())
.DisposeWith(disposables);
AppEvents.AdjustMainLvColWidthRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(_ => AutofitColumnWidth())
.DisposeWith(disposables);
}); });
RestoreUI(); RestoreUI();
ViewModel?.RefreshServers(); ViewModel?.RefreshServers();
MessageBus.Current.Listen<string>(EMsgCommand.AppExit.ToString()).Subscribe(StorageUI);
} }
#region Event #region Event
@@ -107,14 +119,7 @@ public partial class ProfilesView
if (obj is null) if (obj is null)
return false; return false;
WindowsUtils.SetClipboardData((string)obj); WindowsUtils.SetClipboardData((string)obj);
break; break;
case EViewAction.AdjustMainLvColWidth:
Application.Current?.Dispatcher.Invoke((() =>
{
AutofitColumnWidth();
}), DispatcherPriority.Normal);
break;
case EViewAction.ProfilesFocus: case EViewAction.ProfilesFocus:
lstProfiles.Focus(); lstProfiles.Focus();
@@ -158,20 +163,8 @@ public partial class ProfilesView
return false; return false;
return (new SubEditWindow((SubItem)obj)).ShowDialog() ?? false; return (new SubEditWindow((SubItem)obj)).ShowDialog() ?? false;
case EViewAction.DispatcherSpeedTest:
if (obj is null)
return false;
Application.Current?.Dispatcher.Invoke((() =>
{
ViewModel?.SetSpeedTestResult((SpeedTestResult)obj);
}), DispatcherPriority.Normal);
break;
case EViewAction.DispatcherRefreshServersBiz: case EViewAction.DispatcherRefreshServersBiz:
Application.Current?.Dispatcher.Invoke((() => Application.Current?.Dispatcher.Invoke(RefreshServersBiz, DispatcherPriority.Normal);
{
_ = RefreshServersBiz();
}), DispatcherPriority.Normal);
break; break;
} }
@@ -190,13 +183,8 @@ public partial class ProfilesView
await DialogHost.Show(dialog, "RootDialog"); await DialogHost.Show(dialog, "RootDialog");
} }
public async Task RefreshServersBiz() public void RefreshServersBiz()
{ {
if (ViewModel != null)
{
await ViewModel.RefreshServersBiz();
}
if (lstProfiles.SelectedIndex > 0) if (lstProfiles.SelectedIndex > 0)
{ {
lstProfiles.ScrollIntoView(lstProfiles.SelectedItem, null); lstProfiles.ScrollIntoView(lstProfiles.SelectedItem, null);
@@ -377,7 +365,7 @@ public partial class ProfilesView
} }
} }
private void StorageUI(string? n = null) private void StorageUI()
{ {
List<ColumnItem> lvColumnItem = new(); List<ColumnItem> lvColumnItem = new();
foreach (var t in lstProfiles.Columns) foreach (var t in lstProfiles.Columns)

View File

@@ -77,22 +77,6 @@ public partial class StatusBarView
{ {
switch (action) switch (action)
{ {
case EViewAction.DispatcherServerAvailability:
if (obj is null)
return false;
Application.Current?.Dispatcher.Invoke((() =>
{
ViewModel?.TestServerAvailabilityResult((string)obj);
}), DispatcherPriority.Normal);
break;
case EViewAction.DispatcherRefreshServersBiz:
Application.Current?.Dispatcher.Invoke((() =>
{
ViewModel?.RefreshServersBiz();
}), DispatcherPriority.Normal);
break;
case EViewAction.DispatcherRefreshIcon: case EViewAction.DispatcherRefreshIcon:
Application.Current?.Dispatcher.Invoke((async () => Application.Current?.Dispatcher.Invoke((async () =>
{ {
@@ -113,9 +97,7 @@ public partial class StatusBarView
private async void menuExit_Click(object sender, RoutedEventArgs e) private async void menuExit_Click(object sender, RoutedEventArgs e)
{ {
tbNotify.Dispose(); tbNotify.Dispose();
var service = Locator.Current.GetService<MainWindowViewModel>(); await AppManager.Instance.AppExitAsync(true);
if (service != null)
await service.MyAppExitAsync(false);
} }
private void txtRunningInfoDisplay_MouseDoubleClick(object sender, MouseButtonEventArgs e) private void txtRunningInfoDisplay_MouseDoubleClick(object sender, MouseButtonEventArgs e)