1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Plugins - Added importing

This commit is contained in:
Robert 2022-06-18 22:32:03 +02:00
parent f89e7d43fe
commit b875e3d366
4 changed files with 52 additions and 51 deletions

View File

@ -75,6 +75,7 @@
<entry key="Artemis.UI/Screens/Root/SplashView.axaml" value="Artemis.UI.Windows/Artemis.UI.Windows.csproj" />
<entry key="Artemis.UI/Screens/Settings/Tabs/AboutTabView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/Settings/Tabs/GeneralTabView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/Settings/Tabs/PluginsTabView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/Sidebar/ContentDialogs/SidebarCategoryEditView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/Sidebar/Dialogs/ModuleActivationRequirementView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />
<entry key="Artemis.UI/Screens/Sidebar/Dialogs/ModuleActivationRequirementsView.axaml" value="Artemis.UI.Linux/Artemis.UI.Linux.csproj" />

View File

@ -476,6 +476,7 @@ namespace Artemis.Core.Services
lock (_plugins)
{
_plugins.Remove(plugin);
OnPluginUnloaded(new PluginEventArgs(plugin));
}
}

View File

@ -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">
<Grid RowDefinitions="Auto,*" ColumnDefinitions="*,*" Width="900">
<TextBox Grid.Row="0" Grid.Column="0" Text="{Binding SearchPluginInput}" Watermark="Search plugins" Margin="0 10" />
<TextBox Grid.Row="0" Grid.Column="0" Classes="clearButton" Text="{CompiledBinding SearchPluginInput}" Watermark="Search plugins" Margin="0 10" />
<Button Grid.Row="0" Grid.Column="1" Classes="accent" Command="{CompiledBinding ImportPlugin}" HorizontalAlignment="Right">Import plugin</Button>
<ScrollViewer Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<ItemsControl Items="{Binding Plugins}" />
<ItemsControl Items="{CompiledBinding Plugins}" />
</ScrollViewer>
</Grid>
</UserControl>

View File

@ -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<PluginSettingsViewModel>();
this.WhenAnyValue(x => x.SearchPluginInput).Throttle(TimeSpan.FromMilliseconds(100)).Subscribe(SearchPlugins);
this.WhenActivated((CompositeDisposable _) => GetPluginInstances());
SourceList<Plugin> plugins = new();
IObservable<Func<Plugin, bool>> pluginFilter = this.WhenAnyValue(vm => vm.SearchPluginInput).Throttle(TimeSpan.FromMilliseconds(100)).Select(CreatePredicate);
plugins.Connect()
.Filter(pluginFilter)
.Sort(SortExpressionComparer<Plugin>.Ascending(p => p.Info.Name))
.TransformAsync(p => Dispatcher.UIThread.InvokeAsync(() => _settingsVmFactory.CreatePluginSettingsViewModel(p), DispatcherPriority.Background))
.Bind(out ReadOnlyObservableCollection<PluginSettingsViewModel> pluginViewModels)
.Subscribe();
Plugins = pluginViewModels;
this.WhenActivated(d =>
{
plugins.AddRange(_pluginManagementService.GetAllPlugins());
Observable.FromEventPattern<PluginEventArgs>(x => _pluginManagementService.PluginLoaded += x, x => _pluginManagementService.PluginLoaded -= x)
.Subscribe(a => plugins.Add(a.EventArgs.Plugin))
.DisposeWith(d);
Observable.FromEventPattern<PluginEventArgs>(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<PluginSettingsViewModel> Plugins { get; }
public ReadOnlyObservableCollection<PluginSettingsViewModel> Plugins { get; }
public ReactiveCommand<Unit, Unit> 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<Plugin, bool> 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<PluginSettingsViewModel> 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));
}
}
}