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

UI - Implemented exception dialog

UI - Fixed notification opacity
UI - Added position control to notifications
This commit is contained in:
Robert 2021-11-13 17:46:41 +01:00
parent b963aa0909
commit 63eb0ca9b3
15 changed files with 187 additions and 84 deletions

View File

@ -455,17 +455,17 @@
</member> </member>
<member name="T:Artemis.UI.Avalonia.Shared.ViewModelBase"> <member name="T:Artemis.UI.Avalonia.Shared.ViewModelBase">
<summary> <summary>
Represents the base class for Artemis view models Represents the base class for Artemis view models
</summary> </summary>
</member> </member>
<member name="P:Artemis.UI.Avalonia.Shared.ViewModelBase.DisplayName"> <member name="P:Artemis.UI.Avalonia.Shared.ViewModelBase.DisplayName">
<summary> <summary>
Gets or sets the display name of the view model Gets or sets the display name of the view model
</summary> </summary>
</member> </member>
<member name="T:Artemis.UI.Avalonia.Shared.ActivatableViewModelBase"> <member name="T:Artemis.UI.Avalonia.Shared.ActivatableViewModelBase">
<summary> <summary>
Represents the base class for Artemis view models that are interested in the activated event Represents the base class for Artemis view models that are interested in the activated event
</summary> </summary>
</member> </member>
<member name="M:Artemis.UI.Avalonia.Shared.ActivatableViewModelBase.#ctor"> <member name="M:Artemis.UI.Avalonia.Shared.ActivatableViewModelBase.#ctor">
@ -473,11 +473,11 @@
</member> </member>
<member name="M:Artemis.UI.Avalonia.Shared.ActivatableViewModelBase.Dispose(System.Boolean)"> <member name="M:Artemis.UI.Avalonia.Shared.ActivatableViewModelBase.Dispose(System.Boolean)">
<summary> <summary>
Releases the unmanaged resources used by the object and optionally releases the managed resources. Releases the unmanaged resources used by the object and optionally releases the managed resources.
</summary> </summary>
<param name="disposing"> <param name="disposing">
<see langword="true" /> to release both managed and unmanaged resources; <see langword="true" /> to release both managed and unmanaged resources;
<see langword="false" /> to release only unmanaged resources. <see langword="false" /> to release only unmanaged resources.
</param> </param>
</member> </member>
<member name="P:Artemis.UI.Avalonia.Shared.ActivatableViewModelBase.Activator"> <member name="P:Artemis.UI.Avalonia.Shared.ActivatableViewModelBase.Activator">
@ -488,20 +488,18 @@
</member> </member>
<member name="T:Artemis.UI.Avalonia.Shared.DialogViewModelBase`1"> <member name="T:Artemis.UI.Avalonia.Shared.DialogViewModelBase`1">
<summary> <summary>
Represents the base class for Artemis view models used to drive dialogs Represents the base class for Artemis view models used to drive dialogs
</summary> </summary>
</member> </member>
<member name="M:Artemis.UI.Avalonia.Shared.DialogViewModelBase`1.#ctor"> <member name="M:Artemis.UI.Avalonia.Shared.DialogViewModelBase`1.Close(`0)">
<inheritdoc />
</member>
<member name="P:Artemis.UI.Avalonia.Shared.DialogViewModelBase`1.Close">
<summary> <summary>
Closes the dialog with a given result Closes the dialog with the given <paramref name="result" />
</summary> </summary>
<param name="result">The result of the dialog</param>
</member> </member>
<member name="P:Artemis.UI.Avalonia.Shared.DialogViewModelBase`1.Cancel"> <member name="M:Artemis.UI.Avalonia.Shared.DialogViewModelBase`1.Cancel">
<summary> <summary>
Closes the dialog without a result Closes the dialog without a result
</summary> </summary>
</member> </member>
</members> </members>

View File

