Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
74c896c
Feature: Update button design
BornToBeRoot Aug 31, 2025
86f0c23
Feature: Profile tags & filter
BornToBeRoot Aug 31, 2025
95d024c
Fix: #3145
BornToBeRoot Aug 31, 2025
39dea91
Docs: #3145
BornToBeRoot Aug 31, 2025
cdb51cc
Feature: Filter
BornToBeRoot Aug 31, 2025
1d32c22
Feature: Example filter for ping monitor
BornToBeRoot Sep 1, 2025
7cb5912
Chore: Add translation
BornToBeRoot Sep 1, 2025
a3fb1bc
Merge branch 'main' into feature/tags
BornToBeRoot Sep 7, 2025
bc90eee
Feature: Profile tags
BornToBeRoot Sep 7, 2025
4e14f47
Feature: Profile filter
BornToBeRoot Sep 7, 2025
db8405e
Feature: Profile filter
BornToBeRoot Sep 7, 2025
9b2bb36
Feature: Profile filter
BornToBeRoot Sep 7, 2025
a1abbbc
Feature: Profile filter
BornToBeRoot Sep 7, 2025
5a0b11d
Docs: #3144
BornToBeRoot Sep 7, 2025
53aa01f
Feature: Profile filter
BornToBeRoot Sep 7, 2025
b99ae57
Chore: Cleanup
BornToBeRoot Sep 7, 2025
f0e6884
Update PowerShellHostViewModel.cs
BornToBeRoot Sep 12, 2025
a0b572b
Feature: Profile filter
BornToBeRoot Sep 13, 2025
d81c6b0
Merge branch 'main' into feature/tags
BornToBeRoot Sep 13, 2025
ec9569f
Feature: Adjust clear filter button
BornToBeRoot Sep 13, 2025
6dbb0b1
Chore: Cleanup old dialogs
BornToBeRoot Sep 13, 2025
8c2c4d9
Feature: Add clear filter action
BornToBeRoot Sep 13, 2025
19b4a45
Merge branch 'main' into feature/tags
BornToBeRoot Sep 17, 2025
bbc34a9
Chore: Redesign view
BornToBeRoot Sep 18, 2025
b3775fc
Chore: Refactoring
BornToBeRoot Sep 18, 2025
56ce6be
Chore: Cleanup
BornToBeRoot Sep 18, 2025
4d48b54
Feature: Search
BornToBeRoot Sep 18, 2025
4f8b96e
Feature: Case insensitive search
BornToBeRoot Sep 18, 2025
55ef448
Feature: Search / reset filter
BornToBeRoot Sep 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions Source/NETworkManager.Localization/Resources/Strings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 27 additions & 0 deletions Source/NETworkManager.Localization/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -3960,4 +3960,31 @@ Right-click for more options.</value>
<data name="BrowsingDataErrorMessage" xml:space="preserve">
<value>An error occurred while deleting browsing data. Feel free to report this issue on GitHub.</value>
</data>
<data name="ExampleTag" xml:space="preserve">
<value>production</value>
</data>
<data name="AddTag" xml:space="preserve">
<value>Add tag</value>
</data>
<data name="ApplyFilter" xml:space="preserve">
<value>Apply filter</value>
</data>
<data name="FilterProfilesDots" xml:space="preserve">
<value>Filter profiles...</value>
</data>
<data name="FilterByTags" xml:space="preserve">
<value>Filter by tags</value>
</data>
<data name="NoTagsFound" xml:space="preserve">
<value>No tags found!</value>
</data>
<data name="Match" xml:space="preserve">
<value>Match</value>
</data>
<data name="Any" xml:space="preserve">
<value>Any</value>
</data>
<data name="AddGroupDots" xml:space="preserve">
<value>Add group...</value>
</data>
</root>
24 changes: 24 additions & 0 deletions Source/NETworkManager.Profiles/ProfileFilterInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System.Collections.Generic;

namespace NETworkManager.Profiles;

/// <summary>
/// Class used to filter profiles in the UI.
/// </summary>
public class ProfileFilterInfo
{
/// <summary>
/// Search string for filtering profiles.
/// </summary>
public string Search { get; set; } = string.Empty;

/// <summary>
/// Tags for filtering profiles.
/// </summary>
public IEnumerable<string> Tags { get; set; } = [];

/// <summary>
/// Indicates whether to match any or all tags in the filter.
/// </summary>
public ProfileFilterTagsMatch TagsFilterMatch { get; set; } = ProfileFilterTagsMatch.Any;
}
46 changes: 46 additions & 0 deletions Source/NETworkManager.Profiles/ProfileFilterTagsInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using NETworkManager.Utilities;

namespace NETworkManager.Profiles;

