mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Plugins - Reworked bootstrapper
This commit is contained in:
parent
5cae14efd3
commit
21700aaad5
@ -1,26 +0,0 @@
|
|||||||
namespace Artemis.Core
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// An optional entry point for your plugin
|
|
||||||
/// </summary>
|
|
||||||
public interface IPluginBootstrapper
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Called when the plugin is loaded
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="plugin"></param>
|
|
||||||
void OnPluginLoaded(Plugin plugin);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called when the plugin is activated
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="plugin">The plugin instance of your plugin</param>
|
|
||||||
void OnPluginEnabled(Plugin plugin);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called when the plugin is deactivated or when Artemis shuts down
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="plugin">The plugin instance of your plugin</param>
|
|
||||||
void OnPluginDisabled(Plugin plugin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -71,7 +71,7 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the plugin bootstrapper
|
/// Gets the plugin bootstrapper
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IPluginBootstrapper? Bootstrapper { get; internal set; }
|
public PluginBootstrapper? Bootstrapper { get; internal set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Ninject kernel of the plugin
|
/// The Ninject kernel of the plugin
|
||||||
@ -118,10 +118,11 @@ namespace Artemis.Core
|
|||||||
/// Looks up the feature info the feature of type <typeparamref name="T" />
|
/// Looks up the feature info the feature of type <typeparamref name="T" />
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of feature to find</typeparam>
|
/// <typeparam name="T">The type of feature to find</typeparam>
|
||||||
/// <returns>If found, feature info of the feature</returns>
|
/// <returns>Feature info of the feature</returns>
|
||||||
public PluginFeatureInfo? GetFeatureInfo<T>() where T : PluginFeature
|
public PluginFeatureInfo GetFeatureInfo<T>() where T : PluginFeature
|
||||||
{
|
{
|
||||||
return _features.FirstOrDefault(i => i.FeatureType == typeof(T));
|
// This should be a safe assumption because any type of PluginFeature is registered and added
|
||||||
|
return _features.First(i => i.FeatureType == typeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
100
src/Artemis.Core/Plugins/PluginBootstrapper.cs
Normal file
100
src/Artemis.Core/Plugins/PluginBootstrapper.cs
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An optional entry point for your plugin
|
||||||
|
/// </summary>
|
||||||
|
public abstract class PluginBootstrapper
|
||||||
|
{
|
||||||
|
private Plugin? _plugin;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when the plugin is loaded
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="plugin"></param>
|
||||||
|
public virtual void OnPluginLoaded(Plugin plugin)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when the plugin is activated
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="plugin">The plugin instance of your plugin</param>
|
||||||
|
public virtual void OnPluginEnabled(Plugin plugin)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when the plugin is deactivated or when Artemis shuts down
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="plugin">The plugin instance of your plugin</param>
|
||||||
|
public virtual void OnPluginDisabled(Plugin plugin)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the provided prerequisite to the plugin.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="prerequisite">The prerequisite to add</param>
|
||||||
|
public void AddPluginPrerequisite(PluginPrerequisite prerequisite)
|
||||||
|
{
|
||||||
|
// TODO: We can keep track of them and add them after load, same goes for the others
|
||||||
|
if (_plugin == null)
|
||||||
|
throw new ArtemisPluginException("Cannot add plugin prerequisites before the plugin is loaded");
|
||||||
|
|
||||||
|
if (!_plugin.Info.Prerequisites.Contains(prerequisite))
|
||||||
|
_plugin.Info.Prerequisites.Add(prerequisite);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the provided prerequisite from the plugin.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="prerequisite">The prerequisite to remove</param>
|
||||||
|
/// <returns>
|
||||||
|
/// <see langword="true" /> is successfully removed; otherwise <see langword="false" />. This method also returns
|
||||||
|
/// <see langword="false" /> if the prerequisite was not found.
|
||||||
|
/// </returns>
|
||||||
|
public bool RemovePluginPrerequisite(PluginPrerequisite prerequisite)
|
||||||
|
{
|
||||||
|
if (_plugin == null)
|
||||||
|
throw new ArtemisPluginException("Cannot add plugin prerequisites before the plugin is loaded");
|
||||||
|
|
||||||
|
return _plugin.Info.Prerequisites.Remove(prerequisite);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the provided prerequisite to the feature of type <typeparamref name="T"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="prerequisite">The prerequisite to add</param>
|
||||||
|
public void AddFeaturePrerequisite<T>(PluginPrerequisite prerequisite) where T : PluginFeature
|
||||||
|
{
|
||||||
|
if (_plugin == null)
|
||||||
|
throw new ArtemisPluginException("Cannot add feature prerequisites before the plugin is loaded");
|
||||||
|
|
||||||
|
PluginFeatureInfo info = _plugin.GetFeatureInfo<T>();
|
||||||
|
if (!info.Prerequisites.Contains(prerequisite))
|
||||||
|
info.Prerequisites.Add(prerequisite);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the provided prerequisite from the feature of type <typeparamref name="T"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="prerequisite">The prerequisite to remove</param>
|
||||||
|
/// <returns>
|
||||||
|
/// <see langword="true" /> is successfully removed; otherwise <see langword="false" />. This method also returns
|
||||||
|
/// <see langword="false" /> if the prerequisite was not found.
|
||||||
|
/// </returns>
|
||||||
|
public bool RemoveFeaturePrerequisite<T>(PluginPrerequisite prerequisite) where T : PluginFeature
|
||||||
|
{
|
||||||
|
if (_plugin == null)
|
||||||
|
throw new ArtemisPluginException("Cannot add feature prerequisites before the plugin is loaded");
|
||||||
|
|
||||||
|
return _plugin.GetFeatureInfo<T>().Prerequisites.Remove(prerequisite);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void InternalOnPluginLoaded(Plugin plugin)
|
||||||
|
{
|
||||||
|
_plugin = plugin;
|
||||||
|
OnPluginLoaded(plugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -15,7 +15,7 @@ namespace Artemis.Core
|
|||||||
/// Represents basic info about a plugin feature and contains a reference to the instance of said feature
|
/// Represents basic info about a plugin feature and contains a reference to the instance of said feature
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonObject(MemberSerialization.OptIn)]
|
[JsonObject(MemberSerialization.OptIn)]
|
||||||
public class PluginFeatureInfo : CorePropertyChanged
|
public class PluginFeatureInfo : CorePropertyChanged, IPrerequisitesSubject
|
||||||
{
|
{
|
||||||
private string? _description;
|
private string? _description;
|
||||||
private string? _icon;
|
private string? _icon;
|
||||||
@ -31,7 +31,7 @@ namespace Artemis.Core
|
|||||||
Description = attribute?.Description;
|
Description = attribute?.Description;
|
||||||
Icon = attribute?.Icon;
|
Icon = attribute?.Icon;
|
||||||
AlwaysEnabled = attribute?.AlwaysEnabled ?? false;
|
AlwaysEnabled = attribute?.AlwaysEnabled ?? false;
|
||||||
|
|
||||||
if (Icon != null) return;
|
if (Icon != null) return;
|
||||||
if (typeof(BaseDataModelExpansion).IsAssignableFrom(featureType))
|
if (typeof(BaseDataModelExpansion).IsAssignableFrom(featureType))
|
||||||
Icon = "TableAdd";
|
Icon = "TableAdd";
|
||||||
@ -130,18 +130,11 @@ namespace Artemis.Core
|
|||||||
internal set => SetAndNotify(ref _instance, value);
|
internal set => SetAndNotify(ref _instance, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Gets a list of prerequisites for this plugin feature
|
|
||||||
/// </summary>
|
|
||||||
public List<PluginPrerequisite> Prerequisites { get; } = new();
|
public List<PluginPrerequisite> Prerequisites { get; } = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Determines whether the prerequisites of this feature are met
|
public bool ArePrerequisitesMet() => Prerequisites.All(p => p.IsMet());
|
||||||
/// </summary>
|
|
||||||
public bool ArePrerequisitesMet()
|
|
||||||
{
|
|
||||||
return Prerequisites.All(p => p.IsMet());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
|
|||||||
@ -10,7 +10,7 @@ namespace Artemis.Core
|
|||||||
/// Represents basic info about a plugin and contains a reference to the instance of said plugin
|
/// Represents basic info about a plugin and contains a reference to the instance of said plugin
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonObject(MemberSerialization.OptIn)]
|
[JsonObject(MemberSerialization.OptIn)]
|
||||||
public class PluginInfo : CorePropertyChanged
|
public class PluginInfo : CorePropertyChanged, IPrerequisitesSubject
|
||||||
{
|
{
|
||||||
private bool _autoEnableFeatures = true;
|
private bool _autoEnableFeatures = true;
|
||||||
private string? _description;
|
private string? _description;
|
||||||
@ -119,19 +119,11 @@ namespace Artemis.Core
|
|||||||
internal set => SetAndNotify(ref _plugin, value);
|
internal set => SetAndNotify(ref _plugin, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Gets a list of prerequisites for this plugin
|
|
||||||
/// </summary>
|
|
||||||
public List<PluginPrerequisite> Prerequisites { get; } = new();
|
public List<PluginPrerequisite> Prerequisites { get; } = new();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
/// <summary>
|
public bool ArePrerequisitesMet() => Prerequisites.All(p => p.IsMet());
|
||||||
/// Determines whether the prerequisites of this plugin are met
|
|
||||||
/// </summary>
|
|
||||||
public bool ArePrerequisitesMet()
|
|
||||||
{
|
|
||||||
return Prerequisites.All(p => p.IsMet());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
|
|||||||
@ -0,0 +1,21 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a type that has prerequisites
|
||||||
|
/// </summary>
|
||||||
|
public interface IPrerequisitesSubject
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a list of prerequisites for this plugin
|
||||||
|
/// </summary>
|
||||||
|
List<PluginPrerequisite> Prerequisites { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the prerequisites of this plugin are met
|
||||||
|
/// </summary>
|
||||||
|
bool ArePrerequisitesMet();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@ -11,24 +12,6 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
private PluginPrerequisiteAction? _currentAction;
|
private PluginPrerequisiteAction? _currentAction;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new instance of the <see cref="PluginPrerequisite" /> class
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="plugin">The plugin this is a prerequisite for</param>
|
|
||||||
protected PluginPrerequisite(Plugin plugin)
|
|
||||||
{
|
|
||||||
Plugin = plugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new instance of the <see cref="PluginPrerequisite" /> class
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="pluginFeature">The plugin feature this is a prerequisite for</param>
|
|
||||||
protected PluginPrerequisite(PluginFeature pluginFeature)
|
|
||||||
{
|
|
||||||
PluginFeature = pluginFeature;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the name of the prerequisite
|
/// Gets the name of the prerequisite
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -63,18 +46,6 @@ namespace Artemis.Core
|
|||||||
private set => SetAndNotify(ref _currentAction, value);
|
private set => SetAndNotify(ref _currentAction, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the plugin this prerequisite is for
|
|
||||||
/// <para>Note: Only one plugin or a plugin feature can be set at once</para>
|
|
||||||
/// </summary>
|
|
||||||
public Plugin? Plugin { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the feature this prerequisite is for
|
|
||||||
/// <para>Note: Only one plugin or a plugin feature can be set at once</para>
|
|
||||||
/// </summary>
|
|
||||||
public PluginFeature? PluginFeature { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Execute all install actions
|
/// Execute all install actions
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -345,13 +345,13 @@ namespace Artemis.Core.Services
|
|||||||
if (!featureTypes.Any())
|
if (!featureTypes.Any())
|
||||||
_logger.Warning("Plugin {plugin} contains no features", plugin);
|
_logger.Warning("Plugin {plugin} contains no features", plugin);
|
||||||
|
|
||||||
List<Type> bootstrappers = plugin.Assembly.GetTypes().Where(t => typeof(IPluginBootstrapper).IsAssignableFrom(t)).ToList();
|
List<Type> bootstrappers = plugin.Assembly.GetTypes().Where(t => typeof(PluginBootstrapper).IsAssignableFrom(t)).ToList();
|
||||||
if (bootstrappers.Count > 1)
|
if (bootstrappers.Count > 1)
|
||||||
_logger.Warning($"{plugin} has more than one bootstrapper, only initializing {bootstrappers.First().FullName}");
|
_logger.Warning($"{plugin} has more than one bootstrapper, only initializing {bootstrappers.First().FullName}");
|
||||||
if (bootstrappers.Any())
|
if (bootstrappers.Any())
|
||||||
{
|
{
|
||||||
plugin.Bootstrapper = (IPluginBootstrapper?) Activator.CreateInstance(bootstrappers.First());
|
plugin.Bootstrapper = (PluginBootstrapper?) Activator.CreateInstance(bootstrappers.First());
|
||||||
plugin.Bootstrapper?.OnPluginLoaded(plugin);
|
plugin.Bootstrapper?.InternalOnPluginLoaded(plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
lock (_plugins)
|
lock (_plugins)
|
||||||
|
|||||||
@ -20,19 +20,19 @@ namespace Artemis.UI.Screens.Plugins
|
|||||||
private bool _isFinished;
|
private bool _isFinished;
|
||||||
private CancellationTokenSource _tokenSource;
|
private CancellationTokenSource _tokenSource;
|
||||||
|
|
||||||
public PluginPrerequisitesInstallDialogViewModel(object pluginOrFeature, IPrerequisitesVmFactory prerequisitesVmFactory, IDialogService dialogService)
|
public PluginPrerequisitesInstallDialogViewModel(IPrerequisitesSubject subject, IPrerequisitesVmFactory prerequisitesVmFactory, IDialogService dialogService)
|
||||||
{
|
{
|
||||||
_dialogService = dialogService;
|
_dialogService = dialogService;
|
||||||
// Constructor overloading doesn't work very well with Kernel.Get<T> :(
|
// Constructor overloading doesn't work very well with Kernel.Get<T> :(
|
||||||
if (pluginOrFeature is Plugin plugin)
|
if (subject is PluginInfo plugin)
|
||||||
{
|
{
|
||||||
Plugin = plugin;
|
PluginInfo = plugin;
|
||||||
Prerequisites = new BindableCollection<PluginPrerequisiteViewModel>(plugin.Info.Prerequisites.Select(p => prerequisitesVmFactory.PluginPrerequisiteViewModel(p, false)));
|
Prerequisites = new BindableCollection<PluginPrerequisiteViewModel>(plugin.Prerequisites.Select(p => prerequisitesVmFactory.PluginPrerequisiteViewModel(p, false)));
|
||||||
}
|
}
|
||||||
else if (pluginOrFeature is PluginFeature feature)
|
else if (subject is PluginFeatureInfo feature)
|
||||||
{
|
{
|
||||||
Feature = feature;
|
FeatureInfo = feature;
|
||||||
Prerequisites = new BindableCollection<PluginPrerequisiteViewModel>(feature.Info.Prerequisites.Select(p => prerequisitesVmFactory.PluginPrerequisiteViewModel(p, false)));
|
Prerequisites = new BindableCollection<PluginPrerequisiteViewModel>(feature.Prerequisites.Select(p => prerequisitesVmFactory.PluginPrerequisiteViewModel(p, false)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
throw new ArtemisUIException($"Expected plugin or feature to be passed to {nameof(PluginPrerequisitesInstallDialogViewModel)}");
|
throw new ArtemisUIException($"Expected plugin or feature to be passed to {nameof(PluginPrerequisitesInstallDialogViewModel)}");
|
||||||
@ -42,8 +42,8 @@ namespace Artemis.UI.Screens.Plugins
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public PluginFeature Feature { get; }
|
public PluginInfo PluginInfo { get; }
|
||||||
public Plugin Plugin { get; }
|
public PluginFeatureInfo FeatureInfo { get; }
|
||||||
public BindableCollection<PluginPrerequisiteViewModel> Prerequisites { get; }
|
public BindableCollection<PluginPrerequisiteViewModel> Prerequisites { get; }
|
||||||
|
|
||||||
public PluginPrerequisiteViewModel ActivePrerequisite
|
public PluginPrerequisiteViewModel ActivePrerequisite
|
||||||
@ -64,6 +64,9 @@ namespace Artemis.UI.Screens.Plugins
|
|||||||
set => SetAndNotify(ref _isFinished, value);
|
set => SetAndNotify(ref _isFinished, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsSubjectPlugin => PluginInfo != null;
|
||||||
|
public bool IsSubjectFeature => FeatureInfo != null;
|
||||||
|
|
||||||
#region Overrides of DialogViewModelBase
|
#region Overrides of DialogViewModelBase
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -111,7 +114,7 @@ namespace Artemis.UI.Screens.Plugins
|
|||||||
"Confirm",
|
"Confirm",
|
||||||
""
|
""
|
||||||
);
|
);
|
||||||
await _dialogService.ShowDialog<PluginPrerequisitesInstallDialogViewModel>(new Dictionary<string, object> {{"pluginOrFeature", Plugin}});
|
await _dialogService.ShowDialog<PluginPrerequisitesInstallDialogViewModel>(new Dictionary<string, object> {{"subject", PluginInfo}});
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
@ -136,10 +139,10 @@ namespace Artemis.UI.Screens.Plugins
|
|||||||
protected override void OnInitialActivate()
|
protected override void OnInitialActivate()
|
||||||
{
|
{
|
||||||
CanInstall = false;
|
CanInstall = false;
|
||||||
if (Plugin != null)
|
if (PluginInfo != null)
|
||||||
Task.Run(() => CanInstall = !Plugin.Info.ArePrerequisitesMet());
|
Task.Run(() => CanInstall = !PluginInfo.ArePrerequisitesMet());
|
||||||
else
|
else
|
||||||
Task.Run(() => CanInstall = !Feature.Info.ArePrerequisitesMet());
|
Task.Run(() => CanInstall = !FeatureInfo.ArePrerequisitesMet());
|
||||||
|
|
||||||
base.OnInitialActivate();
|
base.OnInitialActivate();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,22 +22,22 @@ namespace Artemis.UI.Screens.Plugins
|
|||||||
private bool _isFinished;
|
private bool _isFinished;
|
||||||
private CancellationTokenSource _tokenSource;
|
private CancellationTokenSource _tokenSource;
|
||||||
|
|
||||||
public PluginPrerequisitesUninstallDialogViewModel(object pluginOrFeature, IPrerequisitesVmFactory prerequisitesVmFactory, IDialogService dialogService,
|
public PluginPrerequisitesUninstallDialogViewModel(IPrerequisitesSubject subject, IPrerequisitesVmFactory prerequisitesVmFactory, IDialogService dialogService,
|
||||||
IPluginManagementService pluginManagementService)
|
IPluginManagementService pluginManagementService)
|
||||||
{
|
{
|
||||||
_dialogService = dialogService;
|
_dialogService = dialogService;
|
||||||
_pluginManagementService = pluginManagementService;
|
_pluginManagementService = pluginManagementService;
|
||||||
|
|
||||||
// Constructor overloading doesn't work very well with Kernel.Get<T> :(
|
// Constructor overloading doesn't work very well with Kernel.Get<T> :(
|
||||||
if (pluginOrFeature is Plugin plugin)
|
if (subject is PluginInfo plugin)
|
||||||
{
|
{
|
||||||
Plugin = plugin;
|
PluginInfo = plugin;
|
||||||
Prerequisites = new BindableCollection<PluginPrerequisiteViewModel>(plugin.Info.Prerequisites.Select(p => prerequisitesVmFactory.PluginPrerequisiteViewModel(p, true)));
|
Prerequisites = new BindableCollection<PluginPrerequisiteViewModel>(plugin.Prerequisites.Select(p => prerequisitesVmFactory.PluginPrerequisiteViewModel(p, true)));
|
||||||
}
|
}
|
||||||
else if (pluginOrFeature is PluginFeature feature)
|
else if (subject is PluginFeatureInfo feature)
|
||||||
{
|
{
|
||||||
Feature = feature;
|
FeatureInfo = feature;
|
||||||
Prerequisites = new BindableCollection<PluginPrerequisiteViewModel>(feature.Info.Prerequisites.Select(p => prerequisitesVmFactory.PluginPrerequisiteViewModel(p, true)));
|
Prerequisites = new BindableCollection<PluginPrerequisiteViewModel>(feature.Prerequisites.Select(p => prerequisitesVmFactory.PluginPrerequisiteViewModel(p, true)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
throw new ArtemisUIException($"Expected plugin or feature to be passed to {nameof(PluginPrerequisitesInstallDialogViewModel)}");
|
throw new ArtemisUIException($"Expected plugin or feature to be passed to {nameof(PluginPrerequisitesInstallDialogViewModel)}");
|
||||||
@ -47,8 +47,8 @@ namespace Artemis.UI.Screens.Plugins
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public PluginFeature Feature { get; }
|
public PluginFeatureInfo FeatureInfo { get; }
|
||||||
public Plugin Plugin { get; }
|
public PluginInfo PluginInfo { get; }
|
||||||
public BindableCollection<PluginPrerequisiteViewModel> Prerequisites { get; }
|
public BindableCollection<PluginPrerequisiteViewModel> Prerequisites { get; }
|
||||||
|
|
||||||
public PluginPrerequisiteViewModel ActivePrerequisite
|
public PluginPrerequisiteViewModel ActivePrerequisite
|
||||||
@ -69,6 +69,9 @@ namespace Artemis.UI.Screens.Plugins
|
|||||||
set => SetAndNotify(ref _isFinished, value);
|
set => SetAndNotify(ref _isFinished, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsSubjectPlugin => PluginInfo != null;
|
||||||
|
public bool IsSubjectFeature => FeatureInfo != null;
|
||||||
|
|
||||||
#region Overrides of DialogViewModelBase
|
#region Overrides of DialogViewModelBase
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -84,7 +87,10 @@ namespace Artemis.UI.Screens.Plugins
|
|||||||
{
|
{
|
||||||
CanUninstall = false;
|
CanUninstall = false;
|
||||||
|
|
||||||
_pluginManagementService.DisablePlugin(Plugin, true);
|
if (PluginInfo != null)
|
||||||
|
_pluginManagementService.DisablePlugin(PluginInfo.Plugin, true);
|
||||||
|
else if (FeatureInfo?.Instance != null)
|
||||||
|
_pluginManagementService.DisablePluginFeature(FeatureInfo.Instance, true);
|
||||||
_tokenSource = new CancellationTokenSource();
|
_tokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -118,7 +124,7 @@ namespace Artemis.UI.Screens.Plugins
|
|||||||
"Confirm",
|
"Confirm",
|
||||||
""
|
""
|
||||||
);
|
);
|
||||||
await _dialogService.ShowDialog<PluginPrerequisitesUninstallDialogViewModel>(new Dictionary<string, object> {{"pluginOrFeature", Plugin}});
|
await _dialogService.ShowDialog<PluginPrerequisitesUninstallDialogViewModel>(new Dictionary<string, object> {{"subject", PluginInfo}});
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
@ -144,10 +150,10 @@ namespace Artemis.UI.Screens.Plugins
|
|||||||
{
|
{
|
||||||
CanUninstall = false;
|
CanUninstall = false;
|
||||||
// Could be slow so take it off of the UI thread
|
// Could be slow so take it off of the UI thread
|
||||||
if (Plugin != null)
|
if (PluginInfo != null)
|
||||||
Task.Run(() => CanUninstall = Plugin.Info.Prerequisites.Any(p => p.IsMet()));
|
Task.Run(() => CanUninstall = PluginInfo.Prerequisites.Any(p => p.IsMet()));
|
||||||
else
|
else
|
||||||
Task.Run(() => CanUninstall = Feature.Info.Prerequisites.Any(p => p.IsMet()));
|
Task.Run(() => CanUninstall = FeatureInfo.Prerequisites.Any(p => p.IsMet()));
|
||||||
|
|
||||||
base.OnInitialActivate();
|
base.OnInitialActivate();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,13 @@
|
|||||||
d:DesignHeight="450" d:DesignWidth="800"
|
d:DesignHeight="450" d:DesignWidth="800"
|
||||||
d:DataContext="{d:DesignInstance local:PluginFeatureViewModel}">
|
d:DataContext="{d:DesignInstance local:PluginFeatureViewModel}">
|
||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
<converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
|
<ResourceDictionary>
|
||||||
|
<ResourceDictionary.MergedDictionaries>
|
||||||
|
<ResourceDictionary
|
||||||
|
Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.PopupBox.xaml" />
|
||||||
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
<converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
|
||||||
|
</ResourceDictionary>
|
||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
<Grid Margin="-3 -8">
|
<Grid Margin="-3 -8">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
@ -23,11 +29,11 @@
|
|||||||
|
|
||||||
<!-- Icon column -->
|
<!-- Icon column -->
|
||||||
<shared:ArtemisIcon Grid.Column="0"
|
<shared:ArtemisIcon Grid.Column="0"
|
||||||
Icon="{Binding FeatureInfo.Icon}"
|
Icon="{Binding FeatureInfo.Icon}"
|
||||||
Width="20"
|
Width="20"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
Visibility="{Binding LoadException, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted, FallbackValue=Collapsed}" />
|
Visibility="{Binding LoadException, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted, FallbackValue=Collapsed}" />
|
||||||
|
|
||||||
<Button Grid.Column="0"
|
<Button Grid.Column="0"
|
||||||
Margin="-8"
|
Margin="-8"
|
||||||
@ -42,16 +48,16 @@
|
|||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<!-- Display name column -->
|
<!-- Display name column -->
|
||||||
<TextBlock Grid.Column="1"
|
<TextBlock Grid.Column="1"
|
||||||
Text="{Binding FeatureInfo.Name}"
|
Text="{Binding FeatureInfo.Name}"
|
||||||
Style="{StaticResource MaterialDesignTextBlock}"
|
Style="{StaticResource MaterialDesignTextBlock}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
TextWrapping="Wrap"
|
TextWrapping="Wrap"
|
||||||
ToolTip="{Binding FeatureInfo.Description}" />
|
ToolTip="{Binding FeatureInfo.Description}" />
|
||||||
|
|
||||||
<!-- Enable toggle column -->
|
<!-- Enable toggle column -->
|
||||||
<StackPanel Grid.Column="2"
|
<StackPanel Grid.Column="2"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
Margin="8"
|
Margin="8"
|
||||||
Visibility="{Binding Enabling, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay, FallbackValue=Collapsed}"
|
Visibility="{Binding Enabling, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay, FallbackValue=Collapsed}"
|
||||||
Orientation="Horizontal"
|
Orientation="Horizontal"
|
||||||
@ -63,11 +69,31 @@
|
|||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Margin="0 0 5 0"
|
Margin="0 0 5 0"
|
||||||
Visibility="{Binding ShowShield, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}" />
|
Visibility="{Binding ShowShield, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}" />
|
||||||
<CheckBox Style="{StaticResource MaterialDesignCheckBox}"
|
<CheckBox Style="{StaticResource MaterialDesignCheckBox}"
|
||||||
IsChecked="{Binding IsEnabled}"
|
IsChecked="{Binding IsEnabled}"
|
||||||
IsEnabled="{Binding CanToggleEnabled}">
|
IsEnabled="{Binding CanToggleEnabled}">
|
||||||
Feature enabled
|
Feature enabled
|
||||||
</CheckBox>
|
</CheckBox>
|
||||||
|
|
||||||
|
<materialDesign:PopupBox Style="{StaticResource MaterialDesignToolPopupBox}"
|
||||||
|
Margin="0 -4 -10 -4"
|
||||||
|
Foreground="{StaticResource MaterialDesignBody}"
|
||||||
|
IsEnabled="{Binding IsPopupEnabled}">
|
||||||
|
<StackPanel>
|
||||||
|
<Button Command="{s:Action InstallPrerequisites}">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<materialDesign:PackIcon Kind="CheckAll" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
||||||
|
<TextBlock VerticalAlignment="Center">Install prerequisites</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
<Button Command="{s:Action RemovePrerequisites}">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<materialDesign:PackIcon Kind="Delete" Margin="0 0 10 0 " VerticalAlignment="Center" />
|
||||||
|
<TextBlock VerticalAlignment="Center">Remove prerequisites</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
</materialDesign:PopupBox>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Grid.Column="2" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="7"
|
<StackPanel Grid.Column="2" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="7"
|
||||||
Visibility="{Binding Enabling, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay, FallbackValue=Collapsed}">
|
Visibility="{Binding Enabling, Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay, FallbackValue=Collapsed}">
|
||||||
|
|||||||
@ -1,9 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
|
using Artemis.UI.Screens.Plugins;
|
||||||
using Artemis.UI.Shared.Services;
|
using Artemis.UI.Shared.Services;
|
||||||
using Stylet;
|
using Stylet;
|
||||||
|
|
||||||
@ -16,6 +19,9 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
|||||||
private readonly IPluginManagementService _pluginManagementService;
|
private readonly IPluginManagementService _pluginManagementService;
|
||||||
private bool _enabling;
|
private bool _enabling;
|
||||||
private readonly IMessageService _messageService;
|
private readonly IMessageService _messageService;
|
||||||
|
private bool _isSettingsPopupOpen;
|
||||||
|
private bool _canInstallPrerequisites;
|
||||||
|
private bool _canRemovePrerequisites;
|
||||||
|
|
||||||
public PluginFeatureViewModel(PluginFeatureInfo pluginFeatureInfo,
|
public PluginFeatureViewModel(PluginFeatureInfo pluginFeatureInfo,
|
||||||
bool showShield,
|
bool showShield,
|
||||||
@ -51,6 +57,9 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
|||||||
}
|
}
|
||||||
|
|
||||||
public bool CanToggleEnabled => FeatureInfo.Plugin.IsEnabled && !FeatureInfo.AlwaysEnabled;
|
public bool CanToggleEnabled => FeatureInfo.Plugin.IsEnabled && !FeatureInfo.AlwaysEnabled;
|
||||||
|
public bool CanInstallPrerequisites => FeatureInfo.Prerequisites.Any();
|
||||||
|
public bool CanRemovePrerequisites => FeatureInfo.Prerequisites.Any(p => p.UninstallActions.Any());
|
||||||
|
public bool IsPopupEnabled => CanInstallPrerequisites || CanRemovePrerequisites;
|
||||||
|
|
||||||
public void ShowLogsFolder()
|
public void ShowLogsFolder()
|
||||||
{
|
{
|
||||||
@ -96,6 +105,21 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
|||||||
base.OnClose();
|
base.OnClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task InstallPrerequisites()
|
||||||
|
{
|
||||||
|
if (FeatureInfo.Prerequisites.Any())
|
||||||
|
await ShowPrerequisitesDialog(false, FeatureInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task RemovePrerequisites()
|
||||||
|
{
|
||||||
|
if (FeatureInfo.Prerequisites.Any(p => p.UninstallActions.Any()))
|
||||||
|
{
|
||||||
|
await ShowPrerequisitesDialog(true, FeatureInfo);
|
||||||
|
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task UpdateEnabled(bool enable)
|
private async Task UpdateEnabled(bool enable)
|
||||||
{
|
{
|
||||||
if (IsEnabled == enable || FeatureInfo.Instance == null)
|
if (IsEnabled == enable || FeatureInfo.Instance == null)
|
||||||
@ -120,7 +144,18 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await Task.Run(() => _pluginManagementService.EnablePluginFeature(FeatureInfo.Instance, true));
|
// Check if all prerequisites are met async
|
||||||
|
if (!FeatureInfo.ArePrerequisitesMet())
|
||||||
|
{
|
||||||
|
await ShowPrerequisitesDialog(false, FeatureInfo);
|
||||||
|
if (!FeatureInfo.ArePrerequisitesMet())
|
||||||
|
{
|
||||||
|
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Run(() => _pluginManagementService.EnablePluginFeature(FeatureInfo.Instance!, true));
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@ -138,6 +173,13 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<object> ShowPrerequisitesDialog(bool uninstall, IPrerequisitesSubject subject)
|
||||||
|
{
|
||||||
|
if (uninstall)
|
||||||
|
return await _dialogService.ShowDialog<PluginPrerequisitesUninstallDialogViewModel>(new Dictionary<string, object> { { "subject", subject } });
|
||||||
|
return await _dialogService.ShowDialog<PluginPrerequisitesInstallDialogViewModel>(new Dictionary<string, object> { { "subject", subject } });
|
||||||
|
}
|
||||||
|
|
||||||
#region Event handlers
|
#region Event handlers
|
||||||
|
|
||||||
private void OnFeatureEnabling(object sender, PluginFeatureEventArgs e)
|
private void OnFeatureEnabling(object sender, PluginFeatureEventArgs e)
|
||||||
|
|||||||
@ -136,24 +136,26 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
|||||||
|
|
||||||
if (wasEnabled)
|
if (wasEnabled)
|
||||||
await UpdateEnabled(true);
|
await UpdateEnabled(true);
|
||||||
|
|
||||||
|
_messageService.ShowMessage("Reloaded plugin.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task InstallPrerequisites()
|
public async Task InstallPrerequisites()
|
||||||
{
|
{
|
||||||
if (Plugin.Info.Prerequisites.Any())
|
if (Plugin.Info.Prerequisites.Any())
|
||||||
await ShowPrerequisitesDialog(false);
|
await ShowPrerequisitesDialog(false, Plugin.Info);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RemovePrerequisites()
|
public async Task RemovePrerequisites()
|
||||||
{
|
{
|
||||||
if (Plugin.Info.Prerequisites.Any(p => p.UninstallActions.Any()))
|
if (Plugin.Info.Prerequisites.Any(p => p.UninstallActions.Any()))
|
||||||
{
|
{
|
||||||
await ShowPrerequisitesDialog(true);
|
await ShowPrerequisitesDialog(true, Plugin.Info);
|
||||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||||
NotifyOfPropertyChange(nameof(CanOpenSettings));
|
NotifyOfPropertyChange(nameof(CanOpenSettings));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RemoveSettings()
|
public async Task RemoveSettings()
|
||||||
{
|
{
|
||||||
bool confirmed = await _dialogService.ShowConfirmDialog("Clear plugin settings", "Are you sure you want to clear the settings of this plugin?");
|
bool confirmed = await _dialogService.ShowConfirmDialog("Clear plugin settings", "Are you sure you want to clear the settings of this plugin?");
|
||||||
@ -180,9 +182,31 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// If the plugin or any of its features has uninstall actions, offer to run these
|
// If the plugin or any of its features has uninstall actions, offer to run these
|
||||||
|
List<PluginFeatureInfo> featuresToUninstall = Plugin.Features.Where(f => f.Prerequisites.Any(fp => fp.UninstallActions.Any())).ToList();
|
||||||
if (Plugin.Info.Prerequisites.Any(p => p.UninstallActions.Any()) || Plugin.Features.Any(f => f.Prerequisites.Any(fp => fp.UninstallActions.Any())))
|
if (Plugin.Info.Prerequisites.Any(p => p.UninstallActions.Any()) || Plugin.Features.Any(f => f.Prerequisites.Any(fp => fp.UninstallActions.Any())))
|
||||||
{
|
{
|
||||||
|
bool remove = await _dialogService.ShowConfirmDialog(
|
||||||
|
"Remove plugin",
|
||||||
|
"This plugin installed one or more prerequisites.\r\nDo you want to remove these?",
|
||||||
|
"Uninstall",
|
||||||
|
"Skip"
|
||||||
|
);
|
||||||
|
if (remove)
|
||||||
|
{
|
||||||
|
if (Plugin.Info.Prerequisites.Any(p => p.UninstallActions.Any()))
|
||||||
|
{
|
||||||
|
object result = await ShowPrerequisitesDialog(true, Plugin.Info);
|
||||||
|
if (result is bool resultBool && !resultBool)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (PluginFeatureInfo pluginFeatureInfo in featuresToUninstall)
|
||||||
|
{
|
||||||
|
object result = await ShowPrerequisitesDialog(true, pluginFeatureInfo);
|
||||||
|
if (result is bool resultBool && !resultBool)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -244,7 +268,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
|||||||
// Check if all prerequisites are met async
|
// Check if all prerequisites are met async
|
||||||
if (!Plugin.Info.ArePrerequisitesMet())
|
if (!Plugin.Info.ArePrerequisitesMet())
|
||||||
{
|
{
|
||||||
await ShowPrerequisitesDialog(false);
|
await ShowPrerequisitesDialog(false, Plugin.Info);
|
||||||
if (!Plugin.Info.ArePrerequisitesMet())
|
if (!Plugin.Info.ArePrerequisitesMet())
|
||||||
{
|
{
|
||||||
CancelEnable();
|
CancelEnable();
|
||||||
@ -285,11 +309,11 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
|
|||||||
CanRemovePrerequisites = Plugin.Info.Prerequisites.Any(p => p.UninstallActions.Any());
|
CanRemovePrerequisites = Plugin.Info.Prerequisites.Any(p => p.UninstallActions.Any());
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<object> ShowPrerequisitesDialog(bool uninstall)
|
private async Task<object> ShowPrerequisitesDialog(bool uninstall, IPrerequisitesSubject subject)
|
||||||
{
|
{
|
||||||
if (uninstall)
|
if (uninstall)
|
||||||
return await _dialogService.ShowDialog<PluginPrerequisitesUninstallDialogViewModel>(new Dictionary<string, object> { { "pluginOrFeature", Plugin } });
|
return await _dialogService.ShowDialog<PluginPrerequisitesUninstallDialogViewModel>(new Dictionary<string, object> {{"subject", subject } });
|
||||||
return await _dialogService.ShowDialog<PluginPrerequisitesInstallDialogViewModel>(new Dictionary<string, object> { { "pluginOrFeature", Plugin } });
|
return await _dialogService.ShowDialog<PluginPrerequisitesInstallDialogViewModel>(new Dictionary<string, object> {{ "subject", subject } });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user