diff --git a/src/.idea/.idea.Artemis/.idea/avalonia.xml b/src/.idea/.idea.Artemis/.idea/avalonia.xml
index 4aa1c9d1b..2363528ee 100644
--- a/src/.idea/.idea.Artemis/.idea/avalonia.xml
+++ b/src/.idea/.idea.Artemis/.idea/avalonia.xml
@@ -75,6 +75,7 @@
+
diff --git a/src/Artemis.Core/Services/PluginManagementService.cs b/src/Artemis.Core/Services/PluginManagementService.cs
index 61d965cdc..4623bd948 100644
--- a/src/Artemis.Core/Services/PluginManagementService.cs
+++ b/src/Artemis.Core/Services/PluginManagementService.cs
@@ -476,6 +476,7 @@ namespace Artemis.Core.Services
lock (_plugins)
{
_plugins.Remove(plugin);
+ OnPluginUnloaded(new PluginEventArgs(plugin));
}
}
diff --git a/src/Artemis.UI/Screens/Settings/Tabs/PluginsTabView.axaml b/src/Artemis.UI/Screens/Settings/Tabs/PluginsTabView.axaml
index cb491bfbc..0e1d2963a 100644
--- a/src/Artemis.UI/Screens/Settings/Tabs/PluginsTabView.axaml
+++ b/src/Artemis.UI/Screens/Settings/Tabs/PluginsTabView.axaml
@@ -2,12 +2,15 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:settings="clr-namespace:Artemis.UI.Screens.Settings"
mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="450"
- x:Class="Artemis.UI.Screens.Settings.PluginsTabView">
+ x:Class="Artemis.UI.Screens.Settings.PluginsTabView"
+ x:DataType="settings:PluginsTabViewModel">
-
+
+
-
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Settings/Tabs/PluginsTabViewModel.cs b/src/Artemis.UI/Screens/Settings/Tabs/PluginsTabViewModel.cs
index 17ee523a3..d1916013b 100644
--- a/src/Artemis.UI/Screens/Settings/Tabs/PluginsTabViewModel.cs
+++ b/src/Artemis.UI/Screens/Settings/Tabs/PluginsTabViewModel.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
+using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Threading.Tasks;
@@ -14,6 +15,8 @@ using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Builders;
using Avalonia.Threading;
+using DynamicData;
+using DynamicData.Binding;
using ReactiveUI;
namespace Artemis.UI.Screens.Settings
@@ -35,13 +38,34 @@ namespace Artemis.UI.Screens.Settings
_settingsVmFactory = settingsVmFactory;
DisplayName = "Plugins";
- Plugins = new ObservableCollection();
- this.WhenAnyValue(x => x.SearchPluginInput).Throttle(TimeSpan.FromMilliseconds(100)).Subscribe(SearchPlugins);
- this.WhenActivated((CompositeDisposable _) => GetPluginInstances());
+ SourceList plugins = new();
+ IObservable> pluginFilter = this.WhenAnyValue(vm => vm.SearchPluginInput).Throttle(TimeSpan.FromMilliseconds(100)).Select(CreatePredicate);
+
+ plugins.Connect()
+ .Filter(pluginFilter)
+ .Sort(SortExpressionComparer.Ascending(p => p.Info.Name))
+ .TransformAsync(p => Dispatcher.UIThread.InvokeAsync(() => _settingsVmFactory.CreatePluginSettingsViewModel(p), DispatcherPriority.Background))
+ .Bind(out ReadOnlyObservableCollection pluginViewModels)
+ .Subscribe();
+ Plugins = pluginViewModels;
+
+ this.WhenActivated(d =>
+ {
+ plugins.AddRange(_pluginManagementService.GetAllPlugins());
+ Observable.FromEventPattern(x => _pluginManagementService.PluginLoaded += x, x => _pluginManagementService.PluginLoaded -= x)
+ .Subscribe(a => plugins.Add(a.EventArgs.Plugin))
+ .DisposeWith(d);
+ Observable.FromEventPattern(x => _pluginManagementService.PluginUnloaded += x, x => _pluginManagementService.PluginUnloaded -= x)
+ .Subscribe(a => plugins.Remove(a.EventArgs.Plugin))
+ .DisposeWith(d);
+ Disposable.Create(() => plugins.Clear()).DisposeWith(d);
+ });
+ ImportPlugin = ReactiveCommand.CreateFromTask(ExecuteImportPlugin);
}
- public ObservableCollection Plugins { get; }
+ public ReadOnlyObservableCollection Plugins { get; }
+ public ReactiveCommand ImportPlugin { get; }
public string? SearchPluginInput
{
@@ -54,71 +78,43 @@ namespace Artemis.UI.Screens.Settings
Utilities.OpenUrl(url);
}
- public async Task ImportPlugin()
+ private async Task ExecuteImportPlugin()
{
string[]? files = await _windowService.CreateOpenFileDialog().WithTitle("Import Artemis plugin").HavingFilter(f => f.WithExtension("zip").WithName("ZIP files")).ShowAsync();
if (files == null)
return;
- // Take the actual import off of the UI thread
- await Task.Run(() =>
+ try
{
Plugin plugin = _pluginManagementService.ImportPlugin(files[0]);
-
- GetPluginInstances();
SearchPluginInput = plugin.Info.Name;
+ // Wait for the VM to be created asynchronously (it would be better to respond to some event here)
+ await Task.Delay(200);
// Enable it via the VM to enable the prerequisite dialog
PluginSettingsViewModel? pluginViewModel = Plugins.FirstOrDefault(i => i.Plugin == plugin);
if (pluginViewModel is {IsEnabled: false})
pluginViewModel.IsEnabled = true;
_notificationService.CreateNotification()
- .WithTitle("Success")
- .WithMessage($"Imported plugin: {plugin.Info.Name}")
+ .WithTitle("Plugin imported")
+ .WithMessage($"Added the '{plugin.Info.Name}' plugin")
.WithSeverity(NotificationSeverity.Success)
.Show();
- });
+ }
+ catch (Exception e)
+ {
+ await _windowService.ShowConfirmContentDialog("Failed to import plugin", "Make sure the selected ZIP file is a valid Artemis plugin.\r\n" + e.Message, "Close", null);
+ }
}
- public void GetPluginInstances()
+ private Func CreatePredicate(string? text)
{
- Plugins.Clear();
- Dispatcher.UIThread.InvokeAsync(() =>
- {
- _instances = _pluginManagementService.GetAllPlugins()
- .Select(p => _settingsVmFactory.CreatePluginSettingsViewModel(p))
- .OrderBy(i => i.Plugin.Info.Name)
- .ToList();
+ if (string.IsNullOrWhiteSpace(text))
+ return _ => true;
- SearchPlugins(SearchPluginInput);
- }, DispatcherPriority.Background);
- }
-
- private void SearchPlugins(string? searchPluginInput)
- {
- if (_instances == null)
- return;
-
- List instances = _instances;
- string? search = searchPluginInput?.ToLower();
- if (!string.IsNullOrWhiteSpace(search))
- instances = instances.Where(i => i.Plugin.Info.Name.ToLower().Contains(search) ||
- i.Plugin.Info.Description != null && i.Plugin.Info.Description.ToLower().Contains(search)).ToList();
-
- foreach (PluginSettingsViewModel pluginSettingsViewModel in instances)
- {
- if (!Plugins.Contains(pluginSettingsViewModel))
- Plugins.Add(pluginSettingsViewModel);
- }
-
- foreach (PluginSettingsViewModel pluginSettingsViewModel in Plugins.ToList())
- {
- if (!instances.Contains(pluginSettingsViewModel))
- Plugins.Remove(pluginSettingsViewModel);
- }
-
- Plugins.Sort(i => i.Plugin.Info.Name);
+ return data => data.Info.Name.Contains(text, StringComparison.InvariantCultureIgnoreCase) ||
+ (data.Info.Description != null && data.Info.Description.Contains(text, StringComparison.InvariantCultureIgnoreCase));
}
}
}
\ No newline at end of file