Compare commits

...

11 commits

Author SHA1 Message Date
Wydy
65b78ce6f9
Merge 7816c1dcca into e96a4818c4 2025-09-23 17:30:43 +08:00
2dust
e96a4818c4 Optimization and improvement
Some checks are pending
release Linux / build (Release) (push) Waiting to run
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
2025-09-23 15:31:19 +08:00
2dust
0377e7ce19 Optimization and improvement, using event subscribers 2025-09-23 14:27:42 +08:00
2dust
6929886b3e Optimization and improvement, using event subscribers 2025-09-23 12:08:43 +08:00
2dust
721d70c8c7 Update Directory.Packages.props 2025-09-23 11:39:57 +08:00
2dust
27b45aee83 Optimization and improvement, using event subscribers 2025-09-23 11:39:55 +08:00
2dust
18ac76e683 up 7.14.12
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
2025-09-21 14:50:01 +08:00
2dust
3e1e23a524 Update Directory.Packages.props 2025-09-21 14:48:54 +08:00
2dust
534c7ab444 Optimize and improve QR code display 2025-09-21 14:35:49 +08:00
2dust
c2c13ad318 Create v2rayN.slnx
https://github.com/2dust/v2rayN/pull/7969
2025-09-21 12:12:24 +08:00
2dust
3a21596d95 Fix node domain resolving in TUN mode
https://github.com/2dust/v2rayN/pull/7989
2025-09-21 12:05:06 +08:00
25 changed files with 227 additions and 102 deletions

View file

@ -1,7 +1,7 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<Version>7.14.11</Version> <Version>7.14.12</Version>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>

View file

@ -18,9 +18,9 @@
<PackageVersion Include="ReactiveUI" Version="20.4.1" /> <PackageVersion Include="ReactiveUI" Version="20.4.1" />
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" /> <PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
<PackageVersion Include="ReactiveUI.WPF" Version="20.4.1" /> <PackageVersion Include="ReactiveUI.WPF" Version="20.4.1" />
<PackageVersion Include="Semi.Avalonia" Version="11.2.1.9" /> <PackageVersion Include="Semi.Avalonia" Version="11.2.1.10" />
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.2.1.9" /> <PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.2.1.10" />
<PackageVersion Include="Splat.NLog" Version="16.2.1" /> <PackageVersion Include="Splat.NLog" Version="17.0.1" />
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" /> <PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
<PackageVersion Include="TaskScheduler" Version="2.12.2" /> <PackageVersion Include="TaskScheduler" Version="2.12.2" />
<PackageVersion Include="WebDav.Client" Version="2.9.0" /> <PackageVersion Include="WebDav.Client" Version="2.9.0" />

View file

@ -1,4 +1,5 @@
using QRCoder; using QRCoder;
using QRCoder.Exceptions;
using SkiaSharp; using SkiaSharp;
using ZXing.SkiaSharp; using ZXing.SkiaSharp;
@ -8,11 +9,46 @@ public class QRCodeUtils
{ {
public static byte[]? GenQRCode(string? url) public static byte[]? GenQRCode(string? url)
{ {
if (url.IsNullOrEmpty())
{
return null;
}
using QRCodeGenerator qrGenerator = new(); using QRCodeGenerator qrGenerator = new();
using var qrCodeData = qrGenerator.CreateQrCode(url ?? string.Empty, QRCodeGenerator.ECCLevel.Q); DataTooLongException? lastDtle = null;
var levels = new[]
{
QRCodeGenerator.ECCLevel.H,
QRCodeGenerator.ECCLevel.Q,
QRCodeGenerator.ECCLevel.M,
QRCodeGenerator.ECCLevel.L
};
foreach (var level in levels)
{
try
{
using var qrCodeData = qrGenerator.CreateQrCode(url, level);
using PngByteQRCode qrCode = new(qrCodeData); using PngByteQRCode qrCode = new(qrCodeData);
return qrCode.GetGraphic(20); return qrCode.GetGraphic(20);
} }
catch (DataTooLongException ex)
{
lastDtle = ex;
continue;
}
catch
{
throw;
}
}
if (lastDtle != null)
{
throw lastDtle;
}
return null;
}
public static string? ParseBarcode(string? fileName) public static string? ParseBarcode(string? fileName)
{ {

View file

@ -598,6 +598,7 @@ public class Global
{ "cloudflare-dns.com", new List<string> { "104.16.249.249", "104.16.248.249", "2606:4700::6810:f8f9", "2606:4700::6810:f9f9" } }, { "cloudflare-dns.com", new List<string> { "104.16.249.249", "104.16.248.249", "2606:4700::6810:f8f9", "2606:4700::6810:f9f9" } },
{ "dns.cloudflare.com", new List<string> { "104.16.132.229", "104.16.133.229", "2606:4700::6810:84e5", "2606:4700::6810:85e5" } }, { "dns.cloudflare.com", new List<string> { "104.16.132.229", "104.16.133.229", "2606:4700::6810:84e5", "2606:4700::6810:85e5" } },
{ "dot.pub", new List<string> { "1.12.12.12", "120.53.53.53" } }, { "dot.pub", new List<string> { "1.12.12.12", "120.53.53.53" } },
{ "doh.pub", new List<string> { "1.12.12.12", "120.53.53.53" } },
{ "dns.quad9.net", new List<string> { "9.9.9.9", "149.112.112.112", "2620:fe::fe", "2620:fe::9" } }, { "dns.quad9.net", new List<string> { "9.9.9.9", "149.112.112.112", "2620:fe::fe", "2620:fe::9" } },
{ "dns.yandex.net", new List<string> { "77.88.8.8", "77.88.8.1", "2a02:6b8::feed:0ff", "2a02:6b8:0:1::feed:0ff" } }, { "dns.yandex.net", new List<string> { "77.88.8.8", "77.88.8.1", "2a02:6b8::feed:0ff", "2a02:6b8:0:1::feed:0ff" } },
{ "dns.sb", new List<string> { "185.222.222.222", "2a09::" } }, { "dns.sb", new List<string> { "185.222.222.222", "2a09::" } },

View file

@ -6,16 +6,22 @@ namespace ServiceLib.Handler;
public static class AppEvents public static class AppEvents
{ {
public static readonly Subject<Unit> ProfilesRefreshRequested = new(); public static readonly Subject<Unit> ProfilesRefreshRequested = new();
public static readonly Subject<Unit> SubscriptionsRefreshRequested = new();
public static readonly Subject<Unit> ProxiesReloadRequested = new();
public static readonly Subject<ServerSpeedItem> DispatcherStatisticsRequested = new();
public static readonly Subject<string> SendSnackMsgRequested = new(); public static readonly Subject<string> SendSnackMsgRequested = new();
public static readonly Subject<string> SendMsgViewRequested = new(); public static readonly Subject<string> SendMsgViewRequested = new();
public static readonly Subject<Unit> AppExitRequested = new(); public static readonly Subject<Unit> AppExitRequested = new();
public static readonly Subject<bool> ShutdownRequested = new(); public static readonly Subject<bool> ShutdownRequested = new();
public static readonly Subject<Unit> AdjustMainLvColWidthRequested = new(); public static readonly Subject<Unit> AdjustMainLvColWidthRequested = new();
public static readonly Subject<ServerSpeedItem> DispatcherStatisticsRequested = new(); public static readonly Subject<string> SetDefaultServerRequested = new();
public static readonly Subject<Unit> RoutingsMenuRefreshRequested = new();
public static readonly Subject<Unit> TestServerRequested = new();
public static readonly Subject<Unit> InboundDisplayRequested = new();
public static readonly Subject<ESysProxyType> SysProxyChangeRequested = new();
} }

View file

@ -1214,11 +1214,11 @@ public static class ConfigHandler
CoreType = ECoreType.sing_box, CoreType = ECoreType.sing_box,
ConfigType = EConfigType.SOCKS, ConfigType = EConfigType.SOCKS,
Address = Global.Loopback, Address = Global.Loopback,
Sni = node.Address, //Tun2SocksAddress SpiderX = node.Address, // Tun2SocksAddress
Port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks) Port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks)
}; };
} }
else if ((node.ConfigType == EConfigType.Custom && node.PreSocksPort > 0)) else if (node.ConfigType == EConfigType.Custom && node.PreSocksPort > 0)
{ {
var preCoreType = config.RunningCoreType = config.TunModeItem.EnableTun ? ECoreType.sing_box : ECoreType.Xray; var preCoreType = config.RunningCoreType = config.TunModeItem.EnableTun ? ECoreType.sing_box : ECoreType.Xray;
itemSocks = new ProfileItem() itemSocks = new ProfileItem()

View file

@ -43,16 +43,7 @@ public partial class CoreConfigSingboxService
}); });
} }
// Tun2SocksAddress await GenOutboundDnsRule(node, singboxConfig, Global.SingboxOutboundResolverTag);
if (node != null && Utils.IsDomain(node.Address))
{
singboxConfig.dns.rules ??= new List<Rule4Sbox>();
singboxConfig.dns.rules.Insert(0, new Rule4Sbox
{
server = Global.SingboxOutboundResolverTag,
domain = [node.Address],
});
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -346,16 +337,7 @@ public partial class CoreConfigSingboxService
await GenDnsDomainsLegacyCompatible(singboxConfig, item); await GenDnsDomainsLegacyCompatible(singboxConfig, item);
} }
// Tun2SocksAddress await GenOutboundDnsRule(node, singboxConfig, Global.SingboxFinalResolverTag);
if (node != null && Utils.IsDomain(node.Address))
{
singboxConfig.dns.rules ??= new List<Rule4Sbox>();
singboxConfig.dns.rules.Insert(0, new Rule4Sbox
{
server = Global.SingboxFinalResolverTag,
domain = [node.Address],
});
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -425,6 +407,37 @@ public partial class CoreConfigSingboxService
return await Task.FromResult(0); return await Task.FromResult(0);
} }
private async Task<int> GenOutboundDnsRule(ProfileItem? node, SingboxConfig singboxConfig, string? server)
{
if (node == null)
{
return 0;
}
var domain = string.Empty;
if (Utils.IsDomain(node.Address)) // normal outbound
{
domain = node.Address;
}
else if (node.Address == Global.Loopback && node.SpiderX.IsNotEmpty() && Utils.IsDomain(node.SpiderX)) // Tun2SocksAddress
{
domain = node.SpiderX;
}
if (domain.IsNullOrEmpty())
{
return 0;
}
singboxConfig.dns.rules ??= new List<Rule4Sbox>();
singboxConfig.dns.rules.Insert(0, new Rule4Sbox
{
server = server,
domain = [domain],
});
return await Task.FromResult(0);
}
private static Server4Sbox? ParseDnsAddress(string address) private static Server4Sbox? ParseDnsAddress(string address)
{ {
var addressFirst = address?.Split(address.Contains(',') ? ',' : ';').FirstOrDefault()?.Trim(); var addressFirst = address?.Split(address.Contains(',') ? ',' : ';').FirstOrDefault()?.Trim();

View file

@ -69,6 +69,8 @@ public class ClashProxiesViewModel : MyReactiveObject
SortingSelected = _config.ClashUIItem.ProxiesSorting; SortingSelected = _config.ClashUIItem.ProxiesSorting;
RuleModeSelected = (int)_config.ClashUIItem.RuleMode; RuleModeSelected = (int)_config.ClashUIItem.RuleMode;
#region WhenAnyValue && ReactiveCommand
this.WhenAnyValue( this.WhenAnyValue(
x => x.SelectedGroup, x => x.SelectedGroup,
y => y != null && y.Name.IsNotEmpty()) y => y != null && y.Name.IsNotEmpty())
@ -89,6 +91,17 @@ public class ClashProxiesViewModel : MyReactiveObject
y => y == true) y => y == true)
.Subscribe(c => { _config.ClashUIItem.ProxiesAutoRefresh = AutoRefresh; }); .Subscribe(c => { _config.ClashUIItem.ProxiesAutoRefresh = AutoRefresh; });
#endregion WhenAnyValue && ReactiveCommand
#region AppEvents
AppEvents.ProxiesReloadRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async _ => await ProxiesReload());
#endregion AppEvents
_ = Init(); _ = Init();
} }

