Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions Flow.Launcher/Languages/en.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<system:String x:Key="restartToDisablePortableMode">Flow Launcher needs to restart to finish disabling portable mode, after the restart your portable data profile will be deleted and roaming data profile kept</system:String>
<system:String x:Key="restartToEnablePortableMode">Flow Launcher needs to restart to finish enabling portable mode, after the restart your roaming data profile will be deleted and portable data profile kept</system:String>
<system:String x:Key="moveToDifferentLocation">Flow Launcher has detected you enabled portable mode, would you like to move it to a different location?</system:String>
<system:String x:Key="shortcutsUninstallerCreated">Flow Launcher has detected you disabled portable mode, the relevant shortcuts and uninstaller entry have been created</system:String>

Check warning on line 25 in Flow.Launcher/Languages/en.xaml

View workflow job for this annotation

GitHub Actions / Check Spelling

`uninstaller` is not a recognized word (unrecognized-spelling)

Check warning on line 25 in Flow.Launcher/Languages/en.xaml

View workflow job for this annotation

GitHub Actions / Report (PR)

`uninstaller` is not a recognized word (unrecognized-spelling)
<system:String x:Key="userDataDuplicated">Flow Launcher detected your user data exists both in {0} and {1}. {2}{2}Please delete {1} in order to proceed. No changes have occurred.</system:String>

<!-- Plugin Loader -->
Expand Down Expand Up @@ -81,8 +81,8 @@
<system:String x:Key="useLogonTaskForStartupTooltip">After uninstallation, you need to manually remove this task (Flow.Launcher Startup) via Task Scheduler</system:String>
<system:String x:Key="setAutoStartFailed">Error setting launch on startup</system:String>
<system:String x:Key="hideFlowLauncherWhenLoseFocus">Hide Flow Launcher when focus is lost</system:String>
<system:String x:Key="showTaskbarWhenOpened">Show taskbar when Flow Launcher is opened</system:String>

Check warning on line 84 in Flow.Launcher/Languages/en.xaml

View workflow job for this annotation

GitHub Actions / Check Spelling

`taskbar` is not a recognized word (unrecognized-spelling)

Check warning on line 84 in Flow.Launcher/Languages/en.xaml

View workflow job for this annotation

GitHub Actions / Report (PR)

`taskbar` is not a recognized word (unrecognized-spelling)
<system:String x:Key="showTaskbarWhenOpenedToolTip">Temporarily show the taskbar when Flow Launcher is opened, useful for auto-hidden taskbars.</system:String>

Check warning on line 85 in Flow.Launcher/Languages/en.xaml

View workflow job for this annotation

GitHub Actions / Check Spelling

`taskbar` is not a recognized word (unrecognized-spelling)

Check warning on line 85 in Flow.Launcher/Languages/en.xaml

View workflow job for this annotation

GitHub Actions / Check Spelling

`taskbars` is not a recognized word (unrecognized-spelling)

Check warning on line 85 in Flow.Launcher/Languages/en.xaml

View workflow job for this annotation

GitHub Actions / Report (PR)

`taskbars` is not a recognized word (unrecognized-spelling)
<system:String x:Key="dontPromptUpdateMsg">Do not show new version notifications</system:String>
<system:String x:Key="SearchWindowPosition">Search Window Location</system:String>
<system:String x:Key="SearchWindowScreenRememberLastLaunchLocation">Remember Last Position</system:String>
Expand Down Expand Up @@ -242,6 +242,11 @@
<system:String x:Key="pluginStore_RecentlyUpdated">Recently Updated</system:String>
<system:String x:Key="pluginStore_None">Plugins</system:String>
<system:String x:Key="pluginStore_Installed">Installed</system:String>
<system:String x:Key="PluginStoreSortModeDefault">Default</system:String>
<system:String x:Key="PluginStoreSortModeName">Name</system:String>
<system:String x:Key="PluginStoreSortModeReleaseDate">Release Date</system:String>
<system:String x:Key="PluginStoreSortModeUpdatedDate">Updated Date</system:String>
<system:String x:Key="PluginStoreSortingModeComboboxLabel">Sorting Mode:</system:String>
<system:String x:Key="refresh">Refresh</system:String>
<system:String x:Key="installbtn">Install</system:String>
<system:String x:Key="uninstallbtn">Uninstall</system:String>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,31 @@ namespace Flow.Launcher.SettingPages.ViewModels;

