Compare commits

..

7 Commits

Author SHA1 Message Date
2dust d433aa3b04 Fix
https://github.com/2dust/v2rayN/issues/10236
2026-09-26 11:43:22 +08:00
dependabot[bot] 91a7ed65d5 Bump ReactiveUI from 24.2.0 to 24.3.0 (#10231)
---
updated-dependencies:
- dependency-name: ReactiveUI
  dependency-version: 24.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-26 11:12:07 +08:00
Miheichev Aleksandr Sergeevich 984e685ab3 chore(deps): update Downloader to 5.9.8 (#10222)
Downloader 5.9.6 -> 5.9.8

5.9.7 adds RemoteFileInfo.ContentType. 5.9.8 fixes stop/dispose races:
a cancelled multi-chunk download could report a NullReferenceException
as its error, and a Dispose racing the completion signal could swallow
DownloadFileCompleted. Nothing DownloaderHelper calls changed its API.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 11:11:20 +08:00
DHR60 29f99ceacc Fix speed test (#10219) 2026-09-26 11:10:30 +08:00
DHR60 93d8174dbe Fix async (#10218)
* Fix async

* Fix async for statistics

* Try fix pac async

* Fix
2026-09-26 11:08:36 +08:00
DHR60 fa1e201c76 Fix sing-box dns (#10234) 2026-09-26 10:39:29 +08:00
dependabot[bot] e1cb99cd6e Bump Avalonia.Desktop from 12.1.2 to 12.1.3 (#10216)
---
updated-dependencies:
- dependency-name: Avalonia.Desktop
  dependency-version: 12.1.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-23 11:03:05 +08:00
14 changed files with 198 additions and 187 deletions
+3 -3
View File
@@ -7,17 +7,17 @@
<ItemGroup> <ItemGroup>
<PackageVersion Include="Avalonia.AvaloniaEdit" Version="12.0.0" /> <PackageVersion Include="Avalonia.AvaloniaEdit" Version="12.0.0" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="12.1.2" /> <PackageVersion Include="Avalonia.Controls.DataGrid" Version="12.1.2" />
<PackageVersion Include="Avalonia.Desktop" Version="12.1.2" /> <PackageVersion Include="Avalonia.Desktop" Version="12.1.3" />
<PackageVersion Include="AvaloniaUI.DiagnosticsSupport" Version="2.2.3" /> <PackageVersion Include="AvaloniaUI.DiagnosticsSupport" Version="2.2.3" />
<PackageVersion Include="DialogHost.Avalonia" Version="0.12.3" /> <PackageVersion Include="DialogHost.Avalonia" Version="0.12.3" />
<PackageVersion Include="IPNetwork2" Version="4.3.0" /> <PackageVersion Include="IPNetwork2" Version="4.3.0" />
<PackageVersion Include="ReactiveUI.Avalonia" Version="12.1.2" /> <PackageVersion Include="ReactiveUI.Avalonia" Version="12.1.2" />
<PackageVersion Include="CliWrap" Version="3.10.5" /> <PackageVersion Include="CliWrap" Version="3.10.5" />
<PackageVersion Include="Downloader" Version="5.9.6" /> <PackageVersion Include="Downloader" Version="5.9.8" />
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.4.1" /> <PackageVersion Include="H.NotifyIcon.Wpf" Version="2.4.1" />
<PackageVersion Include="MaterialDesignThemes" Version="5.3.2" /> <PackageVersion Include="MaterialDesignThemes" Version="5.3.2" />
<PackageVersion Include="QRCoder" Version="1.8.0" /> <PackageVersion Include="QRCoder" Version="1.8.0" />
<PackageVersion Include="ReactiveUI" Version="24.2.0" /> <PackageVersion Include="ReactiveUI" Version="24.3.0" />
<PackageVersion Include="ReactiveUI.SourceGenerators" Version="3.2.0" /> <PackageVersion Include="ReactiveUI.SourceGenerators" Version="3.2.0" />
<PackageVersion Include="ReactiveUI.WPF" Version="24.2.0" /> <PackageVersion Include="ReactiveUI.WPF" Version="24.2.0" />
<PackageVersion Include="Semi.Avalonia" Version="12.1.0.1" /> <PackageVersion Include="Semi.Avalonia" Version="12.1.0.1" />
@@ -3,6 +3,7 @@ namespace ServiceLib.Handler.SysProxy;
public static class SysProxyHandler public static class SysProxyHandler
{ {
private static readonly string _tag = "SysProxyHandler"; private static readonly string _tag = "SysProxyHandler";
private static readonly Lazy<PacManager> _pacManager = new(() => new PacManager());
public static async Task<bool> UpdateSysProxy(Config config, bool forceDisable) public static async Task<bool> UpdateSysProxy(Config config, bool forceDisable)
{ {
@@ -56,7 +57,7 @@ public static class SysProxyHandler
if (type != ESysProxyType.Pac && Utils.IsWindows()) if (type != ESysProxyType.Pac && Utils.IsWindows())
{ {
PacManager.Instance.Stop(); _pacManager.Value.Stop();
} }
} }
catch (Exception ex) catch (Exception ex)
@@ -110,7 +111,7 @@ public static class SysProxyHandler
private static async Task SetWindowsProxyPac(int port) private static async Task SetWindowsProxyPac(int port)
{ {
var portPac = AppManager.Instance.GetLocalPort(EInboundProtocol.pac); var portPac = AppManager.Instance.GetLocalPort(EInboundProtocol.pac);
await PacManager.Instance.StartAsync(port, portPac); await _pacManager.Value.StartAsync(port, portPac);
var strProxy = $"{Global.HttpProtocol}{Global.Loopback}:{portPac}/pac?t={DateTime.Now.Ticks}"; var strProxy = $"{Global.HttpProtocol}{Global.Loopback}:{portPac}/pac?t={DateTime.Now.Ticks}";
ProxySettingWindows.SetProxy(strProxy, "", 4); ProxySettingWindows.SetProxy(strProxy, "", 4);
} }
+7 -3
View File
@@ -22,7 +22,7 @@ public class HttpClientHelper
this.httpClient = httpClient; this.httpClient = httpClient;
} }
public async Task<string?> TryGetAsync(string url) public async Task<string?> TryGetAsync(string url, CancellationToken cancellationToken = default)
{ {
if (url.IsNullOrEmpty()) if (url.IsNullOrEmpty())
{ {
@@ -31,8 +31,12 @@ public class HttpClientHelper
try try
{ {
var response = await httpClient.GetAsync(url); var response = await httpClient.GetAsync(url, cancellationToken);
return await response.Content.ReadAsStringAsync(); return await response.Content.ReadAsStringAsync(cancellationToken);
}
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
{
throw;
} }
catch catch
{ {
+16 -3
View File
@@ -75,15 +75,28 @@ public sealed class SQLiteHelper
public async Task DisposeDbConnectionAsync() public async Task DisposeDbConnectionAsync()
{ {
await Task.Factory.StartNew(() => await Task.Run(() =>
{
try
{ {
_db?.Close(); _db?.Close();
_db?.Dispose(); _db?.Dispose();
}
finally
{
_db = null; _db = null;
}
_dbAsync?.GetConnection()?.Close(); try
_dbAsync?.GetConnection()?.Dispose(); {
var conn = _dbAsync?.GetConnection();
conn?.Close();
conn?.Dispose();
}
finally
{
_dbAsync = null; _dbAsync = null;
}
}); });
} }
} }
+43 -58
View File
@@ -2,36 +2,37 @@ namespace ServiceLib.Manager;
public class PacManager public class PacManager
{ {
private static readonly Lazy<PacManager> _instance = new(() => new PacManager()); private const string Tag = "PacManager";
public static PacManager Instance => _instance.Value; private CancellationTokenSource? _cts;
private int _httpPort;
private int _pacPort; private int _pacPort;
private TcpListener? _tcpListener; private TcpListener? _tcpListener;
private byte[] _writeContent; private byte[] _writeContent = [];
private bool _isRunning;
private bool _needRestart = true;
public async Task StartAsync(int httpPort, int pacPort) public async Task StartAsync(int httpPort, int pacPort)
{ {
_needRestart = httpPort != _httpPort || pacPort != _pacPort || !_isRunning; var content = await InitText(httpPort);
_writeContent = content;
_httpPort = httpPort; if (_tcpListener is not null && _pacPort == pacPort)
_pacPort = pacPort;
await InitText();
if (_needRestart)
{ {
return;
}
Stop(); Stop();
RunListener(); var cts = new CancellationTokenSource();
} var listener = TcpListener.Create(pacPort);
listener.Start();
_cts = cts;
_pacPort = pacPort;
_tcpListener = listener;
_ = ListenLoopAsync(listener, cts.Token);
} }
private async Task InitText() private async Task<byte[]> InitText(int httpPort)
{ {
var customSystemProxyPacPath = AppManager.Instance.Config.SystemProxyItem?.CustomSystemProxyPacPath; var customSystemProxyPacPath = AppManager.Instance.Config.SystemProxyItem.CustomSystemProxyPacPath;
var fileName = (customSystemProxyPacPath.IsNotEmpty() && File.Exists(customSystemProxyPacPath)) var fileName = customSystemProxyPacPath.IsNotEmpty() && File.Exists(customSystemProxyPacPath)
? customSystemProxyPacPath ? customSystemProxyPacPath
: Path.Combine(Utils.GetConfigPath(), "pac.txt"); : Path.Combine(Utils.GetConfigPath(), "pac.txt");
@@ -45,7 +46,7 @@ public class PacManager
} }
var pacText = await File.ReadAllTextAsync(fileName); var pacText = await File.ReadAllTextAsync(fileName);
pacText = pacText.Replace("__PROXY__", $"PROXY 127.0.0.1:{_httpPort};DIRECT;"); pacText = pacText.Replace("__PROXY__", $"PROXY 127.0.0.1:{httpPort};DIRECT;");
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.AppendLine("HTTP/1.0 200 OK"); sb.AppendLine("HTTP/1.0 200 OK");
@@ -54,59 +55,43 @@ public class PacManager
sb.AppendLine("Content-Length:" + Encoding.UTF8.GetByteCount(pacText)); sb.AppendLine("Content-Length:" + Encoding.UTF8.GetByteCount(pacText));
sb.AppendLine(); sb.AppendLine();
sb.Append(pacText); sb.Append(pacText);
_writeContent = Encoding.UTF8.GetBytes(sb.ToString()); return Encoding.UTF8.GetBytes(sb.ToString());
} }
private void RunListener() private async Task ListenLoopAsync(TcpListener listener, CancellationToken token)
{
_tcpListener = TcpListener.Create(_pacPort);
_isRunning = true;
_tcpListener.Start();
Task.Factory.StartNew(async () =>
{
while (_isRunning)
{ {
var buffer = new byte[1024];
try try
{ {
if (!_tcpListener.Pending()) while (!token.IsCancellationRequested)
{ {
await Task.Delay(10); using var client = await listener.AcceptTcpClientAsync(token).ConfigureAwait(false);
continue; await using var stream = client.GetStream();
_ = await stream.ReadAsync(buffer, token).ConfigureAwait(false);
await stream.WriteAsync(_writeContent, token).ConfigureAwait(false);
await stream.FlushAsync(token).ConfigureAwait(false);
} }
var client = await _tcpListener.AcceptTcpClientAsync();
await Task.Run(() => WriteContent(client));
} }
catch catch (OperationCanceledException) when (token.IsCancellationRequested)
{ {
// ignored
} }
} catch (Exception ex)
}, TaskCreationOptions.LongRunning);
}
private void WriteContent(TcpClient client)
{ {
var stream = client.GetStream(); Logging.SaveLog(Tag, ex);
stream.Write(_writeContent, 0, _writeContent.Length); }
stream.Flush(); finally
{
listener.Stop();
}
} }
public void Stop() public void Stop()
{ {
if (_tcpListener == null) _cts?.Cancel();
{ _tcpListener?.Stop();
return; _cts?.Dispose();
} _cts = null;
try _pacPort = 0;
{
_isRunning = false;
_tcpListener.Stop();
_tcpListener = null; _tcpListener = null;
} }
catch
{
// ignored
}
}
} }
+3 -9
View File
@@ -12,23 +12,17 @@ public class TaskManager
_config = config; _config = config;
_updateFunc = updateFunc; _updateFunc = updateFunc;
_ = Task.Factory.StartNew( Task.Run(ScheduledTasks);
ScheduledTasks,
CancellationToken.None,
TaskCreationOptions.LongRunning,
TaskScheduler.Default);
} }
private async Task ScheduledTasks() private async Task ScheduledTasks()
{ {
Logging.SaveLog("Setup Scheduled Tasks"); Logging.SaveLog("Setup Scheduled Tasks");
using var timer = new PeriodicTimer(TimeSpan.FromMinutes(1));
var numOfExecuted = 1; var numOfExecuted = 1;
while (true) while (await timer.WaitForNextTickAsync().ConfigureAwait(false))
{ {
//1 minute
await Task.Delay(1000 * 60);
//Execute once 1 minute //Execute once 1 minute
try try
{ {
+1 -1
View File
@@ -2149,7 +2149,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Not Support 的本地化字符串。 /// 查找类似 Not Supported 的本地化字符串。
/// </summary> /// </summary>
public static string MsgNotSupport { public static string MsgNotSupport {
get { get {
@@ -365,9 +365,16 @@ public partial class CoreConfigSingboxService
rule4ExpectedIPs = JsonUtils.DeepCopy(rule); rule4ExpectedIPs = JsonUtils.DeepCopy(rule);
rule4ExpectedIPs.geosite = regionGeosite; rule4ExpectedIPs.geosite = regionGeosite;
} }
if (rule.geosite?.Count > 0
|| rule.domain?.Count > 0
|| rule.domain_keyword?.Count > 0
|| rule.domain_regex?.Count > 0
|| rule.domain_suffix?.Count > 0)
{
AddRules(rule, item, directDnsList);
} }
}
if (rule.geosite?.Count > 0 || rule.domain?.Count > 0) else
{ {
AddRules(rule, item, directDnsList); AddRules(rule, item, directDnsList);
} }
+20 -12
View File
@@ -8,7 +8,7 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
private readonly Config? _config = config; private readonly Config? _config = config;
private readonly Func<SpeedTestResult, Task>? _updateFunc = updateFunc; private readonly Func<SpeedTestResult, Task>? _updateFunc = updateFunc;
private readonly Lock _runLock = new(); private readonly Lock _runLock = new();
private CancellationTokenSource? _runCts; private readonly List<CancellationTokenSource> _runCtsList = [];
private readonly int _speedTestPageSize = config.SpeedTestItem.SpeedTestPageSize ?? Global.SpeedTestPageSize; private readonly int _speedTestPageSize = config.SpeedTestItem.SpeedTestPageSize ?? Global.SpeedTestPageSize;
private readonly TimeSpan _delayInterval = TimeSpan.FromSeconds(config.SpeedTestItem.SpeedTestDelayInterval ?? 1); private readonly TimeSpan _delayInterval = TimeSpan.FromSeconds(config.SpeedTestItem.SpeedTestDelayInterval ?? 1);
@@ -18,11 +18,9 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
lock (_runLock) lock (_runLock)
{ {
_runCts?.Cancel();
runCts = CancellationTokenSource.CreateLinkedTokenSource(ct); runCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
_runCts = runCts; _runCtsList.Add(runCts);
} }
return RunLoopAsync(actionType, selecteds, runCts); return RunLoopAsync(actionType, selecteds, runCts);
@@ -30,17 +28,30 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
public void ExitLoop() public void ExitLoop()
{ {
CancellationTokenSource? runCts; var counter = 0;
List<CancellationTokenSource> listToCancel;
lock (_runLock) lock (_runLock)
{ {
runCts = _runCts; listToCancel = _runCtsList.ToList();
counter = listToCancel.Count;
} }
if (runCts is not null) foreach (var cts in listToCancel)
{
try
{
cts.Cancel();
}
catch (ObjectDisposedException)
{
// Ignored
}
}
if (counter > 0)
{ {
_ = UpdateFunc("", ResUI.SpeedtestingStop); _ = UpdateFunc("", ResUI.SpeedtestingStop);
runCts.Cancel();
} }
} }
@@ -67,10 +78,7 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
lock (_runLock) lock (_runLock)
{ {
if (ReferenceEquals(_runCts, runCts)) _runCtsList.Remove(runCts);
{
_runCts = null;
}
} }
runCts.Dispose(); runCts.Dispose();
@@ -5,8 +5,7 @@ namespace ServiceLib.Services.Statistics;
public class StatisticsSingboxService public class StatisticsSingboxService
{ {
private readonly Config _config; private readonly Config _config;
private bool _exitFlag; private CancellationTokenSource? _cts;
private ClientWebSocket? webSocket;
private readonly Func<ServerSpeedItem, Task>? _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";
@@ -15,40 +14,17 @@ public class StatisticsSingboxService
{ {
_config = config; _config = config;
_updateFunc = updateFunc; _updateFunc = updateFunc;
_exitFlag = false;
_ = Task.Factory.StartNew( Task.Run(Run);
Run,
CancellationToken.None,
TaskCreationOptions.LongRunning,
TaskScheduler.Default);
}
private async Task Init()
{
await Task.Delay(5000);
try
{
if (webSocket == null)
{
webSocket = new ClientWebSocket();
await webSocket.ConnectAsync(new Uri(Url), CancellationToken.None);
}
}
catch { }
} }
public void Close() public void Close()
{ {
try try
{ {
_exitFlag = true; _cts?.Cancel();
if (webSocket != null) _cts?.Dispose();
{ _cts = null;
webSocket.Abort();
webSocket = null;
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -58,53 +34,63 @@ public class StatisticsSingboxService
private async Task Run() private async Task Run()
{ {
await Init(); Close();
_cts = new CancellationTokenSource();
var token = _cts.Token;
while (!_exitFlag) while (!token.IsCancellationRequested)
{ {
await Task.Delay(1000);
try try
{ {
if (!AppManager.Instance.IsRunningCore(ECoreType.sing_box)) if (!AppManager.Instance.IsRunningCore(ECoreType.sing_box))
{ {
await Task.Delay(1000, token).ConfigureAwait(false);
continue; continue;
} }
if (webSocket != null) using var ws = new ClientWebSocket();
{ await ws.ConnectAsync(new Uri(Url), token).ConfigureAwait(false);
if (webSocket.State is WebSocketState.Aborted or WebSocketState.Closed)
{
webSocket.Abort();
webSocket = null;
await Init();
continue;
}
if (webSocket.State != WebSocketState.Open)
{
continue;
}
var buffer = new byte[1024]; var buffer = new byte[1024];
var res = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); while (ws.State == WebSocketState.Open
while (!res.CloseStatus.HasValue) && !token.IsCancellationRequested)
{ {
var result = Encoding.UTF8.GetString(buffer, 0, res.Count); var res = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), token);
if (result.IsNotEmpty()) if (res.MessageType == WebSocketMessageType.Close)
{ {
break;
}
using var ms = new MemoryStream();
ms.Write(buffer, 0, res.Count);
while (!res.EndOfMessage)
{
res = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), token).ConfigureAwait(false);
ms.Write(buffer, 0, res.Count);
}
var result = Encoding.UTF8.GetString(ms.ToArray());
if (!result.IsNotEmpty())
{
continue;
}
ParseOutput(result, out var up, out var down); ParseOutput(result, out var up, out var down);
await _updateFunc?.Invoke(new ServerSpeedItem() if (_updateFunc != null)
{
await _updateFunc.Invoke(new ServerSpeedItem
{ {
ProxyUp = (long)(up / 1000), ProxyUp = (long)(up / 1000),
ProxyDown = (long)(down / 1000) ProxyDown = (long)(down / 1000),
}); }).ConfigureAwait(false);
}
res = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
} }
} }
} }
catch (OperationCanceledException) when (token.IsCancellationRequested)
{
break;
}
catch catch
{ {
await Task.Delay(3000, token).ConfigureAwait(false);
} }
} }
} }
@@ -5,33 +5,42 @@ public class StatisticsXrayService
private const long linkBase = 1024; private const long linkBase = 1024;
private ServerSpeedItem _serverSpeedItem = new(); private ServerSpeedItem _serverSpeedItem = new();
private readonly Config _config; private readonly Config _config;
private bool _exitFlag; private CancellationTokenSource? _cts;
private readonly Func<ServerSpeedItem, Task>? _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";
private static readonly string _tag = "StatisticsXrayService";
public StatisticsXrayService(Config config, Func<ServerSpeedItem, Task> updateFunc) public StatisticsXrayService(Config config, Func<ServerSpeedItem, Task> updateFunc)
{ {
_config = config; _config = config;
_updateFunc = updateFunc; _updateFunc = updateFunc;
_exitFlag = false;
_ = Task.Factory.StartNew( Task.Run(Run);
Run,
CancellationToken.None,
TaskCreationOptions.LongRunning,
TaskScheduler.Default);
} }
public void Close() public void Close()
{ {
_exitFlag = true; try
{
_cts?.Cancel();
_cts?.Dispose();
_cts = null;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
} }
private async Task Run() private async Task Run()
{ {
while (!_exitFlag) Close();
_cts = new CancellationTokenSource();
var token = _cts.Token;
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(1));
while (await timer.WaitForNextTickAsync(token).ConfigureAwait(false))
{ {
await Task.Delay(1000);
try try
{ {
if (AppManager.Instance.RunningCoreType != ECoreType.Xray) if (AppManager.Instance.RunningCoreType != ECoreType.Xray)
@@ -39,16 +48,20 @@ public class StatisticsXrayService
continue; continue;
} }
var result = await HttpClientHelper.Instance.TryGetAsync(Url); var result = await HttpClientHelper.Instance.TryGetAsync(Url, token);
if (result != null) if (result != null)
{ {
var server = ParseOutput(result) ?? new ServerSpeedItem(); var server = ParseOutput(result) ?? new ServerSpeedItem();
await _updateFunc?.Invoke(server); await _updateFunc!.Invoke(server);
} }
} }
catch (OperationCanceledException) when (token.IsCancellationRequested)
{
break;
}
catch catch
{ {
// ignored await Task.Delay(3000, token).ConfigureAwait(false);
} }
} }
} }
@@ -42,11 +42,21 @@ public partial class CheckUpdateViewModel : MyReactiveObject
this.WhenAnyValue(x => x.EnableUpdateViaProxy) this.WhenAnyValue(x => x.EnableUpdateViaProxy)
.Subscribe(c => _ = OnUpdateViaProxyChanged()); .Subscribe(c => _ = OnUpdateViaProxyChanged());
RefreshCheckUpdateItems(); AppEvents.HasUpdateNotified
.AsObservable()
.ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(bl => RefreshCheckUpdateItems(bl));
RefreshCheckUpdateItems(true);
} }
private void RefreshCheckUpdateItems() private void RefreshCheckUpdateItems(bool hasUpdate)
{ {
if (!hasUpdate)
{
return;
}
var models = CoreInfoManager.Instance.GetCheckUpdateCoreTypes() var models = CoreInfoManager.Instance.GetCheckUpdateCoreTypes()
.Select(t => GetCheckUpdateModel(t)) .Select(t => GetCheckUpdateModel(t))
.ToList(); .ToList();
@@ -30,12 +30,7 @@ public partial class ClashConnectionsViewModel : MyReactiveObject
cancelDisposable.DisposeWith(disposables); cancelDisposable.DisposeWith(disposables);
var token = cancelDisposable.Token; var token = cancelDisposable.Token;
Task.Factory.StartNew( Task.Run(() => GetClashConnectionsTask(token));
async () => await GetClashConnectionsTask(token),
token,
TaskCreationOptions.LongRunning,
TaskScheduler.Default
);
}); });
} }
@@ -129,9 +124,9 @@ public partial class ClashConnectionsViewModel : MyReactiveObject
try try
{ {
var numOfExecuted = 1; var numOfExecuted = 1;
while (!token.IsCancellationRequested) using var timer = new PeriodicTimer(TimeSpan.FromSeconds(1));
while (await timer.WaitForNextTickAsync(token).ConfigureAwait(false))
{ {
await Task.Delay(1000, token);
numOfExecuted++; numOfExecuted++;
if (!(AutoRefresh && AppManager.Instance.ShowInTaskbar && if (!(AutoRefresh && AppManager.Instance.ShowInTaskbar &&
AppManager.Instance.IsRunningCore(ECoreType.sing_box))) AppManager.Instance.IsRunningCore(ECoreType.sing_box)))
@@ -61,12 +61,7 @@ public partial class ClashProxiesViewModel : MyReactiveObject
cancelDisposable.DisposeWith(disposables); cancelDisposable.DisposeWith(disposables);
var token = cancelDisposable.Token; var token = cancelDisposable.Token;
Task.Factory.StartNew( Task.Run(() => GetClashProxiesTask(token));
async () => await GetClashProxiesTask(token),
token,
TaskCreationOptions.LongRunning,
TaskScheduler.Default
);
}); });
} }
@@ -113,9 +108,9 @@ public partial class ClashProxiesViewModel : MyReactiveObject
try try
{ {
var numOfExecuted = 1; var numOfExecuted = 1;
while (!token.IsCancellationRequested) using var timer = new PeriodicTimer(TimeSpan.FromSeconds(1));
while (await timer.WaitForNextTickAsync(token).ConfigureAwait(false))
{ {
await Task.Delay(1000, token);
numOfExecuted++; numOfExecuted++;
if (!(AutoRefresh && AppManager.Instance.ShowInTaskbar && if (!(AutoRefresh && AppManager.Instance.ShowInTaskbar &&
AppManager.Instance.IsRunningCore(ECoreType.sing_box))) AppManager.Instance.IsRunningCore(ECoreType.sing_box)))