@@ -239,6 +239,70 @@ public bool IsSearching
239239 OnPropertyChanged ( ) ;
240240 }
241241 }
242+
243+ private bool _profileFilterIsOpen ;
244+
245+ public bool ProfileFilterIsOpen
246+ {
247+ get => _profileFilterIsOpen ;
248+ set
249+ {
250+ if ( value == _profileFilterIsOpen )
251+ return ;
252+
253+ _profileFilterIsOpen = value ;
254+ OnPropertyChanged ( ) ;
255+ }
256+ }
257+
258+ public ICollectionView ProfileFilterTagsView { get ; }
259+
260+ private ObservableCollection < ProfileFilterTagsInfo > ProfileFilterTags { get ; } = [ ] ;
261+
262+ private bool _profileFilterTagsMatchAny = GlobalStaticConfiguration . Profile_TagsMatchAny ;
263+
264+ public bool ProfileFilterTagsMatchAny
265+ {
266+ get => _profileFilterTagsMatchAny ;
267+ set
268+ {
269+ if ( value == _profileFilterTagsMatchAny )
270+ return ;
271+
272+ _profileFilterTagsMatchAny = value ;
273+ OnPropertyChanged ( ) ;
274+ }
275+ }
276+
277+ private bool _profileFilterTagsMatchAll ;
278+
279+ public bool ProfileFilterTagsMatchAll
280+ {
281+ get => _profileFilterTagsMatchAll ;
282+ set
283+ {
284+ if ( value == _profileFilterTagsMatchAll )
285+ return ;
286+
287+ _profileFilterTagsMatchAll = value ;
288+ OnPropertyChanged ( ) ;
289+ }
290+ }
291+
292+ private bool _isProfileFilterSet ;
293+
294+ public bool IsProfileFilterSet
295+ {
296+ get => _isProfileFilterSet ;
297+ set
298+ {
299+ if ( value == _isProfileFilterSet )
300+ return ;
301+
302+ _isProfileFilterSet = value ;
303+ OnPropertyChanged ( ) ;
304+ }
305+ }
242306
243307 private bool _canProfileWidthChange = true ;
244308 private double _tempProfileWidth ;
@@ -333,7 +397,12 @@ public AWSSessionManagerHostViewModel(IDialogCoordinator instance)
333397 TabItems = [ ] ;
334398
335399 // Profiles
336- SetProfilesView ( ) ;
400+ CreateTags ( ) ;
401+
402+ ProfileFilterTagsView = CollectionViewSource . GetDefaultView ( ProfileFilterTags ) ;
403+ ProfileFilterTagsView . SortDescriptions . Add ( new SortDescription ( nameof ( ProfileFilterTagsInfo . Name ) , ListSortDirection . Ascending ) ) ;
404+
405+ SetProfilesView ( new ProfileFilterInfo ( ) ) ;
337406
338407 ProfileManager . OnProfilesUpdated += ProfileManager_OnProfilesUpdated ;
339408
@@ -520,6 +589,36 @@ private void ClearSearchAction()
520589 {
521590 Search = string . Empty ;
522591 }
592+
593+ public ICommand OpenProfileFilterCommand => new RelayCommand ( _ => OpenProfileFilterAction ( ) ) ;
594+
595+ private void OpenProfileFilterAction ( )
596+ {
597+ ProfileFilterIsOpen = true ;
598+ }
599+
600+ public ICommand ApplyProfileFilterCommand => new RelayCommand ( _ => ApplyProfileFilterAction ( ) ) ;
601+
602+ private void ApplyProfileFilterAction ( )
603+ {
604+ RefreshProfiles ( ) ;
605+
606+ IsProfileFilterSet = true ;
607+ ProfileFilterIsOpen = false ;
608+ }
609+
610+ public ICommand ClearProfileFilterCommand => new RelayCommand ( _ => ClearProfileFilterAction ( ) ) ;
611+
612+ private void ClearProfileFilterAction ( )
613+ {
614+ foreach ( var tag in ProfileFilterTags )
615+ tag . IsSelected = false ;
616+
617+ RefreshProfiles ( ) ;
618+
619+ IsProfileFilterSet = false ;
620+ ProfileFilterIsOpen = false ;
621+ }
523622
524623 public ICommand OpenDocumentationCommand
525624 {
@@ -986,36 +1085,42 @@ public void OnProfileLoaded()
9861085 SyncAllInstanceIDsFromAWS ( ) . ConfigureAwait ( false ) ;
9871086 }
9881087
989- private void SetProfilesView ( ProfileInfo profile = null )
1088+ private void CreateTags ( )
9901089 {
991- Profiles = new CollectionViewSource
992- {
993- Source = ProfileManager . Groups . SelectMany ( x => x . Profiles ) . Where ( x => x . AWSSessionManager_Enabled )
994- . OrderBy ( x => x . Group ) . ThenBy ( x => x . Name )
995- } . View ;
1090+ var tags = ProfileManager . Groups . SelectMany ( x => x . Profiles ) . Where ( x => x . AWSSessionManager_Enabled ) . SelectMany ( x => x . TagsCollection ) . Distinct ( ) . ToList ( ) ;
9961091
997- Profiles . GroupDescriptions . Add ( new PropertyGroupDescription ( nameof ( ProfileInfo . Group ) ) ) ;
1092+ var tagSet = new HashSet < string > ( tags ) ;
9981093
999- Profiles . Filter = o =>
1094+ for ( var i = ProfileFilterTags . Count - 1 ; i >= 0 ; i -- )
10001095 {
1001- if ( string . IsNullOrEmpty ( Search ) )
1002- return true ;
1003-
1004- if ( o is not ProfileInfo info )
1005- return false ;
1096+ if ( ! tagSet . Contains ( ProfileFilterTags [ i ] . Name ) )
1097+ ProfileFilterTags . RemoveAt ( i ) ;
1098+ }
10061099
1007- var search = Search . Trim ( ) ;
1100+ var existingTagNames = new HashSet < string > ( ProfileFilterTags . Select ( ft => ft . Name ) ) ;
10081101
1009- // Search by: Tag=xxx (exact match, ignore case)
1010- /*
1011- if (search.StartsWith(ProfileManager.TagIdentifier, StringComparison.OrdinalIgnoreCase))
1012- return !string.IsNullOrEmpty(info.Tags) && info.PingMonitor_Enabled && info.Tags.Replace(" ", "").Split(';').Any(str => search.Substring(ProfileManager.TagIdentifier.Length, search.Length - ProfileManager.TagIdentifier.Length).Equals(str, StringComparison.OrdinalIgnoreCase));
1013- */
1102+ foreach ( var tag in tags . Where ( tag => ! existingTagNames . Contains ( tag ) ) )
1103+ {
1104+ ProfileFilterTags . Add ( new ProfileFilterTagsInfo ( false , tag ) ) ;
1105+ }
1106+ }
1107+
1108+ private void SetProfilesView ( ProfileFilterInfo filter , ProfileInfo profile = null )
1109+ {
1110+ Profiles = new CollectionViewSource
1111+ {
1112+ Source = ProfileManager . Groups . SelectMany ( x => x . Profiles ) . Where ( x => x . AWSSessionManager_Enabled && (
1113+ string . IsNullOrEmpty ( filter . Search ) || x . Name . IndexOf ( filter . Search , StringComparison . Ordinal ) > - 1 || x . AWSSessionManager_InstanceID . IndexOf ( filter . Search , StringComparison . Ordinal ) > - 1 ) && (
1114+ // If no tags are selected, show all profiles
1115+ ( ! filter . Tags . Any ( ) ) ||
1116+ // Any tag can match
1117+ ( filter . TagsFilterMatch == ProfileFilterTagsMatch . Any && filter . Tags . Any ( tag => x . TagsCollection . Contains ( tag ) ) ) ||
1118+ // All tags must match
1119+ ( filter . TagsFilterMatch == ProfileFilterTagsMatch . All && filter . Tags . All ( tag => x . TagsCollection . Contains ( tag ) ) ) )
1120+ ) . OrderBy ( x => x . Group ) . ThenBy ( x => x . Name )
1121+ } . View ;
10141122
1015- // Search by: Name, AWSSessionManager_InstanceID
1016- return info . Name . IndexOf ( search , StringComparison . OrdinalIgnoreCase ) > - 1 ||
1017- info . AWSSessionManager_InstanceID . IndexOf ( search , StringComparison . OrdinalIgnoreCase ) > - 1 ;
1018- } ;
1123+ Profiles . GroupDescriptions . Add ( new PropertyGroupDescription ( nameof ( ProfileInfo . Group ) ) ) ;
10191124
10201125 // Set specific profile or first if null
10211126 SelectedProfile = null ;
@@ -1032,7 +1137,12 @@ private void RefreshProfiles()
10321137 if ( ! _isViewActive )
10331138 return ;
10341139
1035- SetProfilesView ( SelectedProfile ) ;
1140+ SetProfilesView ( new ProfileFilterInfo
1141+ {
1142+ Search = Search ,
1143+ Tags = [ .. ProfileFilterTags . Where ( x => x . IsSelected ) . Select ( x => x . Name ) ] ,
1144+ TagsFilterMatch = ProfileFilterTagsMatchAny ? ProfileFilterTagsMatch . Any : ProfileFilterTagsMatch . All
1145+ } , SelectedProfile ) ;
10361146 }
10371147
10381148 public void OnProfileManagerDialogOpen ( )
@@ -1053,7 +1163,7 @@ private void WriteDefaultProfileToRegistry()
10531163 if ( ! IsExecutableConfigured )
10541164 return ;
10551165
1056- Log . Info ( "Write PowerShell profile to registry..." ) ;
1166+ Log . Debug ( "Write PowerShell profile to registry..." ) ;
10571167
10581168 PowerShell . WriteDefaultProfileToRegistry (
10591169 SettingsManager . Current . Appearance_Theme ,
@@ -1111,6 +1221,8 @@ private void AWSSessionManager_AWSProfiles_CollectionChanged(object sender,
11111221
11121222 private void ProfileManager_OnProfilesUpdated ( object sender , EventArgs e )
11131223 {
1224+ CreateTags ( ) ;
1225+
11141226 RefreshProfiles ( ) ;
11151227 }
11161228
0 commit comments