public partial class SettingsPanePluginStoreViewModel : BaseModel
{
public class SortModeData : DropdownDataGeneric<PluginStoreSortMode> { }

public List<SortModeData> SortModes { get; } =
DropdownDataGeneric<PluginStoreSortMode>.GetValues<SortModeData>("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
{
Expand Down Expand Up @@ -82,13 +107,8 @@ public bool ShowExecutable
}
}

public IList<PluginStoreItemViewModel> 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<PluginStoreItemViewModel> ExternalPlugins => GetSortedPlugins(
App.API.GetPluginManifest().Select(p => new PluginStoreItemViewModel(p)));

[RelayCommand]
private async Task RefreshExternalPluginsAsync()
Expand Down Expand Up @@ -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<PluginStoreSortMode>.UpdateLabels(SortModes);
}

private IList<PluginStoreItemViewModel> GetSortedPlugins(IEnumerable<PluginStoreItemViewModel> 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
}
159 changes: 93 additions & 66 deletions Flow.Launcher/SettingPages/Views/SettingsPanePluginStore.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"

Check warning on line 6 in Flow.Launcher/SettingPages/Views/SettingsPanePluginStore.xaml

View workflow job for this annotation

GitHub Actions / Check Spelling

`ikw` is not a recognized word (unrecognized-spelling)
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
Expand All @@ -22,7 +22,7 @@
Filter="PluginStoreCollectionView_OnFilter"
Source="{Binding ExternalPlugins}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Category" />
<PropertyGroupDescription PropertyName="DefaultCategory" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</ui:Page.Resources>
Expand All @@ -32,7 +32,7 @@
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="72" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border
Expand All @@ -50,77 +50,104 @@
<DockPanel
Grid.Row="0"
Grid.Column="1"
Margin="5 24 0 0">