@ -0,0 +1,14 @@
using System;
namespace Artemis.UI.Avalonia.Shared.Events
{
internal class DialogClosedEventArgs<TResult> : EventArgs
{
public TResult Result { get; }
public DialogClosedEventArgs(TResult result)
{
Result = result;
}
}
}

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Layout;
using Avalonia.Threading; using Avalonia.Threading;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using ReactiveUI; using ReactiveUI;
@ -17,7 +18,12 @@ namespace Artemis.UI.Avalonia.Shared.Services.Builders
public NotificationBuilder(Window parent) public NotificationBuilder(Window parent)
{ {
_parent = parent; _parent = parent;
_infoBar = new InfoBar {Classes = Classes.Parse("notification-info-bar")}; _infoBar = new InfoBar
{
Classes = Classes.Parse("notification-info-bar"),
VerticalAlignment = VerticalAlignment.Bottom,
HorizontalAlignment = HorizontalAlignment.Right
};
} }
public NotificationBuilder WithTitle(string? title) public NotificationBuilder WithTitle(string? title)
@ -38,6 +44,17 @@ namespace Artemis.UI.Avalonia.Shared.Services.Builders
return this; return this;
} }
public NotificationBuilder WithVerticalPosition(VerticalAlignment position)
{
_infoBar.VerticalAlignment = position;
return this;
}
public NotificationBuilder WithHorizontalPosition(HorizontalAlignment position)
{
_infoBar.HorizontalAlignment = position;
return this;
}
/// <summary> /// <summary>
/// Add a filter to the dialog /// Add a filter to the dialog
@ -105,8 +122,8 @@ namespace Artemis.UI.Avalonia.Shared.Services.Builders
public IControl Build() public IControl Build()
{ {
return _action != null return _action != null
? new Button {Content = _text, Command = ReactiveCommand.Create(() => _action)} ? new Button {Content = _text, Command = ReactiveCommand.Create(() => _action)}
: new Button {Content = _text}; : new Button {Content = _text};
} }
} }

View File