/// <summary>
/// Class used to represent a tag filter in the UI (Popup).
/// </summary>
public class ProfileFilterTagsInfo : PropertyChangedBase
{
/// <summary>
/// Private field for <see cref="IsSelected"/>.
/// </summary>
private bool _isSelected;

/// <summary>
/// Indicates whether the tag is selected for filtering.
/// </summary>
public bool IsSelected
{
get => _isSelected;
set
{
if (_isSelected == value)
return;

_isSelected = value;
OnPropertyChanged();
}
}

/// <summary>
/// Tag name.
/// </summary>
public string Name { get; set; }

/// <summary>
/// Creates a new instance of <see cref="ProfileFilterTagsInfo"/> with parameters.
/// </summary>
/// <param name="isSelected">Indicates whether the tag is selected for filtering.</param>
/// <param name="name">Tag name.</param>
public ProfileFilterTagsInfo(bool isSelected, string name)
{
IsSelected = isSelected;
Name = name;
}
}
17 changes: 17 additions & 0 deletions Source/NETworkManager.Profiles/ProfileFilterTagsMatch.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace NETworkManager.Profiles;

/// <summary>
/// Enumeration for tag filtering options.
/// </summary>
public enum ProfileFilterTagsMatch
{
/// <summary>
/// Any of the tags can match.
/// </summary>
Any,

/// <summary>
/// All of the tags must match.
/// </summary>
All
}
21 changes: 12 additions & 9 deletions Source/NETworkManager.Profiles/ProfileInfo.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
using System.Security;
using System.Xml.Serialization;
using NETworkManager.Controls;
using NETworkManager.Models.Network;
using NETworkManager.Models.PowerShell;
using NETworkManager.Models.PuTTY;
using NETworkManager.Models.RemoteDesktop;
using NETworkManager.Settings;
using System.Security;
using System.Xml.Serialization;

// ReSharper disable InconsistentNaming
// ReSharper disable PropertyCanBeMadeInitOnly.Global
Expand Down Expand Up @@ -33,7 +34,7 @@ public ProfileInfo(ProfileInfo profile)
Host = profile.Host;
Description = profile.Description;
Group = profile.Group;
Tags = profile.Tags;
TagsCollection = profile.TagsCollection;

IsDynamic = profile.IsDynamic;

Expand All @@ -42,7 +43,6 @@ public ProfileInfo(ProfileInfo profile)
NetworkInterface_EnableStaticIPAddress = profile.NetworkInterface_EnableStaticIPAddress;
NetworkInterface_IPAddress = profile.NetworkInterface_IPAddress;
NetworkInterface_Subnetmask = profile.NetworkInterface_Subnetmask;
NetworkInterface_SubnetmaskOrCidr = profile.NetworkInterface_SubnetmaskOrCidr;
NetworkInterface_Gateway = profile.NetworkInterface_Gateway;
NetworkInterface_EnableStaticDNS = profile.NetworkInterface_EnableStaticDNS;
NetworkInterface_PrimaryDNSServer = profile.NetworkInterface_PrimaryDNSServer;
Expand Down Expand Up @@ -253,17 +253,23 @@ public ProfileInfo(ProfileInfo profile)
/// Description of the profile.
/// </summary>
public string Description { get; set; }

/// <summary>
/// Name of the group. Profiles are grouped based on the name.
/// </summary>
public string Group { get; set; }

/// <summary>
/// Tags to classify the profiles and to filter by it.
/// </summary>
//[Obsolete("Replaced with TagsList")]
public string Tags { get; set; }

/// <summary>
/// Tags collection to classify the profiles and to filter by it.
/// </summary>
public ObservableSetCollection<string> TagsCollection { get; set; } = [];

/// <summary>
/// Dynamic profiles (e.g. AWS) are not save to file.
/// </summary>
Expand All @@ -273,9 +279,6 @@ public ProfileInfo(ProfileInfo profile)
public bool NetworkInterface_Enabled { get; set; }
public bool NetworkInterface_EnableStaticIPAddress { get; set; }
public string NetworkInterface_IPAddress { get; set; }

//[Obsolete("Replaced by NetworkInterface_Subnetmask")]
public string NetworkInterface_SubnetmaskOrCidr { get; set; }
public string NetworkInterface_Subnetmask { get; set; }
public string NetworkInterface_Gateway { get; set; }
public bool NetworkInterface_EnableStaticDNS { get; set; }
Expand Down
16 changes: 8 additions & 8 deletions Source/NETworkManager.Profiles/ProfileManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ static ProfileManager()
/// <summary>
/// ObservableCollection of all profile files.
/// </summary>
public static ObservableCollection<ProfileFileInfo> ProfileFiles { get; set; } = new();
public static ObservableCollection<ProfileFileInfo> ProfileFiles { get; set; } = [];

/// <summary>
/// Currently loaded profile file.
Expand All @@ -75,7 +75,7 @@ private set
/// <summary>
/// Currently loaded groups with profiles.
/// </summary>
public static List<GroupInfo> Groups { get; set; } = new();
public static List<GroupInfo> Groups { get; set; } = [];

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

// Convert passwords to secure strings
RemoteDesktop_Password = !string.IsNullOrEmpty(profileSerializable.RemoteDesktop_Password)
Expand Down
Loading