Margin="5 24 0 8">
<ikw:SimpleStackPanel
HorizontalAlignment="Right"
VerticalAlignment="Center"
DockPanel.Dock="Right"
Orientation="Horizontal"
Orientation="Vertical"
Spacing="8">
<Button
Height="34"
Margin="0 5 0 5"
Padding="12 4"
<ikw:SimpleStackPanel
HorizontalAlignment="Right"
VerticalAlignment="Center"
Command="{Binding RefreshExternalPluginsCommand}"
Content="{DynamicResource refresh}"
FontSize="13" />
<Button Height="34">
<ui:FontIcon FontSize="14" Glyph="&#xe71c;" />
<ui:FlyoutService.Flyout>
<ui:MenuFlyout x:Name="FilterFlyout" Placement="Bottom">
<MenuItem
Header=".Net"
IsCheckable="True"
IsChecked="{Binding ShowDotNet, Mode=TwoWay}"
StaysOpenOnClick="True" />
<MenuItem
Header="Python"
IsCheckable="True"
IsChecked="{Binding ShowPython, Mode=TwoWay}"
StaysOpenOnClick="True" />
<MenuItem
Header="Node.js"
IsCheckable="True"
IsChecked="{Binding ShowNodeJs, Mode=TwoWay}"
StaysOpenOnClick="True" />
<MenuItem
Header="Exe"
IsCheckable="True"
IsChecked="{Binding ShowExecutable, Mode=TwoWay}"
StaysOpenOnClick="True" />
</ui:MenuFlyout>
</ui:FlyoutService.Flyout>
</Button>
<Button
Height="34"
Command="{Binding InstallPluginCommand}"
ToolTip="{DynamicResource installLocalPluginTooltip}">
<ui:FontIcon FontSize="14" Glyph="&#xE8DA;" />
</Button>
<Button
Height="34"
Command="{Binding CheckPluginUpdatesCommand}"
ToolTip="{DynamicResource checkPluginUpdatesTooltip}">
<ui:FontIcon FontSize="14" Glyph="&#xecc5;" />
</Button>
<TextBox
Name="PluginStoreFilterTextbox"
Width="150"
Height="34"
Margin="0 0 26 0"
Orientation="Horizontal"
Spacing="8">
<Button
Height="34"
Padding="12 4"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Command="{Binding RefreshExternalPluginsCommand}"
Content="{DynamicResource refresh}"
FontSize="13" />
<Button Height="34">
<ui:FontIcon FontSize="14" Glyph="&#xe71c;" />
<ui:FlyoutService.Flyout>
<ui:MenuFlyout x:Name="FilterFlyout" Placement="Bottom">
<MenuItem
Header=".Net"
IsCheckable="True"
IsChecked="{Binding ShowDotNet, Mode=TwoWay}"
StaysOpenOnClick="True" />
<MenuItem
Header="Python"
IsCheckable="True"
IsChecked="{Binding ShowPython, Mode=TwoWay}"
StaysOpenOnClick="True" />
<MenuItem
Header="Node.js"
IsCheckable="True"
IsChecked="{Binding ShowNodeJs, Mode=TwoWay}"
StaysOpenOnClick="True" />
<MenuItem
Header="Exe"
IsCheckable="True"
IsChecked="{Binding ShowExecutable, Mode=TwoWay}"
StaysOpenOnClick="True" />
</ui:MenuFlyout>
</ui:FlyoutService.Flyout>
</Button>
<Button
Height="34"
Command="{Binding InstallPluginCommand}"
ToolTip="{DynamicResource installLocalPluginTooltip}">
<ui:FontIcon FontSize="14" Glyph="&#xE8DA;" />
</Button>
<Button
Height="34"
Command="{Binding CheckPluginUpdatesCommand}"
ToolTip="{DynamicResource checkPluginUpdatesTooltip}">
<ui:FontIcon FontSize="14" Glyph="&#xecc5;" />
</Button>
<TextBox
Name="PluginStoreFilterTextbox"
Width="150"
Height="34"
Margin="0 0 26 0"
HorizontalAlignment="Right"
VerticalContentAlignment="Center"
ui:ControlHelper.PlaceholderText="{DynamicResource searchplugin}"
ContextMenu="{StaticResource TextBoxContextMenu}"
DockPanel.Dock="Right"
FontSize="14"
Text="{Binding FilterText, UpdateSourceTrigger=PropertyChanged}"
ToolTip="{DynamicResource searchpluginToolTip}"
ToolTipService.InitialShowDelay="200"
ToolTipService.Placement="Top" />
</ikw:SimpleStackPanel>

<ikw:SimpleStackPanel
HorizontalAlignment="Right"
VerticalContentAlignment="Center"
ui:ControlHelper.PlaceholderText="{DynamicResource searchplugin}"
ContextMenu="{StaticResource TextBoxContextMenu}"
DockPanel.Dock="Right"
FontSize="14"
Text="{Binding FilterText, UpdateSourceTrigger=PropertyChanged}"
ToolTip="{DynamicResource searchpluginToolTip}"
ToolTipService.InitialShowDelay="200"
ToolTipService.Placement="Top" />
Margin="0 0 26 0"
Orientation="Horizontal"
Spacing="8">
<TextBlock
VerticalAlignment="Center"
FontSize="14"
Foreground="{DynamicResource Color15B}"
Text="{DynamicResource PluginStoreSortingModeComboboxLabel}" />
<ComboBox
x:Name="SortModeComboBox"
Width="Auto"
Height="34"
MinWidth="150"
MaxWidth="150"
HorizontalContentAlignment="Left"
Background="{DynamicResource Color00B}"
ItemsSource="{Binding SortModes}"
DisplayMemberPath="Display"
SelectedValuePath="Value"
SelectedValue="{Binding SelectedSortMode, Mode=TwoWay}"/>
</ikw:SimpleStackPanel>
</ikw:SimpleStackPanel>
</DockPanel>