@ -2,8 +2,47 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="800"
x:Class="Artemis.UI.Avalonia.Shared.Services.ExceptionDialogView" x:Class="Artemis.UI.Avalonia.Shared.Services.ExceptionDialogView"
Title="ExceptionDialogView"> Title="{Binding Title}"
Eh you got an exception but I didn't write the viewer yet :( ExtendClientAreaToDecorationsHint="True"
</Window> Width="800"
Height="800"
WindowStartupLocation="CenterOwner">
<Grid>
<TextBlock Margin="10" IsHitTestVisible="False" Text="{Binding Title}" />
<Grid Margin="0 32 0 0" ColumnDefinitions="*,Auto" RowDefinitions="*,Auto">
<Border Classes="card" Grid.Row="0" Grid.ColumnSpan="2" Margin="10">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Margin="0 5">
<Grid RowDefinitions="Auto,Auto,*">
<TextBlock Grid.Row="0" Classes="h3">Awww :(</TextBlock>
<TextBlock Grid.Row="1">
It looks like Artemis ran into an unhandled exception. If this keeps happening feel free to hit us up on Discord.
</TextBlock>
<TextBox Grid.Row="2" Text="{Binding Exception, Mode=OneTime}"
Margin="0 10"
AcceptsReturn="True"
IsReadOnly="True"
FontFamily="Consolas"
FontSize="12"
BorderThickness="0" />
</Grid>
</ScrollViewer>
</Border>
<TextBlock Grid.Row="1" Grid.Column="0" TextWrapping="Wrap" Margin="15" VerticalAlignment="Center">
When reporting errors please don't take a screenshot of the error, instead copy the text, thanks!
</TextBlock>
<StackPanel Grid.Row="1" Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right" Margin="15">
<Button Command="{Binding CopyException}" Classes="AppBarButton" Width="150" Margin="0 0 5 0">
Copy exception
</Button>
<Button Command="{Binding Close}" Width="150" Margin="5 0 0 0">
Close
</Button>
</StackPanel>
</Grid>
</Grid>
</Window>

View File

@ -4,7 +4,7 @@ using Avalonia.ReactiveUI;
namespace Artemis.UI.Avalonia.Shared.Services namespace Artemis.UI.Avalonia.Shared.Services
{ {
public partial class ExceptionDialogView : ReactiveWindow<ExceptionDialogViewModel> internal class ExceptionDialogView : ReactiveWindow<ExceptionDialogViewModel>
{ {
public ExceptionDialogView() public ExceptionDialogView()
{ {
@ -19,4 +19,4 @@ namespace Artemis.UI.Avalonia.Shared.Services
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
} }
} }
} }

View File

@ -1,12 +1,35 @@
using System; using System;
using System.Threading.Tasks;
using Artemis.UI.Avalonia.Shared.Services.Builders;
using Artemis.UI.Avalonia.Shared.Services.Interfaces;
using Avalonia;
using Avalonia.Layout;
namespace Artemis.UI.Avalonia.Shared.Services namespace Artemis.UI.Avalonia.Shared.Services
{ {
public class ExceptionDialogViewModel : DialogViewModelBase<object> internal class ExceptionDialogViewModel : DialogViewModelBase<object>
{ {
public ExceptionDialogViewModel(string title, Exception exception) private readonly INotificationService _notificationService;
public ExceptionDialogViewModel(string title, Exception exception, INotificationService notificationService)
{ {
_notificationService = notificationService;
Title = $"Artemis | {title}";
Exception = exception;
}
public string Title { get; }
public Exception Exception { get; }
public async Task CopyException()
{
await Application.Current.Clipboard.SetTextAsync(Exception.ToString());
_notificationService.CreateNotification()
.WithMessage("Copied stack trace to clipboard.")
.WithSeverity(NotificationSeverity.Success)
.WithHorizontalPosition(HorizontalAlignment.Center)
.Show();
} }
} }
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Reactive.Disposables;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.UI.Avalonia.Shared.Exceptions; using Artemis.UI.Avalonia.Shared.Exceptions;
using Artemis.UI.Avalonia.Shared.Services.Builders; using Artemis.UI.Avalonia.Shared.Services.Builders;
@ -7,9 +8,11 @@ using Artemis.UI.Avalonia.Shared.Services.Interfaces;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Threading;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ninject; using Ninject;
using Ninject.Parameters; using Ninject.Parameters;
using ReactiveUI;
namespace Artemis.UI.Avalonia.Shared.Services namespace Artemis.UI.Avalonia.Shared.Services
{ {
@ -91,25 +94,30 @@ namespace Artemis.UI.Avalonia.Shared.Services
Window window = (Window) Activator.CreateInstance(type)!; Window window = (Window) Activator.CreateInstance(type)!;
window.DataContext = viewModel; window.DataContext = viewModel;
viewModel.CloseRequested += (_, args) => window.Close(args.Result);
viewModel.CancelRequested += (_, _) => window.Close();
return await window.ShowDialog<TResult>(parent); return await window.ShowDialog<TResult>(parent);
} }
public void ShowExceptionDialog(string title, Exception exception) public void ShowExceptionDialog(string title, Exception exception)
{ {
if (_exceptionDialogOpen) if (_exceptionDialogOpen)
{
return; return;
}
try _exceptionDialogOpen = true;
// Fire and forget the dialog
Dispatcher.UIThread.InvokeAsync(async () =>
{ {
_exceptionDialogOpen = true; try
ShowDialogAsync(new ExceptionDialogViewModel(title, exception)).GetAwaiter().GetResult(); {
} await ShowDialogAsync(new ExceptionDialogViewModel(title, exception, _kernel.Get<INotificationService>()));
finally }
{ finally
_exceptionDialogOpen = false; {
} _exceptionDialogOpen = false;
}
});
} }
public ContentDialogBuilder CreateContentDialog() public ContentDialogBuilder CreateContentDialog()

View File

@ -13,9 +13,6 @@
<Setter Property="Opacity" Value="0" /> <Setter Property="Opacity" Value="0" />
<Setter Property="MaxWidth" Value="600" /> <Setter Property="MaxWidth" Value="600" />
<Setter Property="Margin" Value="15"/> <Setter Property="Margin" Value="15"/>
<Setter Property="Background" Value="#25a3a3a3"/>
<Setter Property="VerticalAlignment" Value="Bottom" />
<Setter Property="HorizontalAlignment" Value="Right" />
<Setter Property="Transitions"> <Setter Property="Transitions">
<Transitions> <Transitions>
<DoubleTransition Property="Opacity" Duration="0:0:0.2"/> <DoubleTransition Property="Opacity" Duration="0:0:0.2"/>
@ -25,4 +22,7 @@
<Style Selector="controls|InfoBar.notification-info-bar[IsOpen=True]"> <Style Selector="controls|InfoBar.notification-info-bar[IsOpen=True]">
<Setter Property="Opacity" Value="1" /> <Setter Property="Opacity" Value="1" />
</Style> </Style>
<Style Selector="controls|InfoBar.notification-info-bar:informational /template/ Border#ContentRoot">
<Setter Property="Background" Value="#ff3c3c3c" />
</Style>
</Styles> </Styles>

View File

@ -1,19 +1,19 @@
using System; using System;
using System.Reactive;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using Artemis.UI.Avalonia.Shared.Events;
using ReactiveUI; using ReactiveUI;
namespace Artemis.UI.Avalonia.Shared namespace Artemis.UI.Avalonia.Shared
{ {
/// <summary> /// <summary>
/// Represents the base class for Artemis view models /// Represents the base class for Artemis view models
/// </summary> /// </summary>
public abstract class ViewModelBase : ReactiveObject public abstract class ViewModelBase : ReactiveObject
{ {
private string? _displayName; private string? _displayName;
/// <summary> /// <summary>
/// Gets or sets the display name of the view model /// Gets or sets the display name of the view model
/// </summary> /// </summary>
public string? DisplayName public string? DisplayName
{ {
@ -23,7 +23,7 @@ namespace Artemis.UI.Avalonia.Shared
} }
/// <summary> /// <summary>
/// Represents the base class for Artemis view models that are interested in the activated event /// Represents the base class for Artemis view models that are interested in the activated event
/// </summary> /// </summary>
public abstract class ActivatableViewModelBase : ViewModelBase, IActivatableViewModel, IDisposable public abstract class ActivatableViewModelBase : ViewModelBase, IActivatableViewModel, IDisposable
{ {
@ -34,11 +34,11 @@ namespace Artemis.UI.Avalonia.Shared
} }
/// <summary> /// <summary>
/// Releases the unmanaged resources used by the object and optionally releases the managed resources. /// Releases the unmanaged resources used by the object and optionally releases the managed resources.
/// </summary> /// </summary>
/// <param name="disposing"> /// <param name="disposing">
/// <see langword="true" /> to release both managed and unmanaged resources; /// <see langword="true" /> to release both managed and unmanaged resources;
/// <see langword="false" /> to release only unmanaged resources. /// <see langword="false" /> to release only unmanaged resources.
/// </param> /// </param>
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
@ -56,25 +56,28 @@ namespace Artemis.UI.Avalonia.Shared
} }
/// <summary> /// <summary>
/// Represents the base class for Artemis view models used to drive dialogs /// Represents the base class for Artemis view models used to drive dialogs
/// </summary> /// </summary>
public abstract class DialogViewModelBase<TResult> : ActivatableViewModelBase public abstract class DialogViewModelBase<TResult> : ActivatableViewModelBase
{ {
/// <inheritdoc /> /// <summary>
protected DialogViewModelBase() /// Closes the dialog with the given <paramref name="result" />
/// </summary>
/// <param name="result">The result of the dialog</param>
public void Close(TResult result)
{ {
Close = ReactiveCommand.Create<TResult, TResult>(t => t); CloseRequested?.Invoke(this, new DialogClosedEventArgs<TResult>(result));
Cancel = ReactiveCommand.Create(() => { });
} }
/// <summary> /// <summary>
/// Closes the dialog with a given result /// Closes the dialog without a result
/// </summary> /// </summary>
public ReactiveCommand<TResult, TResult> Close { get; } public void Cancel()
{
CancelRequested?.Invoke(this, EventArgs.Empty);
}
/// <summary> internal event EventHandler<DialogClosedEventArgs<TResult>>? CloseRequested;
/// Closes the dialog without a result internal event EventHandler? CancelRequested;
/// </summary>
public ReactiveCommand<Unit, Unit> Cancel { get; }
} }
} }

View File

@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=events/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@ -118,7 +118,7 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
public void Accept() public void Accept()
{ {
Close.Execute(true); Close(true);
} }
public static async Task<bool> Show(IWindowService windowService, List<IPrerequisitesSubject> subjects) public static async Task<bool> Show(IWindowService windowService, List<IPrerequisitesSubject> subjects)

View File

@ -105,7 +105,7 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
// This shouldn't be happening and the experience isn't very nice for the user (too lazy to make a nice UI for such an edge case) // This shouldn't be happening and the experience isn't very nice for the user (too lazy to make a nice UI for such an edge case)
// but at least give some feedback // but at least give some feedback
Close.Execute(false); Close(false);
await _windowService.CreateContentDialog() await _windowService.CreateContentDialog()
.WithTitle("Plugin prerequisites") .WithTitle("Plugin prerequisites")
.WithContent("The plugin was not able to fully remove all prerequisites. \r\nPlease try again or contact the plugin creator.") .WithContent("The plugin was not able to fully remove all prerequisites. \r\nPlease try again or contact the plugin creator.")
@ -126,7 +126,7 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
public void Accept() public void Accept()
{ {
Close.Execute(true); Close(true);
} }
public static async Task<object> Show(IWindowService windowService, List<IPrerequisitesSubject> subjects, string cancelLabel = "Cancel") public static async Task<object> Show(IWindowService windowService, List<IPrerequisitesSubject> subjects, string cancelLabel = "Cancel")

View File

@ -12,6 +12,7 @@ using Artemis.UI.Avalonia.Exceptions;
using Artemis.UI.Avalonia.Ninject.Factories; using Artemis.UI.Avalonia.Ninject.Factories;
using Artemis.UI.Avalonia.Shared; using Artemis.UI.Avalonia.Shared;
using Artemis.UI.Avalonia.Shared.Services.Interfaces; using Artemis.UI.Avalonia.Shared.Services.Interfaces;
using Avalonia.Threading;
using Ninject; using Ninject;
using ReactiveUI; using ReactiveUI;
@ -53,9 +54,13 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
_pluginManagementService.PluginEnabled += PluginManagementServiceOnPluginToggled; _pluginManagementService.PluginEnabled += PluginManagementServiceOnPluginToggled;
OpenSettings = ReactiveCommand.Create(ExecuteOpenSettings, this.WhenAnyValue(x => x.IsEnabled).Select(isEnabled => isEnabled && Plugin.ConfigurationDialog != null)); OpenSettings = ReactiveCommand.Create(ExecuteOpenSettings, this.WhenAnyValue(x => x.IsEnabled).Select(isEnabled => isEnabled && Plugin.ConfigurationDialog != null));
InstallPrerequisites = ReactiveCommand.CreateFromTask(ExecuteInstallPrerequisites, this.WhenAnyValue(x => x.CanInstallPrerequisites));
RemovePrerequisites = ReactiveCommand.CreateFromTask<bool>(ExecuteRemovePrerequisites, this.WhenAnyValue(x => x.CanRemovePrerequisites));
} }
public ReactiveCommand<Unit, Unit> OpenSettings { get; } public ReactiveCommand<Unit, Unit> OpenSettings { get; }
public ReactiveCommand<Unit, Unit> InstallPrerequisites { get; }
public ReactiveCommand<bool, Unit> RemovePrerequisites { get; }
public ObservableCollection<PluginFeatureViewModel> PluginFeatures { get; } public ObservableCollection<PluginFeatureViewModel> PluginFeatures { get; }
@ -72,7 +77,7 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
} }
public string Type => Plugin.GetType().BaseType?.Name ?? Plugin.GetType().Name; public string Type => Plugin.GetType().BaseType?.Name ?? Plugin.GetType().Name;
public bool IsEnabled public bool IsEnabled
{ {
get => Plugin.IsEnabled; get => Plugin.IsEnabled;
@ -137,7 +142,8 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
{ {
bool wasEnabled = IsEnabled; bool wasEnabled = IsEnabled;
_pluginManagementService.UnloadPlugin(Plugin); await Task.Run(() => _pluginManagementService.UnloadPlugin(Plugin));
PluginFeatures.Clear(); PluginFeatures.Clear();
Plugin = _pluginManagementService.LoadPlugin(Plugin.Directory); Plugin = _pluginManagementService.LoadPlugin(Plugin.Directory);
@ -150,7 +156,7 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
_notificationService.CreateNotification().WithTitle("Reloaded plugin.").Show(); _notificationService.CreateNotification().WithTitle("Reloaded plugin.").Show();
} }
public async Task InstallPrerequisites() public async Task ExecuteInstallPrerequisites()
{ {
List<IPrerequisitesSubject> subjects = new() {Plugin.Info}; List<IPrerequisitesSubject> subjects = new() {Plugin.Info};
subjects.AddRange(Plugin.Features.Where(f => f.AlwaysEnabled)); subjects.AddRange(Plugin.Features.Where(f => f.AlwaysEnabled));
@ -159,7 +165,7 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
await PluginPrerequisitesInstallDialogViewModel.Show(_windowService, subjects); await PluginPrerequisitesInstallDialogViewModel.Show(_windowService, subjects);
} }
public async Task RemovePrerequisites(bool forPluginRemoval = false) public async Task ExecuteRemovePrerequisites(bool forPluginRemoval = false)
{ {
List<IPrerequisitesSubject> subjects = new() {Plugin.Info}; List<IPrerequisitesSubject> subjects = new() {Plugin.Info};
subjects.AddRange(!forPluginRemoval ? Plugin.Features.Where(f => f.AlwaysEnabled) : Plugin.Features); subjects.AddRange(!forPluginRemoval ? Plugin.Features.Where(f => f.AlwaysEnabled) : Plugin.Features);
@ -200,7 +206,7 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
List<IPrerequisitesSubject> subjects = new() {Plugin.Info}; List<IPrerequisitesSubject> subjects = new() {Plugin.Info};
subjects.AddRange(Plugin.Features); subjects.AddRange(Plugin.Features);
if (subjects.Any(s => s.Prerequisites.Any(p => p.UninstallActions.Any()))) if (subjects.Any(s => s.Prerequisites.Any(p => p.UninstallActions.Any())))
await RemovePrerequisites(true); await ExecuteRemovePrerequisites(true);
try try
{ {
@ -284,7 +290,7 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
} }
} }
await Task.Run(() => await Task.Run(async () =>
{ {
try try
{ {
@ -292,10 +298,10 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.ViewModels
} }
catch (Exception e) catch (Exception e)
{ {
_notificationService.CreateNotification() await Dispatcher.UIThread.InvokeAsync(() => _notificationService.CreateNotification()
.WithMessage($"Failed to enable plugin {Plugin.Info.Name}\r\n{e.Message}") .WithMessage($"Failed to enable plugin {Plugin.Info.Name}\r\n{e.Message}")
.HavingButton(b => b.WithText("View logs").WithAction(ShowLogsFolder)) .HavingButton(b => b.WithText("View logs").WithAction(ShowLogsFolder))
.Show(); .Show());
} }
finally finally
{ {

View File

@ -27,12 +27,10 @@
Icon="{Binding FeatureInfo.ResolvedIcon}" Icon="{Binding FeatureInfo.ResolvedIcon}"
Width="20" Width="20"
Height="20" Height="20"
IsVisible="{Binding LoadException, Converter={x:Static ObjectConverters.IsNull}}" /> IsVisible="{Binding LoadException, Converter={x:Static ObjectConverters.IsNull}}" />
<Button Grid.Column="0" <Button Grid.Column="0"
Margin="-8" Classes="AppBarButton icon-button"
IsVisible="{Binding LoadException, Converter={x:Static ObjectConverters.IsNotNull}}" IsVisible="{Binding LoadException, Converter={x:Static ObjectConverters.IsNotNull}}"
Foreground="#E74C4C" Foreground="#E74C4C"
ToolTip.Tip="An exception occurred while enabling this feature, click to view" ToolTip.Tip="An exception occurred while enabling this feature, click to view"

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Reactive.Linq;
using Artemis.UI.Avalonia.Screens.Plugins.ViewModels; using Artemis.UI.Avalonia.Screens.Plugins.ViewModels;
using Avalonia; using Avalonia;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
@ -19,22 +20,16 @@ namespace Artemis.UI.Avalonia.Screens.Plugins.Views
this.WhenActivated(disposables => this.WhenActivated(disposables =>
{ {
ViewModel!.ConfigurationViewModel.CloseRequested += ConfigurationViewModelOnCloseRequested; Observable.FromEventPattern(
Disposable.Create(HandleDeactivation).DisposeWith(disposables); x => ViewModel!.ConfigurationViewModel.CloseRequested += x,
x => ViewModel!.ConfigurationViewModel.CloseRequested -= x
)
.Subscribe(_ => Close())
.DisposeWith(disposables);
} }
); );
} }
private void HandleDeactivation()
{
ViewModel!.ConfigurationViewModel.CloseRequested -= ConfigurationViewModelOnCloseRequested;
}
private void ConfigurationViewModelOnCloseRequested(object? sender, EventArgs e)
{
Close();
}
private void InitializeComponent() private void InitializeComponent()
{ {
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);