diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml
index 02c7becacca..62b32b2c136 100644
--- a/Flow.Launcher/Languages/en.xaml
+++ b/Flow.Launcher/Languages/en.xaml
@@ -242,6 +242,11 @@
Recently Updated
Plugins
Installed
+ Default
+ Name
+ Release Date
+ Updated Date
+ Sorting Mode:
Refresh
Install
Uninstall
diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginStoreViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginStoreViewModel.cs
index 752d6890338..a85c06a1eb1 100644
--- a/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginStoreViewModel.cs
+++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginStoreViewModel.cs
@@ -12,6 +12,31 @@ namespace Flow.Launcher.SettingPages.ViewModels;
public partial class SettingsPanePluginStoreViewModel : BaseModel
{
+ public class SortModeData : DropdownDataGeneric { }
+
+ public List SortModes { get; } =
+ DropdownDataGeneric.GetValues("PluginStoreSortMode");
+
+ public SettingsPanePluginStoreViewModel()
+ {
+ UpdateEnumDropdownLocalizations();
+ }
+
+ private PluginStoreSortMode _selectedSortMode = PluginStoreSortMode.Default;
+ public PluginStoreSortMode SelectedSortMode
+ {
+ get => _selectedSortMode;
+ set
+ {
+ if (_selectedSortMode != value)
+ {
+ _selectedSortMode = value;
+ OnPropertyChanged();
+ OnPropertyChanged(nameof(ExternalPlugins));
+ }
+ }
+ }
+
private string filterText = string.Empty;
public string FilterText
{
@@ -82,13 +107,8 @@ public bool ShowExecutable
}
}
- public IList ExternalPlugins => App.API.GetPluginManifest()
- .Select(p => new PluginStoreItemViewModel(p))
- .OrderByDescending(p => p.Category == PluginStoreItemViewModel.NewRelease)
- .ThenByDescending(p => p.Category == PluginStoreItemViewModel.RecentlyUpdated)
- .ThenByDescending(p => p.Category == PluginStoreItemViewModel.None)
- .ThenByDescending(p => p.Category == PluginStoreItemViewModel.Installed)
- .ToList();
+ public IList ExternalPlugins => GetSortedPlugins(
+ App.API.GetPluginManifest().Select(p => new PluginStoreItemViewModel(p)));
[RelayCommand]
private async Task RefreshExternalPluginsAsync()
@@ -168,4 +188,48 @@ public bool SatisfiesFilter(PluginStoreItemViewModel plugin)
App.API.FuzzySearch(FilterText, plugin.Name).IsSearchPrecisionScoreMet() ||
App.API.FuzzySearch(FilterText, plugin.Description).IsSearchPrecisionScoreMet();
}
+
+ private void UpdateEnumDropdownLocalizations()
+ {
+ DropdownDataGeneric.UpdateLabels(SortModes);
+ }
+
+ private IList GetSortedPlugins(IEnumerable plugins)
+ {
+ return SelectedSortMode switch
+ {
+ PluginStoreSortMode.Name => plugins
+ .OrderBy(p => p.LabelInstalled)
+ .ThenBy(p => p.Name)
+ .ToList(),
+
+ PluginStoreSortMode.ReleaseDate => plugins
+ .OrderBy(p => p.LabelInstalled)
+ .ThenByDescending(p => p.DateAdded.HasValue)
+ .ThenByDescending(p => p.DateAdded)
+ .ToList(),
+
+ PluginStoreSortMode.UpdatedDate => plugins
+ .OrderBy(p => p.LabelInstalled)
+ .ThenByDescending(p => p.UpdatedDate.HasValue)
+ .ThenByDescending(p => p.UpdatedDate)
+ .ToList(),
+
+ _ => plugins
+ .OrderByDescending(p => p.DefaultCategory == PluginStoreItemViewModel.NewRelease)
+ .ThenByDescending(p => p.DefaultCategory == PluginStoreItemViewModel.RecentlyUpdated)
+ .ThenByDescending(p => p.DefaultCategory == PluginStoreItemViewModel.None)
+ .ThenByDescending(p => p.DefaultCategory == PluginStoreItemViewModel.Installed)
+ .ToList(),
+ };
+ }
+
+}
+
+public enum PluginStoreSortMode
+{
+ Default,
+ Name,
+ ReleaseDate,
+ UpdatedDate
}
diff --git a/Flow.Launcher/SettingPages/Views/SettingsPanePluginStore.xaml b/Flow.Launcher/SettingPages/Views/SettingsPanePluginStore.xaml
index 4d37dc93acc..d729cc72008 100644
--- a/Flow.Launcher/SettingPages/Views/SettingsPanePluginStore.xaml
+++ b/Flow.Launcher/SettingPages/Views/SettingsPanePluginStore.xaml
@@ -22,7 +22,7 @@
Filter="PluginStoreCollectionView_OnFilter"
Source="{Binding ExternalPlugins}">
-
+
@@ -32,7 +32,7 @@
-
+
-
+ Margin="5 24 0 8">
-
-
-
-
-
+
+
+
+
+
+
+
+
+ Margin="0 0 26 0"
+ Orientation="Horizontal"
+ Spacing="8">
+
+
+
diff --git a/Flow.Launcher/SettingPages/Views/SettingsPanePluginStore.xaml.cs b/Flow.Launcher/SettingPages/Views/SettingsPanePluginStore.xaml.cs
index c0a77957ac8..686f22ef333 100644
--- a/Flow.Launcher/SettingPages/Views/SettingsPanePluginStore.xaml.cs
+++ b/Flow.Launcher/SettingPages/Views/SettingsPanePluginStore.xaml.cs
@@ -29,12 +29,20 @@ protected override void OnNavigatedTo(NavigationEventArgs e)
{
InitializeComponent();
}
+ UpdateCategoryGrouping();
_viewModel.PropertyChanged += ViewModel_PropertyChanged;
base.OnNavigatedTo(e);
}
private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
+ // If SelectedSortMode changed, then we need to update the categories
+ if (e.PropertyName == nameof(SettingsPanePluginStoreViewModel.SelectedSortMode))
+ {
+ UpdateCategoryGrouping();
+ }
+
+ // Check if changed property requires PluginStoreCollectionView refresh
switch (e.PropertyName)
{
case nameof(SettingsPanePluginStoreViewModel.FilterText):
@@ -42,6 +50,7 @@ private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e
case nameof(SettingsPanePluginStoreViewModel.ShowPython):
case nameof(SettingsPanePluginStoreViewModel.ShowNodeJs):
case nameof(SettingsPanePluginStoreViewModel.ShowExecutable):
+ case nameof(SettingsPanePluginStoreViewModel.SelectedSortMode):
((CollectionViewSource)FindResource("PluginStoreCollectionView")).View.Refresh();
break;
}
@@ -75,4 +84,23 @@ private void PluginStoreCollectionView_OnFilter(object sender, FilterEventArgs e
e.Accepted = _viewModel.SatisfiesFilter(plugin);
}
+
+ private void UpdateCategoryGrouping()
+ {
+ var collectionView = (CollectionViewSource)FindResource("PluginStoreCollectionView");
+ var groupDescriptions = collectionView.GroupDescriptions;
+
+ groupDescriptions.Clear();
+
+ // For default sorting mode we use the default categories
+ if (_viewModel.SelectedSortMode == PluginStoreSortMode.Default)
+ {
+ groupDescriptions.Add(new PropertyGroupDescription(nameof(PluginStoreItemViewModel.DefaultCategory)));
+ }
+
+ // Otherwise we only split by installed or not
+ else{
+ groupDescriptions.Add(new PropertyGroupDescription(nameof(PluginStoreItemViewModel.InstallCategory)));
+ }
+ }
}
diff --git a/Flow.Launcher/ViewModel/PluginStoreItemViewModel.cs b/Flow.Launcher/ViewModel/PluginStoreItemViewModel.cs
index f5523212e13..177534b612e 100644
--- a/Flow.Launcher/ViewModel/PluginStoreItemViewModel.cs
+++ b/Flow.Launcher/ViewModel/PluginStoreItemViewModel.cs
@@ -28,6 +28,8 @@ public PluginStoreItemViewModel(UserPlugin plugin)
public string UrlDownload => _newPlugin.UrlDownload;
public string UrlSourceCode => _newPlugin.UrlSourceCode;
public string IcoPath => _newPlugin.IcoPath;
+ public DateTime? DateAdded => _newPlugin.DateAdded;
+ public DateTime? UpdatedDate => _newPlugin.LatestReleaseDate;
public bool LabelInstalled => _oldPluginPair != null;
public bool LabelUpdate => LabelInstalled && new Version(_newPlugin.Version) > new Version(_oldPluginPair.Metadata.Version);
@@ -37,7 +39,9 @@ public PluginStoreItemViewModel(UserPlugin plugin)
internal const string NewRelease = "NewRelease";
internal const string Installed = "Installed";
- public string Category
+ public string InstallCategory => LabelInstalled ? Installed : None;
+
+ public string DefaultCategory
{
get
{