mirror of
https://github.com/2dust/v2rayN.git
synced 2026-02-18 08:13:02 +00:00
Group preview (#8760)
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
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
* Group Preview * Fix
This commit is contained in:
parent
54608ab2b9
commit
0f3a3eac02
14 changed files with 213 additions and 21 deletions
|
|
@ -75,7 +75,7 @@ public class GroupProfileManager
|
|||
return (await GetChildProfileItemsByProtocolExtra(protocolExtra), protocolExtra);
|
||||
}
|
||||
|
||||
private static async Task<List<ProfileItem>> GetChildProfileItemsByProtocolExtra(ProtocolExtraItem? protocolExtra)
|
||||
public static async Task<List<ProfileItem>> GetChildProfileItemsByProtocolExtra(ProtocolExtraItem? protocolExtra)
|
||||
{
|
||||
if (protocolExtra == null)
|
||||
{
|
||||
|
|
|
|||
9
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
9
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
|
|
@ -1680,6 +1680,15 @@ namespace ServiceLib.Resx {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Configuration item preview 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuServerListPreview {
|
||||
get {
|
||||
return ResourceManager.GetString("menuServerListPreview", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Configuration 的本地化字符串。
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -1668,4 +1668,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
|||
<data name="TbHopInt7" xml:space="preserve">
|
||||
<value>Port hopping interval</value>
|
||||
</data>
|
||||
<data name="menuServerListPreview" xml:space="preserve">
|
||||
<value>Configuration item preview</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -1665,4 +1665,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
|||
<data name="TbHopInt7" xml:space="preserve">
|
||||
<value>Port hopping interval</value>
|
||||
</data>
|
||||
<data name="menuServerListPreview" xml:space="preserve">
|
||||
<value>Configuration item preview</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -1668,4 +1668,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
|||
<data name="TbHopInt7" xml:space="preserve">
|
||||
<value>Port hopping interval</value>
|
||||
</data>
|
||||
<data name="menuServerListPreview" xml:space="preserve">
|
||||
<value>Configuration item preview</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -1668,4 +1668,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
|||
<data name="TbHopInt7" xml:space="preserve">
|
||||
<value>Port hopping interval</value>
|
||||
</data>
|
||||
<data name="menuServerListPreview" xml:space="preserve">
|
||||
<value>Configuration item preview</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -1668,4 +1668,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
|||
<data name="TbHopInt7" xml:space="preserve">
|
||||
<value>Port hopping interval</value>
|
||||
</data>
|
||||
<data name="menuServerListPreview" xml:space="preserve">
|
||||
<value>Configuration item preview</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -1665,4 +1665,7 @@
|
|||
<data name="TbHopInt7" xml:space="preserve">
|
||||
<value>端口跳跃间隔</value>
|
||||
</data>
|
||||
<data name="menuServerListPreview" xml:space="preserve">
|
||||
<value>子配置项预览</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -1665,4 +1665,7 @@
|
|||
<data name="TbHopInt7" xml:space="preserve">
|
||||
<value>Port hopping interval</value>
|
||||
</data>
|
||||
<data name="menuServerListPreview" xml:space="preserve">
|
||||
<value>Configuration item preview</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -27,6 +27,8 @@ public class AddGroupServerViewModel : MyReactiveObject
|
|||
|
||||
public IObservableCollection<ProfileItem> ChildItemsObs { get; } = new ObservableCollectionExtended<ProfileItem>();
|
||||
|
||||
public IObservableCollection<ProfileItem> AllProfilePreviewItemsObs { get; } = new ObservableCollectionExtended<ProfileItem>();
|
||||
|
||||
//public ReactiveCommand<Unit, Unit> AddCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> RemoveCmd { get; }
|
||||
|
||||
|
|
@ -182,6 +184,32 @@ public class AddGroupServerViewModel : MyReactiveObject
|
|||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private ProtocolExtraItem GetUpdatedProtocolExtra()
|
||||
{
|
||||
return SelectedSource.GetProtocolExtra() with
|
||||
{
|
||||
ChildItems =
|
||||
Utils.List2String(ChildItemsObs.Where(s => !s.IndexId.IsNullOrEmpty()).Select(s => s.IndexId).ToList()),
|
||||
MultipleLoad = PolicyGroupType switch
|
||||
{
|
||||
var s when s == ResUI.TbLeastPing => EMultipleLoad.LeastPing,
|
||||
var s when s == ResUI.TbFallback => EMultipleLoad.Fallback,
|
||||
var s when s == ResUI.TbRandom => EMultipleLoad.Random,
|
||||
var s when s == ResUI.TbRoundRobin => EMultipleLoad.RoundRobin,
|
||||
var s when s == ResUI.TbLeastLoad => EMultipleLoad.LeastLoad,
|
||||
_ => EMultipleLoad.LeastPing,
|
||||
},
|
||||
SubChildItems = SelectedSubItem?.Id,
|
||||
Filter = Filter,
|
||||
};
|
||||
}
|
||||
|
||||
public async Task UpdatePreviewList()
|
||||
{
|
||||
AllProfilePreviewItemsObs.Clear();
|
||||
AllProfilePreviewItemsObs.AddRange(await GroupProfileManager.GetChildProfileItemsByProtocolExtra(GetUpdatedProtocolExtra()));
|
||||
}
|
||||
|
||||
private async Task SaveServerAsync()
|
||||
{
|
||||
var remarks = SelectedSource.Remarks;
|
||||
|
|
@ -202,24 +230,11 @@ public class AddGroupServerViewModel : MyReactiveObject
|
|||
return;
|
||||
}
|
||||
|
||||
SelectedSource.SetProtocolExtra(SelectedSource.GetProtocolExtra() with
|
||||
{
|
||||
ChildItems =
|
||||
Utils.List2String(ChildItemsObs.Where(s => !s.IndexId.IsNullOrEmpty()).Select(s => s.IndexId).ToList()),
|
||||
MultipleLoad = PolicyGroupType switch
|
||||
{
|
||||
var s when s == ResUI.TbLeastPing => EMultipleLoad.LeastPing,
|
||||
var s when s == ResUI.TbFallback => EMultipleLoad.Fallback,
|
||||
var s when s == ResUI.TbRandom => EMultipleLoad.Random,
|
||||
var s when s == ResUI.TbRoundRobin => EMultipleLoad.RoundRobin,
|
||||
var s when s == ResUI.TbLeastLoad => EMultipleLoad.LeastLoad,
|
||||
_ => EMultipleLoad.LeastPing,
|
||||
},
|
||||
SubChildItems = SelectedSubItem?.Id,
|
||||
Filter = Filter,
|
||||
});
|
||||
var protocolExtra = GetUpdatedProtocolExtra();
|
||||
|
||||
var hasCycle = await GroupProfileManager.HasCycle(SelectedSource.IndexId, SelectedSource.GetProtocolExtra());
|
||||
SelectedSource.SetProtocolExtra(protocolExtra);
|
||||
|
||||
var hasCycle = await GroupProfileManager.HasCycle(SelectedSource.IndexId, protocolExtra);
|
||||
if (hasCycle)
|
||||
{
|
||||
NoticeManager.Instance.Enqueue(string.Format(ResUI.GroupSelfReference, remarks));
|
||||
|
|
|
|||
|
|
@ -88,7 +88,10 @@
|
|||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<TabControl HorizontalContentAlignment="Stretch" DockPanel.Dock="Top">
|
||||
<TabControl
|
||||
x:Name="tabControl"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
DockPanel.Dock="Top">
|
||||
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.menuServerList}">
|
||||
<Grid
|
||||
Margin="{StaticResource Margin8}"
|
||||
|
|
@ -134,7 +137,6 @@
|
|||
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.menuServerList2}">
|
||||
<DataGrid
|
||||
x:Name="lstChild"
|
||||
Grid.Row="1"
|
||||
AutoGenerateColumns="False"
|
||||
Background="Transparent"
|
||||
BorderThickness="1"
|
||||
|
|
@ -204,6 +206,48 @@
|
|||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</TabItem>
|
||||
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.menuServerListPreview}">
|
||||
<DataGrid
|
||||
x:Name="lstPreviewChild"
|
||||
AutoGenerateColumns="False"
|
||||
Background="Transparent"
|
||||
BorderThickness="1"
|
||||
CanUserReorderColumns="False"
|
||||
CanUserResizeColumns="True"
|
||||
CanUserSortColumns="False"
|
||||
GridLinesVisibility="All"
|
||||
HeadersVisibility="Column"
|
||||
IsReadOnly="True"
|
||||
ItemsSource="{Binding AllProfilePreviewItemsObs}"
|
||||
SelectionMode="Extended">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn
|
||||
Width="150"
|
||||
Binding="{Binding ConfigType}"
|
||||
Header="{x:Static resx:ResUI.LvServiceType}" />
|
||||
<DataGridTextColumn
|
||||
Width="150"
|
||||
Binding="{Binding Remarks}"
|
||||
Header="{x:Static resx:ResUI.LvRemarks}" />
|
||||
<DataGridTextColumn
|
||||
Width="120"
|
||||
Binding="{Binding Address}"
|
||||
Header="{x:Static resx:ResUI.LvAddress}" />
|
||||
<DataGridTextColumn
|
||||
Width="100"
|
||||
Binding="{Binding Port}"
|
||||
Header="{x:Static resx:ResUI.LvPort}" />
|
||||
<DataGridTextColumn
|
||||
Width="100"
|
||||
Binding="{Binding Network}"
|
||||
Header="{x:Static resx:ResUI.LvTransportProtocol}" />
|
||||
<DataGridTextColumn
|
||||
Width="100"
|
||||
Binding="{Binding StreamSecurity}"
|
||||
Header="{x:Static resx:ResUI.LvTLS}" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</DockPanel>
|
||||
</Window>
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
|
|||
Loaded += Window_Loaded;
|
||||
btnCancel.Click += (s, e) => Close();
|
||||
lstChild.SelectionChanged += LstChild_SelectionChanged;
|
||||
tabControl.SelectionChanged += TabControl_SelectionChanged;
|
||||
|
||||
ViewModel = new AddGroupServerViewModel(profileItem, UpdateViewHandler);
|
||||
|
||||
|
|
@ -38,6 +39,10 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
|
|||
case EConfigType.ProxyChain:
|
||||
Title = ResUI.TbConfigTypeProxyChain;
|
||||
gridPolicyGroup.IsVisible = false;
|
||||
if (tabControl.Items.Count > 0)
|
||||
{
|
||||
tabControl.Items.RemoveAt(0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -50,7 +55,6 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
|
|||
this.Bind(ViewModel, vm => vm.SelectedSubItem, v => v.cmbSubChildItems.SelectedItem).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.Filter, v => v.txtFilter.Text).DisposeWith(disposables);
|
||||
|
||||
this.OneWayBind(ViewModel, vm => vm.ChildItemsObs, v => v.lstChild.ItemsSource).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedChild, v => v.lstChild.SelectedItem).DisposeWith(disposables);
|
||||
|
||||
this.BindCommand(ViewModel, vm => vm.RemoveCmd, v => v.menuRemoveChildServer).DisposeWith(disposables);
|
||||
|
|
@ -167,4 +171,29 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
|
|||
ViewModel.SelectedChildren = lstChild.SelectedItems.Cast<ProfileItem>().ToList();
|
||||
}
|
||||
}
|
||||
|
||||
private async void TabControl_SelectionChanged(object? sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (e.Source is not TabControl tc)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!(tc.SelectedIndex == tc.Items.Count - 1 && tc.Items.Count > 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (ViewModel == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await ViewModel.UpdatePreviewList();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -135,6 +135,7 @@
|
|||
</Grid>
|
||||
|
||||
<TabControl
|
||||
x:Name="tabControl"
|
||||
Margin="{StaticResource Margin8}"
|
||||
HorizontalContentAlignment="Left"
|
||||
DockPanel.Dock="Top">
|
||||
|
|
@ -272,6 +273,47 @@
|
|||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</TabItem>
|
||||
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.menuServerListPreview}">
|
||||
<DataGrid
|
||||
x:Name="lstPreviewChild"
|
||||
AutoGenerateColumns="False"
|
||||
BorderThickness="1"
|
||||
CanUserAddRows="False"
|
||||
CanUserResizeRows="False"
|
||||
CanUserSortColumns="False"
|
||||
EnableRowVirtualization="True"
|
||||
GridLinesVisibility="All"
|
||||
HeadersVisibility="Column"
|
||||
IsReadOnly="True"
|
||||
Style="{StaticResource DefDataGrid}">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn
|
||||
Width="150"
|
||||
Binding="{Binding ConfigType}"
|
||||
Header="{x:Static resx:ResUI.LvServiceType}" />
|
||||
<DataGridTextColumn
|
||||
Width="150"
|
||||
Binding="{Binding Remarks}"
|
||||
Header="{x:Static resx:ResUI.LvRemarks}" />
|
||||
<DataGridTextColumn
|
||||
Width="120"
|
||||
Binding="{Binding Address}"
|
||||
Header="{x:Static resx:ResUI.LvAddress}" />
|
||||
<DataGridTextColumn
|
||||
Width="100"
|
||||
Binding="{Binding Port}"
|
||||
Header="{x:Static resx:ResUI.LvPort}" />
|
||||
<DataGridTextColumn
|
||||
Width="100"
|
||||
Binding="{Binding Network}"
|
||||
Header="{x:Static resx:ResUI.LvTransportProtocol}" />
|
||||
<DataGridTextColumn
|
||||
Width="100"
|
||||
Binding="{Binding StreamSecurity}"
|
||||
Header="{x:Static resx:ResUI.LvTLS}" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</DockPanel>
|
||||
</base:WindowBase>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ public partial class AddGroupServerWindow
|
|||
PreviewKeyDown += AddGroupServerWindow_PreviewKeyDown;
|
||||
lstChild.SelectionChanged += LstChild_SelectionChanged;
|
||||
menuSelectAllChild.Click += MenuSelectAllChild_Click;
|
||||
tabControl.SelectionChanged += TabControl_SelectionChanged;
|
||||
|
||||
ViewModel = new AddGroupServerViewModel(profileItem, UpdateViewHandler);
|
||||
|
||||
|
|
@ -33,6 +34,10 @@ public partial class AddGroupServerWindow
|
|||
case EConfigType.ProxyChain:
|
||||
Title = ResUI.TbConfigTypeProxyChain;
|
||||
gridPolicyGroup.Visibility = Visibility.Collapsed;
|
||||
if (tabControl.Items.Count > 0)
|
||||
{
|
||||
tabControl.Items.RemoveAt(0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -48,6 +53,8 @@ public partial class AddGroupServerWindow
|
|||
this.OneWayBind(ViewModel, vm => vm.ChildItemsObs, v => v.lstChild.ItemsSource).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedChild, v => v.lstChild.SelectedItem).DisposeWith(disposables);
|
||||
|
||||
this.OneWayBind(ViewModel, vm => vm.AllProfilePreviewItemsObs, v => v.lstPreviewChild.ItemsSource).DisposeWith(disposables);
|
||||
|
||||
this.BindCommand(ViewModel, vm => vm.RemoveCmd, v => v.menuRemoveChildServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.MoveTopCmd, v => v.menuMoveTop).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.MoveUpCmd, v => v.menuMoveUp).DisposeWith(disposables);
|
||||
|
|
@ -148,4 +155,29 @@ public partial class AddGroupServerWindow
|
|||
{
|
||||
lstChild.SelectAll();
|
||||
}
|
||||
|
||||
private async void TabControl_SelectionChanged(object? sender, System.Windows.Controls.SelectionChangedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (e.Source is not System.Windows.Controls.TabControl tc)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!(tc.SelectedIndex == tc.Items.Count - 1 && tc.Items.Count > 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (ViewModel == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await ViewModel.UpdatePreviewList();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue