Use a help page URL instead of doing things in-app

This commit is contained in:
Robert committed 2023-04-14 14:43:33 +02:00
1 parent 949106e470
commit 2b15657f8d
17 files changed
+46 -367

No files matched your search

@@ -23,7 +23,6 @@ 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;
@@ -32,12 +31,10 @@ public class PluginFeatureViewModel : ActivatableViewModelBase
bool showShield,
ICoreService coreService,
IWindowService windowService,
INotificationService notificationService,
IPluginManagementService pluginManagementService)
{
_coreService = coreService;
_windowService = windowService;
_notificationService = notificationService;
_pluginManagementService = pluginManagementService;
FeatureInfo = pluginFeatureInfo;
@@ -176,11 +173,7 @@ public class PluginFeatureViewModel : ActivatableViewModelBase
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();
await ShowFailureDialog($"Failed to enable '{FeatureInfo.Name}'", $"Feature '{FeatureInfo.Name}' is in a broken state and cannot enable.");
return;
}
@@ -215,11 +208,7 @@ public class PluginFeatureViewModel : ActivatableViewModelBase
}
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();
await ShowFailureDialog($"Failed to enable '{FeatureInfo.Name}'", e.Message);
}
finally
{
@@ -254,4 +243,17 @@ public class PluginFeatureViewModel : ActivatableViewModelBase
{
this.RaisePropertyChanged(nameof(CanToggleEnabled));
}
private async Task ShowFailureDialog(string action, string message)
{
ContentDialogBuilder builder = _windowService.CreateContentDialog()
.WithTitle(action)
.WithContent(message)
.HavingPrimaryButton(b => b.WithText("View logs").WithCommand(ShowLogsFolder));
// If available, add a secondary button pointing to the support page
if (FeatureInfo.Plugin.Info.HelpPage != null)
builder = builder.HavingSecondaryButton(b => b.WithText("Open support page").WithAction(() => Utilities.OpenUrl(FeatureInfo.Plugin.Info.HelpPage.ToString())));
await builder.ShowAsync();
}
}
@@ -1,15 +0,0 @@
<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:Markdown.Avalonia;assembly=Markdown.Avalonia"
xmlns:help="clr-namespace:Artemis.UI.Screens.Plugins.Help"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.Plugins.Help.MarkdownPluginHelpPageView"
x:DataType="help:MarkdownPluginHelpPageViewModel">
<avalonia:MarkdownScrollViewer Markdown="{CompiledBinding MarkdownText}" MarkdownStyleName="FluentAvalonia">
<avalonia:MarkdownScrollViewer.Styles>
<StyleInclude Source="/Styles/Markdown.axaml"/>
</avalonia:MarkdownScrollViewer.Styles>
</avalonia:MarkdownScrollViewer>
</UserControl>
@@ -1,18 +0,0 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
namespace Artemis.UI.Screens.Plugins.Help;
public partial class MarkdownPluginHelpPageView : ReactiveUserControl<MarkdownPluginHelpPageViewModel>
{
public MarkdownPluginHelpPageView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
@@ -1,65 +0,0 @@
using System;
using System.IO;
using System.Reactive.Disposables;
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.UI.Shared;
using ReactiveUI;
namespace Artemis.UI.Screens.Plugins.Help;
public class MarkdownPluginHelpPageViewModel : ActivatableViewModelBase, IPluginHelpPage
{
private readonly MarkdownPluginHelpPage _helpPage;
private string? _markdownText;
public MarkdownPluginHelpPageViewModel(MarkdownPluginHelpPage helpPage)
{
_helpPage = helpPage;
this.WhenActivated(d =>
{
FileSystemWatcher watcher = new();
watcher.Path = Path.GetDirectoryName(_helpPage.MarkdownFile) ?? throw new InvalidOperationException($"Path \"{_helpPage.MarkdownFile}\" does not contain a directory");
watcher.Filter = Path.GetFileName(_helpPage.MarkdownFile);
watcher.EnableRaisingEvents = true;
watcher.Changed += WatcherOnChanged;
watcher.DisposeWith(d);
LoadMarkdown().DisposeWith(d);
});
}
public string? MarkdownText
{
get => _markdownText;
set => RaiseAndSetIfChanged(ref _markdownText, value);
}
private async void WatcherOnChanged(object sender, FileSystemEventArgs e)
{
await LoadMarkdown();
}
private async Task LoadMarkdown()
{
try
{
await using FileStream stream = new(_helpPage.MarkdownFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using StreamReader reader = new(stream);
MarkdownText = await reader.ReadToEndAsync();
}
catch (Exception e)
{
MarkdownText = e.Message;
}
}
/// <inheritdoc />
public Plugin Plugin => _helpPage.Plugin;
/// <inheritdoc />
public string Title => _helpPage.Title;
/// <inheritdoc />
public string Id => _helpPage.Id;
}
@@ -1,31 +0,0 @@
<Window 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:plugins="clr-namespace:Artemis.UI.Screens.Plugins"
xmlns:core="clr-namespace:Artemis.Core;assembly=Artemis.Core"
xmlns:help="clr-namespace:Artemis.UI.Screens.Plugins.Help"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Screens.Plugins.Help.PluginHelpWindowView"
x:DataType="help:PluginHelpWindowViewModel"
Icon="/Assets/Images/Logo/application.ico"
Title="{CompiledBinding DisplayName}"
Width="1200"
Height="800"
WindowStartupLocation="CenterOwner">
<Grid ColumnDefinitions="240,*">
<ListBox Items="{CompiledBinding HelpPages}" SelectedItem="{CompiledBinding SelectedHelpPage}" Grid.Column="0" Margin="10">
<ListBox.ItemTemplate>
<DataTemplate x:DataType="core:IPluginHelpPage">
<TextBlock Text="{CompiledBinding Title}" VerticalAlignment="Center" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Border Classes="router-container" Grid.Column="1">
<ContentControl Content="{CompiledBinding SelectedHelpPage}" Margin="15"></ContentControl>
</Border>
<StackPanel Grid.Column="0" Grid.ColumnSpan="2" Classes="notification-container" Name="NotificationContainer" VerticalAlignment="Bottom" HorizontalAlignment="Right" />
</Grid>
</Window>
@@ -1,22 +0,0 @@
using Artemis.UI.Shared;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace Artemis.UI.Screens.Plugins.Help;
public partial class PluginHelpWindowView : ReactiveAppWindow<PluginHelpWindowViewModel>
{
public PluginHelpWindowView()
{
InitializeComponent();
#if DEBUG
this.AttachDevTools();
#endif
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
@@ -1,32 +0,0 @@
using System.Collections.ObjectModel;
using System.Linq;
using Artemis.Core;
using Artemis.UI.Shared;
namespace Artemis.UI.Screens.Plugins.Help;
public class PluginHelpWindowViewModel : ActivatableViewModelBase
{
private IPluginHelpPage? _selectedHelpPage;
public PluginHelpWindowViewModel(Plugin plugin, string? preselectId)
{
Plugin = plugin;
DisplayName = $"{Plugin.Info.Name} | Help";
// Populate help pages by wrapping MarkdownHelpPages into a MarkdownHelpPageViewModel
// other types are used directly, up to them to implement a VM directly as well
HelpPages = new ReadOnlyCollection<IPluginHelpPage>(plugin.HelpPages.Select(p => p is MarkdownPluginHelpPage m ? new MarkdownPluginHelpPageViewModel(m) : p).ToList());
_selectedHelpPage = preselectId != null ? HelpPages.FirstOrDefault(p => p.Id == preselectId) : HelpPages.FirstOrDefault();
}
public Plugin Plugin { get; }
public ReadOnlyCollection<IPluginHelpPage> HelpPages { get; }
public IPluginHelpPage? SelectedHelpPage
{
get => _selectedHelpPage;
set => RaiseAndSetIfChanged(ref _selectedHelpPage, value);
}
}
@@ -92,9 +92,9 @@
<avalonia:MaterialIcon Kind="Cog" />
</controls:HyperlinkButton>
<controls:HyperlinkButton Classes="icon-button icon-button-large"
IsVisible="{CompiledBinding HasHelpPages}"
Command="{CompiledBinding OpenHelp}"
ToolTip.Tip="View help pages">
IsVisible="{CompiledBinding Plugin.Info.HelpPage, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
NavigateUri="{CompiledBinding Plugin.Info.HelpPage}"
ToolTip.Tip="{CompiledBinding Plugin.Info.HelpPage}">
<avalonia:MaterialIcon Kind="HelpCircle" />
</controls:HyperlinkButton>
<controls:HyperlinkButton Classes="icon-button icon-button-large"
@@ -4,12 +4,10 @@ using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Exceptions;
using Artemis.UI.Screens.Plugins.Help;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Builders;
@@ -31,7 +29,6 @@ public class PluginViewModel : ActivatableViewModelBase
private bool _enabling;
private Plugin _plugin;
private Window? _settingsWindow;
private Window? _helpWindow;
public PluginViewModel(Plugin plugin,
ReactiveCommand<Unit, Unit>? reload,
@@ -59,7 +56,6 @@ public class PluginViewModel : ActivatableViewModelBase
Reload = reload;
OpenSettings = ReactiveCommand.Create(ExecuteOpenSettings, this.WhenAnyValue(vm => vm.IsEnabled, e => e && Plugin.ConfigurationDialog != null));
OpenHelp = ReactiveCommand.Create(ExecuteOpenHelp, this.WhenAnyValue(vm => vm.HasHelpPages));
RemoveSettings = ReactiveCommand.CreateFromTask(ExecuteRemoveSettings);
Remove = ReactiveCommand.CreateFromTask(ExecuteRemove);
InstallPrerequisites = ReactiveCommand.CreateFromTask(ExecuteInstallPrerequisites, this.WhenAnyValue(x => x.CanInstallPrerequisites));
@@ -77,14 +73,12 @@ public class PluginViewModel : ActivatableViewModelBase
Plugin.Enabled -= OnPluginToggled;
Plugin.Disabled -= OnPluginToggled;
_settingsWindow?.Close();
_helpWindow?.Close();
}).DisposeWith(d);
});
}
public ReactiveCommand<Unit, Unit>? Reload { get; }
public ReactiveCommand<Unit, Unit> OpenSettings { get; }
public ReactiveCommand<Unit,Unit> OpenHelp { get; }
public ReactiveCommand<Unit, Unit> RemoveSettings { get; }
public ReactiveCommand<Unit, Unit> Remove { get; }
public ReactiveCommand<Unit, Unit> InstallPrerequisites { get; }
@@ -108,7 +102,6 @@ public class PluginViewModel : ActivatableViewModelBase
public string Type => Plugin.GetType().BaseType?.Name ?? Plugin.GetType().Name;
public bool IsEnabled => Plugin.IsEnabled;
public bool HasHelpPages => Plugin.HelpPages.Any();
public bool CanInstallPrerequisites
{
@@ -135,11 +128,7 @@ public class PluginViewModel : ActivatableViewModelBase
}
catch (Exception e)
{
await Dispatcher.UIThread.InvokeAsync(() => _notificationService.CreateNotification()
.WithSeverity(NotificationSeverity.Error)
.WithMessage($"Failed to disable plugin {Plugin.Info.Name}\r\n{e.Message}")
.HavingButton(b => b.WithText("View logs").WithCommand(ShowLogsFolder))
.Show());
await ShowUpdateEnableFailure(enable, e);
}
finally
{
@@ -174,11 +163,7 @@ public class PluginViewModel : ActivatableViewModelBase
}
catch (Exception e)
{
await Dispatcher.UIThread.InvokeAsync(() => _notificationService.CreateNotification()
.WithSeverity(NotificationSeverity.Error)
.WithMessage($"Failed to enable plugin {Plugin.Info.Name}\r\n{e.Message}")
.HavingButton(b => b.WithText("View logs").WithCommand(ShowLogsFolder))
.Show());
await ShowUpdateEnableFailure(enable, e);
}
finally
{
@@ -187,7 +172,6 @@ public class PluginViewModel : ActivatableViewModelBase
}
}
public void CheckPrerequisites()
{
CanInstallPrerequisites = false;
@@ -228,27 +212,6 @@ public class PluginViewModel : ActivatableViewModelBase
}
}
private void ExecuteOpenHelp()
{
if (_helpWindow != null)
{
_helpWindow.WindowState = WindowState.Normal;
_helpWindow.Activate();
return;
}
try
{
_helpWindow = _windowService.ShowWindow(new PluginHelpWindowViewModel(Plugin, null));
_helpWindow.Closed += (_, _) => _helpWindow = null;
}
catch (Exception e)
{
_windowService.ShowExceptionDialog("An exception occured while trying to show the plugin's help window", e);
throw;
}
}
private void ExecuteOpenPluginDirectory()
{
try
@@ -334,6 +297,20 @@ public class PluginViewModel : ActivatableViewModelBase
_windowService.ShowExceptionDialog("Welp, we couldn\'t open the logs folder for you", e);
}
}
private async Task ShowUpdateEnableFailure(bool enable, Exception e)
{
string action = enable ? "enable" : "disable";
ContentDialogBuilder builder = _windowService.CreateContentDialog()
.WithTitle($"Failed to {action} plugin {Plugin.Info.Name}")
.WithContent(e.Message)
.HavingPrimaryButton(b => b.WithText("View logs").WithCommand(ShowLogsFolder));
// If available, add a secondary button pointing to the support page
if (Plugin.Info.HelpPage != null)
builder = builder.HavingSecondaryButton(b => b.WithText("Open support page").WithAction(() => Utilities.OpenUrl(Plugin.Info.HelpPage.ToString())));
await builder.ShowAsync();
}
private void OnPluginToggled(object? sender, EventArgs e)
{