Skip to content

Commit f8f65d5

Browse files
authored
Feature: Profile tags & filter (#3144)
* Feature: Update button design * Feature: Profile tags & filter * Fix: #3145 * Docs: #3145 * Feature: Filter * Feature: Example filter for ping monitor * Chore: Add translation * Feature: Profile tags * Feature: Profile filter * Feature: Profile filter * Feature: Profile filter * Feature: Profile filter * Docs: #3144 * Feature: Profile filter * Chore: Cleanup * Update PowerShellHostViewModel.cs * Feature: Profile filter * Feature: Adjust clear filter button * Chore: Cleanup old dialogs * Feature: Add clear filter action * Chore: Redesign view * Chore: Refactoring * Chore: Cleanup * Feature: Search * Feature: Case insensitive search * Feature: Search / reset filter
1 parent 0fb346e commit f8f65d5

File tree

59 files changed

+5749
-1065
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+5749
-1065
lines changed

Source/NETworkManager.Localization/Resources/Strings.Designer.cs

Lines changed: 81 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Source/NETworkManager.Localization/Resources/Strings.resx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3960,4 +3960,31 @@ Right-click for more options.</value>
39603960
<data name="BrowsingDataErrorMessage" xml:space="preserve">
39613961
<value>An error occurred while deleting browsing data. Feel free to report this issue on GitHub.</value>
39623962
</data>
3963+
<data name="ExampleTag" xml:space="preserve">
3964+
<value>production</value>
3965+
</data>
3966+
<data name="AddTag" xml:space="preserve">
3967+
<value>Add tag</value>
3968+
</data>
3969+
<data name="ApplyFilter" xml:space="preserve">
3970+
<value>Apply filter</value>
3971+
</data>
3972+
<data name="FilterProfilesDots" xml:space="preserve">
3973+
<value>Filter profiles...</value>
3974+
</data>
3975+
<data name="FilterByTags" xml:space="preserve">
3976+
<value>Filter by tags</value>
3977+
</data>
3978+
<data name="NoTagsFound" xml:space="preserve">
3979+
<value>No tags found!</value>
3980+
</data>
3981+
<data name="Match" xml:space="preserve">
3982+
<value>Match</value>
3983+
</data>
3984+
<data name="Any" xml:space="preserve">
3985+
<value>Any</value>
3986+
</data>
3987+
<data name="AddGroupDots" xml:space="preserve">
3988+
<value>Add group...</value>
3989+
</data>
39633990
</root>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using System.Collections.Generic;
2+
3+
namespace NETworkManager.Profiles;
4+
5+
/// <summary>
6+
/// Class used to filter profiles in the UI.
7+
/// </summary>
8+
public class ProfileFilterInfo
9+
{
10+
/// <summary>
11+
/// Search string for filtering profiles.
12+
/// </summary>
13+
public string Search { get; set; } = string.Empty;
14+
15+
/// <summary>
16+
/// Tags for filtering profiles.
17+
/// </summary>
18+
public IEnumerable<string> Tags { get; set; } = [];
19+
20+
/// <summary>
21+
/// Indicates whether to match any or all tags in the filter.
22+
/// </summary>
23+
public ProfileFilterTagsMatch TagsFilterMatch { get; set; } = ProfileFilterTagsMatch.Any;
24+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using NETworkManager.Utilities;
2+
3+
namespace NETworkManager.Profiles;
4+
5+
/// <summary>
6+
/// Class used to represent a tag filter in the UI (Popup).
7+
/// </summary>
8+
public class ProfileFilterTagsInfo : PropertyChangedBase
9+
{
10+
/// <summary>
11+
/// Private field for <see cref="IsSelected"/>.
12+
/// </summary>
13+
private bool _isSelected;
14+
15+
/// <summary>
16+
/// Indicates whether the tag is selected for filtering.
17+
/// </summary>
18+
public bool IsSelected
19+
{
20+
get => _isSelected;
21+
set
22+
{
23+
if (_isSelected == value)
24+
return;
25+
26+
_isSelected = value;
27+
OnPropertyChanged();
28+
}
29+
}
30+
31+
/// <summary>
32+
/// Tag name.
33+
/// </summary>
34+
public string Name { get; set; }
35+
36+
/// <summary>
37+
/// Creates a new instance of <see cref="ProfileFilterTagsInfo"/> with parameters.
38+
/// </summary>
39+
/// <param name="isSelected">Indicates whether the tag is selected for filtering.</param>
40+
/// <param name="name">Tag name.</param>
41+
public ProfileFilterTagsInfo(bool isSelected, string name)
42+
{
43+
IsSelected = isSelected;
44+
Name = name;
45+
}
46+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace NETworkManager.Profiles;
2+
3+
/// <summary>
4+
/// Enumeration for tag filtering options.
5+
/// </summary>
6+
public enum ProfileFilterTagsMatch
7+
{
8+
/// <summary>
9+
/// Any of the tags can match.
10+
/// </summary>
11+
Any,
12+
13+
/// <summary>
14+
/// All of the tags must match.
15+
/// </summary>
16+
All
17+
}

Source/NETworkManager.Profiles/ProfileInfo.cs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
using System.Security;
2-
using System.Xml.Serialization;
1+
using NETworkManager.Controls;
32
using NETworkManager.Models.Network;
43
using NETworkManager.Models.PowerShell;
54
using NETworkManager.Models.PuTTY;
65
using NETworkManager.Models.RemoteDesktop;
76
using NETworkManager.Settings;
7+
using System.Security;
8+
using System.Xml.Serialization;
89

910
// ReSharper disable InconsistentNaming
1011
// ReSharper disable PropertyCanBeMadeInitOnly.Global
@@ -33,7 +34,7 @@ public ProfileInfo(ProfileInfo profile)
3334
Host = profile.Host;
3435
Description = profile.Description;
3536
Group = profile.Group;
36-
Tags = profile.Tags;
37+
TagsCollection = profile.TagsCollection;
3738

3839
IsDynamic = profile.IsDynamic;
3940

@@ -42,7 +43,6 @@ public ProfileInfo(ProfileInfo profile)
4243
NetworkInterface_EnableStaticIPAddress = profile.NetworkInterface_EnableStaticIPAddress;
4344
NetworkInterface_IPAddress = profile.NetworkInterface_IPAddress;
4445
NetworkInterface_Subnetmask = profile.NetworkInterface_Subnetmask;
45-
NetworkInterface_SubnetmaskOrCidr = profile.NetworkInterface_SubnetmaskOrCidr;
4646
NetworkInterface_Gateway = profile.NetworkInterface_Gateway;
4747
NetworkInterface_EnableStaticDNS = profile.NetworkInterface_EnableStaticDNS;
4848
NetworkInterface_PrimaryDNSServer = profile.NetworkInterface_PrimaryDNSServer;
@@ -253,17 +253,23 @@ public ProfileInfo(ProfileInfo profile)
253253
/// Description of the profile.
254254
/// </summary>
255255
public string Description { get; set; }
256-
256+
257257
/// <summary>
258258
/// Name of the group. Profiles are grouped based on the name.
259259
/// </summary>
260260
public string Group { get; set; }
261-
261+
262262
/// <summary>
263263
/// Tags to classify the profiles and to filter by it.
264264
/// </summary>
265+
//[Obsolete("Replaced with TagsList")]
265266
public string Tags { get; set; }
266267

268+
/// <summary>
269+
/// Tags collection to classify the profiles and to filter by it.
270+
/// </summary>
271+
public ObservableSetCollection<string> TagsCollection { get; set; } = [];
272+
267273
/// <summary>
268274
/// Dynamic profiles (e.g. AWS) are not save to file.
269275
/// </summary>
@@ -273,9 +279,6 @@ public ProfileInfo(ProfileInfo profile)
273279
public bool NetworkInterface_Enabled { get; set; }
274280
public bool NetworkInterface_EnableStaticIPAddress { get; set; }
275281
public string NetworkInterface_IPAddress { get; set; }
276-
277-
//[Obsolete("Replaced by NetworkInterface_Subnetmask")]
278-
public string NetworkInterface_SubnetmaskOrCidr { get; set; }
279282
public string NetworkInterface_Subnetmask { get; set; }
280283
public string NetworkInterface_Gateway { get; set; }
281284
public bool NetworkInterface_EnableStaticDNS { get; set; }

Source/NETworkManager.Profiles/ProfileManager.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ static ProfileManager()
5050
/// <summary>
5151
/// ObservableCollection of all profile files.
5252
/// </summary>
53-
public static ObservableCollection<ProfileFileInfo> ProfileFiles { get; set; } = new();
53+
public static ObservableCollection<ProfileFileInfo> ProfileFiles { get; set; } = [];
5454

5555
/// <summary>
5656
/// Currently loaded profile file.
@@ -75,7 +75,7 @@ private set
7575
/// <summary>
7676
/// Currently loaded groups with profiles.
7777
/// </summary>
78-
public static List<GroupInfo> Groups { get; set; } = new();
78+
public static List<GroupInfo> Groups { get; set; } = [];
7979

8080
/// <summary>
8181
/// Indicates if profiles have changed.
@@ -657,12 +657,12 @@ private static List<GroupInfo> DeserializeGroup(Stream stream)
657657
return (from groupSerializable in ((List<GroupInfoSerializable>)xmlSerializer.Deserialize(stream))!
658658
let profiles = groupSerializable.Profiles.Select(profileSerializable => new ProfileInfo(profileSerializable)
659659
{
660-
// Migrate old data
661-
NetworkInterface_Subnetmask =
662-
string.IsNullOrEmpty(profileSerializable.NetworkInterface_Subnetmask) &&
663-
!string.IsNullOrEmpty(profileSerializable.NetworkInterface_SubnetmaskOrCidr)
664-
? profileSerializable.NetworkInterface_SubnetmaskOrCidr
665-
: profileSerializable.NetworkInterface_Subnetmask,
660+
// Migrate old tags to new tags list
661+
// if TagsList is null or empty and Tags is not null or empty, split Tags by ';' and create a new ObservableSetCollection
662+
// else use the existing TagsList
663+
TagsCollection = (profileSerializable.TagsCollection == null || profileSerializable.TagsCollection.Count == 0) && !string.IsNullOrEmpty(profileSerializable.Tags) ?
664+
new Controls.ObservableSetCollection<string>(profileSerializable.Tags.Split([';'], StringSplitOptions.RemoveEmptyEntries)) :
665+
profileSerializable.TagsCollection,
666666

667667
// Convert passwords to secure strings
668668
RemoteDesktop_Password = !string.IsNullOrEmpty(profileSerializable.RemoteDesktop_Password)

0 commit comments

Comments
 (0)