Expand Down Expand Up @@ -297,7 +324,7 @@
VerticalAlignment="Center"
Command="{Binding ShowCommandQueryCommand}"
CommandParameter="install"
Content="{DynamicResource installbtn}"

Check warning on line 327 in Flow.Launcher/SettingPages/Views/SettingsPanePluginStore.xaml

View workflow job for this annotation

GitHub Actions / Check Spelling

`installbtn` is not a recognized word (unrecognized-spelling)

Check warning on line 327 in Flow.Launcher/SettingPages/Views/SettingsPanePluginStore.xaml

View workflow job for this annotation

GitHub Actions / Report (PR)

`installbtn` is not a recognized word (unrecognized-spelling)
Visibility="{Binding LabelInstalled, Converter={StaticResource BoolToVisibilityConverter}, ConverterParameter='!'}" />
<Button
MinHeight="42"
Expand All @@ -307,7 +334,7 @@
VerticalAlignment="Center"
Command="{Binding ShowCommandQueryCommand}"
CommandParameter="uninstall"
Content="{DynamicResource uninstallbtn}"

Check warning on line 337 in Flow.Launcher/SettingPages/Views/SettingsPanePluginStore.xaml

View workflow job for this annotation

GitHub Actions / Check Spelling

`uninstallbtn` is not a recognized word (unrecognized-spelling)

Check warning on line 337 in Flow.Launcher/SettingPages/Views/SettingsPanePluginStore.xaml

View workflow job for this annotation

GitHub Actions / Report (PR)

`uninstallbtn` is not a recognized word (unrecognized-spelling)
Visibility="{Binding LabelInstalled, Converter={StaticResource BoolToVisibilityConverter}}" />
<Button
MinHeight="42"
Expand All @@ -317,7 +344,7 @@
VerticalAlignment="Center"
Command="{Binding ShowCommandQueryCommand}"
CommandParameter="update"
Content="{DynamicResource updatebtn}"

Check warning on line 347 in Flow.Launcher/SettingPages/Views/SettingsPanePluginStore.xaml

View workflow job for this annotation

GitHub Actions / Check Spelling

`updatebtn` is not a recognized word (unrecognized-spelling)

Check warning on line 347 in Flow.Launcher/SettingPages/Views/SettingsPanePluginStore.xaml

View workflow job for this annotation

GitHub Actions / Report (PR)

`updatebtn` is not a recognized word (unrecognized-spelling)
Style="{DynamicResource AccentButtonStyle}"
Visibility="{Binding LabelUpdate, Converter={StaticResource BoolToVisibilityConverter}}" />
</VirtualizingStackPanel>
Expand Down
28 changes: 28 additions & 0 deletions Flow.Launcher/SettingPages/Views/SettingsPanePluginStore.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,28 @@ 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):
case nameof(SettingsPanePluginStoreViewModel.ShowDotNet):
case nameof(SettingsPanePluginStoreViewModel.ShowPython):
case nameof(SettingsPanePluginStoreViewModel.ShowNodeJs):
case nameof(SettingsPanePluginStoreViewModel.ShowExecutable):
case nameof(SettingsPanePluginStoreViewModel.SelectedSortMode):
((CollectionViewSource)FindResource("PluginStoreCollectionView")).View.Refresh();
break;
}
Expand Down Expand Up @@ -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)));
}
}
}
6 changes: 5 additions & 1 deletion Flow.Launcher/ViewModel/PluginStoreItemViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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
{
Expand Down
Loading