From a47eedf1c297d3aeae77457874600c2463c58055 Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 25 Jun 2020 19:25:58 +0200 Subject: [PATCH] Plugin info - Implemented property changed Plugins - Improved enable failure detection Plugins UI - Show an indicator on plugins that failed to enable Plugins UI - Show a progress indicator on plugins that are enabling UI - Added reusable Snackbar (not the Dutch kind with kroketten) --- src/Artemis.Core/Plugins/Abstract/Plugin.cs | 19 +- src/Artemis.Core/Plugins/Models/PluginInfo.cs | 95 +++++++-- src/Artemis.Core/Services/PluginService.cs | 112 ++++++----- .../Entities/Plugins/PluginEntity.cs | 1 - ...=> M1AttributeBasedPropertiesMigration.cs} | 2 +- ...s => M2ProfileEntitiesEnabledMigration.cs} | 2 +- .../M3PluginEntitiesIndexChangesMigration.cs | 18 ++ .../Repositories/PluginRepository.cs | 7 +- .../Ninject/SharedUIModule.cs | 3 + src/Artemis.UI/App.xaml | 4 + .../Ninject/Factories/IVMFactory.cs | 6 + .../LayerProperties/LayerPropertiesView.xaml | 181 ++++++++++++------ src/Artemis.UI/Screens/RootView.xaml | 47 ++--- src/Artemis.UI/Screens/RootViewModel.cs | 9 +- .../Screens/Settings/SettingsViewModel.cs | 10 +- .../Tabs/Plugins/PluginSettingsView.xaml | 20 +- .../Tabs/Plugins/PluginSettingsViewModel.cs | 39 +++- src/Artemis.sln.DotSettings | 3 +- 18 files changed, 399 insertions(+), 179 deletions(-) rename src/Artemis.Storage/Migrations/{AttributeBasedPropertiesMigration.cs => M1AttributeBasedPropertiesMigration.cs} (83%) rename src/Artemis.Storage/Migrations/{ProfileEntitiesEnabledMigration.cs => M2ProfileEntitiesEnabledMigration.cs} (93%) create mode 100644 src/Artemis.Storage/Migrations/M3PluginEntitiesIndexChangesMigration.cs diff --git a/src/Artemis.Core/Plugins/Abstract/Plugin.cs b/src/Artemis.Core/Plugins/Abstract/Plugin.cs index a0f01b1d7..702675d42 100644 --- a/src/Artemis.Core/Plugins/Abstract/Plugin.cs +++ b/src/Artemis.Core/Plugins/Abstract/Plugin.cs @@ -52,13 +52,30 @@ namespace Artemis.Core.Plugins.Abstract if (enable && !Enabled) { Enabled = true; - EnablePlugin(); + PluginInfo.Enabled = true; + + // If enable failed, put it back in a disabled state + try + { + EnablePlugin(); + } + catch + { + Enabled = false; + PluginInfo.Enabled = false; + throw; + } + OnPluginEnabled(); } else if (!enable && Enabled) { Enabled = false; + PluginInfo.Enabled = false; + + // Even if disable failed, still leave it in a disabled state to avoid more issues DisablePlugin(); + OnPluginDisabled(); } } diff --git a/src/Artemis.Core/Plugins/Models/PluginInfo.cs b/src/Artemis.Core/Plugins/Models/PluginInfo.cs index 0dfc01cf2..ebc61b150 100644 --- a/src/Artemis.Core/Plugins/Models/PluginInfo.cs +++ b/src/Artemis.Core/Plugins/Models/PluginInfo.cs @@ -5,11 +5,24 @@ using Artemis.Core.Plugins.Abstract; using Artemis.Storage.Entities.Plugins; using McMaster.NETCore.Plugins; using Newtonsoft.Json; +using Stylet; namespace Artemis.Core.Plugins.Models { - public class PluginInfo + [JsonObject(MemberSerialization.OptIn)] + public class PluginInfo : PropertyChangedBase { + private Guid _guid; + private string _name; + private string _description; + private string _icon; + private Version _version; + private string _main; + private DirectoryInfo _directory; + private Plugin _instance; + private bool _enabled; + private bool _lastEnableSuccessful; + internal PluginInfo() { } @@ -18,77 +31,125 @@ namespace Artemis.Core.Plugins.Models /// The plugins GUID /// [JsonProperty(Required = Required.Always)] - public Guid Guid { get; internal set; } + public Guid Guid + { + get => _guid; + internal set => SetAndNotify(ref _guid, value); + } /// /// The name of the plugin /// [JsonProperty(Required = Required.Always)] - public string Name { get; internal set; } + public string Name + { + get => _name; + internal set => SetAndNotify(ref _name, value); + } /// /// A short description of the plugin /// - public string Description { get; set; } + [JsonProperty] + public string Description + { + get => _description; + set => SetAndNotify(ref _description, value); + } /// /// The plugins display icon that's shown in the settings see for /// available /// icons /// - public string Icon { get; set; } + [JsonProperty] + public string Icon + { + get => _icon; + set => SetAndNotify(ref _icon, value); + } /// /// The version of the plugin /// [JsonProperty(Required = Required.Always)] - public Version Version { get; internal set; } + public Version Version + { + get => _version; + internal set => SetAndNotify(ref _version, value); + } /// /// The main entry DLL, should contain a class implementing Plugin /// [JsonProperty(Required = Required.Always)] - public string Main { get; internal set; } + public string Main + { + get => _main; + internal set => SetAndNotify(ref _main, value); + } /// /// The plugins root directory /// - [JsonIgnore] - public DirectoryInfo Directory { get; internal set; } + public DirectoryInfo Directory + { + get => _directory; + internal set => SetAndNotify(ref _directory, value); + } /// /// A reference to the type implementing Plugin, available after successful load /// - [JsonIgnore] - public Plugin Instance { get; internal set; } + public Plugin Instance + { + get => _instance; + internal set => SetAndNotify(ref _instance, value); + } /// /// Indicates whether the user enabled the plugin or not /// - [JsonIgnore] - public bool Enabled { get; internal set; } + public bool Enabled + { + get => _enabled; + internal set => SetAndNotify(ref _enabled, value); + } + + /// + /// Indicates whether the last time the plugin loaded, it loaded correctly + /// + public bool LastEnableSuccessful + { + get => _lastEnableSuccessful; + internal set => SetAndNotify(ref _lastEnableSuccessful, value); + } /// /// The PluginLoader backing this plugin /// - [JsonIgnore] internal PluginLoader PluginLoader { get; set; } /// /// The assembly the plugin code lives in /// - [JsonIgnore] internal Assembly Assembly { get; set; } /// /// The entity representing the plugin /// - [JsonIgnore] internal PluginEntity PluginEntity { get; set; } public override string ToString() { - return $"{nameof(Guid)}: {Guid}, {nameof(Name)}: {Name}, {nameof(Version)}: {Version}"; + return $"{Name} v{Version} - {Guid}"; + } + + internal void ApplyToEntity() + { + PluginEntity.Id = Guid; + PluginEntity.IsEnabled = Enabled; + PluginEntity.LastEnableSuccessful = LastEnableSuccessful; } } } \ No newline at end of file diff --git a/src/Artemis.Core/Services/PluginService.cs b/src/Artemis.Core/Services/PluginService.cs index 17c0ed90b..cfe0a5649 100644 --- a/src/Artemis.Core/Services/PluginService.cs +++ b/src/Artemis.Core/Services/PluginService.cs @@ -137,8 +137,6 @@ namespace Artemis.Core.Services var pluginInfo = JsonConvert.DeserializeObject(File.ReadAllText(metadataFile)); pluginInfo.Directory = subDirectory; - _logger.Debug("Loading plugin {pluginInfo}", pluginInfo); - OnPluginLoading(new PluginEventArgs(pluginInfo)); LoadPlugin(pluginInfo); } catch (Exception e) @@ -150,38 +148,22 @@ namespace Artemis.Core.Services // Activate plugins after they are all loaded foreach (var pluginInfo in _plugins.Where(p => p.Enabled)) { - if (!pluginInfo.PluginEntity.LastEnableSuccessful) + if (!pluginInfo.LastEnableSuccessful) { pluginInfo.Enabled = false; _logger.Warning("Plugin failed to load last time, disabling it now to avoid instability. Plugin info: {pluginInfo}", pluginInfo); continue; } - // Mark this as false until the plugin enabled successfully and save it in case the plugin drags us down into a crash - pluginInfo.PluginEntity.LastEnableSuccessful = false; - _pluginRepository.SavePlugin(pluginInfo.PluginEntity); - - var threwException = false; try { - _logger.Debug("Enabling plugin {pluginInfo}", pluginInfo); - pluginInfo.Instance.SetEnabled(true); + EnablePlugin(pluginInfo.Instance); } - catch (Exception e) + catch (Exception) { - _logger.Warning(new ArtemisPluginException(pluginInfo, "Failed to enable plugin", e), "Plugin exception"); - pluginInfo.Enabled = false; - threwException = true; + // ignored, logged in EnablePlugin } - - // We got this far so the plugin enabled and we didn't crash horribly, yay - if (!threwException) - { - pluginInfo.PluginEntity.LastEnableSuccessful = true; - _pluginRepository.SavePlugin(pluginInfo.PluginEntity); - } - - OnPluginEnabled(new PluginEventArgs(pluginInfo)); + } LoadingPlugins = false; @@ -213,16 +195,20 @@ namespace Artemis.Core.Services { lock (_plugins) { + _logger.Debug("Loading plugin {pluginInfo}", pluginInfo); + OnPluginLoading(new PluginEventArgs(pluginInfo)); + // Unload the plugin first if it is already loaded if (_plugins.Contains(pluginInfo)) UnloadPlugin(pluginInfo); var pluginEntity = _pluginRepository.GetPluginByGuid(pluginInfo.Guid); if (pluginEntity == null) - pluginEntity = new PluginEntity {PluginGuid = pluginInfo.Guid, IsEnabled = true, LastEnableSuccessful = true}; + pluginEntity = new PluginEntity {Id = pluginInfo.Guid, IsEnabled = true, LastEnableSuccessful = true}; pluginInfo.PluginEntity = pluginEntity; pluginInfo.Enabled = pluginEntity.IsEnabled; + pluginInfo.LastEnableSuccessful = pluginEntity.LastEnableSuccessful; var mainFile = Path.Combine(pluginInfo.Directory.FullName, pluginInfo.Main); if (!File.Exists(mainFile)) @@ -310,28 +296,37 @@ namespace Artemis.Core.Services public void EnablePlugin(Plugin plugin) { - plugin.PluginInfo.Enabled = true; - plugin.PluginInfo.PluginEntity.IsEnabled = true; - plugin.PluginInfo.PluginEntity.LastEnableSuccessful = false; - _pluginRepository.SavePlugin(plugin.PluginInfo.PluginEntity); + lock (_plugins) + { + _logger.Debug("Enabling plugin {pluginInfo}", plugin.PluginInfo); - var threwException = false; - try - { - plugin.SetEnabled(true); - } - catch (Exception e) - { - _logger.Warning(new ArtemisPluginException(plugin.PluginInfo, "Failed to enable plugin", e), "Plugin exception"); - plugin.PluginInfo.Enabled = false; - threwException = true; - } + plugin.PluginInfo.LastEnableSuccessful = false; + plugin.PluginInfo.ApplyToEntity(); - // We got this far so the plugin enabled and we didn't crash horribly, yay - if (!threwException) - { - plugin.PluginInfo.PluginEntity.LastEnableSuccessful = true; _pluginRepository.SavePlugin(plugin.PluginInfo.PluginEntity); + + try + { + plugin.SetEnabled(true); + } + catch (Exception e) + { + _logger.Warning(new ArtemisPluginException(plugin.PluginInfo, "Exception during SetEnabled(true)", e), "Failed to enable plugin"); + throw; + } + finally + { + // We got this far so the plugin enabled and we didn't crash horribly, yay + if (plugin.PluginInfo.Enabled) + { + plugin.PluginInfo.LastEnableSuccessful = true; + plugin.PluginInfo.ApplyToEntity(); + + _pluginRepository.SavePlugin(plugin.PluginInfo.PluginEntity); + + _logger.Debug("Successfully enabled plugin {pluginInfo}", plugin.PluginInfo); + } + } } OnPluginEnabled(new PluginEventArgs(plugin.PluginInfo)); @@ -339,18 +334,29 @@ namespace Artemis.Core.Services public void DisablePlugin(Plugin plugin) { - plugin.PluginInfo.Enabled = false; - plugin.PluginInfo.PluginEntity.IsEnabled = false; - _pluginRepository.SavePlugin(plugin.PluginInfo.PluginEntity); - - // Device providers cannot be disabled at runtime, restart the application - if (plugin is DeviceProvider) + lock (_plugins) { - CurrentProcessUtilities.Shutdown(2, true); - return; - } + _logger.Debug("Disabling plugin {pluginInfo}", plugin.PluginInfo); - plugin.SetEnabled(false); + // Device providers cannot be disabled at runtime, restart the application + if (plugin is DeviceProvider) + { + // Don't call SetEnabled(false) but simply update enabled state and save it + plugin.PluginInfo.Enabled = false; + plugin.PluginInfo.ApplyToEntity(); + _pluginRepository.SavePlugin(plugin.PluginInfo.PluginEntity); + + _logger.Debug("Shutting down for device provider disable {pluginInfo}", plugin.PluginInfo); + CurrentProcessUtilities.Shutdown(2, true); + return; + } + + plugin.SetEnabled(false); + plugin.PluginInfo.ApplyToEntity(); + _pluginRepository.SavePlugin(plugin.PluginInfo.PluginEntity); + + _logger.Debug("Successfully disabled plugin {pluginInfo}", plugin.PluginInfo); + } OnPluginDisabled(new PluginEventArgs(plugin.PluginInfo)); } diff --git a/src/Artemis.Storage/Entities/Plugins/PluginEntity.cs b/src/Artemis.Storage/Entities/Plugins/PluginEntity.cs index 5f0091f3b..ae59c9769 100644 --- a/src/Artemis.Storage/Entities/Plugins/PluginEntity.cs +++ b/src/Artemis.Storage/Entities/Plugins/PluginEntity.cs @@ -8,7 +8,6 @@ namespace Artemis.Storage.Entities.Plugins public class PluginEntity { public Guid Id { get; set; } - public Guid PluginGuid { get; set; } public bool IsEnabled { get; set; } public bool LastEnableSuccessful { get; set; } diff --git a/src/Artemis.Storage/Migrations/AttributeBasedPropertiesMigration.cs b/src/Artemis.Storage/Migrations/M1AttributeBasedPropertiesMigration.cs similarity index 83% rename from src/Artemis.Storage/Migrations/AttributeBasedPropertiesMigration.cs rename to src/Artemis.Storage/Migrations/M1AttributeBasedPropertiesMigration.cs index 32c51984e..18a5e644f 100644 --- a/src/Artemis.Storage/Migrations/AttributeBasedPropertiesMigration.cs +++ b/src/Artemis.Storage/Migrations/M1AttributeBasedPropertiesMigration.cs @@ -3,7 +3,7 @@ using LiteDB; namespace Artemis.Storage.Migrations { - public class AttributeBasedPropertiesMigration : IStorageMigration + public class M1AttributeBasedPropertiesMigration : IStorageMigration { public int UserVersion => 1; diff --git a/src/Artemis.Storage/Migrations/ProfileEntitiesEnabledMigration.cs b/src/Artemis.Storage/Migrations/M2ProfileEntitiesEnabledMigration.cs similarity index 93% rename from src/Artemis.Storage/Migrations/ProfileEntitiesEnabledMigration.cs rename to src/Artemis.Storage/Migrations/M2ProfileEntitiesEnabledMigration.cs index 50dd42251..836362c57 100644 --- a/src/Artemis.Storage/Migrations/ProfileEntitiesEnabledMigration.cs +++ b/src/Artemis.Storage/Migrations/M2ProfileEntitiesEnabledMigration.cs @@ -4,7 +4,7 @@ using LiteDB; namespace Artemis.Storage.Migrations { - public class ProfileEntitiesEnabledMigration : IStorageMigration + public class M2ProfileEntitiesEnabledMigration : IStorageMigration { public int UserVersion => 2; diff --git a/src/Artemis.Storage/Migrations/M3PluginEntitiesIndexChangesMigration.cs b/src/Artemis.Storage/Migrations/M3PluginEntitiesIndexChangesMigration.cs new file mode 100644 index 000000000..fb1658c26 --- /dev/null +++ b/src/Artemis.Storage/Migrations/M3PluginEntitiesIndexChangesMigration.cs @@ -0,0 +1,18 @@ +using Artemis.Storage.Migrations.Interfaces; +using LiteDB; + +namespace Artemis.Storage.Migrations +{ + public class M3PluginEntitiesIndexChangesMigration : IStorageMigration + { + public int UserVersion => 3; + + public void Apply(LiteRepository repository) + { + if (repository.Database.CollectionExists("PluginEntity")) + repository.Database.DropCollection("PluginEntity"); + if (repository.Database.CollectionExists("PluginSettingEntity")) + repository.Database.DropCollection("PluginSettingEntity"); + } + } +} \ No newline at end of file diff --git a/src/Artemis.Storage/Repositories/PluginRepository.cs b/src/Artemis.Storage/Repositories/PluginRepository.cs index 2dfb52076..7d0f79652 100644 --- a/src/Artemis.Storage/Repositories/PluginRepository.cs +++ b/src/Artemis.Storage/Repositories/PluginRepository.cs @@ -13,9 +13,7 @@ namespace Artemis.Storage.Repositories { _repository = repository; - _repository.Database.GetCollection().EnsureIndex(s => s.PluginGuid); - _repository.Database.GetCollection().EnsureIndex(s => s.Name); - _repository.Database.GetCollection().EnsureIndex(s => s.PluginGuid); + _repository.Database.GetCollection().EnsureIndex(s => new {s.Name, s.PluginGuid}, true); } public void AddPlugin(PluginEntity pluginEntity) @@ -25,12 +23,13 @@ namespace Artemis.Storage.Repositories public PluginEntity GetPluginByGuid(Guid pluginGuid) { - return _repository.FirstOrDefault(p => p.PluginGuid == pluginGuid); + return _repository.FirstOrDefault(p => p.Id == pluginGuid); } public void SavePlugin(PluginEntity pluginEntity) { _repository.Upsert(pluginEntity); + _repository.Database.Checkpoint(); } public void AddSetting(PluginSettingEntity pluginSettingEntity) diff --git a/src/Artemis.UI.Shared/Ninject/SharedUIModule.cs b/src/Artemis.UI.Shared/Ninject/SharedUIModule.cs index 82e2d5cfa..9e80aea2f 100644 --- a/src/Artemis.UI.Shared/Ninject/SharedUIModule.cs +++ b/src/Artemis.UI.Shared/Ninject/SharedUIModule.cs @@ -1,6 +1,7 @@ using System; using Artemis.UI.Shared.Ninject.Factories; using Artemis.UI.Shared.Services.Interfaces; +using MaterialDesignThemes.Wpf; using Ninject.Extensions.Conventions; using Ninject.Modules; @@ -32,6 +33,8 @@ namespace Artemis.UI.Shared.Ninject .BindAllInterfaces() .Configure(c => c.InSingletonScope()); }); + + Kernel.Bind().ToConstant(new SnackbarMessageQueue()).InSingletonScope(); } } } \ No newline at end of file diff --git a/src/Artemis.UI/App.xaml b/src/Artemis.UI/App.xaml index 1f9e1615d..bcd49c34f 100644 --- a/src/Artemis.UI/App.xaml +++ b/src/Artemis.UI/App.xaml @@ -70,6 +70,10 @@ + \ No newline at end of file diff --git a/src/Artemis.UI/Ninject/Factories/IVMFactory.cs b/src/Artemis.UI/Ninject/Factories/IVMFactory.cs index d8d368d7f..fc2119706 100644 --- a/src/Artemis.UI/Ninject/Factories/IVMFactory.cs +++ b/src/Artemis.UI/Ninject/Factories/IVMFactory.cs @@ -13,6 +13,7 @@ using Artemis.UI.Screens.Module.ProfileEditor.ProfileTree.TreeItem; using Artemis.UI.Screens.Module.ProfileEditor.Visualization; using Artemis.UI.Screens.Settings.Debug; using Artemis.UI.Screens.Settings.Tabs.Devices; +using Artemis.UI.Screens.Settings.Tabs.Plugins; using Stylet; namespace Artemis.UI.Ninject.Factories @@ -26,6 +27,11 @@ namespace Artemis.UI.Ninject.Factories ModuleRootViewModel Create(Module module); } + public interface IPluginSettingsVmFactory : IVmFactory + { + PluginSettingsViewModel Create(Plugin plugin); + } + public interface IDeviceSettingsVmFactory : IVmFactory { DeviceSettingsViewModel Create(ArtemisDevice device); diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesView.xaml index fdb982d2f..2e7726bfb 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesView.xaml @@ -66,8 +66,9 @@ - - + + + @@ -157,8 +158,11 @@ + + + - + @@ -214,72 +218,124 @@ - - + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + Select the enter timeline + Played when the folder/layer starts displaying (condition is met) + + + + + + ENTER + + + + + + + Select the main timeline + Played after the enter timeline finishes, either on repeat or once + + + + + + MAIN + + + + + + + Select the exit timeline + Played when the folder/layer stops displaying (conditon no longer met) + + + + + + + EXIT + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/RootView.xaml b/src/Artemis.UI/Screens/RootView.xaml index 33a3cb100..72bb1b592 100644 --- a/src/Artemis.UI/Screens/RootView.xaml +++ b/src/Artemis.UI/Screens/RootView.xaml @@ -47,27 +47,30 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/RootViewModel.cs b/src/Artemis.UI/Screens/RootViewModel.cs index c673eddac..e64bfc986 100644 --- a/src/Artemis.UI/Screens/RootViewModel.cs +++ b/src/Artemis.UI/Screens/RootViewModel.cs @@ -27,10 +27,11 @@ namespace Artemis.UI.Screens private readonly Timer _titleUpdateTimer; private bool _lostFocus; - public RootViewModel(IEventAggregator eventAggregator, SidebarViewModel sidebarViewModel, ISettingsService settingsService, ICoreService coreService, - IDebugService debugService) + public RootViewModel(IEventAggregator eventAggregator, SidebarViewModel sidebarViewModel, ISettingsService settingsService, ICoreService coreService, + IDebugService debugService, ISnackbarMessageQueue snackbarMessageQueue) { SidebarViewModel = sidebarViewModel; + MainMessageQueue = snackbarMessageQueue; _eventAggregator = eventAggregator; _coreService = coreService; _debugService = debugService; @@ -49,11 +50,11 @@ namespace Artemis.UI.Screens } public SidebarViewModel SidebarViewModel { get; } + public ISnackbarMessageQueue MainMessageQueue { get; set; } public bool IsSidebarVisible { get; set; } public bool ActiveItemReady { get; set; } - public string WindowTitle { get; set; } - + public void WindowDeactivated() { var windowState = ((Window) View).WindowState; diff --git a/src/Artemis.UI/Screens/Settings/SettingsViewModel.cs b/src/Artemis.UI/Screens/Settings/SettingsViewModel.cs index 21b85099e..04509b556 100644 --- a/src/Artemis.UI/Screens/Settings/SettingsViewModel.cs +++ b/src/Artemis.UI/Screens/Settings/SettingsViewModel.cs @@ -27,20 +27,20 @@ namespace Artemis.UI.Screens.Settings private readonly IDialogService _dialogService; private readonly IPluginService _pluginService; private readonly ISettingsService _settingsService; + private readonly IPluginSettingsVmFactory _pluginSettingsVmFactory; private readonly ISurfaceService _surfaceService; - private readonly IWindowManager _windowManager; - public SettingsViewModel(ISurfaceService surfaceService, IPluginService pluginService, IDialogService dialogService, IWindowManager windowManager, - IDebugService debugService, ISettingsService settingsService, IDeviceSettingsVmFactory deviceSettingsVmFactory) + public SettingsViewModel(ISurfaceService surfaceService, IPluginService pluginService, IDialogService dialogService, IDebugService debugService, + ISettingsService settingsService, IPluginSettingsVmFactory pluginSettingsVmFactory, IDeviceSettingsVmFactory deviceSettingsVmFactory) { DisplayName = "Settings"; _surfaceService = surfaceService; _pluginService = pluginService; _dialogService = dialogService; - _windowManager = windowManager; _debugService = debugService; _settingsService = settingsService; + _pluginSettingsVmFactory = pluginSettingsVmFactory; _deviceSettingsVmFactory = deviceSettingsVmFactory; DeviceSettingsViewModels = new BindableCollection(); @@ -189,7 +189,7 @@ namespace Artemis.UI.Screens.Settings DeviceSettingsViewModels.AddRange(_surfaceService.ActiveSurface.Devices.Select(d => _deviceSettingsVmFactory.Create(d))); Plugins.Clear(); - Plugins.AddRange(_pluginService.GetAllPluginInfo().Select(p => new PluginSettingsViewModel(p.Instance, _windowManager, _dialogService, _pluginService))); + Plugins.AddRange(_pluginService.GetAllPluginInfo().Select(p => _pluginSettingsVmFactory.Create(p.Instance))); base.OnInitialActivate(); } diff --git a/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsView.xaml b/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsView.xaml index c1f3cb626..21cf53303 100644 --- a/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsView.xaml +++ b/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsView.xaml @@ -36,6 +36,7 @@ + @@ -43,9 +44,16 @@ - + + + + LOAD FAILED + + SETTINGS - - - - + Plugin enabled + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsViewModel.cs b/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsViewModel.cs index aa41b0322..36f3571a0 100644 --- a/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsViewModel.cs +++ b/src/Artemis.UI/Screens/Settings/Tabs/Plugins/PluginSettingsViewModel.cs @@ -1,4 +1,6 @@ using System; +using System.Diagnostics; +using System.IO; using System.Threading.Tasks; using Artemis.Core.Plugins.Abstract; using Artemis.Core.Plugins.Models; @@ -13,9 +15,11 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins { private readonly IDialogService _dialogService; private readonly IPluginService _pluginService; + private readonly ISnackbarMessageQueue _snackbarMessageQueue; private readonly IWindowManager _windowManager; - public PluginSettingsViewModel(Plugin plugin, IWindowManager windowManager, IDialogService dialogService, IPluginService pluginService) + public PluginSettingsViewModel(Plugin plugin, IWindowManager windowManager, IDialogService dialogService, IPluginService pluginService, + ISnackbarMessageQueue snackbarMessageQueue) { Plugin = plugin; PluginInfo = plugin.PluginInfo; @@ -23,6 +27,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins _windowManager = windowManager; _dialogService = dialogService; _pluginService = pluginService; + _snackbarMessageQueue = snackbarMessageQueue; } public Plugin Plugin { get; set; } @@ -40,6 +45,8 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins public bool CanOpenSettings => IsEnabled && Plugin.HasConfigurationViewModel; + public bool Enabling { get; set; } + public async Task OpenSettings() { try @@ -55,6 +62,18 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins } } + public async Task ShowLogsFolder() + { + try + { + Process.Start(Environment.GetEnvironmentVariable("WINDIR") + @"\explorer.exe", Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs")); + } + catch (Exception e) + { + await _dialogService.ShowExceptionDialog("Welp, we couldn\'t open the logs folder for you", e); + } + } + private PackIconKind GetIconKind() { if (PluginInfo.Icon != null) @@ -105,7 +124,23 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins } if (enable) - _pluginService.EnablePlugin(Plugin); + { + Enabling = true; + try + { + _pluginService.EnablePlugin(Plugin); + _snackbarMessageQueue.Enqueue($"Enabled plugin {PluginInfo.Name}"); + } + catch (Exception) + { + _snackbarMessageQueue.Enqueue($"Failed to enable plugin {PluginInfo.Name}", "VIEW LOGS", async () => await ShowLogsFolder()); + } + finally + { + Enabling = false; + NotifyOfPropertyChange(() => IsEnabled); + } + } else _pluginService.DisablePlugin(Plugin); diff --git a/src/Artemis.sln.DotSettings b/src/Artemis.sln.DotSettings index 1f66a73f0..456bc6b94 100644 --- a/src/Artemis.sln.DotSettings +++ b/src/Artemis.sln.DotSettings @@ -244,4 +244,5 @@ True True True - True \ No newline at end of file + True + True \ No newline at end of file