View file

@ -2,7 +2,6 @@ using System.Reactive;
using System.Reactive.Concurrency; using System.Reactive.Concurrency;
using ReactiveUI; using ReactiveUI;
using ReactiveUI.Fody.Helpers; using ReactiveUI.Fody.Helpers;
using Splat;
namespace ServiceLib.ViewModels; namespace ServiceLib.ViewModels;
@ -240,7 +239,6 @@ public class MainWindowViewModel : MyReactiveObject
BlReloadEnabled = true; BlReloadEnabled = true;
await Reload(); await Reload();
await AutoHideStartup(); await AutoHideStartup();
Locator.Current.GetService<StatusBarViewModel>()?.RefreshRoutingsMenu();
} }
#endregion Init #endregion Init
@ -301,7 +299,7 @@ public class MainWindowViewModel : MyReactiveObject
private void RefreshSubscriptions() private void RefreshSubscriptions()
{ {
Locator.Current.GetService<ProfilesViewModel>()?.RefreshSubscriptions(); AppEvents.SubscriptionsRefreshRequested.OnNext(Unit.Default);
} }
#endregion Servers && Groups #endregion Servers && Groups
@ -433,7 +431,7 @@ public class MainWindowViewModel : MyReactiveObject
var ret = await _updateView?.Invoke(EViewAction.OptionSettingWindow, null); var ret = await _updateView?.Invoke(EViewAction.OptionSettingWindow, null);
if (ret == true) if (ret == true)
{ {
Locator.Current.GetService<StatusBarViewModel>()?.InboundDisplayStatus(); AppEvents.InboundDisplayRequested.OnNext(Unit.Default);
await Reload(); await Reload();
} }
} }
@ -444,7 +442,7 @@ public class MainWindowViewModel : MyReactiveObject
if (ret == true) if (ret == true)
{ {
await ConfigHandler.InitBuiltinRouting(_config); await ConfigHandler.InitBuiltinRouting(_config);
Locator.Current.GetService<StatusBarViewModel>()?.RefreshRoutingsMenu(); AppEvents.RoutingsMenuRefreshRequested.OnNext(Unit.Default);
await Reload(); await Reload();
} }
} }
@ -518,9 +516,15 @@ public class MainWindowViewModel : MyReactiveObject
await SysProxyHandler.UpdateSysProxy(_config, false); await SysProxyHandler.UpdateSysProxy(_config, false);
await Task.Delay(1000); await Task.Delay(1000);
}); });
Locator.Current.GetService<StatusBarViewModel>()?.TestServerAvailability(); AppEvents.TestServerRequested.OnNext(Unit.Default);
RxApp.MainThreadScheduler.Schedule(() => _ = ReloadResult()); var showClashUI = _config.IsRunningCore(ECoreType.sing_box);
if (showClashUI)
{
AppEvents.ProxiesReloadRequested.OnNext(Unit.Default);
}
RxApp.MainThreadScheduler.Schedule(() => ReloadResult(showClashUI));
BlReloadEnabled = true; BlReloadEnabled = true;
if (_hasNextReloadJob) if (_hasNextReloadJob)
@ -530,19 +534,11 @@ public class MainWindowViewModel : MyReactiveObject
} }
} }
public async Task ReloadResult() private void ReloadResult(bool showClashUI)
{ {
// BlReloadEnabled = true; // BlReloadEnabled = true;
//Locator.Current.GetService<StatusBarViewModel>()?.ChangeSystemProxyAsync(_config.systemProxyItem.sysProxyType, false); ShowClashUI = showClashUI;
ShowClashUI = _config.IsRunningCore(ECoreType.sing_box); TabMainSelectedIndex = showClashUI ? TabMainSelectedIndex : 0;
if (ShowClashUI)
{
Locator.Current.GetService<ClashProxiesViewModel>()?.ProxiesReload();
}
else
{
TabMainSelectedIndex = 0;
}
} }
private async Task LoadCore() private async Task LoadCore()
@ -574,7 +570,7 @@ public class MainWindowViewModel : MyReactiveObject
{ {
await ConfigHandler.ApplyRegionalPreset(_config, type); await ConfigHandler.ApplyRegionalPreset(_config, type);
await ConfigHandler.InitRouting(_config); await ConfigHandler.InitRouting(_config);
Locator.Current.GetService<StatusBarViewModel>()?.RefreshRoutingsMenu(); AppEvents.RoutingsMenuRefreshRequested.OnNext(Unit.Default);
await ConfigHandler.SaveConfig(_config); await ConfigHandler.SaveConfig(_config);
await new UpdateService().UpdateGeoFileAll(_config, UpdateTaskHandler); await new UpdateService().UpdateGeoFileAll(_config, UpdateTaskHandler);

View file

@ -240,11 +240,21 @@ public class ProfilesViewModel : MyReactiveObject
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async _ => await RefreshServersBiz()); .Subscribe(async _ => await RefreshServersBiz());
AppEvents.SubscriptionsRefreshRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async _ => await RefreshSubscriptions());
AppEvents.DispatcherStatisticsRequested AppEvents.DispatcherStatisticsRequested
.AsObservable() .AsObservable()
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async result => await UpdateStatistics(result)); .Subscribe(async result => await UpdateStatistics(result));
AppEvents.SetDefaultServerRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async indexId => await SetDefaultServer(indexId));
#endregion AppEvents #endregion AppEvents
_ = Init(); _ = Init();
@ -380,7 +390,7 @@ public class ProfilesViewModel : MyReactiveObject
await _updateView?.Invoke(EViewAction.DispatcherRefreshServersBiz, null); await _updateView?.Invoke(EViewAction.DispatcherRefreshServersBiz, null);
} }
public async Task RefreshSubscriptions() private async Task RefreshSubscriptions()
{ {
SubItems.Clear(); SubItems.Clear();
@ -565,7 +575,7 @@ public class ProfilesViewModel : MyReactiveObject
await SetDefaultServer(SelectedProfile.IndexId); await SetDefaultServer(SelectedProfile.IndexId);
} }
public async Task SetDefaultServer(string? indexId) private async Task SetDefaultServer(string? indexId)
{ {
if (indexId.IsNullOrEmpty()) if (indexId.IsNullOrEmpty())
{ {

View file

@ -11,6 +11,9 @@ namespace ServiceLib.ViewModels;
public class StatusBarViewModel : MyReactiveObject public class StatusBarViewModel : MyReactiveObject
{ {
private static readonly Lazy<StatusBarViewModel> _instance = new(() => new(null));
public static StatusBarViewModel Instance => _instance.Value;
#region ObservableCollection #region ObservableCollection
public IObservableCollection<RoutingItem> RoutingItems { get; } = new ObservableCollectionExtended<RoutingItem>(); public IObservableCollection<RoutingItem> RoutingItems { get; } = new ObservableCollectionExtended<RoutingItem>();
@ -209,6 +212,26 @@ public class StatusBarViewModel : MyReactiveObject
.ObserveOn(RxApp.MainThreadScheduler) .ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async result => await UpdateStatistics(result)); .Subscribe(async result => await UpdateStatistics(result));
AppEvents.RoutingsMenuRefreshRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async _ => await RefreshRoutingsMenu());
AppEvents.TestServerRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async _ => await TestServerAvailability());
AppEvents.InboundDisplayRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async _ => await InboundDisplayStatus());
AppEvents.SysProxyChangeRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async result => await SetListenerType(result));
#endregion AppEvents #endregion AppEvents
_ = Init(); _ = Init();
@ -329,7 +352,7 @@ public class StatusBarViewModel : MyReactiveObject
{ {
return; return;
} }
Locator.Current.GetService<ProfilesViewModel>()?.SetDefaultServer(SelectedServer.ID); AppEvents.SetDefaultServerRequested.OnNext(SelectedServer.ID);
} }
public async Task TestServerAvailability() public async Task TestServerAvailability()
@ -364,7 +387,7 @@ public class StatusBarViewModel : MyReactiveObject
#region System proxy and Routings #region System proxy and Routings
public async Task SetListenerType(ESysProxyType type) private async Task SetListenerType(ESysProxyType type)
{ {
if (_config.SystemProxyItem.SysProxyType == type) if (_config.SystemProxyItem.SysProxyType == type)
{ {
@ -393,7 +416,7 @@ public class StatusBarViewModel : MyReactiveObject
} }
} }
public async Task RefreshRoutingsMenu() private async Task RefreshRoutingsMenu()
{ {
RoutingItems.Clear(); RoutingItems.Clear();
@ -501,7 +524,7 @@ public class StatusBarViewModel : MyReactiveObject
#region UI #region UI
public async Task InboundDisplayStatus() private async Task InboundDisplayStatus()
{ {
StringBuilder sb = new(); StringBuilder sb = new();
sb.Append($"[{EInboundProtocol.mixed}:{AppManager.Instance.GetLocalPort(EInboundProtocol.socks)}"); sb.Append($"[{EInboundProtocol.mixed}:{AppManager.Instance.GetLocalPort(EInboundProtocol.socks)}");

View file

@ -16,9 +16,7 @@ public partial class App : Application
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
var ViewModel = new StatusBarViewModel(null); DataContext = StatusBarViewModel.Instance;
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(StatusBarViewModel));
DataContext = ViewModel;
} }
public override void OnFrameworkInitializationCompleted() public override void OnFrameworkInitializationCompleted()

View file

@ -3,7 +3,6 @@ using Avalonia.Input;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using DynamicData; using DynamicData;
using ReactiveUI; using ReactiveUI;
using Splat;
namespace v2rayN.Desktop.Views; namespace v2rayN.Desktop.Views;
@ -13,7 +12,6 @@ public partial class ClashProxiesView : ReactiveUserControl<ClashProxiesViewMode
{ {
InitializeComponent(); InitializeComponent();
ViewModel = new ClashProxiesViewModel(UpdateViewHandler); ViewModel = new ClashProxiesViewModel(UpdateViewHandler);
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(ClashProxiesViewModel));
lstProxyDetails.DoubleTapped += LstProxyDetails_DoubleTapped; lstProxyDetails.DoubleTapped += LstProxyDetails_DoubleTapped;
this.KeyDown += ClashProxiesView_KeyDown; this.KeyDown += ClashProxiesView_KeyDown;

View file

@ -20,7 +20,7 @@ namespace v2rayN.Desktop.Views;
public partial class MainWindow : WindowBase<MainWindowViewModel> public partial class MainWindow : WindowBase<MainWindowViewModel>
{ {
private static Config _config; private static Config _config;
private WindowNotificationManager? _manager; private readonly WindowNotificationManager? _manager;
private CheckUpdateView? _checkUpdateView; private CheckUpdateView? _checkUpdateView;
private BackupAndRestoreView? _backupAndRestoreView; private BackupAndRestoreView? _backupAndRestoreView;
private bool _blCloseByUser = false; private bool _blCloseByUser = false;
@ -259,7 +259,7 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
case EGlobalHotkey.SystemProxySet: case EGlobalHotkey.SystemProxySet:
case EGlobalHotkey.SystemProxyUnchanged: case EGlobalHotkey.SystemProxyUnchanged:
case EGlobalHotkey.SystemProxyPac: case EGlobalHotkey.SystemProxyPac:
Locator.Current.GetService<StatusBarViewModel>()?.SetListenerType((ESysProxyType)((int)e - 1)); AppEvents.SysProxyChangeRequested.OnNext((ESysProxyType)((int)e - 1));
break; break;
} }
} }

