Plugins - Separated VMs into folders

Plugins - Added help pages
This commit is contained in:
Robert committed 2023-04-13 17:41:54 +02:00
1 parent 2f270c9c03
commit b59e898dd3
29 files changed
+350 -43

No files matched your search

@@ -0,0 +1,92 @@
<UserControl xmlns="https://github.com/avaloniaui"
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:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
xmlns:plugins="clr-namespace:Artemis.UI.Screens.Plugins"
xmlns:features="clr-namespace:Artemis.UI.Screens.Plugins.Features"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.Plugins.Features.PluginFeatureView"
x:DataType="features:PluginFeatureViewModel">
<Grid ColumnDefinitions="30,*,Auto">
<Grid.ContextFlyout>
<MenuFlyout>
<MenuItem Header="Install prerequisites" Command="{CompiledBinding InstallPrerequisites}">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="CheckAll" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Remove prerequisites" Command="{CompiledBinding RemovePrerequisites}">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Delete" />
</MenuItem.Icon>
</MenuItem>
</MenuFlyout>
</Grid.ContextFlyout>
<!-- Icon column -->
<avalonia:MaterialIcon Grid.Column="0"
ToolTip.Tip="{CompiledBinding FeatureType}"
Kind="{CompiledBinding FeatureIcon}"
Width="20"
Height="20"
IsVisible="{CompiledBinding LoadException, Converter={x:Static ObjectConverters.IsNull}}" />
<Button Grid.Column="0"
Classes="AppBarButton icon-button"
IsVisible="{CompiledBinding LoadException, Converter={x:Static ObjectConverters.IsNotNull}}"
Foreground="#E74C4C"
ToolTip.Tip="An exception occurred while enabling this feature, click to view"
Command="{Binding ViewLoadException}">
<avalonia:MaterialIcon Kind="AlertCircle" />
</Button>
<!-- Display name column -->
<TextBlock Grid.Column="1"
Text="{CompiledBinding FeatureInfo.Name}"
TextWrapping="Wrap"
VerticalAlignment="Center"
ToolTip.Tip="{CompiledBinding FeatureInfo.Description}" />
<!-- Enable toggle column -->
<StackPanel Grid.Column="2"
HorizontalAlignment="Right"
IsVisible="{CompiledBinding !Enabling}"
Orientation="Horizontal"
ToolTip.Tip="This feature cannot be disabled without disabling the whole plugin">
<avalonia:MaterialIcon Kind="ShieldHalfFull"
ToolTip.Tip="Plugin requires admin rights"
VerticalAlignment="Center"
Margin="0 0 5 0"
IsVisible="{CompiledBinding ShowShield}" />
<CheckBox IsChecked="{CompiledBinding IsEnabled}" IsEnabled="{CompiledBinding CanToggleEnabled}">
Enable feature
</CheckBox>
<Button Classes="icon-button" Margin="5 0 0 0" IsVisible="{CompiledBinding CanInstallPrerequisites}">
<Button.Flyout>
<MenuFlyout Placement="Bottom" >
<MenuItem Header="Install prerequisites" Command="{CompiledBinding InstallPrerequisites}">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="CheckAll" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Remove prerequisites" Command="{CompiledBinding RemovePrerequisites}">
<MenuItem.Icon>
<avalonia:MaterialIcon Kind="Delete" />
</MenuItem.Icon>
</MenuItem>
</MenuFlyout>
</Button.Flyout>
<avalonia:MaterialIcon Kind="DotsVertical"></avalonia:MaterialIcon>
</Button>
</StackPanel>
<StackPanel Grid.Column="2" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="7" IsVisible="{CompiledBinding Enabling}">
<ProgressBar Value="0" IsIndeterminate="True" />
</StackPanel>
</Grid>
</UserControl>
@@ -0,0 +1,12 @@
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.Plugins.Features;
public partial class PluginFeatureView : ReactiveUserControl<PluginFeatureViewModel>
{
public PluginFeatureView()
{
InitializeComponent();
}
}
@@ -0,0 +1,257 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive;
using System.Reactive.Disposables;
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.Core.DeviceProviders;
using Artemis.Core.LayerBrushes;
using Artemis.Core.LayerEffects;
using Artemis.Core.Modules;
using Artemis.Core.ScriptingProviders;
using Artemis.Core.Services;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Builders;
using Avalonia.Threading;
using Material.Icons;
using ReactiveUI;
namespace Artemis.UI.Screens.Plugins.Features;
public class PluginFeatureViewModel : ActivatableViewModelBase
{
private readonly ICoreService _coreService;
private readonly INotificationService _notificationService;
private readonly IPluginManagementService _pluginManagementService;
private readonly IWindowService _windowService;
private bool _enabling;
public PluginFeatureViewModel(PluginFeatureInfo pluginFeatureInfo,
bool showShield,
ICoreService coreService,
IWindowService windowService,
INotificationService notificationService,
IPluginManagementService pluginManagementService)
{
_coreService = coreService;
_windowService = windowService;
_notificationService = notificationService;
_pluginManagementService = pluginManagementService;
FeatureInfo = pluginFeatureInfo;
ShowShield = FeatureInfo.Plugin.Info.RequiresAdmin && showShield;
ShowLogsFolder = ReactiveCommand.Create(ExecuteShowLogsFolder);
ViewLoadException = ReactiveCommand.Create(ExecuteViewLoadException);
InstallPrerequisites = ReactiveCommand.CreateFromTask(ExecuteInstallPrerequisites);
RemovePrerequisites = ReactiveCommand.CreateFromTask(ExecuteRemovePrerequisites);
this.WhenActivated(d =>
{
_pluginManagementService.PluginFeatureEnabling += OnFeatureEnabling;
_pluginManagementService.PluginFeatureEnabled += OnFeatureEnableStopped;
_pluginManagementService.PluginFeatureEnableFailed += OnFeatureEnableStopped;
FeatureInfo.Plugin.Enabled += PluginOnToggled;
FeatureInfo.Plugin.Disabled += PluginOnToggled;
Disposable.Create(() =>
{
_pluginManagementService.PluginFeatureEnabling -= OnFeatureEnabling;
_pluginManagementService.PluginFeatureEnabled -= OnFeatureEnableStopped;
_pluginManagementService.PluginFeatureEnableFailed -= OnFeatureEnableStopped;
FeatureInfo.Plugin.Enabled -= PluginOnToggled;
FeatureInfo.Plugin.Disabled -= PluginOnToggled;
}).DisposeWith(d);
});
}
public ReactiveCommand<Unit, Unit> ShowLogsFolder { get; }
public ReactiveCommand<Unit, Unit> ViewLoadException { get; }
public ReactiveCommand<Unit, Unit> InstallPrerequisites { get; }
public ReactiveCommand<Unit, Unit> RemovePrerequisites { get; }
public PluginFeatureInfo FeatureInfo { get; }
public Exception? LoadException => FeatureInfo.LoadException;
public bool ShowShield { get; }
public bool Enabling
{
get => _enabling;
set => RaiseAndSetIfChanged(ref _enabling, value);
}
public bool IsEnabled
{
get => FeatureInfo.AlwaysEnabled || (FeatureInfo.Instance != null && FeatureInfo.Instance.IsEnabled);
set => Dispatcher.UIThread.InvokeAsync(() => UpdateEnabled(value));
}
public bool CanToggleEnabled => FeatureInfo.Plugin.IsEnabled && !FeatureInfo.AlwaysEnabled;
public bool CanInstallPrerequisites => FeatureInfo.PlatformPrerequisites.Any();
public bool CanRemovePrerequisites => FeatureInfo.PlatformPrerequisites.Any(p => p.UninstallActions.Any());
public bool IsPopupEnabled => CanInstallPrerequisites || CanRemovePrerequisites;
public MaterialIconKind FeatureIcon
{
get
{
if (FeatureInfo.FeatureType.IsAssignableTo(typeof(DeviceProvider)))
return MaterialIconKind.Devices;
if (FeatureInfo.FeatureType.IsAssignableTo(typeof(Module)))
return MaterialIconKind.VectorRectangle;
if (FeatureInfo.FeatureType.IsAssignableTo(typeof(LayerBrushProvider)))
return MaterialIconKind.Brush;
if (FeatureInfo.FeatureType.IsAssignableTo(typeof(LayerEffectProvider)))
return MaterialIconKind.AutoAwesome;
if (FeatureInfo.FeatureType.IsAssignableTo(typeof(ScriptingProvider)))
return MaterialIconKind.Code;
return MaterialIconKind.Extension;
}
}
public string FeatureType
{
get
{
if (FeatureInfo.FeatureType.IsAssignableTo(typeof(DeviceProvider)))
return "Device Provider";
if (FeatureInfo.FeatureType.IsAssignableTo(typeof(Module)))
return "Module";
if (FeatureInfo.FeatureType.IsAssignableTo(typeof(LayerBrushProvider)))
return "Layer Brush";
if (FeatureInfo.FeatureType.IsAssignableTo(typeof(LayerEffectProvider)))
return "Layer Effect";
if (FeatureInfo.FeatureType.IsAssignableTo(typeof(ScriptingProvider)))
return "Scripting Provider";
return "Miscellaneous feature";
}
}
private void ExecuteShowLogsFolder()
{
try
{
Utilities.OpenFolder(Constants.LogsFolder);
}
catch (Exception e)
{
_windowService.ShowExceptionDialog("Welp, we couldn\'t open the logs folder for you", e);
}
}
private void ExecuteViewLoadException()
{
if (LoadException != null)
_windowService.ShowExceptionDialog("Feature failed to enable", LoadException);
}
private async Task ExecuteInstallPrerequisites()
{
if (FeatureInfo.PlatformPrerequisites.Any())
await PluginPrerequisitesInstallDialogViewModel.Show(_windowService, new List<IPrerequisitesSubject> {FeatureInfo});
}
private async Task ExecuteRemovePrerequisites()
{
if (FeatureInfo.PlatformPrerequisites.Any(p => p.UninstallActions.Any()))
{
await PluginPrerequisitesUninstallDialogViewModel.Show(_windowService, new List<IPrerequisitesSubject> {FeatureInfo});
this.RaisePropertyChanged(nameof(IsEnabled));
}
}
private async Task UpdateEnabled(bool enable)
{
if (IsEnabled == enable)
{
this.RaisePropertyChanged(nameof(IsEnabled));
return;
}
if (FeatureInfo.Instance == null)
{
this.RaisePropertyChanged(nameof(IsEnabled));
_notificationService.CreateNotification()
.WithMessage($"Feature '{FeatureInfo.Name}' is in a broken state and cannot enable.")
.HavingButton(b => b.WithText("View logs").WithCommand(ShowLogsFolder))
.WithSeverity(NotificationSeverity.Error)
.Show();
return;
}
if (enable)
{
Enabling = true;
try
{
if (FeatureInfo.Plugin.Info.RequiresAdmin && !_coreService.IsElevated)
{
bool confirmed = await _windowService.ShowConfirmContentDialog("Enable feature", "The plugin of this feature requires admin rights, are you sure you want to enable it?");
if (!confirmed)
{
this.RaisePropertyChanged(nameof(IsEnabled));
return;
}
}
// Check if all prerequisites are met async
if (!FeatureInfo.ArePrerequisitesMet())
{
await PluginPrerequisitesInstallDialogViewModel.Show(_windowService, new List<IPrerequisitesSubject> {FeatureInfo});
if (!FeatureInfo.ArePrerequisitesMet())
{
this.RaisePropertyChanged(nameof(IsEnabled));
return;
}
}
await Dispatcher.UIThread.InvokeAsync(() => _pluginManagementService.EnablePluginFeature(FeatureInfo.Instance!, true));
}
catch (Exception e)
{
_notificationService.CreateNotification()
.WithMessage($"Failed to enable '{FeatureInfo.Name}'.\r\n{e.Message}")
.HavingButton(b => b.WithText("View logs").WithCommand(ShowLogsFolder))
.WithSeverity(NotificationSeverity.Error)
.Show();
}
finally
{
Enabling = false;
}
}
else
{
_pluginManagementService.DisablePluginFeature(FeatureInfo.Instance, true);
this.RaisePropertyChanged(nameof(IsEnabled));
}
}
private void OnFeatureEnabling(object? sender, PluginFeatureEventArgs e)
{
if (e.PluginFeature != FeatureInfo.Instance) return;
Enabling = true;
}
private void OnFeatureEnableStopped(object? sender, PluginFeatureEventArgs e)
{
if (e.PluginFeature != FeatureInfo.Instance) return;
Enabling = false;
this.RaisePropertyChanged(nameof(IsEnabled));
this.RaisePropertyChanged(nameof(LoadException));
}
private void PluginOnToggled(object? sender, EventArgs e)
{
this.RaisePropertyChanged(nameof(CanToggleEnabled));
}
}