Compare commits

...

8 commits

Author SHA1 Message Date
DHR60
8932ed71ab
Merge 5414bae069 into 214a09bc48 2026-03-13 11:00:02 +08:00
2dust
214a09bc48 up 7.19.4
Some checks failed
release Linux / build (Release) (push) Has been cancelled
release macOS / build (Release) (push) Has been cancelled
release Windows desktop (Avalonia UI) / build (Release) (push) Has been cancelled
release Windows / build (Release) (push) Has been cancelled
release Linux / rpm (push) Has been cancelled
2026-03-13 10:44:20 +08:00
2dust
e6af9ab342 Fix
https://github.com/2dust/v2rayN/issues/8916
2026-03-13 10:36:58 +08:00
DHR60
0f4031f445
Update dep (#8926)
Some checks are pending
release Linux / build (Release) (push) Waiting to run
release Linux / rpm (push) Blocked by required conditions
release macOS / build (Release) (push) Waiting to run
release Windows desktop (Avalonia UI) / build (Release) (push) Waiting to run
release Windows / build (Release) (push) Waiting to run
2026-03-12 20:45:28 +08:00
2dust
5cf3d6eff6 Modify routing rule process name description
Some checks are pending
release Linux / build (Release) (push) Waiting to run
release Linux / rpm (push) Blocked by required conditions
release macOS / build (Release) (push) Waiting to run
release Windows desktop (Avalonia UI) / build (Release) (push) Waiting to run
release Windows / build (Release) (push) Waiting to run
2026-03-12 17:19:30 +08:00
DHR60
5414bae069 Sync 2026-03-09 22:20:05 +08:00
DHR60
4a10a862eb Fix 2026-03-09 22:17:47 +08:00
DHR60
ecf8b8f939 Support new hysteria2 stream settings 2026-03-08 16:36:55 +08:00
31 changed files with 120 additions and 107 deletions

View file

@ -1,7 +1,7 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<Version>7.19.3</Version> <Version>7.19.4</Version>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>

View file

@ -9,18 +9,18 @@
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.12" /> <PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.12" />
<PackageVersion Include="Avalonia.Desktop" Version="11.3.12" /> <PackageVersion Include="Avalonia.Desktop" Version="11.3.12" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.12" /> <PackageVersion Include="Avalonia.Diagnostics" Version="11.3.12" />
<PackageVersion Include="ReactiveUI.Avalonia" Version="11.3.8" /> <PackageVersion Include="ReactiveUI.Avalonia" Version="11.4.12" />
<PackageVersion Include="CliWrap" Version="3.10.0" /> <PackageVersion Include="CliWrap" Version="3.10.0" />
<PackageVersion Include="Downloader" Version="4.1.1" /> <PackageVersion Include="Downloader" Version="5.1.0" />
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.4.1" /> <PackageVersion Include="H.NotifyIcon.Wpf" Version="2.4.1" />
<PackageVersion Include="MaterialDesignThemes" Version="5.3.0" /> <PackageVersion Include="MaterialDesignThemes" Version="5.3.0" />
<PackageVersion Include="MessageBox.Avalonia" Version="3.3.1.1" /> <PackageVersion Include="MessageBox.Avalonia" Version="3.3.1.1" />
<PackageVersion Include="QRCoder" Version="1.7.0" /> <PackageVersion Include="QRCoder" Version="1.7.0" />
<PackageVersion Include="ReactiveUI" Version="22.3.1" /> <PackageVersion Include="ReactiveUI" Version="23.1.8" />
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" /> <PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
<PackageVersion Include="ReactiveUI.WPF" Version="22.3.1" /> <PackageVersion Include="ReactiveUI.WPF" Version="23.1.8" />
<PackageVersion Include="Semi.Avalonia" Version="11.3.7.3" /> <PackageVersion Include="Semi.Avalonia" Version="11.3.7.3" />
<PackageVersion Include="Semi.Avalonia.AvaloniaEdit" Version="11.2.0.1" /> <PackageVersion Include="Semi.Avalonia.AvaloniaEdit" Version="11.2.0.2" />
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.3.7.3" /> <PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.3.7.3" />
<PackageVersion Include="NLog" Version="6.1.1" /> <PackageVersion Include="NLog" Version="6.1.1" />
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" /> <PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />

View file

@ -335,7 +335,7 @@ public class StreamSettings4Ray
public HysteriaSettings4Ray? hysteriaSettings { get; set; } public HysteriaSettings4Ray? hysteriaSettings { get; set; }
public Finalmask4Ray? finalmask { get; set; } public object? finalmask { get; set; }
public Sockopt4Ray? sockopt { get; set; } public Sockopt4Ray? sockopt { get; set; }
} }
@ -460,27 +460,24 @@ public class HysteriaSettings4Ray
{ {
public int version { get; set; } public int version { get; set; }
public string? auth { get; set; } public string? auth { get; set; }
public string? up { get; set; }
public string? down { get; set; }
public HysteriaUdpHop4Ray? udphop { get; set; }
} }
public class HysteriaUdpHop4Ray public class UdpHop4Ray
{ {
public string? port { get; set; } public string? ports { get; set; }
public string? interval { get; set; } public string? interval { get; set; }
} }
public class Finalmask4Ray public class Finalmask4Ray
{ {
public List<Mask4Ray>? tcp { get; set; }
public List<Mask4Ray>? udp { get; set; } public List<Mask4Ray>? udp { get; set; }
public QuicParams4Ray? quicParams { get; set; }
} }
public class Mask4Ray public class Mask4Ray
{ {
public string type { get; set; } public string type { get; set; }
public object? settings { get; set; } public MaskSettings4Ray? settings { get; set; }
} }
public class MaskSettings4Ray public class MaskSettings4Ray
@ -489,6 +486,14 @@ public class MaskSettings4Ray
public string? domain { get; set; } public string? domain { get; set; }
} }
public class QuicParams4Ray
{
public string? congestion { get; set; }
public string? brutalUp { get; set; }
public string? brutalDown { get; set; }
public UdpHop4Ray? udpHop { get; set; }
}
public class AccountsItem4Ray public class AccountsItem4Ray
{ {
public string user { get; set; } public string user { get; set; }

View file

@ -3376,7 +3376,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Process name: Linux/Windows/macOS (sing-box) 的本地化字符串。 /// 查找类似 Process (Linux/Windows) 的本地化字符串。
/// </summary> /// </summary>
public static string TbRoutingRuleProcess { public static string TbRoutingRuleProcess {
get { get {

View file

@ -1027,7 +1027,7 @@
<value>پروتکل sing-box Mux</value> <value>پروتکل sing-box Mux</value>
</data> </data>
<data name="TbRoutingRuleProcess" xml:space="preserve"> <data name="TbRoutingRuleProcess" xml:space="preserve">
<value>Process name: Linux/Windows/macOS (sing-box)</value> <value>Process (Linux/Windows)</value>
</data> </data>
<data name="TbRoutingRuleIP" xml:space="preserve"> <data name="TbRoutingRuleIP" xml:space="preserve">
<value>IP or IP CIDR</value> <value>IP or IP CIDR</value>

View file

@ -1024,7 +1024,7 @@
<value>Protocole de multiplexage Mux (sing-box)</value> <value>Protocole de multiplexage Mux (sing-box)</value>
</data> </data>
<data name="TbRoutingRuleProcess" xml:space="preserve"> <data name="TbRoutingRuleProcess" xml:space="preserve">
<value>Process name: Linux/Windows/macOS (sing-box)</value> <value>Process (Linux/Windows)</value>
</data> </data>
<data name="TbRoutingRuleIP" xml:space="preserve"> <data name="TbRoutingRuleIP" xml:space="preserve">
<value>IP ou IP CIDR</value> <value>IP ou IP CIDR</value>

View file

@ -1027,7 +1027,7 @@
<value>sing-box Mux protokoll</value> <value>sing-box Mux protokoll</value>
</data> </data>
<data name="TbRoutingRuleProcess" xml:space="preserve"> <data name="TbRoutingRuleProcess" xml:space="preserve">
<value>Process name: Linux/Windows/macOS (sing-box)</value> <value>Process (Linux/Windows)</value>
</data> </data>
<data name="TbRoutingRuleIP" xml:space="preserve"> <data name="TbRoutingRuleIP" xml:space="preserve">
<value>IP vagy IP CIDR</value> <value>IP vagy IP CIDR</value>

View file

@ -1027,7 +1027,7 @@
<value>sing-box Mux Protocol</value> <value>sing-box Mux Protocol</value>
</data> </data>
<data name="TbRoutingRuleProcess" xml:space="preserve"> <data name="TbRoutingRuleProcess" xml:space="preserve">
<value>Process name: Linux/Windows/macOS (sing-box)</value> <value>Process (Linux/Windows)</value>
</data> </data>
<data name="TbRoutingRuleIP" xml:space="preserve"> <data name="TbRoutingRuleIP" xml:space="preserve">
<value>IP or IP CIDR</value> <value>IP or IP CIDR</value>

View file

@ -1027,7 +1027,7 @@
<value>Протокол Mux для sing-box</value> <value>Протокол Mux для sing-box</value>
</data> </data>
<data name="TbRoutingRuleProcess" xml:space="preserve"> <data name="TbRoutingRuleProcess" xml:space="preserve">
<value>Process name: Linux/Windows/macOS (sing-box)</value> <value>Process (Linux/Windows)</value>
</data> </data>
<data name="TbRoutingRuleIP" xml:space="preserve"> <data name="TbRoutingRuleIP" xml:space="preserve">
<value>IP-адрес или сеть CIDR</value> <value>IP-адрес или сеть CIDR</value>

View file

@ -1024,7 +1024,7 @@
<value>sing-box Mux 多路复用协议</value> <value>sing-box Mux 多路复用协议</value>
</data> </data>
<data name="TbRoutingRuleProcess" xml:space="preserve"> <data name="TbRoutingRuleProcess" xml:space="preserve">
<value>进程 Linux/Windows/macOS(sing-box)</value> <value>进程 (Linux/Windows)</value>
</data> </data>
<data name="TbRoutingRuleIP" xml:space="preserve"> <data name="TbRoutingRuleIP" xml:space="preserve">
<value>IP 或 IP CIDR</value> <value>IP 或 IP CIDR</value>

View file

@ -1024,7 +1024,7 @@
<value>sing-box Mux 多路復用協定</value> <value>sing-box Mux 多路復用協定</value>
</data> </data>
<data name="TbRoutingRuleProcess" xml:space="preserve"> <data name="TbRoutingRuleProcess" xml:space="preserve">
<value>行程 Linux/Windows/macOS(sing-box)</value> <value>行程 (Linux/Windows)</value>
</data> </data>
<data name="TbRoutingRuleIP" xml:space="preserve"> <data name="TbRoutingRuleIP" xml:space="preserve">
<value>IP 或 IP CIDR</value> <value>IP 或 IP CIDR</value>

View file

@ -223,13 +223,14 @@ public partial class CoreConfigSingboxService
password = protocolExtra.SalamanderPass.TrimEx(), password = protocolExtra.SalamanderPass.TrimEx(),
}; };
} }
int? upMbps = protocolExtra?.UpMbps is { } su and >= 0
outbound.up_mbps = protocolExtra?.UpMbps is { } su and >= 0
? su ? su
: _config.HysteriaItem.UpMbps; : _config.HysteriaItem.UpMbps;
outbound.down_mbps = protocolExtra?.DownMbps is { } sd and >= 0 int? downMbps = protocolExtra?.DownMbps is { } sd and >= 0
? sd ? sd
: _config.HysteriaItem.DownMbps; : _config.HysteriaItem.UpMbps;
outbound.up_mbps = upMbps > 0 ? upMbps : null;
outbound.down_mbps = downMbps > 0 ? downMbps : null;
var ports = protocolExtra?.Ports?.IsNullOrEmpty() == false ? protocolExtra.Ports : null; var ports = protocolExtra?.Ports?.IsNullOrEmpty() == false ? protocolExtra.Ports : null;
if ((!ports.IsNullOrEmpty()) && (ports.Contains(':') || ports.Contains('-') || ports.Contains(','))) if ((!ports.IsNullOrEmpty()) && (ports.Contains(':') || ports.Contains('-') || ports.Contains(',')))
{ {

View file

@ -440,10 +440,10 @@ public partial class CoreConfigV2rayService
kcpSettings.congestion = _config.KcpItem.Congestion; kcpSettings.congestion = _config.KcpItem.Congestion;
kcpSettings.readBufferSize = _config.KcpItem.ReadBufferSize; kcpSettings.readBufferSize = _config.KcpItem.ReadBufferSize;
kcpSettings.writeBufferSize = _config.KcpItem.WriteBufferSize; kcpSettings.writeBufferSize = _config.KcpItem.WriteBufferSize;
streamSettings.finalmask ??= new(); var kcpFinalmask = new Finalmask4Ray();
if (Global.KcpHeaderMaskMap.TryGetValue(_node.HeaderType, out var header)) if (Global.KcpHeaderMaskMap.TryGetValue(_node.HeaderType, out var header))
{ {
streamSettings.finalmask.udp = kcpFinalmask.udp =
[ [
new Mask4Ray new Mask4Ray
{ {
@ -452,23 +452,24 @@ public partial class CoreConfigV2rayService
} }
]; ];
} }
streamSettings.finalmask.udp ??= []; kcpFinalmask.udp ??= [];
if (path.IsNullOrEmpty()) if (path.IsNullOrEmpty())
{ {
streamSettings.finalmask.udp.Add(new Mask4Ray kcpFinalmask.udp.Add(new Mask4Ray
{ {
type = "mkcp-original" type = "mkcp-original"
}); });
} }
else else
{ {
streamSettings.finalmask.udp.Add(new Mask4Ray kcpFinalmask.udp.Add(new Mask4Ray
{ {
type = "mkcp-aes128gcm", type = "mkcp-aes128gcm",
settings = new MaskSettings4Ray { password = path } settings = new MaskSettings4Ray { password = path }
}); });
} }
streamSettings.kcpSettings = kcpSettings; streamSettings.kcpSettings = kcpSettings;
streamSettings.finalmask = kcpFinalmask;
break; break;
//ws //ws
case nameof(ETransport.ws): case nameof(ETransport.ws):
@ -597,36 +598,46 @@ public partial class CoreConfigV2rayService
: (_config.HysteriaItem.HopInterval >= 5 : (_config.HysteriaItem.HopInterval >= 5
? _config.HysteriaItem.HopInterval ? _config.HysteriaItem.HopInterval
: Global.Hysteria2DefaultHopInt).ToString(); : Global.Hysteria2DefaultHopInt).ToString();
HysteriaUdpHop4Ray? udpHop = null; var hy2Finalmask = new Finalmask4Ray();
var quicParams = new QuicParams4Ray();
if (!ports.IsNullOrEmpty() && if (!ports.IsNullOrEmpty() &&
(ports.Contains(':') || ports.Contains('-') || ports.Contains(','))) (ports.Contains(':') || ports.Contains('-') || ports.Contains(',')))
{ {
udpHop = new HysteriaUdpHop4Ray var udpHop = new UdpHop4Ray
{ {
port = ports.Replace(':', '-'), ports = ports.Replace(':', '-'),
interval = hopInterval, interval = hopInterval,
}; };
quicParams.udpHop = udpHop;
}
if (upMbps > 0 || downMbps > 0)
{
quicParams.congestion = "brutal";
quicParams.brutalUp = upMbps > 0 ? $"{upMbps}mbps" : null;
quicParams.brutalDown = downMbps > 0 ? $"{downMbps}mbps" : null;
}
else
{
quicParams.congestion = "bbr";
}
hy2Finalmask.quicParams = quicParams;
if (!protocolExtra.SalamanderPass.IsNullOrEmpty())
{
hy2Finalmask.udp =
[
new Mask4Ray
{
type = "salamander",
settings = new MaskSettings4Ray { password = protocolExtra.SalamanderPass.TrimEx(), }
}
];
} }
streamSettings.hysteriaSettings = new() streamSettings.hysteriaSettings = new()
{ {
version = 2, version = 2,
auth = _node.Password, auth = _node.Password,
up = upMbps > 0 ? $"{upMbps}mbps" : null,
down = downMbps > 0 ? $"{downMbps}mbps" : null,
udphop = udpHop,
}; };
if (!protocolExtra.SalamanderPass.IsNullOrEmpty()) streamSettings.finalmask = hy2Finalmask;
{
streamSettings.finalmask ??= new();
streamSettings.finalmask.udp =
[
new Mask4Ray
{
type = "salamander",
settings = new MaskSettings4Ray { password = protocolExtra.SalamanderPass.TrimEx(), }
}
];
}
break; break;
default: default:
@ -664,7 +675,7 @@ public partial class CoreConfigV2rayService
if (!_node.Finalmask.IsNullOrEmpty()) if (!_node.Finalmask.IsNullOrEmpty())
{ {
streamSettings.finalmask = JsonUtils.Deserialize<Finalmask4Ray>(_node.Finalmask); streamSettings.finalmask = JsonUtils.ParseJson(_node.Finalmask);
} }
} }
catch (Exception ex) catch (Exception ex)

View file

@ -204,7 +204,7 @@ public class CheckUpdateViewModel : MyReactiveObject
private async Task UpdateFinishedSub(bool blReload) private async Task UpdateFinishedSub(bool blReload)
{ {
RxApp.MainThreadScheduler.Schedule(blReload, (scheduler, blReload) => RxSchedulers.MainThreadScheduler.Schedule(blReload, (scheduler, blReload) =>
{ {
_ = UpdateFinishedResult(blReload); _ = UpdateFinishedResult(blReload);
return Disposable.Empty; return Disposable.Empty;
@ -317,7 +317,7 @@ public class CheckUpdateViewModel : MyReactiveObject
Remarks = msg, Remarks = msg,
}; };
RxApp.MainThreadScheduler.Schedule(item, (scheduler, model) => RxSchedulers.MainThreadScheduler.Schedule(item, (scheduler, model) =>
{ {
_ = UpdateViewResult(model); _ = UpdateViewResult(model);
return Disposable.Empty; return Disposable.Empty;

View file

@ -56,7 +56,7 @@ public class ClashConnectionsViewModel : MyReactiveObject
return; return;
} }
RxApp.MainThreadScheduler.Schedule(ret?.connections, (scheduler, model) => RxSchedulers.MainThreadScheduler.Schedule(ret?.connections, (scheduler, model) =>
{ {
_ = RefreshConnections(model); _ = RefreshConnections(model);
return Disposable.Empty; return Disposable.Empty;

View file

@ -90,7 +90,7 @@ public class ClashProxiesViewModel : MyReactiveObject
AppEvents.ProxiesReloadRequested AppEvents.ProxiesReloadRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(async _ => await ProxiesReload()); .Subscribe(async _ => await ProxiesReload());
#endregion AppEvents #endregion AppEvents
@ -173,7 +173,7 @@ public class ClashProxiesViewModel : MyReactiveObject
if (refreshUI) if (refreshUI)
{ {
RxApp.MainThreadScheduler.Schedule(() => _ = RefreshProxyGroups()); RxSchedulers.MainThreadScheduler.Schedule(() => _ = RefreshProxyGroups());
} }
} }
@ -387,7 +387,7 @@ public class ClashProxiesViewModel : MyReactiveObject
} }
var model = new SpeedTestResult() { IndexId = item.Name, Delay = result }; var model = new SpeedTestResult() { IndexId = item.Name, Delay = result };
RxApp.MainThreadScheduler.Schedule(model, (scheduler, model) => RxSchedulers.MainThreadScheduler.Schedule(model, (scheduler, model) =>
{ {
_ = ProxiesDelayTestResult(model); _ = ProxiesDelayTestResult(model);
return Disposable.Empty; return Disposable.Empty;

View file

@ -228,22 +228,22 @@ public class MainWindowViewModel : MyReactiveObject
AppEvents.ReloadRequested AppEvents.ReloadRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(async _ => await Reload()); .Subscribe(async _ => await Reload());
AppEvents.AddServerViaScanRequested AppEvents.AddServerViaScanRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(async _ => await AddServerViaScanAsync()); .Subscribe(async _ => await AddServerViaScanAsync());
AppEvents.AddServerViaClipboardRequested AppEvents.AddServerViaClipboardRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(async _ => await AddServerViaClipboardAsync(null)); .Subscribe(async _ => await AddServerViaClipboardAsync(null));
AppEvents.SubscriptionsUpdateRequested AppEvents.SubscriptionsUpdateRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(async blProxy => await UpdateSubscriptionProcess("", blProxy)); .Subscribe(async blProxy => await UpdateSubscriptionProcess("", blProxy));
#endregion AppEvents #endregion AppEvents
@ -583,7 +583,7 @@ public class MainWindowViewModel : MyReactiveObject
private void ReloadResult(bool showClashUI) private void ReloadResult(bool showClashUI)
{ {
RxApp.MainThreadScheduler.Schedule(() => RxSchedulers.MainThreadScheduler.Schedule(() =>
{ {
ShowClashUI = showClashUI; ShowClashUI = showClashUI;
TabMainSelectedIndex = showClashUI ? TabMainSelectedIndex : 0; TabMainSelectedIndex = showClashUI ? TabMainSelectedIndex : 0;
@ -592,7 +592,7 @@ public class MainWindowViewModel : MyReactiveObject
private void SetReloadEnabled(bool enabled) private void SetReloadEnabled(bool enabled)
{ {
RxApp.MainThreadScheduler.Schedule(() => BlReloadEnabled = enabled); RxSchedulers.MainThreadScheduler.Schedule(() => BlReloadEnabled = enabled);
} }
private async Task LoadCore(CoreConfigContext? mainContext, CoreConfigContext? preContext) private async Task LoadCore(CoreConfigContext? mainContext, CoreConfigContext? preContext)

View file

@ -31,7 +31,7 @@ public class MsgViewModel : MyReactiveObject
AppEvents.SendMsgViewRequested AppEvents.SendMsgViewRequested
.AsObservable() .AsObservable()
//.ObserveOn(RxApp.MainThreadScheduler) //.ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(content => _ = AppendQueueMsg(content)); .Subscribe(content => _ = AppendQueueMsg(content));
} }

View file

@ -188,14 +188,9 @@ public class ProfilesSelectViewModel : MyReactiveObject
{ {
SubItems.Add(item); SubItems.Add(item);
} }
if (_subIndexId != null && SubItems.FirstOrDefault(t => t.Id == _subIndexId) != null) SelectedSub = (_config.SubIndexId.IsNotEmpty()
{ ? SubItems.FirstOrDefault(t => t.Id == _config.SubIndexId)
SelectedSub = SubItems.FirstOrDefault(t => t.Id == _subIndexId); : null) ?? SubItems.LastOrDefault();
}
else
{
SelectedSub = SubItems.First();
}
} }
private async Task<List<ProfileItemModel>?> GetProfileItemsEx(string subid, string filter) private async Task<List<ProfileItemModel>?> GetProfileItemsEx(string subid, string filter)

View file

@ -228,22 +228,22 @@ public class ProfilesViewModel : MyReactiveObject
AppEvents.ProfilesRefreshRequested AppEvents.ProfilesRefreshRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(async _ => await RefreshServersBiz()); .Subscribe(async _ => await RefreshServersBiz());
AppEvents.SubscriptionsRefreshRequested AppEvents.SubscriptionsRefreshRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(async _ => await RefreshSubscriptions()); .Subscribe(async _ => await RefreshSubscriptions());
AppEvents.DispatcherStatisticsRequested AppEvents.DispatcherStatisticsRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(async result => await UpdateStatistics(result)); .Subscribe(async result => await UpdateStatistics(result));
AppEvents.SetDefaultServerRequested AppEvents.SetDefaultServerRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(async indexId => await SetDefaultServer(indexId)); .Subscribe(async indexId => await SetDefaultServer(indexId));
#endregion AppEvents #endregion AppEvents
@ -391,14 +391,9 @@ public class ProfilesViewModel : MyReactiveObject
{ {
SubItems.Add(item); SubItems.Add(item);
} }
if (_config.SubIndexId != null && SubItems.FirstOrDefault(t => t.Id == _config.SubIndexId) != null) SelectedSub = (_config.SubIndexId.IsNotEmpty()
{ ? SubItems.FirstOrDefault(t => t.Id == _config.SubIndexId)
SelectedSub = SubItems.FirstOrDefault(t => t.Id == _config.SubIndexId); : null) ?? SubItems.LastOrDefault();
}
else
{
SelectedSub = SubItems.First();
}
} }
private async Task<List<ProfileItemModel>?> GetProfileItemsEx(string subid, string filter) private async Task<List<ProfileItemModel>?> GetProfileItemsEx(string subid, string filter)
@ -732,7 +727,7 @@ public class ProfilesViewModel : MyReactiveObject
_speedtestService ??= new SpeedtestService(_config, async (SpeedTestResult result) => _speedtestService ??= new SpeedtestService(_config, async (SpeedTestResult result) =>
{ {
RxApp.MainThreadScheduler.Schedule(result, (scheduler, result) => RxSchedulers.MainThreadScheduler.Schedule(result, (scheduler, result) =>
{ {
_ = SetSpeedTestResult(result); _ = SetSpeedTestResult(result);
return Disposable.Empty; return Disposable.Empty;

View file

@ -200,27 +200,27 @@ public class StatusBarViewModel : MyReactiveObject
AppEvents.DispatcherStatisticsRequested AppEvents.DispatcherStatisticsRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(async result => await UpdateStatistics(result)); .Subscribe(async result => await UpdateStatistics(result));
AppEvents.RoutingsMenuRefreshRequested AppEvents.RoutingsMenuRefreshRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(async _ => await RefreshRoutingsMenu()); .Subscribe(async _ => await RefreshRoutingsMenu());
AppEvents.TestServerRequested AppEvents.TestServerRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(async _ => await TestServerAvailability()); .Subscribe(async _ => await TestServerAvailability());
AppEvents.InboundDisplayRequested AppEvents.InboundDisplayRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(async _ => await InboundDisplayStatus()); .Subscribe(async _ => await InboundDisplayStatus());
AppEvents.SysProxyChangeRequested AppEvents.SysProxyChangeRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(async result => await SetListenerType(result)); .Subscribe(async result => await SetListenerType(result));
#endregion AppEvents #endregion AppEvents
@ -243,7 +243,7 @@ public class StatusBarViewModel : MyReactiveObject
{ {
AppEvents.ProfilesRefreshRequested AppEvents.ProfilesRefreshRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(async _ => await RefreshServersBiz()); //.DisposeWith(_disposables); .Subscribe(async _ => await RefreshServersBiz()); //.DisposeWith(_disposables);
} }
} }
@ -362,7 +362,7 @@ public class StatusBarViewModel : MyReactiveObject
private async Task TestServerAvailabilitySub(string msg) private async Task TestServerAvailabilitySub(string msg)
{ {
RxApp.MainThreadScheduler.Schedule(msg, (scheduler, msg) => RxSchedulers.MainThreadScheduler.Schedule(msg, (scheduler, msg) =>
{ {
_ = TestServerAvailabilityResult(msg); _ = TestServerAvailabilityResult(msg);
return Disposable.Empty; return Disposable.Empty;

View file

@ -59,7 +59,7 @@ internal class Program
//.WithInterFont() //.WithInterFont()
.WithFontByDefault() .WithFontByDefault()
.LogToTrace() .LogToTrace()
.UseReactiveUI(); .UseReactiveUI(_ => { });
if (OperatingSystem.IsMacOS()) if (OperatingSystem.IsMacOS())
{ {

View file

@ -28,7 +28,7 @@ public partial class ClashConnectionsView : ReactiveUserControl<ClashConnections
AppEvents.AppExitRequested AppEvents.AppExitRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(_ => StorageUI()) .Subscribe(_ => StorageUI())
.DisposeWith(disposables); .DisposeWith(disposables);
}); });

View file

@ -128,25 +128,25 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
AppEvents.SendSnackMsgRequested AppEvents.SendSnackMsgRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(async content => await DelegateSnackMsg(content)) .Subscribe(async content => await DelegateSnackMsg(content))
.DisposeWith(disposables); .DisposeWith(disposables);
AppEvents.AppExitRequested AppEvents.AppExitRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(_ => StorageUI()) .Subscribe(_ => StorageUI())
.DisposeWith(disposables); .DisposeWith(disposables);
AppEvents.ShutdownRequested AppEvents.ShutdownRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(content => Shutdown(content)) .Subscribe(content => Shutdown(content))
.DisposeWith(disposables); .DisposeWith(disposables);
AppEvents.ShowHideWindowRequested AppEvents.ShowHideWindowRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(blShow => ShowHideWindow(blShow)) .Subscribe(blShow => ShowHideWindow(blShow))
.DisposeWith(disposables); .DisposeWith(disposables);
}); });

View file

@ -90,13 +90,13 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
AppEvents.AppExitRequested AppEvents.AppExitRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(_ => StorageUI()) .Subscribe(_ => StorageUI())
.DisposeWith(disposables); .DisposeWith(disposables);
AppEvents.AdjustMainLvColWidthRequested AppEvents.AdjustMainLvColWidthRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(_ => AutofitColumnWidth()) .Subscribe(_ => AutofitColumnWidth())
.DisposeWith(disposables); .DisposeWith(disposables);
}); });

View file

@ -39,6 +39,11 @@ public partial class App : Application
} }
AppManager.Instance.InitComponents(); AppManager.Instance.InitComponents();
RxAppBuilder.CreateReactiveUIBuilder()
.WithWpf()
.BuildApp();
base.OnStartup(e); base.OnStartup(e);
} }

View file

@ -20,6 +20,7 @@ global using System.Windows.Threading;
global using DynamicData; global using DynamicData;
global using DynamicData.Binding; global using DynamicData.Binding;
global using ReactiveUI; global using ReactiveUI;
global using ReactiveUI.Builder;
global using ReactiveUI.Fody.Helpers; global using ReactiveUI.Fody.Helpers;
global using ServiceLib; global using ServiceLib;
global using ServiceLib.Base; global using ServiceLib.Base;

View file

@ -33,7 +33,7 @@ public partial class ClashConnectionsView
AppEvents.AppExitRequested AppEvents.AppExitRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(_ => StorageUI()) .Subscribe(_ => StorageUI())
.DisposeWith(disposables); .DisposeWith(disposables);
}); });

View file

@ -127,25 +127,25 @@ public partial class MainWindow
AppEvents.SendSnackMsgRequested AppEvents.SendSnackMsgRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(async content => await DelegateSnackMsg(content)) .Subscribe(async content => await DelegateSnackMsg(content))
.DisposeWith(disposables); .DisposeWith(disposables);
AppEvents.AppExitRequested AppEvents.AppExitRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(_ => StorageUI()) .Subscribe(_ => StorageUI())
.DisposeWith(disposables); .DisposeWith(disposables);
AppEvents.ShutdownRequested AppEvents.ShutdownRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(content => Shutdown(content)) .Subscribe(content => Shutdown(content))
.DisposeWith(disposables); .DisposeWith(disposables);
AppEvents.ShowHideWindowRequested AppEvents.ShowHideWindowRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(blShow => ShowHideWindow(blShow)) .Subscribe(blShow => ShowHideWindow(blShow))
.DisposeWith(disposables); .DisposeWith(disposables);
}); });

View file

@ -84,13 +84,13 @@ public partial class ProfilesView
AppEvents.AppExitRequested AppEvents.AppExitRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(_ => StorageUI()) .Subscribe(_ => StorageUI())
.DisposeWith(disposables); .DisposeWith(disposables);
AppEvents.AdjustMainLvColWidthRequested AppEvents.AdjustMainLvColWidthRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxSchedulers.MainThreadScheduler)
.Subscribe(_ => AutofitColumnWidth()) .Subscribe(_ => AutofitColumnWidth())
.DisposeWith(disposables); .DisposeWith(disposables);
}); });

View file

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows10.0.17763</TargetFramework> <TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
<GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore> <GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<ApplicationIcon>Resources\v2rayN.ico</ApplicationIcon> <ApplicationIcon>Resources\v2rayN.ico</ApplicationIcon>