View file

@ -8,7 +8,6 @@ using Avalonia.Threading;
using DialogHostAvalonia; using DialogHostAvalonia;
using MsBox.Avalonia.Enums; using MsBox.Avalonia.Enums;
using ReactiveUI; using ReactiveUI;
using Splat;
using v2rayN.Desktop.Common; using v2rayN.Desktop.Common;
namespace v2rayN.Desktop.Views; namespace v2rayN.Desktop.Views;
@ -48,7 +47,6 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
//} //}
ViewModel = new ProfilesViewModel(UpdateViewHandler); ViewModel = new ProfilesViewModel(UpdateViewHandler);
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(ProfilesViewModel));
this.WhenActivated(disposables => this.WhenActivated(disposables =>
{ {

View file

@ -4,19 +4,25 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="480" xmlns:sys="clr-namespace:System;assembly=netstandard"
d:DesignWidth="400" d:DesignHeight="600"
d:DesignWidth="600"
mc:Ignorable="d"> mc:Ignorable="d">
<UserControl.Resources>
<sys:Double x:Key="QrcodeWidth">500</sys:Double>
</UserControl.Resources>
<Grid Margin="32" RowDefinitions="Auto,Auto"> <Grid Margin="32" RowDefinitions="Auto,Auto">
<Image <Image
Name="imgQrcode" Name="imgQrcode"
Width="300" Width="{StaticResource QrcodeWidth}"
Height="300" /> Height="{StaticResource QrcodeWidth}" />
<TextBox <TextBox
x:Name="txtContent" x:Name="txtContent"
Grid.Row="1" Grid.Row="1"
Width="300" Width="{StaticResource QrcodeWidth}"
MaxHeight="100" MaxHeight="100"
Margin="{StaticResource MarginTb8}" Margin="{StaticResource MarginTb8}"
VerticalAlignment="Center" VerticalAlignment="Center"

View file

@ -22,10 +22,18 @@ public partial class QrcodeView : UserControl
} }
private Bitmap? GetQRCode(string? url) private Bitmap? GetQRCode(string? url)
{
try
{ {
var bytes = QRCodeUtils.GenQRCode(url); var bytes = QRCodeUtils.GenQRCode(url);
return ByteToBitmap(bytes); return ByteToBitmap(bytes);
} }
catch (Exception ex)
{
Logging.SaveLog("GetQRCode", ex);
return null;
}
}
private Bitmap? ByteToBitmap(byte[]? bytes) private Bitmap? ByteToBitmap(byte[]? bytes)
{ {

View file

@ -6,7 +6,6 @@ using Avalonia.ReactiveUI;
using Avalonia.Threading; using Avalonia.Threading;
using DialogHostAvalonia; using DialogHostAvalonia;
using ReactiveUI; using ReactiveUI;
using Splat;
using v2rayN.Desktop.Common; using v2rayN.Desktop.Common;
namespace v2rayN.Desktop.Views; namespace v2rayN.Desktop.Views;
@ -20,9 +19,8 @@ public partial class StatusBarView : ReactiveUserControl<StatusBarViewModel>
InitializeComponent(); InitializeComponent();
_config = AppManager.Instance.Config; _config = AppManager.Instance.Config;
//ViewModel = new StatusBarViewModel(UpdateViewHandler);
//Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(StatusBarViewModel)); ViewModel = StatusBarViewModel.Instance;
ViewModel = Locator.Current.GetService<StatusBarViewModel>();
ViewModel?.InitUpdateView(UpdateViewHandler); ViewModel?.InitUpdateView(UpdateViewHandler);
txtRunningServerDisplay.Tapped += TxtRunningServerDisplay_Tapped; txtRunningServerDisplay.Tapped += TxtRunningServerDisplay_Tapped;

24
v2rayN/v2rayN.slnx Normal file
View file

@ -0,0 +1,24 @@
<Solution>
<Folder Name="/GitHub Action/">
<File Path="../.github/workflows/build-all.yml" />
<File Path="../.github/workflows/build-linux.yml" />
<File Path="../.github/workflows/build-osx.yml" />
<File Path="../.github/workflows/build-windows-desktop.yml" />
<File Path="../.github/workflows/build-windows.yml" />
<File Path="../.github/workflows/winget-publish.yml" />
<File Path="../package-appimage.sh" />
<File Path="../package-debian.sh" />
<File Path="../package-osx.sh" />
<File Path="../package-release-zip.sh" />
<File Path="../pkg2appimage.yml" />
</Folder>
<Folder Name="/Solution Files/">
<File Path="Directory.Build.props" />
<File Path="Directory.Packages.props" />
</Folder>
<Project Path="AmazTool/AmazTool.csproj" />
<Project Path="GlobalHotKeys/src/GlobalHotKeys/GlobalHotKeys.csproj" />
<Project Path="ServiceLib/ServiceLib.csproj" />
<Project Path="v2rayN.Desktop/v2rayN.Desktop.csproj" />
<Project Path="v2rayN/v2rayN.csproj" />
</Solution>

View file

@ -20,8 +20,9 @@ public class QRCodeUtils
var qrCodeImage = ServiceLib.Common.QRCodeUtils.GenQRCode(strContent); var qrCodeImage = ServiceLib.Common.QRCodeUtils.GenQRCode(strContent);
return qrCodeImage is null ? null : ByteToImage(qrCodeImage); return qrCodeImage is null ? null : ByteToImage(qrCodeImage);
} }
catch catch (Exception ex)
{ {
Logging.SaveLog("GetQRCode", ex);
return null; return null;
} }
} }
@ -32,8 +33,8 @@ public class QRCodeUtils
{ {
GetDpi(window, out var dpiX, out var dpiY); GetDpi(window, out var dpiX, out var dpiY);
var left = (int)(SystemParameters.WorkArea.Left); var left = (int)SystemParameters.WorkArea.Left;
var top = (int)(SystemParameters.WorkArea.Top); var top = (int)SystemParameters.WorkArea.Top;
var width = (int)(SystemParameters.WorkArea.Width / dpiX); var width = (int)(SystemParameters.WorkArea.Width / dpiX);
var height = (int)(SystemParameters.WorkArea.Height / dpiY); var height = (int)(SystemParameters.WorkArea.Height / dpiY);

View file

@ -1,7 +1,6 @@
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Windows.Input; using System.Windows.Input;
using ReactiveUI; using ReactiveUI;
using Splat;
namespace v2rayN.Views; namespace v2rayN.Views;
@ -14,7 +13,6 @@ public partial class ClashProxiesView
{ {
InitializeComponent(); InitializeComponent();
ViewModel = new ClashProxiesViewModel(UpdateViewHandler); ViewModel = new ClashProxiesViewModel(UpdateViewHandler);
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(ClashProxiesViewModel));
lstProxyDetails.PreviewMouseDoubleClick += lstProxyDetails_PreviewMouseDoubleClick; lstProxyDetails.PreviewMouseDoubleClick += lstProxyDetails_PreviewMouseDoubleClick;
this.WhenActivated(disposables => this.WhenActivated(disposables =>

View file

@ -249,7 +249,7 @@ public partial class MainWindow
case EGlobalHotkey.SystemProxySet: case EGlobalHotkey.SystemProxySet:
case EGlobalHotkey.SystemProxyUnchanged: case EGlobalHotkey.SystemProxyUnchanged:
case EGlobalHotkey.SystemProxyPac: case EGlobalHotkey.SystemProxyPac:
Locator.Current.GetService<StatusBarViewModel>()?.SetListenerType((ESysProxyType)((int)e - 1)); AppEvents.SysProxyChangeRequested.OnNext((ESysProxyType)((int)e - 1));
break; break;
} }
} }
@ -287,11 +287,7 @@ public partial class MainWindow
var clipboardData = WindowsUtils.GetClipboardData(); var clipboardData = WindowsUtils.GetClipboardData();
if (clipboardData.IsNotEmpty()) if (clipboardData.IsNotEmpty())
{ {
var service = Locator.Current.GetService<MainWindowViewModel>(); ViewModel?.AddServerViaClipboardAsync(clipboardData);
if (service != null)
{
_ = service.AddServerViaClipboardAsync(clipboardData);
}
} }
break; break;

View file

@ -8,7 +8,6 @@ using System.Windows.Media;
using System.Windows.Threading; using System.Windows.Threading;
using MaterialDesignThemes.Wpf; using MaterialDesignThemes.Wpf;
using ReactiveUI; using ReactiveUI;
using Splat;
using v2rayN.Base; using v2rayN.Base;
using Point = System.Windows.Point; using Point = System.Windows.Point;
@ -42,7 +41,6 @@ public partial class ProfilesView
} }
ViewModel = new ProfilesViewModel(UpdateViewHandler); ViewModel = new ProfilesViewModel(UpdateViewHandler);
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(ProfilesViewModel));
this.WhenActivated(disposables => this.WhenActivated(disposables =>
{ {

View file

@ -1,4 +1,4 @@
<UserControl <UserControl
x:Class="v2rayN.Views.QrcodeView" x:Class="v2rayN.Views.QrcodeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@ -6,28 +6,33 @@
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib" xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
d:DesignHeight="300" xmlns:sys="clr-namespace:System;assembly=mscorlib"
d:DesignWidth="300" d:DesignHeight="600"
d:DesignWidth="600"
Style="{StaticResource ViewGlobal}" Style="{StaticResource ViewGlobal}"
mc:Ignorable="d"> mc:Ignorable="d">
<UserControl.Resources>
<sys:Double x:Key="QrcodeWidth">500</sys:Double>
</UserControl.Resources>
<Grid Margin="32"> <Grid Margin="32">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="60" /> <RowDefinition Height="80" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Image <Image
x:Name="imgQrcode" x:Name="imgQrcode"
Grid.Row="0" Grid.Row="0"
Width="300" Width="{StaticResource QrcodeWidth}"
Height="300" Height="{StaticResource QrcodeWidth}"
Stretch="UniformToFill" /> Stretch="UniformToFill" />
<TextBox <TextBox
x:Name="txtContent" x:Name="txtContent"
Grid.Row="1" Grid.Row="1"
Width="300" Width="{StaticResource QrcodeWidth}"
Margin="0,8" Margin="0,8"
VerticalAlignment="Center" VerticalAlignment="Center"
IsReadOnly="True" IsReadOnly="True"

View file

@ -3,7 +3,6 @@ using System.Windows;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Threading; using System.Windows.Threading;
using ReactiveUI; using ReactiveUI;
using Splat;
using v2rayN.Manager; using v2rayN.Manager;
namespace v2rayN.Views; namespace v2rayN.Views;
@ -16,8 +15,8 @@ public partial class StatusBarView
{ {
InitializeComponent(); InitializeComponent();
_config = AppManager.Instance.Config; _config = AppManager.Instance.Config;
ViewModel = new StatusBarViewModel(UpdateViewHandler); ViewModel = StatusBarViewModel.Instance;
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(StatusBarViewModel)); ViewModel?.InitUpdateView(UpdateViewHandler);
menuExit.Click += menuExit_Click; menuExit.Click += menuExit_Click;
txtRunningServerDisplay.PreviewMouseDown += txtRunningInfoDisplay_MouseDoubleClick; txtRunningServerDisplay.PreviewMouseDown += txtRunningInfoDisplay_MouseDoubleClick;