1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00
Artemis/src/Artemis.UI/Screens/Settings/Tabs/PluginsTabViewModel.cs

121 lines
5.0 KiB
C#

using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.DryIoc.Factories;
using Artemis.UI.Screens.Plugins;
using Artemis.UI.Shared.Routing;
using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Builders;
using Avalonia.ReactiveUI;
using DynamicData;
using DynamicData.Binding;
using ReactiveUI;
namespace Artemis.UI.Screens.Settings;
public class PluginsTabViewModel : RoutableScreen
{
private readonly INotificationService _notificationService;
private readonly IPluginManagementService _pluginManagementService;
private readonly IWindowService _windowService;
private string? _searchPluginInput;
public PluginsTabViewModel(IPluginManagementService pluginManagementService, INotificationService notificationService, IWindowService windowService, ISettingsVmFactory settingsVmFactory)
{
_pluginManagementService = pluginManagementService;
_notificationService = notificationService;
_windowService = windowService;
DisplayName = "Plugins";
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))
.Transform(settingsVmFactory.PluginSettingsViewModel)
.ObserveOn(AvaloniaScheduler.Instance)
.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 ReadOnlyObservableCollection<PluginSettingsViewModel> Plugins { get; }
public ReactiveCommand<Unit, Unit> ImportPlugin { get; }
public string? SearchPluginInput
{
get => _searchPluginInput;
set => RaiseAndSetIfChanged(ref _searchPluginInput, value);
}
public void OpenUrl(string url)
{
Utilities.OpenUrl(url);
}
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;
try
{
Plugin? plugin = _pluginManagementService.ImportPlugin(files[0]);
if (plugin == null)
{
await _windowService.ShowConfirmContentDialog("Failed to import plugin", "Make sure it is compatible with the current platform.", "Close", null);
return;
}
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? settingsViewModel = Plugins.FirstOrDefault(i => i.PluginViewModel.Plugin == plugin);
if (settingsViewModel != null && !settingsViewModel.PluginViewModel.IsEnabled)
await settingsViewModel.PluginViewModel.UpdateEnabled(true);
_notificationService.CreateNotification()
.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);
}
}
private Func<Plugin, bool> CreatePredicate(string? text)
{
if (string.IsNullOrWhiteSpace(text))
return _ => true;
return data => data.Info.Name.Contains(text, StringComparison.InvariantCultureIgnoreCase) ||
(data.Info.Description != null && data.Info.Description.Contains(text, StringComparison.InvariantCultureIgnoreCase));
}
}